@lukekaalim/act-web 2.5.4 → 3.0.0
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/element.ts +55 -0
- package/mod.ts +4 -0
- package/package.json +6 -18
- package/props.ts +119 -0
- package/render.ts +10 -0
- package/space.ts +59 -0
- package/tags.ts +31 -0
- package/README.md +0 -24
- package/entry.d.ts +0 -7
- package/entry.js +0 -6
- package/node.d.ts +0 -1
- package/node.js +0 -54
- package/prop.js +0 -156
- package/props.d.ts +0 -7
- package/reconciler.js +0 -27
- package/renderer.d.ts +0 -6
- package/renderer.js +0 -85
package/element.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as act from '@lukekaalim/act';
|
|
2
|
+
import { HTMLTagName } from "./tags";
|
|
3
|
+
|
|
4
|
+
type EventMap = {
|
|
5
|
+
onClick: "click",
|
|
6
|
+
|
|
7
|
+
onMouseEnter: "mouseenter",
|
|
8
|
+
onMouseMove: "mousemove",
|
|
9
|
+
onMouseLeave: "mouseleave",
|
|
10
|
+
|
|
11
|
+
onPointerEnter: "pointerenter",
|
|
12
|
+
onPointerMove: "pointermove",
|
|
13
|
+
onPointerLeave: "pointerleave",
|
|
14
|
+
|
|
15
|
+
onKeyDown: "keydown",
|
|
16
|
+
onKeyUp: "keyup",
|
|
17
|
+
|
|
18
|
+
onFocus: "focus",
|
|
19
|
+
onBlur: 'blur',
|
|
20
|
+
|
|
21
|
+
onInput: "input",
|
|
22
|
+
onChange: "change",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type ElementMap = {
|
|
26
|
+
"button": HTMLButtonElement,
|
|
27
|
+
"div": HTMLDivElement,
|
|
28
|
+
"form": HTMLFormElement,
|
|
29
|
+
"input": HTMLInputElement,
|
|
30
|
+
"pre": HTMLPreElement,
|
|
31
|
+
"canvas": HTMLCanvasElement,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const createSpiderElement = <Type extends HTMLTagName>(
|
|
35
|
+
type: Type,
|
|
36
|
+
props?: {
|
|
37
|
+
ref?: act.Ref<null | HTMLElement>,
|
|
38
|
+
key?: string | number,
|
|
39
|
+
style?: { [key in keyof CSSStyleDeclaration]?: number | string },
|
|
40
|
+
classList?: readonly (string | boolean | null | void)[],
|
|
41
|
+
className?: string,
|
|
42
|
+
}
|
|
43
|
+
& Record<string, unknown>
|
|
44
|
+
& {
|
|
45
|
+
//[key in keyof HTMLElement]?: HTMLElement[key] extends Function ? never : HTMLElement[key]
|
|
46
|
+
}
|
|
47
|
+
& {
|
|
48
|
+
[key in keyof EventMap]?: (this: HTMLElement, ev: HTMLElementEventMap[EventMap[key]]) => unknown
|
|
49
|
+
},
|
|
50
|
+
children?: act.Node,
|
|
51
|
+
): act.Element => {
|
|
52
|
+
return act.createElement(type, props, children)
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const hs = createSpiderElement;
|
package/mod.ts
ADDED
package/package.json
CHANGED
|
@@ -1,23 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lukekaalim/act-web",
|
|
3
|
-
"
|
|
4
|
-
"types": "entry.d.ts",
|
|
3
|
+
"version": "3.0.0",
|
|
5
4
|
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
"sideEffects": false,
|
|
8
|
-
"scripts": {
|
|
9
|
-
"test": "cd ../..; npm test",
|
|
10
|
-
"postversion": "../../scripts/postversion.sh",
|
|
11
|
-
"preversion": "npm run test"
|
|
12
|
-
},
|
|
5
|
+
"main": "mod.ts",
|
|
13
6
|
"dependencies": {
|
|
14
|
-
"@lukekaalim/act
|
|
15
|
-
"@lukekaalim/act-
|
|
16
|
-
|
|
17
|
-
"devDependencies": {
|
|
18
|
-
"@lukekaalim/act": "*"
|
|
19
|
-
},
|
|
20
|
-
"peerDependencies": {
|
|
21
|
-
"@lukekaalim/act": "^2.5.0"
|
|
7
|
+
"@lukekaalim/act": "^3.0.0",
|
|
8
|
+
"@lukekaalim/act-recon": "^1.0.0",
|
|
9
|
+
"@lukekaalim/act-backstage": "^1.0.0"
|
|
22
10
|
}
|
|
23
|
-
}
|
|
11
|
+
}
|
package/props.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as act from '@lukekaalim/act';
|
|
2
|
+
|
|
3
|
+
export const setProps = (
|
|
4
|
+
node: HTMLElement | SVGElement | Text,
|
|
5
|
+
|
|
6
|
+
next: act.Element,
|
|
7
|
+
prev: null | act.Element
|
|
8
|
+
) => {
|
|
9
|
+
if (node instanceof HTMLElement) {
|
|
10
|
+
setHTMLElementProps(node, next, prev);
|
|
11
|
+
}
|
|
12
|
+
if (node instanceof SVGElement) {
|
|
13
|
+
setSVGElementProps(node, next, prev);
|
|
14
|
+
}
|
|
15
|
+
if (node instanceof Text) {
|
|
16
|
+
if (node.textContent !== next.props.value)
|
|
17
|
+
node.textContent = next.props.value as string
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const setSVGElementProps = (
|
|
22
|
+
node: SVGElement,
|
|
23
|
+
|
|
24
|
+
next: act.Element,
|
|
25
|
+
prev: null | act.Element
|
|
26
|
+
) => {
|
|
27
|
+
setPropObject(node as any, next.props, prev && prev.props, (name, next, prev) => {
|
|
28
|
+
if (name.startsWith('on')) {
|
|
29
|
+
const eventName = name.slice(2).toLocaleLowerCase();
|
|
30
|
+
setEventProp(node as any, eventName, next, prev);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
switch (name) {
|
|
34
|
+
case 'ref':
|
|
35
|
+
(next as any).current = node;
|
|
36
|
+
return true;
|
|
37
|
+
default:
|
|
38
|
+
node.setAttribute(name, next as any);
|
|
39
|
+
return true;
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const setHTMLElementProps = (
|
|
45
|
+
node: HTMLElement,
|
|
46
|
+
|
|
47
|
+
next: act.Element,
|
|
48
|
+
prev: null | act.Element
|
|
49
|
+
) => {
|
|
50
|
+
setPropObject(node as any, next.props, prev && prev.props, (name, next, prev) => {
|
|
51
|
+
if (name.startsWith('on')) {
|
|
52
|
+
const eventName = name.slice(2).toLocaleLowerCase();
|
|
53
|
+
setEventProp(node as any, eventName, next, prev);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
switch (name) {
|
|
57
|
+
case 'ref':
|
|
58
|
+
(next as any).current = node;
|
|
59
|
+
return true;
|
|
60
|
+
case 'style':
|
|
61
|
+
return (setStyleProp(node.style, next as any, prev as any), true);
|
|
62
|
+
case 'className':
|
|
63
|
+
node.className = next as string;
|
|
64
|
+
return true;
|
|
65
|
+
case 'classList':
|
|
66
|
+
const classNames = (next as string[]).filter(Boolean).join(' ');
|
|
67
|
+
node.className = classNames;
|
|
68
|
+
return true;
|
|
69
|
+
default:
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const setEventProp = (
|
|
76
|
+
node: EventSource,
|
|
77
|
+
type: string,
|
|
78
|
+
next: unknown,
|
|
79
|
+
prev: unknown,
|
|
80
|
+
) => {
|
|
81
|
+
if (prev === next)
|
|
82
|
+
return;
|
|
83
|
+
if (prev) {
|
|
84
|
+
node.removeEventListener(type, prev as any)
|
|
85
|
+
}
|
|
86
|
+
if (next) {
|
|
87
|
+
node.addEventListener(type, next as any)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const setStyleProp = (
|
|
92
|
+
node: CSSStyleDeclaration,
|
|
93
|
+
style: null | Record<keyof CSSStyleDeclaration, string | number>,
|
|
94
|
+
prevStyle: null | Record<keyof CSSStyleDeclaration, string | number>,
|
|
95
|
+
) => {
|
|
96
|
+
setPropObject(node as any, style, prevStyle)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const setPropObject = (
|
|
100
|
+
target: Record<string, unknown>,
|
|
101
|
+
next: null | Record<string, unknown>,
|
|
102
|
+
prev: null | Record<string, unknown>,
|
|
103
|
+
assign: null | ((name: string, next: unknown, prev: unknown) => boolean) = null,
|
|
104
|
+
) => {
|
|
105
|
+
const names = new Set([
|
|
106
|
+
...Object.keys(next || {}),
|
|
107
|
+
...Object.keys(prev || {})
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
for (const name of names) {
|
|
111
|
+
const nextValue = (next || {})[name];
|
|
112
|
+
const successfulAssign = assign && assign(name, nextValue, (prev || {})[name]);
|
|
113
|
+
if (!successfulAssign) {
|
|
114
|
+
if (target[name] !== nextValue) {
|
|
115
|
+
target[name] = nextValue;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
package/render.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createWebSpace } from "./space";
|
|
2
|
+
import { createRenderFunction, Scheduler } from "@lukekaalim/act-backstage";
|
|
3
|
+
|
|
4
|
+
const intervalScheduler: Scheduler<NodeJS.Timeout> = {
|
|
5
|
+
duration: 10,
|
|
6
|
+
request: func => setInterval(func, 10),
|
|
7
|
+
cancel: id => clearInterval(id),
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const render = createRenderFunction(intervalScheduler, createWebSpace);
|
package/space.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as act from '@lukekaalim/act';
|
|
2
|
+
import * as recon from '@lukekaalim/act-recon';
|
|
3
|
+
|
|
4
|
+
import { createSimpleRenderSpace } from '@lukekaalim/act-backstage/mod.ts';
|
|
5
|
+
|
|
6
|
+
import { setProps } from './props.ts';
|
|
7
|
+
|
|
8
|
+
export const HTML: act.Component = ({ children }) => act.h(act.primitiveNodeTypes.render, { type: 'web:html' }, children);
|
|
9
|
+
export const SVG: act.Component = ({ children }) => act.h(act.primitiveNodeTypes.render, { type: 'web:svg' }, children);
|
|
10
|
+
|
|
11
|
+
export const createWebSpace = (tree: recon.CommitTree, root: HTMLElement, document: Document = window.document) => {
|
|
12
|
+
return createSimpleRenderSpace(tree, {
|
|
13
|
+
rootTypes: new Set(['web:html', 'web:svg']),
|
|
14
|
+
create(element, rootType) {
|
|
15
|
+
const tag = element.type;
|
|
16
|
+
|
|
17
|
+
switch (typeof tag) {
|
|
18
|
+
case 'symbol': {
|
|
19
|
+
switch (tag) {
|
|
20
|
+
case act.primitiveNodeTypes.string:
|
|
21
|
+
return document.createTextNode("");
|
|
22
|
+
default:
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
case 'string': {
|
|
27
|
+
switch (rootType) {
|
|
28
|
+
case 'web:html':
|
|
29
|
+
return document.createElementNS('http://www.w3.org/1999/xhtml', tag);
|
|
30
|
+
case 'web:svg':
|
|
31
|
+
return document.createElementNS('http://www.w3.org/2000/svg', tag);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
default:
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
update(el, next, prev) {
|
|
39
|
+
setProps(el, next, prev);
|
|
40
|
+
},
|
|
41
|
+
link(el, parent) {
|
|
42
|
+
(parent || root).appendChild(el);
|
|
43
|
+
},
|
|
44
|
+
sort(el, newChildren) {
|
|
45
|
+
if (el instanceof Text)
|
|
46
|
+
return;
|
|
47
|
+
if (newChildren.length < 2)
|
|
48
|
+
return;
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < newChildren.length; i++)
|
|
51
|
+
if (el.children[i] !== newChildren[i])
|
|
52
|
+
el.insertBefore(newChildren[i], el.children[i])
|
|
53
|
+
},
|
|
54
|
+
destroy(prev) {
|
|
55
|
+
if (prev.parentNode)
|
|
56
|
+
prev.parentNode.removeChild(prev);
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
};
|
package/tags.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const htmlTagNames = new Set([
|
|
2
|
+
"div", "span",
|
|
3
|
+
"img", "picture", "canvas",
|
|
4
|
+
"details", "summary", "section",
|
|
5
|
+
"video", "audio",
|
|
6
|
+
"h1", "h2", "h3", "h4", "h5", "h6",
|
|
7
|
+
"hl",
|
|
8
|
+
'p',
|
|
9
|
+
"ol", "ul", "li",
|
|
10
|
+
"table", 'td', 'tr', 'th',
|
|
11
|
+
"pre",
|
|
12
|
+
|
|
13
|
+
'article', 'code', 'a',
|
|
14
|
+
|
|
15
|
+
"form", "input", "label", "button", "select", "textarea",
|
|
16
|
+
] as const);
|
|
17
|
+
export const svgTagName = new Set([
|
|
18
|
+
"svg",
|
|
19
|
+
"line",
|
|
20
|
+
"path",
|
|
21
|
+
"polyline",
|
|
22
|
+
"circle",
|
|
23
|
+
"rect",
|
|
24
|
+
"g",
|
|
25
|
+
"text",
|
|
26
|
+
] as const);
|
|
27
|
+
|
|
28
|
+
type SetValue<T extends Set<string>> = T extends Set<infer X> ? X : never;
|
|
29
|
+
|
|
30
|
+
export type HTMLTagName = SetValue<typeof htmlTagNames>;
|
|
31
|
+
export type SVGTagName = SetValue<typeof svgTagName>;
|
package/README.md
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# Act-Web
|
|
2
|
-
The web renderer for Act, turning components into HTML or SVG elements.
|
|
3
|
-
> ⚠️ Warning: This library is designed as an experiment, and should not be used for production.
|
|
4
|
-
|
|
5
|
-
> ⚠️ Warning: This library is only available as an ESM.
|
|
6
|
-
|
|
7
|
-
## API
|
|
8
|
-
### `render`
|
|
9
|
-
```
|
|
10
|
-
(root: Element, htmlRoot: HTMLElement) => void
|
|
11
|
-
```
|
|
12
|
-
Starts the rendering of a root Act element, under a htmlRoot parent. You can create Act elements from the `@lukekaalim/act`'s `createElement` function.
|
|
13
|
-
|
|
14
|
-
```js
|
|
15
|
-
import { h } from '@lukekaalim/act';
|
|
16
|
-
import { render } from '@lukekaalim/act-web';
|
|
17
|
-
|
|
18
|
-
const App = () => [
|
|
19
|
-
h('h1', {}, 'Hello, World!'),
|
|
20
|
-
h('p', {}, 'Welcome to my first webpage!'),
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
render(h(App), document.body);
|
|
24
|
-
```
|
package/entry.d.ts
DELETED
package/entry.js
DELETED
package/node.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function setNodeChildren2(parent: Node, children: Node[]): void
|
package/node.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
// @flow strict
|
|
2
|
-
/*:: import type { Element } from '@lukekaalim/act'; */
|
|
3
|
-
/*:: import type { RenderResult, RenderResult2 } from '@lukekaalim/act-renderer-core'; */
|
|
4
|
-
/*:: import type { CommitDiff } from '@lukekaalim/act-reconciler'; */
|
|
5
|
-
|
|
6
|
-
import { calculateIndexChanges } from "@lukekaalim/act-reconciler/util";
|
|
7
|
-
|
|
8
|
-
export const createNode = (type/*: string*/, namespace/*: string*/)/*: Node*/ => {
|
|
9
|
-
if (type === 'act:string')
|
|
10
|
-
return document.createTextNode('');
|
|
11
|
-
return document.createElementNS(namespace, type);
|
|
12
|
-
};
|
|
13
|
-
export const removeNode = (node/*: Node*/) => {
|
|
14
|
-
const parent = node.parentNode;
|
|
15
|
-
if (parent)
|
|
16
|
-
parent.removeChild(node);
|
|
17
|
-
}
|
|
18
|
-
export const setNodeChildren = (parent/*: Node*/, children/*: RenderResult<Node>[]*/) => {
|
|
19
|
-
const childrenToAttach = children.filter(r => !r.commit.suspension).map(r => r.node);
|
|
20
|
-
const childrenToRemove = children.filter(r => r.commit.suspension).map(r => r.node);
|
|
21
|
-
for (let i = 0; i < childrenToRemove.length; i++)
|
|
22
|
-
removeNode(childrenToRemove[i]);
|
|
23
|
-
|
|
24
|
-
// iterate backwards through the children
|
|
25
|
-
for (let i = childrenToAttach.length; i > 0; i--) {
|
|
26
|
-
const child = childrenToAttach[i - 1];
|
|
27
|
-
const rightSibling = childrenToAttach[i];
|
|
28
|
-
if (parent !== child.parentNode || (rightSibling && child.nextSibling !== rightSibling)) {
|
|
29
|
-
parent.insertBefore(child, rightSibling);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export const setNodeChildren2 = (parent/*: Node*/, children/*: Node[]*/) => {
|
|
35
|
-
const parentNodeLength = parent.childNodes.length;
|
|
36
|
-
const parentNodes = [...parent.childNodes];
|
|
37
|
-
|
|
38
|
-
for (let i = 0; i < parentNodeLength; i++) {
|
|
39
|
-
if (!children.includes(parentNodes[i]))
|
|
40
|
-
parent.removeChild(parentNodes[i]);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
for (let i = 0; i < children.length; i++) {
|
|
44
|
-
const prevChild = parent.childNodes[i] || null;
|
|
45
|
-
const nextChild = children[i] || null;
|
|
46
|
-
|
|
47
|
-
if (prevChild !== nextChild) {
|
|
48
|
-
if (!prevChild)
|
|
49
|
-
parent.appendChild(nextChild)
|
|
50
|
-
else
|
|
51
|
-
parent.replaceChild(nextChild, prevChild);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
package/prop.js
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
// @flow strict
|
|
2
|
-
/*::
|
|
3
|
-
import type { Commit3 } from "../../reconciler/commit3";
|
|
4
|
-
import type { Diff3, DiffSet } from "../../reconciler/diff";
|
|
5
|
-
import type { CommitDiff } from '@lukekaalim/act-reconciler';
|
|
6
|
-
import type { PropDiff, PropDiffRegistry } from '@lukekaalim/act-renderer-core';
|
|
7
|
-
*/
|
|
8
|
-
import { calculatePropsDiff } from '@lukekaalim/act-renderer-core'
|
|
9
|
-
|
|
10
|
-
const propBlacklist = new Set(['ref', 'key', 'children']);
|
|
11
|
-
|
|
12
|
-
export const setStylesProp = (
|
|
13
|
-
element/*: HTMLElement | SVGElement*/,
|
|
14
|
-
{ prev, next }/*: PropDiff*/
|
|
15
|
-
) => {
|
|
16
|
-
const style = element.style;
|
|
17
|
-
const prevRules = typeof prev === 'object' && prev || {};
|
|
18
|
-
const nextRules = typeof next === 'object' && next || {};
|
|
19
|
-
|
|
20
|
-
const reg = calculatePropsDiff(prevRules, nextRules);
|
|
21
|
-
|
|
22
|
-
for (const [rule, { next }] of reg.map) {
|
|
23
|
-
const nextRuleValue = (typeof next === 'string' || typeof next === 'number') ? next : null;
|
|
24
|
-
if (typeof (style/*: any*/)[rule] !== 'undefined')
|
|
25
|
-
(style/*: any*/)[rule] = nextRuleValue;
|
|
26
|
-
else
|
|
27
|
-
if (nextRuleValue === null)
|
|
28
|
-
style.removeProperty(rule);
|
|
29
|
-
else
|
|
30
|
-
style.setProperty(rule, nextRuleValue.toString());
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export const setEventListenerProp = (
|
|
35
|
-
element/*: Element*/,
|
|
36
|
-
{ key, prev, next }/*: PropDiff*/
|
|
37
|
-
) => {
|
|
38
|
-
const event = key.slice(2).toLowerCase();
|
|
39
|
-
|
|
40
|
-
if (typeof prev === 'function')
|
|
41
|
-
element.removeEventListener(event, ((prev/*: any*/)/*: EventListener*/));
|
|
42
|
-
if (typeof next === 'function')
|
|
43
|
-
element.addEventListener(event, ((next/*: any*/)/*: EventListener*/));
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export const setAttributeProp = (
|
|
47
|
-
element/*: Element*/,
|
|
48
|
-
{ key, prev, next }/*: PropDiff*/
|
|
49
|
-
) => {
|
|
50
|
-
if (next === null && prev !== null)
|
|
51
|
-
element.removeAttribute(key);
|
|
52
|
-
else if (next !== null)
|
|
53
|
-
switch (typeof next) {
|
|
54
|
-
case 'string':
|
|
55
|
-
element.setAttribute(key, next);
|
|
56
|
-
case 'number':
|
|
57
|
-
case 'boolean':
|
|
58
|
-
element.setAttribute(key, next.toString());
|
|
59
|
-
default:
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export const setDOMTokenList = (
|
|
64
|
-
list/*: DOMTokenList*/,
|
|
65
|
-
prop/*: PropDiff*/
|
|
66
|
-
)/*: void*/ => {
|
|
67
|
-
const next = prop.next;
|
|
68
|
-
if (typeof prop.next === 'string')
|
|
69
|
-
// $FlowFixMe
|
|
70
|
-
return list.value = prop.next;
|
|
71
|
-
else if (Array.isArray(next)) {
|
|
72
|
-
const removed = [...list].filter(e => !next.includes(e));
|
|
73
|
-
for (const item of removed)
|
|
74
|
-
list.remove(item);
|
|
75
|
-
for (const item of next)
|
|
76
|
-
typeof item === 'string' && list.add(item);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export const setHTMLProp = (
|
|
81
|
-
element/*: HTMLElement | SVGElement*/,
|
|
82
|
-
prop/*: PropDiff*/
|
|
83
|
-
) => {
|
|
84
|
-
if (prop.key.startsWith('on'))
|
|
85
|
-
setEventListenerProp(element, prop);
|
|
86
|
-
else if (prop.key === 'style')
|
|
87
|
-
setStylesProp(element, prop);
|
|
88
|
-
else if (prop.key in element && !propBlacklist.has(prop.key) && element instanceof HTMLElement) {
|
|
89
|
-
const htmlElement = (element/*: Object*/);
|
|
90
|
-
if (htmlElement[prop.key] instanceof DOMTokenList)
|
|
91
|
-
setDOMTokenList(htmlElement[prop.key], prop);
|
|
92
|
-
else
|
|
93
|
-
htmlElement[prop.key] = prop.next;
|
|
94
|
-
}
|
|
95
|
-
else
|
|
96
|
-
setAttributeProp(element, prop);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export const setHTMLProps = (
|
|
100
|
-
element/*: HTMLElement | SVGElement*/,
|
|
101
|
-
registry/*: PropDiffRegistry*/
|
|
102
|
-
) => {
|
|
103
|
-
for (const [_, propDiff] of registry.map)
|
|
104
|
-
setHTMLProp(element, propDiff);
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
export const setTextProps = (
|
|
108
|
-
element/*: Text*/,
|
|
109
|
-
registry/*: PropDiffRegistry*/
|
|
110
|
-
) => {
|
|
111
|
-
const content = registry.prop('content', '').next;
|
|
112
|
-
if (content !== element.textContent)
|
|
113
|
-
element.textContent = typeof content === 'string' ? content : '';
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
export const setProps = (
|
|
117
|
-
element/*: ?Node*/,
|
|
118
|
-
diff/*: CommitDiff*/
|
|
119
|
-
) => {
|
|
120
|
-
const propDiff = calculatePropsDiff(diff.prev.element.props, diff.next.element.props);
|
|
121
|
-
if (element instanceof HTMLElement || element instanceof SVGElement)
|
|
122
|
-
setHTMLProps(element, propDiff);
|
|
123
|
-
if (element instanceof Text)
|
|
124
|
-
setTextProps(element, propDiff);
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
export const setWebProps2 = (
|
|
128
|
-
element/*: Node*/,
|
|
129
|
-
set/*: DiffSet*/,
|
|
130
|
-
diff/*: Diff3*/,
|
|
131
|
-
) => {
|
|
132
|
-
const prev = set.prevs.map.get(diff.commit.id);
|
|
133
|
-
const propDiff = calculatePropsDiff(prev ? prev.element.props : {}, diff.commit.element.props);
|
|
134
|
-
if (element instanceof HTMLElement || element instanceof SVGElement)
|
|
135
|
-
setHTMLProps(element, propDiff);
|
|
136
|
-
if (element instanceof Text)
|
|
137
|
-
setTextProps(element, propDiff);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export const setRef = (
|
|
141
|
-
node/*: ?Node*/,
|
|
142
|
-
diff/*: CommitDiff*/
|
|
143
|
-
) => {;
|
|
144
|
-
const ref/*: any*/ = diff.next.element.props['ref'];
|
|
145
|
-
if (typeof ref === 'function') {
|
|
146
|
-
if (diff.prev.pruned)
|
|
147
|
-
(ref/*: Function*/)(node);
|
|
148
|
-
else if (diff.next.pruned)
|
|
149
|
-
(ref/*: Function*/)(null);
|
|
150
|
-
} else if (ref && typeof ref === 'object') {
|
|
151
|
-
if (diff.prev.pruned)
|
|
152
|
-
ref['current'] = node;
|
|
153
|
-
else if (diff.next.pruned)
|
|
154
|
-
ref['current'] = null;
|
|
155
|
-
}
|
|
156
|
-
};
|
package/props.d.ts
DELETED
package/reconciler.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
// @flow strict
|
|
2
|
-
/*:: import type { Element } from '@lukekaalim/act'; */
|
|
3
|
-
import { createBoundaryService, createEffectService, createReconciler, createSchedule2, createTree, createTreeService2 } from '@lukekaalim/act-reconciler';
|
|
4
|
-
import { createWebRenderer } from './renderer.js';
|
|
5
|
-
import { setNodeChildren2 } from "./node.js";
|
|
6
|
-
|
|
7
|
-
export const render = (element/*: Element*/, node/*: HTMLElement*/) => {
|
|
8
|
-
const web = createWebRenderer();
|
|
9
|
-
|
|
10
|
-
const onScheduleRequest = (callback) => {
|
|
11
|
-
const id = requestAnimationFrame(() => callback(16));
|
|
12
|
-
return () => cancelAnimationFrame(id);
|
|
13
|
-
};
|
|
14
|
-
const scheduler = createSchedule2(onScheduleRequest)
|
|
15
|
-
const effect = createEffectService(scheduler);
|
|
16
|
-
const boundary = createBoundaryService();
|
|
17
|
-
const reconciler = createReconciler(scheduler);
|
|
18
|
-
|
|
19
|
-
reconciler.diff.subscribeDiff((set) => {
|
|
20
|
-
setNodeChildren2(node, web.render(set, set.root));
|
|
21
|
-
reconciler.tree.live.registry = effect.runEffectRegistry(set.registry);
|
|
22
|
-
|
|
23
|
-
const map = boundary.calcBoundaryMap(set);
|
|
24
|
-
boundary.getRootBoundaryValue(set.suspensions, set, map);
|
|
25
|
-
});
|
|
26
|
-
reconciler.tree.mount(element);
|
|
27
|
-
};
|
package/renderer.d.ts
DELETED
package/renderer.js
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
// @flow strict
|
|
2
|
-
/*::
|
|
3
|
-
import type { ElementType } from '@lukekaalim/act';
|
|
4
|
-
import type { CommitDiff, Commit } from '@lukekaalim/act-reconciler';
|
|
5
|
-
import type { Renderer2 } from "../core/renderer2";
|
|
6
|
-
*/
|
|
7
|
-
/*:: import type { Renderer, RenderResult } from '@lukekaalim/act-renderer-core'; */
|
|
8
|
-
import { setProps, setRef, setWebProps2 } from './prop.js';
|
|
9
|
-
import { createNode, removeNode, setNodeChildren, setNodeChildren2 } from './node.js';
|
|
10
|
-
import { createRenderer2, setRef2 } from '@lukekaalim/act-renderer-core';
|
|
11
|
-
|
|
12
|
-
export const createDOMRenderer = (
|
|
13
|
-
namespace/*: string*/,
|
|
14
|
-
getNextRenderer/*: null | (ElementType => null | Renderer2<Node>)*/ = null,
|
|
15
|
-
)/*: Renderer2<Node>*/ => {
|
|
16
|
-
const create = (type) => {
|
|
17
|
-
return createNode(type, namespace);
|
|
18
|
-
}
|
|
19
|
-
const remove = (node, children, diff) => {
|
|
20
|
-
setRef2(node, diff.commit, diff.change);
|
|
21
|
-
removeNode(node);
|
|
22
|
-
};
|
|
23
|
-
const update = (node, set, diff) => {
|
|
24
|
-
if (diff.change.type === 'create')
|
|
25
|
-
setRef2(node, diff.commit, diff.change);
|
|
26
|
-
|
|
27
|
-
setWebProps2(node, set, diff);
|
|
28
|
-
}
|
|
29
|
-
const attach = (node, set, diff, children) => {
|
|
30
|
-
if (diff.commit.element.props["ignoreChildren"])
|
|
31
|
-
return;
|
|
32
|
-
setNodeChildren2(node, children);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const getRenderer = (set, commitId) => {
|
|
36
|
-
const commit = set.nexts.get(commitId);
|
|
37
|
-
return getNextRenderer && getNextRenderer(commit.element.type) || nodeRenderer;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const getNodes = (set, commitId) => {
|
|
41
|
-
const renderer = getRenderer(set, commitId)
|
|
42
|
-
return renderer.getNodes(set, commitId);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const render = (set, commitId) => {
|
|
46
|
-
const renderer = getRenderer(set, commitId)
|
|
47
|
-
return renderer.render(set, commitId);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const nodeRenderer = createRenderer2({
|
|
51
|
-
create,
|
|
52
|
-
update,
|
|
53
|
-
remove,
|
|
54
|
-
attach,
|
|
55
|
-
}, {
|
|
56
|
-
getNodes,
|
|
57
|
-
render,
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
return nodeRenderer;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export const createWebRenderer = (
|
|
64
|
-
getNextRenderer/*: null | (ElementType => null | Renderer2<Node>)*/ = null,
|
|
65
|
-
)/*: Renderer2<Node>*/ => {
|
|
66
|
-
const svgRenderer = createDOMRenderer('http://www.w3.org/2000/svg', type => {
|
|
67
|
-
switch (type) {
|
|
68
|
-
case 'div':
|
|
69
|
-
return htmlRenderer;
|
|
70
|
-
default:
|
|
71
|
-
return getNextRenderer && getNextRenderer(type);
|
|
72
|
-
}
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
const htmlRenderer = createDOMRenderer('http://www.w3.org/1999/xhtml', type => {
|
|
76
|
-
switch (type) {
|
|
77
|
-
case 'svg':
|
|
78
|
-
return svgRenderer;
|
|
79
|
-
default:
|
|
80
|
-
return getNextRenderer && getNextRenderer(type);
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
return htmlRenderer;
|
|
85
|
-
}
|