Skip to content

Commit

Permalink
new version: Fragments, nested arrays and directives (#36)
Browse files Browse the repository at this point in the history
* refactor & add fragment support

* rename children to content

* update docs & examples

* add fragment diffs

* directives + simple scheduler

* remove unwanted files

* add import

* updated docs and examples

* Add jsx runtime
  • Loading branch information
yelouafi authored Jan 25, 2021
1 parent 86d2751 commit ce3b654
Show file tree
Hide file tree
Showing 35 changed files with 1,588 additions and 3,075 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"es6": true
},
"parserOptions": {
"ecmaVersion": 10,
"ecmaVersion": 2020,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@ dist
_book

# parcel cache files
.cache
.cache

#VS code
.vscode
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.babelrc*
.eslintrc*
.bookignore
.vscode
book.json
test
examples
Expand Down
108 changes: 75 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@

A minimalist virtual DOM library.

## Install
- Supports HTML & SVG elements.
- Supports Render functions and Fragments.
- Custom components allows to build your own abstraction around DOM elements.
- Directives allows you to attach custom behavior to DOM elements.

## Installation

The library is provided as a set of ES modules. You can install using `npm` or `yarn` and then import
from `petit-dom` (see example below).

```sh
$ npm install --save petit-dom
Expand All @@ -14,13 +22,27 @@ or
$ yarn add petit-dom
```

> Note however no transpiled build is provided. The library will work with all recent versions of `Node` and major browsers. If you're targeting older platforms, make sure to transpile to the desired ES version.
To run the examples, you can run a local web server (like npm `http-server` module) from the root folder of the project. Since all example use ES6 modules, you can simply navigate to the example you want and load the desired HTML file.

## Usage

If you're using Babel you can use JSX syntax by putting a `/* @jsx h */` at the top of the source file.
If you're using Babel you can use JSX syntax by configuring the jsx runtime

```json
{
"presets": [
[
"@babel/preset-react",
{ "runtime": "automatic", "importSource": "petit-dom" }
]
]
}
```

```js
/* @jsx h */
import { h, render } from "petit-dom";
import { render } from "petit-dom";

// assuming your HTML contains a node with "root" id
const parentNode = document.getElementById("root");
Expand All @@ -32,13 +54,12 @@ render(<h1>Hello world!</h1>, parentNode);
render(<h1>Hello again</h1>, parentNode);
```

You can also use raw `h` function calls if you want, see examples folder for usage.
Alternatively you can use the classic Babel transform via `/* @jsx h */` on the top. You can also use the raw `h` function calls if you want, see examples folder for usage.

petit-dom also supports render functions

```js
/* @jsx h */
import { h, render } from "petit-dom";
import { render } from "petit-dom";

function Box(props) {
return (
Expand All @@ -59,46 +80,62 @@ props are tested for shallow equality).

## Custom components

Besides HTML/SVG tag names and render fucntions, the 'h' function also accepts any object
Besides HTML/SVG tag names, fragments and render fucntions, the `h` function also accepts any object
with the following signature

```js
{
mount(props, stateRef, env) => DomNode
patch(newProps, oldProps, stateRef, domNode, env) => DomNode
unmount(stateRef, domNode, env)
mount(props, state, env) => Ref
patch(parentDomNode, newProps, oldProps, ref, state, env) => Ref
unmount(props, ref, state, env)
}
```

Each of the 3 functions (they are not methods, i.e. no `this`) will be called
by the library at the moment suggested by its name:

- `mount` is called when the library needs to create a new DOM Node to be inserted at some
palce into the DOM tree.
- `mount` is called when the library needs to create new DOM content (see blow for an
explanation of what is a `Ref`) to be inserted at some palce into the DOM tree.

- `patch` is called when the library needs to update the previously created DOM with
new props.
- `patch` is called when the library needs to update the previously created DOM content
with new props.

- `unmount` is called after the DOM node has been removed from DOM tree.
- `unmount` is called after the DOM content has been removed from DOM tree.

`props`, `newProps` and `oldProps` all refer to the properties provided to the `h` function
(or via JSX). The children are stored in the `children` property of the props object.
(or via JSX). The child or children are stored in the `content` property of the props object.

`stateRef` is an object provided to persist any needed data between different invocations. As
`state` is an object provided to persist any needed data between different invocations. As
mentioned, the 3 functions above are not to be treated as instance methods (no `this`) but as
ordinary functions. Any instance specific data must be stored in the `stateRef` object.
ordinary functions. Any instance specific data must be stored in the `state` object.

`domNode` is obviously the DOM node to be mounted or patched.
`Ref` is an opaque data structure used by the library to represent DOM content. You can construct
`Ref` objects by recursively calling `mount` or `patch`.

`env` is used internally by the mount/patch process; **This argument must be forwarded to all
nested `mount`, `patch` and `unmount` calls (see below example)**.
nested `mount`, `patch` and `unmount`**.

Custom components are pretty raw, but they are also flexible and allow
implementing higher-level solution (for example, render functions are implemented
on top of them).
implementing higher-level solution.

The examples folder contains a simple illustration on how create a custom component.

## Directives

The examples folder contains simple (and partial) implementations of React like
components and hooks using the custom component API.
You can also attach custom behaviors to DOM nodes. Directives allows you to obtain references
to DOM nodes and manage their lifecycle.

A directive is an object with the current interface

```js
{
mount(domElement, value);
patch(domElement, newValue, oldValue);
unmount(element, lastValue);
}
```

There's an example of a simple log directive in the examples folder.

## API

Expand All @@ -109,8 +146,8 @@ Creates a virtual node.
- `type`: a string (HTML or SVG tag name), or a custom component (see above)

- `props`: in the case of HTML/SVG tags, this corresponds to the attributes/properties
to be set in the real DOM node. In the case of components, `{ ...props, children }` is
passed to the appropriate component function (`mount` or `patch`).
to be set in the real DOM node. In the case of components, `{ ...props, children }` is
passed to the appropriate component function (`mount` or `patch`).

### `render(vnode, parentDom)`

Expand All @@ -119,19 +156,24 @@ as specified the virtual node `vnode`. Subsequent calls will update the previous
DOM node (or replace it if it's a different tag).

There are also lower level methods that are typically used when implementing
custom components:
custom components.

### `mount(vnode, env)`
### `mount(vnode, env) => Ref`

Creates a real DOM node as specified by `vnode`. The `env` argument is optional (e.g. when
Creates a DOM content as specified by `vnode`. The `env` argument is optional (e.g. when
called from top level), but typically you'll have to forward something passed from
upstream (e.g. when called inside a custom component).

### `patch(newVNode, oldVNode, domNode, env)`
### `patch(parentDOM, newVNode, oldVNode, ref, env) => Ref`

Updates (or eventually replaces) `domNode` based on the difference between `newVNode` and `oldVNode`.
Updates (or eventually replaces) the actual DOM content `ref` based on the difference
between `newVNode` and `oldVNode`. Returns either the `ref` updated or a newly created `Ref`.

### `unmount(vnode, domNode, env)`
### `unmount(vnode, ref, env)`

This is called after `domNode` has been retired from the DOM tree. This is typically
needed by custom components to implement cleanup logic.

### `getParentNode(ref) => Ref`

Retreive the parent DOM of a given DOM content.
82 changes: 0 additions & 82 deletions examples/browser/component/Component.js

This file was deleted.

52 changes: 0 additions & 52 deletions examples/browser/component/index.html

This file was deleted.

38 changes: 0 additions & 38 deletions examples/browser/functional.html

This file was deleted.

9 changes: 0 additions & 9 deletions examples/browser/hello.html

This file was deleted.

Loading

0 comments on commit ce3b654

Please sign in to comment.