-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
142 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
# HTML-UI | ||
|
||
This is a simple-as-it-gets library for using a virtual DOM to separate the handling of state and view. | ||
|
||
It uses simple JavaScript, has no dependencies, requires no transpilation and has 300 lines. | ||
|
||
### Example | ||
|
||
```html | ||
<html> | ||
<body> | ||
<div id="container"></div> | ||
<script src="./ui.js"></script> | ||
<script> | ||
const { init, h, text } = UI; | ||
const root = document.querySelector("#container"); | ||
const initialState = 0; | ||
const update = (state, msg, enqueue) => state + msg; | ||
const view = (state) => [ | ||
h("div", { style: "color: red", onClick: () => 2 }, [ | ||
h("p", {}, [ | ||
text(`The count is ${state}. Click here to increment.`) | ||
]) | ||
]) | ||
]; | ||
const { enqueue } = init(root, initialState, update, view); | ||
enqueue(1); | ||
</script> | ||
</body> | ||
</html> | ||
``` | ||
|
||
## Quickstart | ||
|
||
After importing `ui.js` start your application by calling `UI.init` with the appropriate arguments. | ||
|
||
You can use the library as follows: | ||
|
||
```javascript | ||
// `UI` is the only identifier the library adds to the global | ||
// scope. It is an object with a function to start the | ||
// application and two functions to build html elements | ||
const { init, h, text } = UI; | ||
|
||
// Where the application will be rendered | ||
const root = document.querySelector("#container"); | ||
|
||
// An initial state for the application. | ||
const initialState = { ... }; | ||
|
||
// How to update that state | ||
function update(state, msg, enqueue) { ... } | ||
|
||
// A function to build the view using the `h` and `text` | ||
// functions from in the UI object. | ||
function view(state) { ... } | ||
|
||
// Start the application. `init` returns an object with the | ||
// enqueue function, which is is used to scheduled messages | ||
// to be used to update the state. | ||
const { enqueue } = init(root, initialState, update, view); | ||
``` | ||
|
||
### Creating HTML | ||
|
||
This library creates elements in the page based on a description of how the page should look like. | ||
|
||
The user-defined `view` function should return an this description in the form of an | ||
array of element descriptions. | ||
|
||
There are two functions to create HTML element descriptions. | ||
|
||
* `text(str: string) : Element` - Describe a text node. | ||
* `h(tag: string, properties: Object, children: Array<Element>) : Element` - Describe an HTML element. | ||
|
||
The `properties` object should contain HTML properties and attributes as keys and either | ||
a string or a function as the value. Functions are used for event listeners. | ||
|
||
Keys that start with the `on` prefix in the `properties` attributes are treated as event listeners | ||
and its values should be functions. | ||
|
||
If an event listener returns a value different from `undefined` this value will | ||
be used as a message and queued to be sent to the update function. | ||
|
||
Examples: | ||
* `h("div", { class: "container" }, [ ... ])` - A `div` with a class. | ||
* `h("button", { disabled: "true" }, [ ... ])` - A disabled button. | ||
* `h("button", { onClick: () => "toggle" }, [ ... ])` - | ||
A button that will emit the string `"toggle"` as a message when clicked. | ||
* `h("button", { onClick: e => e }, [ ... ])` - A button that will emit a message containing the HTML event handled by the onClick listener. | ||
|
||
### Emitting messages | ||
|
||
The state is updated by handling messages in the user-defined `update` function. | ||
|
||
There are two ways to emit messages. | ||
|
||
*Returning from an event listener* | ||
|
||
Any value that is not `undefined` returned by an event listener will be emitted as a message. | ||
|
||
```javascript | ||
// A button which when clicked will emit as a message | ||
// the object { tag: "clicked, event: <event> } | ||
// where <event> is the HTML event being handled. | ||
h( | ||
"button", | ||
{ onClick : e => ({ tag: "clicked", event: e }) }, | ||
[ text("click") ] | ||
) | ||
``` | ||
|
||
*Using the `enqueue` function* | ||
|
||
This function is in the object returned by `init` and will schedule a message to be handled. | ||
|
||
``` javascript | ||
const { enqueue } = init(root, initialState, update, view); | ||
|
||
// Emit a message. | ||
enqueue({ tag: "setCounter", value: 2 }); | ||
``` | ||
|
||
It is safe to call `enqueue` inside the `update` function. | ||
|
||
Messages are put in a queue and the `update` function is called for each of them in order. | ||
|
||
### Updating the state | ||
|
||
The user-defined `update` function is the part of the program where the state | ||
should be changed. It must return the new state. | ||
|
||
It takes three arguments: | ||
|
||
* The current state. | ||
* The message to be handled. | ||
* The `enqueue` function. | ||
|
||
It is safe to call `enqueue` from inside `update`. |