mise 2.0.1 → 2026.5.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +213 -81
- package/installArchSpecificPackage.js +57 -0
- package/package.json +13 -45
- package/.babelrc +0 -9
- package/.eslintrc.js +0 -30
- package/.travis.yml +0 -11
- package/LICENSE +0 -21
- package/dist/mise.js +0 -1
- package/docs/actions.md +0 -96
- package/docs/components.md +0 -40
- package/docs/lifecycle.md +0 -71
- package/docs/state.md +0 -71
- package/docs/templates.md +0 -97
- package/example/counter/counter.jsx +0 -18
- package/example/todos/todo/todo.jsx +0 -16
- package/example/todos/todo/todo.style.js +0 -6
- package/example/todos/todos.actions.js +0 -36
- package/example/todos/todos.component.js +0 -11
- package/example/todos/todos.state.js +0 -7
- package/example/todos/todos.template.jsx +0 -35
- package/src/component.js +0 -67
- package/src/dom.js +0 -27
- package/src/index.d.ts +0 -20
- package/src/index.js +0 -2
- package/src/utils.js +0 -4
- package/src/vdom.js +0 -159
- package/test/e2e/counter.test.js +0 -78
- package/test/e2e/todos.test.js +0 -211
- package/test/ts/index.test.ts +0 -12
- package/test/unit/component.test.js +0 -55
- package/test/unit/dom.test.js +0 -111
- package/test/unit/utils.test.js +0 -26
- package/test/unit/vdom/vdom.createlement.test.js +0 -42
- package/test/unit/vdom/vdom.update.test.js +0 -151
- package/tsconfig.json +0 -15
- package/webpack.config.js +0 -39
package/docs/lifecycle.md
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# Lifecycle
|
|
2
|
-
|
|
3
|
-
Mise uses a handful of lifecycle methods to allow you fine-grain control of your elements.
|
|
4
|
-
|
|
5
|
-
### `oncreate`
|
|
6
|
-
|
|
7
|
-
The `oncreate` function returns the newly created element.
|
|
8
|
-
|
|
9
|
-
```javascript
|
|
10
|
-
component({
|
|
11
|
-
template: state => actions => (
|
|
12
|
-
<div>
|
|
13
|
-
<span
|
|
14
|
-
oncreate={el => actions.hydrate(el)}
|
|
15
|
-
</div>
|
|
16
|
-
),
|
|
17
|
-
state: {
|
|
18
|
-
// ..
|
|
19
|
-
},
|
|
20
|
-
actions: {
|
|
21
|
-
hydrate: (state, actions, el) => {
|
|
22
|
-
// ...
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### `onremove`
|
|
29
|
-
|
|
30
|
-
`onremove` returns a curried function, one containing the actual element and the other containing a `done` function. Once you've finished your business with `onremove`, call `done` to actually remove it from the dom;
|
|
31
|
-
|
|
32
|
-
```javascript
|
|
33
|
-
import { removalAnimation } from './animations';
|
|
34
|
-
|
|
35
|
-
component({
|
|
36
|
-
template: state => actions => (
|
|
37
|
-
<div>
|
|
38
|
-
<span
|
|
39
|
-
onremove={
|
|
40
|
-
el => done =>
|
|
41
|
-
el.animate([
|
|
42
|
-
removalAnimation
|
|
43
|
-
], 2000);
|
|
44
|
-
|
|
45
|
-
setTimeout(done, 2000)
|
|
46
|
-
}
|
|
47
|
-
</div>
|
|
48
|
-
),
|
|
49
|
-
});
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### `onupdate`
|
|
53
|
-
|
|
54
|
-
`onupdate` is tricky. Mise calls `onupdate` anytime your element is either set to be replaced or if the props are going to be diffed. Since we can't know if any props have changed until we go through all the props, we'd rather let you know this is coming. To faciliate any change, we'll also pass you the old props so you can determine if you want to run your update.
|
|
55
|
-
|
|
56
|
-
```javascript
|
|
57
|
-
component({
|
|
58
|
-
template: state => actions => (
|
|
59
|
-
<div>
|
|
60
|
-
<span
|
|
61
|
-
onupdate={
|
|
62
|
-
el => oldProps => {
|
|
63
|
-
if (oldProps.TTL < state.TTL) {
|
|
64
|
-
actions.updateTTL(state.TTL);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
</div>
|
|
69
|
-
),
|
|
70
|
-
});
|
|
71
|
-
```
|
package/docs/state.md
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# State
|
|
2
|
-
|
|
3
|
-
State has proven to be a pain-point in more traditional frameworks, so we've worked on making state front-and-center in Mise.
|
|
4
|
-
|
|
5
|
-
Every component takes an initial state as one of the required parameters
|
|
6
|
-
|
|
7
|
-
```javascript
|
|
8
|
-
import { dom, component } from 'mise';
|
|
9
|
-
|
|
10
|
-
component({
|
|
11
|
-
template:
|
|
12
|
-
//...
|
|
13
|
-
actions:
|
|
14
|
-
//...
|
|
15
|
-
state: {
|
|
16
|
-
greeting: 'Hello World!',
|
|
17
|
-
},
|
|
18
|
-
})
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
State is always an object and accessibly in both templates and actions as a parameter.
|
|
22
|
-
|
|
23
|
-
#### State is Read-Only.
|
|
24
|
-
|
|
25
|
-
You can't ever directly modify the state.
|
|
26
|
-
|
|
27
|
-
```javascript
|
|
28
|
-
import { dom, component } from 'mise';
|
|
29
|
-
component({
|
|
30
|
-
template:
|
|
31
|
-
//...
|
|
32
|
-
state: {
|
|
33
|
-
id: 0
|
|
34
|
-
},
|
|
35
|
-
actions: {
|
|
36
|
-
// wont actually work!
|
|
37
|
-
increment: state => { state.id++ },
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
#### State can _only_ be modified by actions
|
|
43
|
-
|
|
44
|
-
If you want to modify the state of your application, you can do so via an action. Actions must return a new object which is merged with the current state.
|
|
45
|
-
|
|
46
|
-
```javascript
|
|
47
|
-
import { dom, component } from 'mise';
|
|
48
|
-
component({
|
|
49
|
-
template:
|
|
50
|
-
//...
|
|
51
|
-
state: {
|
|
52
|
-
id: 0
|
|
53
|
-
},
|
|
54
|
-
actions: {
|
|
55
|
-
// works!
|
|
56
|
-
increment: state => ({ state.id + 1 }),
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
In this example, after using the increment action, the new state is `1`. After setting a new state, the application re-renders and generates a new template.
|
|
62
|
-
|
|
63
|
-
#### Testing
|
|
64
|
-
Since state is directly modified by actions, testing an action and having it return a new state is a sufficiently good test.
|
|
65
|
-
|
|
66
|
-
```javascript
|
|
67
|
-
describe('increment action', () => {
|
|
68
|
-
it('should increment the state by one', () => {
|
|
69
|
-
expect(increment({ id: 0 })).toEqual({ id: 1 });
|
|
70
|
-
})
|
|
71
|
-
})
|
package/docs/templates.md
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# Templates
|
|
2
|
-
|
|
3
|
-
Mise templates are simple affairs. Primary templates are curried functions that accept state and actions as parameters and return a JSX representation of what your UI looks like. Generally, you only have one primary template.
|
|
4
|
-
|
|
5
|
-
```javascript
|
|
6
|
-
const template = state => actions => (
|
|
7
|
-
<h1>{state.greeting}</h1>
|
|
8
|
-
)
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
Secondary templates (i.e. templates that aren't at the root level) are functions that accept props and children as their parameters.
|
|
12
|
-
|
|
13
|
-
```javascript
|
|
14
|
-
const secondary = (props, children) => {
|
|
15
|
-
<li className={props.className}>
|
|
16
|
-
<div>{children}</div>
|
|
17
|
-
</li>
|
|
18
|
-
}
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
#### All templates in Mise are presentational templates.
|
|
22
|
-
Templates don't contain actual logic in them. They instead contain references to functions that contain the actual logic. The closest a template will get to logic is to pass in parameters that a function will need.
|
|
23
|
-
|
|
24
|
-
```javascript
|
|
25
|
-
const template => state => actions => (
|
|
26
|
-
<div id="click-counter">
|
|
27
|
-
<span>{state.clicked}</span>
|
|
28
|
-
<button onclick={actions.increment}>Click me!</span>
|
|
29
|
-
<button onclick={e => actions.reset(e)}>Reset</span>
|
|
30
|
-
</div>
|
|
31
|
-
)
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
#### Templates in Mise are composable
|
|
35
|
-
|
|
36
|
-
```javascript
|
|
37
|
-
const template = state => actions => (
|
|
38
|
-
<div id="todos">
|
|
39
|
-
<h1>Todos!</h1>
|
|
40
|
-
<ul>
|
|
41
|
-
{state.todos.map(todo =>
|
|
42
|
-
<TodoItem
|
|
43
|
-
id={todo.id}
|
|
44
|
-
text={todo.text}
|
|
45
|
-
complete={actions.complete}
|
|
46
|
-
remove={actions.remove} />
|
|
47
|
-
)}
|
|
48
|
-
</ul>
|
|
49
|
-
</div>
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
const TodoItem = ({
|
|
53
|
-
id,
|
|
54
|
-
text,
|
|
55
|
-
complete,
|
|
56
|
-
remove
|
|
57
|
-
}) => (
|
|
58
|
-
<li>
|
|
59
|
-
<span onclick={() => complete(id)}>
|
|
60
|
-
{todo.text}
|
|
61
|
-
</span>
|
|
62
|
-
<button onclick={() => remove(id)}>
|
|
63
|
-
x
|
|
64
|
-
</button>
|
|
65
|
-
</li>
|
|
66
|
-
)
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
By composing templates, we can keep items compartmentalized and easy to test. This also reduces the cognative load of onboarding a new developer - it's easy to see the hierarchy.
|
|
70
|
-
|
|
71
|
-
#### Testing
|
|
72
|
-
|
|
73
|
-
Since Mise components are all pure functions, testing them is simple. We use Jest and strongly suggest that you use it to test Mise.
|
|
74
|
-
|
|
75
|
-
```javascript
|
|
76
|
-
import { createElement } from 'mise/src/vdom';
|
|
77
|
-
|
|
78
|
-
import { template } from './src/todo.template';
|
|
79
|
-
import { actions } from './src/todo.actions';
|
|
80
|
-
import { state } from './src/todo.state';
|
|
81
|
-
|
|
82
|
-
describe('template test', () => {
|
|
83
|
-
const { body } = document;
|
|
84
|
-
|
|
85
|
-
beforeEach(() => {
|
|
86
|
-
body.innerHTML = '';
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('should render a todo ul', () => {
|
|
90
|
-
/* this is basically what mise does, too! */
|
|
91
|
-
const result = template(state)(actions);
|
|
92
|
-
body.appendChild(createElement(result));
|
|
93
|
-
|
|
94
|
-
expect(body.querySelector('ul')).not.toBeNull();
|
|
95
|
-
})
|
|
96
|
-
})
|
|
97
|
-
```
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { dom, component } from 'mise';
|
|
2
|
-
|
|
3
|
-
component({
|
|
4
|
-
template: state => actions => (
|
|
5
|
-
<div>
|
|
6
|
-
<span>{state.counter}</span>
|
|
7
|
-
<button onclick={actions.increment}>+</button>
|
|
8
|
-
<button onclick={actions.decrement}>-</button>
|
|
9
|
-
</div>
|
|
10
|
-
),
|
|
11
|
-
state: {
|
|
12
|
-
counter: 0,
|
|
13
|
-
},
|
|
14
|
-
actions: {
|
|
15
|
-
increment: state => ({ counter: state.counter + 1 }),
|
|
16
|
-
decrement: state => ({ counter: state.counter - 1 }),
|
|
17
|
-
}
|
|
18
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { spanStyle } from './todo.style';
|
|
2
|
-
|
|
3
|
-
const TodoItem = ({ id, text, done, toggle, remove }) =>
|
|
4
|
-
<li>
|
|
5
|
-
<div
|
|
6
|
-
onclick={() => toggle({id})}
|
|
7
|
-
style={done ? {textDecoration: 'line-through' } : {}}>
|
|
8
|
-
{text}
|
|
9
|
-
</div>
|
|
10
|
-
<span
|
|
11
|
-
onclick={() => remove({ id })}
|
|
12
|
-
style={spanStyle}>
|
|
13
|
-
x</span>
|
|
14
|
-
</li>;
|
|
15
|
-
|
|
16
|
-
export { TodoItem };
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
const add = (state, actions) => {
|
|
2
|
-
const text = state.input;
|
|
3
|
-
actions.clearInput();
|
|
4
|
-
return {
|
|
5
|
-
todos: [
|
|
6
|
-
...state.todos,
|
|
7
|
-
{
|
|
8
|
-
text,
|
|
9
|
-
id: state.id,
|
|
10
|
-
done: false
|
|
11
|
-
}
|
|
12
|
-
],
|
|
13
|
-
id: state.id + 1,
|
|
14
|
-
};
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const input = (state, actions, { value }) => ({ input: value });
|
|
18
|
-
|
|
19
|
-
const toggle = (state, actions, { id }) => ({
|
|
20
|
-
todos: state.todos.map(todo => todo.id === id ? Object.assign({}, todo, { done: !todo.done }) : todo),
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const remove = (state, actions, { id }) => ({ todos: state.todos.filter(todo => todo.id !== id )});
|
|
24
|
-
|
|
25
|
-
const clearInput = () => ({ input: '' });
|
|
26
|
-
|
|
27
|
-
const clearTodos = () => ({ todos : [] });
|
|
28
|
-
|
|
29
|
-
export {
|
|
30
|
-
add,
|
|
31
|
-
input,
|
|
32
|
-
toggle,
|
|
33
|
-
remove,
|
|
34
|
-
clearInput,
|
|
35
|
-
clearTodos,
|
|
36
|
-
};
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { TodoItem } from './todo/todo.template';
|
|
2
|
-
|
|
3
|
-
const template = state => actions => (
|
|
4
|
-
<div>
|
|
5
|
-
<h1>Todos</h1>
|
|
6
|
-
<ul>
|
|
7
|
-
{state.todos.map(
|
|
8
|
-
todo =>
|
|
9
|
-
<TodoItem
|
|
10
|
-
text={todo.text}
|
|
11
|
-
id={todo.id}
|
|
12
|
-
done={todo.done}
|
|
13
|
-
toggle={actions.toggle}
|
|
14
|
-
remove={actions.remove}/>
|
|
15
|
-
)}
|
|
16
|
-
</ul>
|
|
17
|
-
|
|
18
|
-
<input
|
|
19
|
-
type="text"
|
|
20
|
-
value={state.input}
|
|
21
|
-
oninput= {
|
|
22
|
-
e => actions.input({ value: e.target.value })
|
|
23
|
-
}/>
|
|
24
|
-
<button
|
|
25
|
-
onclick={actions.add}>
|
|
26
|
-
Add Todo
|
|
27
|
-
</button>
|
|
28
|
-
<button
|
|
29
|
-
onclick={actions.clearTodos}>
|
|
30
|
-
Clear All Todos
|
|
31
|
-
</button>
|
|
32
|
-
</div>
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
export { template };
|
package/src/component.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { vdom } from './vdom';
|
|
2
|
-
|
|
3
|
-
const component = ({
|
|
4
|
-
template, state, actions, root = document.body,
|
|
5
|
-
}) => {
|
|
6
|
-
const VDOM = vdom();
|
|
7
|
-
let appState;
|
|
8
|
-
let appTemplate;
|
|
9
|
-
let appActions;
|
|
10
|
-
|
|
11
|
-
const generateTemplate = unsafeState => (unsafeActions) => {
|
|
12
|
-
const readOnlyState = { ...unsafeState };
|
|
13
|
-
const readOnlyActions = { ...unsafeActions };
|
|
14
|
-
|
|
15
|
-
return template(readOnlyState)(readOnlyActions);
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const render = () => {
|
|
19
|
-
const oldTemplate = appTemplate;
|
|
20
|
-
appTemplate = generateTemplate(appState)(appActions);
|
|
21
|
-
|
|
22
|
-
VDOM.update(root, root.childNodes[0], oldTemplate, appTemplate);
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const requestRender = () => {
|
|
26
|
-
requestAnimationFrame(render);
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const update = (partialState) => {
|
|
30
|
-
appState = {
|
|
31
|
-
...appState,
|
|
32
|
-
...partialState,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
requestRender();
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const bindUpdateToActions = (unboundActions) => {
|
|
39
|
-
const tempActions = {};
|
|
40
|
-
|
|
41
|
-
for (const [key, fn] of Object.entries(unboundActions)) {
|
|
42
|
-
tempActions[key] = (data) => {
|
|
43
|
-
const result = fn(appState, appActions, data);
|
|
44
|
-
|
|
45
|
-
if (typeof result === 'function') {
|
|
46
|
-
result(update);
|
|
47
|
-
} else {
|
|
48
|
-
update(result);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return tempActions;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const init = (initialActions) => {
|
|
57
|
-
appState = state;
|
|
58
|
-
appActions = bindUpdateToActions(initialActions);
|
|
59
|
-
appTemplate = generateTemplate(appState)(appActions);
|
|
60
|
-
|
|
61
|
-
root.appendChild(VDOM.createElement(appTemplate));
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
init(actions);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export { component };
|
package/src/dom.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
const dom = (type, uncheckedProps = {}, ...uncheckedChildren) => {
|
|
2
|
-
let children = [];
|
|
3
|
-
let props = uncheckedProps;
|
|
4
|
-
|
|
5
|
-
for (const child of uncheckedChildren) {
|
|
6
|
-
if (Array.isArray(child)) {
|
|
7
|
-
children = [...children, ...child];
|
|
8
|
-
} else {
|
|
9
|
-
children = [...children, typeof child === 'number' ? String(child) : child];
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
if (uncheckedProps === null) { props = {}; }
|
|
14
|
-
|
|
15
|
-
if (props.className) {
|
|
16
|
-
props.class = props.className;
|
|
17
|
-
props.className = undefined;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (typeof type === 'function') {
|
|
21
|
-
return type(props, children);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return { type, props, children };
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export { dom };
|
package/src/index.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export as namespace mise;
|
|
2
|
-
export default Mise;
|
|
3
|
-
|
|
4
|
-
declare namespace Mise {
|
|
5
|
-
|
|
6
|
-
export class VNode {
|
|
7
|
-
type: string | Function;
|
|
8
|
-
props: object | null;
|
|
9
|
-
children: Array<string | VNode>
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export class Component {
|
|
13
|
-
constructor(
|
|
14
|
-
template: (state: object) => (actions: object) => VNode,
|
|
15
|
-
state: object,
|
|
16
|
-
actions: object,
|
|
17
|
-
root?: Element
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
}
|
package/src/index.js
DELETED
package/src/utils.js
DELETED
package/src/vdom.js
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { getUniques } from './utils';
|
|
2
|
-
|
|
3
|
-
const vdom = () => {
|
|
4
|
-
const setProp = (element, attribute, original, updated) => {
|
|
5
|
-
if (attribute === 'value') {
|
|
6
|
-
if (updated || updated === 0 || updated === '') {
|
|
7
|
-
element[attribute] = updated;
|
|
8
|
-
}
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
if (typeof updated === 'boolean') {
|
|
13
|
-
if (updated) {
|
|
14
|
-
element.setAttribute(attribute, updated);
|
|
15
|
-
element[attribute] = updated;
|
|
16
|
-
}
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (typeof updated === 'function') {
|
|
21
|
-
try {
|
|
22
|
-
element[attribute] = updated;
|
|
23
|
-
} catch (e) {
|
|
24
|
-
/* sometimes lifecycle methods throw. we don't particularly care */
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (!updated || (typeof updated === 'object' && !Object.keys(updated).length)) {
|
|
31
|
-
element.removeAttribute(attribute);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (attribute === 'style') {
|
|
36
|
-
const styles = getUniques(updated, original);
|
|
37
|
-
|
|
38
|
-
for (const style of styles) {
|
|
39
|
-
if (!updated[style]) {
|
|
40
|
-
element.style[style] = '';
|
|
41
|
-
} else if (!original || !original[style] || original[style] !== updated[style]) {
|
|
42
|
-
element.style[style] = updated[style];
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
element.setAttribute(attribute, updated);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const setProps = (element, props) => {
|
|
53
|
-
for (const [attribute, value] of Object.entries(props)) {
|
|
54
|
-
setProp(element, attribute, {}, value);
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const createElement = (node) => {
|
|
59
|
-
if (typeof node === 'string') {
|
|
60
|
-
return document.createTextNode(node);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const { type, props, children } = node;
|
|
64
|
-
const element = document.createElement(type);
|
|
65
|
-
setProps(element, props);
|
|
66
|
-
|
|
67
|
-
children
|
|
68
|
-
.map(createElement)
|
|
69
|
-
.forEach(child => element.appendChild(child));
|
|
70
|
-
|
|
71
|
-
return element;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
const shouldReplace = (original, updated) =>
|
|
75
|
-
typeof original !== typeof updated ||
|
|
76
|
-
(typeof original === 'string' && original !== updated) ||
|
|
77
|
-
original.type !== updated.type;
|
|
78
|
-
|
|
79
|
-
const updateProps = (element, original, updated) => {
|
|
80
|
-
const props = getUniques(original, updated);
|
|
81
|
-
|
|
82
|
-
for (const prop of props) {
|
|
83
|
-
setProp(element, prop, original[prop], updated[prop]);
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const reconcile = (parent, element, previous, next) => {
|
|
88
|
-
if (!previous) {
|
|
89
|
-
const el = createElement(next);
|
|
90
|
-
|
|
91
|
-
if (next.props.oncreate) {
|
|
92
|
-
next.props.oncreate(el);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
parent.appendChild(el);
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (!next) {
|
|
100
|
-
const remove = () => element.remove();
|
|
101
|
-
|
|
102
|
-
if (previous.props && previous.props.onremove) {
|
|
103
|
-
previous.props.onremove(element)(remove);
|
|
104
|
-
} else {
|
|
105
|
-
remove();
|
|
106
|
-
}
|
|
107
|
-
return false;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (shouldReplace(previous, next)) {
|
|
111
|
-
const nextElement = createElement(next);
|
|
112
|
-
|
|
113
|
-
if (next.props && next.props.onupdate) {
|
|
114
|
-
next.props.onupdate(nextElement)(previous.props);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
parent.replaceChild(
|
|
118
|
-
nextElement,
|
|
119
|
-
element,
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (next.props && next.props.onupdate) {
|
|
126
|
-
next.props.onupdate(element)(previous.props);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
updateProps(element, previous.props, next.props);
|
|
130
|
-
return true;
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const update = (parent, element, previous, next) => {
|
|
134
|
-
const updated = reconcile(parent, element, previous, next);
|
|
135
|
-
|
|
136
|
-
if (updated) {
|
|
137
|
-
const prevChildren = previous.children || [];
|
|
138
|
-
const nextChildren = next.children || [];
|
|
139
|
-
const length = Math.max(prevChildren.length, nextChildren.length);
|
|
140
|
-
const childNodes = [...(element && element.childNodes) || []];
|
|
141
|
-
|
|
142
|
-
for (let i = 0; i < length; i += 1) {
|
|
143
|
-
update(
|
|
144
|
-
element,
|
|
145
|
-
childNodes[i],
|
|
146
|
-
previous.children[i],
|
|
147
|
-
next.children[i],
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
return {
|
|
154
|
-
createElement,
|
|
155
|
-
update,
|
|
156
|
-
};
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
export { vdom };
|