@esportsplus/template 0.16.1 → 0.18.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/build/attributes.js +29 -24
- package/build/constants.d.ts +13 -3
- package/build/constants.js +14 -4
- package/build/event.js +48 -46
- package/build/html/hydrate.d.ts +2 -8
- package/build/html/hydrate.js +28 -79
- package/build/html/index.d.ts +2106 -5
- package/build/html/index.js +12 -4
- package/build/html/{cache.d.ts → parser.d.ts} +1 -1
- package/build/html/{cache.js → parser.js} +6 -4
- package/build/render.d.ts +1 -2
- package/build/render.js +7 -10
- package/build/slot/cleanup.d.ts +4 -0
- package/build/slot/cleanup.js +52 -0
- package/build/slot/effect.d.ts +3 -0
- package/build/slot/effect.js +56 -0
- package/build/slot/index.d.ts +3 -0
- package/build/slot/index.js +8 -0
- package/build/slot/reactive.d.ts +3 -0
- package/build/slot/reactive.js +125 -0
- package/build/slot/render.d.ts +2 -0
- package/build/slot/render.js +81 -0
- package/build/svg.d.ts +1 -2
- package/build/types.d.ts +21 -17
- package/build/utilities/element.d.ts +11 -0
- package/build/utilities/element.js +9 -0
- package/build/utilities/fragment.d.ts +3 -0
- package/build/utilities/fragment.js +11 -0
- package/build/utilities/node.d.ts +8 -0
- package/build/utilities/node.js +9 -0
- package/build/utilities/queue.d.ts +3 -0
- package/build/utilities/queue.js +4 -0
- package/build/utilities/text.d.ts +3 -0
- package/build/utilities/text.js +9 -0
- package/package.json +2 -2
- package/src/attributes.ts +32 -29
- package/src/constants.ts +32 -5
- package/src/event.ts +55 -52
- package/src/html/hydrate.ts +36 -108
- package/src/html/index.ts +16 -8
- package/src/html/{cache.ts → parser.ts} +5 -3
- package/src/render.ts +8 -12
- package/src/slot/cleanup.ts +74 -0
- package/src/slot/effect.ts +74 -0
- package/src/slot/index.ts +12 -0
- package/src/slot/reactive.ts +177 -0
- package/src/slot/render.ts +117 -0
- package/src/svg.ts +1 -2
- package/src/types.ts +25 -18
- package/src/utilities/element.ts +28 -0
- package/src/utilities/fragment.ts +22 -0
- package/src/utilities/node.ts +26 -0
- package/src/utilities/queue.ts +9 -0
- package/src/utilities/text.ts +16 -0
- package/build/slot.d.ts +0 -21
- package/build/slot.js +0 -204
- package/build/utilities.d.ts +0 -28
- package/build/utilities.js +0 -37
- package/src/slot.ts +0 -277
- package/src/utilities.ts +0 -87
package/src/attributes.ts
CHANGED
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
import { effect } from '@esportsplus/reactivity';
|
|
2
|
-
import { isArray,
|
|
3
|
-
import { ondisconnect } from './slot';
|
|
2
|
+
import { isArray, isObject, isString } from '@esportsplus/utilities';
|
|
3
|
+
import { ondisconnect } from './slot/cleanup';
|
|
4
|
+
import { STATE_HYDRATING, STATE_NONE, STATE_WAITING } from './constants';
|
|
4
5
|
import { Attributes, Element } from './types';
|
|
5
|
-
import { className,
|
|
6
|
+
import { className, removeAttribute, setAttribute } from './utilities/element';
|
|
7
|
+
import { raf } from './utilities/queue';
|
|
6
8
|
import q from '@esportsplus/queue';
|
|
7
9
|
import event from './event';
|
|
8
10
|
|
|
9
11
|
|
|
10
|
-
const
|
|
12
|
+
const EFFECT = Symbol();
|
|
11
13
|
|
|
12
|
-
const
|
|
14
|
+
const STORE = Symbol();
|
|
13
15
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const STATE_HYDRATING = 0;
|
|
18
|
-
|
|
19
|
-
const STATE_NONE = 1;
|
|
20
|
-
|
|
21
|
-
const STATE_WAITING = 2;
|
|
16
|
+
const UPDATES = Symbol();
|
|
22
17
|
|
|
23
18
|
|
|
24
19
|
type Context = {
|
|
@@ -64,19 +59,20 @@ function schedule() {
|
|
|
64
59
|
}
|
|
65
60
|
|
|
66
61
|
function set(context: Context, name: string, value: unknown, state: State) {
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
set(context, name, value[i], state);
|
|
70
|
-
}
|
|
62
|
+
if (value === false || value == null) {
|
|
63
|
+
value = '';
|
|
71
64
|
}
|
|
72
|
-
|
|
65
|
+
|
|
66
|
+
let type = typeof value;
|
|
67
|
+
|
|
68
|
+
if (type === 'function') {
|
|
73
69
|
if (name.startsWith('on')) {
|
|
74
|
-
event(context.element, name as `on${string}`, value);
|
|
70
|
+
event(context.element, name as `on${string}`, value as Function);
|
|
75
71
|
}
|
|
76
72
|
else {
|
|
77
|
-
context.store[
|
|
73
|
+
context.store[EFFECT] ??= 0;
|
|
78
74
|
|
|
79
|
-
let id = (context.store[
|
|
75
|
+
let id = (context.store[EFFECT] as number)++;
|
|
80
76
|
|
|
81
77
|
ondisconnect(
|
|
82
78
|
context.element,
|
|
@@ -107,6 +103,13 @@ function set(context: Context, name: string, value: unknown, state: State) {
|
|
|
107
103
|
state = STATE_NONE;
|
|
108
104
|
}
|
|
109
105
|
}
|
|
106
|
+
else if (type === 'object') {
|
|
107
|
+
if (isArray(value)) {
|
|
108
|
+
for (let i = 0, n = value.length; i < n; i++) {
|
|
109
|
+
set(context, name, value[i], state);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
110
113
|
else {
|
|
111
114
|
update(context, null, name, value, state);
|
|
112
115
|
}
|
|
@@ -232,15 +235,20 @@ function update(
|
|
|
232
235
|
|
|
233
236
|
|
|
234
237
|
const spread = function (element: Element, value: Attributes | Attributes[]) {
|
|
235
|
-
let cache = (element[
|
|
238
|
+
let cache = (element[STORE] ??= { [UPDATES]: {} }) as Record<PropertyKey, unknown>,
|
|
236
239
|
context = {
|
|
237
240
|
element,
|
|
238
241
|
store: cache,
|
|
239
|
-
updates: cache[
|
|
242
|
+
updates: cache[UPDATES] as Record<PropertyKey, unknown>,
|
|
240
243
|
updating: false
|
|
241
244
|
};
|
|
242
245
|
|
|
243
|
-
if (
|
|
246
|
+
if (isObject(value)) {
|
|
247
|
+
for (let name in value) {
|
|
248
|
+
set(context, name, value[name], STATE_HYDRATING);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
else if (isArray(value)) {
|
|
244
252
|
for (let i = 0, n = value.length; i < n; i++) {
|
|
245
253
|
let v = value[i];
|
|
246
254
|
|
|
@@ -249,11 +257,6 @@ const spread = function (element: Element, value: Attributes | Attributes[]) {
|
|
|
249
257
|
}
|
|
250
258
|
}
|
|
251
259
|
}
|
|
252
|
-
else if (isObject(value)) {
|
|
253
|
-
for (let name in value) {
|
|
254
|
-
set(context, name, value[name], STATE_HYDRATING);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
260
|
};
|
|
258
261
|
|
|
259
262
|
|
package/src/constants.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { fragment } from './utilities';
|
|
1
|
+
import { fragment } from './utilities/fragment';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const CLEANUP = Symbol();
|
|
2
5
|
|
|
3
6
|
|
|
4
7
|
const EMPTY_FRAGMENT = fragment('');
|
|
@@ -44,9 +47,21 @@ const REGEX_SLOT_NODES = /<([\w-]+|[\/!])(?:([^><]*{{\$}}[^><]*)|(?:[^><]*))?>|{
|
|
|
44
47
|
|
|
45
48
|
const RENDERABLE = Symbol();
|
|
46
49
|
|
|
47
|
-
const
|
|
50
|
+
const RENDERABLE_ARRAY = 0;
|
|
51
|
+
|
|
52
|
+
const RENDERABLE_FRAGMENT = 1;
|
|
53
|
+
|
|
54
|
+
const RENDERABLE_HTML_FRAGMENT = 2;
|
|
55
|
+
|
|
56
|
+
const RENDERABLE_HTML_REACTIVE_ARRAY = 3;
|
|
57
|
+
|
|
58
|
+
const RENDERABLE_NODE = 4;
|
|
48
59
|
|
|
49
|
-
const
|
|
60
|
+
const RENDERABLE_NODE_LIST = 5;
|
|
61
|
+
|
|
62
|
+
const RENDERABLE_TEXT = 6;
|
|
63
|
+
|
|
64
|
+
const RENDERABLE_VOID = 7;
|
|
50
65
|
|
|
51
66
|
|
|
52
67
|
const SLOT_HTML = '<!--$-->';
|
|
@@ -56,10 +71,22 @@ const SLOT_MARKER = '{{$}}';
|
|
|
56
71
|
const SLOT_MARKER_LENGTH = SLOT_MARKER.length;
|
|
57
72
|
|
|
58
73
|
|
|
74
|
+
const STATE_HYDRATING = 0;
|
|
75
|
+
|
|
76
|
+
const STATE_NONE = 1;
|
|
77
|
+
|
|
78
|
+
const STATE_WAITING = 2;
|
|
79
|
+
|
|
80
|
+
|
|
59
81
|
export {
|
|
82
|
+
CLEANUP,
|
|
60
83
|
EMPTY_FRAGMENT,
|
|
61
84
|
NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST,
|
|
62
85
|
REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES,
|
|
63
|
-
RENDERABLE,
|
|
64
|
-
|
|
86
|
+
RENDERABLE, RENDERABLE_ARRAY, RENDERABLE_FRAGMENT,
|
|
87
|
+
RENDERABLE_HTML_FRAGMENT, RENDERABLE_HTML_REACTIVE_ARRAY,
|
|
88
|
+
RENDERABLE_NODE, RENDERABLE_NODE_LIST, RENDERABLE_TEXT,
|
|
89
|
+
RENDERABLE_VOID,
|
|
90
|
+
SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH,
|
|
91
|
+
STATE_HYDRATING, STATE_NONE, STATE_WAITING
|
|
65
92
|
};
|
package/src/event.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { root } from '@esportsplus/reactivity';
|
|
2
2
|
import { defineProperty } from '@esportsplus/utilities';
|
|
3
|
-
import { ondisconnect } from './slot';
|
|
4
3
|
import { Element } from './types';
|
|
5
|
-
import { addEventListener
|
|
4
|
+
import { addEventListener } from './utilities/element';
|
|
5
|
+
import { parentElement } from './utilities/node';
|
|
6
|
+
import { raf } from './utilities/queue';
|
|
7
|
+
import { ondisconnect } from './slot/cleanup';
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
let capture = new Set<`on${string}`>(['onblur', 'onfocus', 'onscroll']),
|
|
@@ -24,35 +26,7 @@ let capture = new Set<`on${string}`>(['onblur', 'onfocus', 'onscroll']),
|
|
|
24
26
|
});
|
|
25
27
|
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
if (event === 'onconnect') {
|
|
29
|
-
let retry = 60,
|
|
30
|
-
task = () => {
|
|
31
|
-
retry--;
|
|
32
|
-
|
|
33
|
-
if (element.isConnected) {
|
|
34
|
-
retry = 0;
|
|
35
|
-
root(() => listener(element));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (retry) {
|
|
39
|
-
raf.add(task);
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
raf.add(task);
|
|
44
|
-
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
else if (event === 'ondisconnect') {
|
|
48
|
-
ondisconnect(element, () => listener(element));
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
else if (event === 'onrender') {
|
|
52
|
-
root(() => listener(element));
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
29
|
+
function register(element: Element, event: `on${string}`) {
|
|
56
30
|
let controller = controllers.get(event),
|
|
57
31
|
signal: AbortController['signal'] | undefined;
|
|
58
32
|
|
|
@@ -83,34 +57,63 @@ export default (element: Element, event: `on${string}`, listener: Function): voi
|
|
|
83
57
|
signal = controller.signal;
|
|
84
58
|
}
|
|
85
59
|
|
|
86
|
-
let key = keys[event];
|
|
60
|
+
let key = keys[event] = Symbol();
|
|
87
61
|
|
|
88
|
-
|
|
89
|
-
|
|
62
|
+
addEventListener.call(window.document, event.slice(2), (e) => {
|
|
63
|
+
let node = e.target as Element | null;
|
|
90
64
|
|
|
91
|
-
|
|
92
|
-
|
|
65
|
+
while (node) {
|
|
66
|
+
if (key in node) {
|
|
67
|
+
defineProperty(e, 'currentTarget', {
|
|
68
|
+
configurable: true,
|
|
69
|
+
get() {
|
|
70
|
+
return node || window.document;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
93
73
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
74
|
+
return (node[key] as Function).call(node, e);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
node = parentElement.call(node);
|
|
78
|
+
}
|
|
79
|
+
}, {
|
|
80
|
+
capture: capture.has(event),
|
|
81
|
+
passive: passive.has(event),
|
|
82
|
+
signal
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return key;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
export default (element: Element, event: `on${string}`, listener: Function): void => {
|
|
90
|
+
if (event === 'onconnect') {
|
|
91
|
+
let retry = 60,
|
|
92
|
+
task = () => {
|
|
93
|
+
retry--;
|
|
94
|
+
|
|
95
|
+
if (element.isConnected) {
|
|
96
|
+
retry = 0;
|
|
97
|
+
root(() => listener(element));
|
|
98
98
|
}
|
|
99
|
-
});
|
|
100
99
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return (node[key] as Function).call(node, e);
|
|
100
|
+
if (retry) {
|
|
101
|
+
raf.add(task);
|
|
104
102
|
}
|
|
103
|
+
};
|
|
105
104
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
105
|
+
raf.add(task);
|
|
106
|
+
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
else if (event === 'ondisconnect') {
|
|
110
|
+
ondisconnect(element, () => listener(element));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
else if (event === 'onrender') {
|
|
114
|
+
root(() => listener(element));
|
|
115
|
+
return;
|
|
113
116
|
}
|
|
114
117
|
|
|
115
|
-
element[
|
|
118
|
+
element[ keys[event] || register(element, event) ] = listener;
|
|
116
119
|
};
|
package/src/html/hydrate.ts
CHANGED
|
@@ -1,125 +1,53 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { Elements, Fragment, RenderableReactive, RenderableTemplate } from '~/types';
|
|
4
|
-
import { Slot } from '~/slot';
|
|
5
|
-
import { cloneNode } from '~/utilities';
|
|
6
|
-
import cache from './cache';
|
|
1
|
+
import { Fragment, RenderableValues, Template } from '~/types';
|
|
2
|
+
import { cloneNode } from '~/utilities/node';
|
|
7
3
|
|
|
8
4
|
|
|
9
|
-
|
|
10
|
-
let
|
|
11
|
-
factory = renderable.template,
|
|
12
|
-
refresh = () => {
|
|
13
|
-
root(() => array.map(template));
|
|
5
|
+
export default ({ fragment, slots }: Template, values: RenderableValues[]): Fragment => {
|
|
6
|
+
let clone = cloneNode.call(fragment, true);
|
|
14
7
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
reset();
|
|
20
|
-
},
|
|
21
|
-
reset = () => {
|
|
22
|
-
elements = [];
|
|
23
|
-
fragment = cloneNode.call(EMPTY_FRAGMENT);
|
|
24
|
-
},
|
|
25
|
-
template = function(data, i) {
|
|
26
|
-
hydrate(elements, fragment, factory.call(this, data, i));
|
|
27
|
-
} as (this: typeof array, ...args: Parameters<Parameters<typeof array['map']>[0]>) => void;
|
|
28
|
-
|
|
29
|
-
array.on('clear', () => slot.clear());
|
|
30
|
-
array.on('pop', () => slot.pop());
|
|
31
|
-
array.on('reverse', refresh);
|
|
32
|
-
array.on('shift', () => slot.shift());
|
|
33
|
-
array.on('sort', refresh);
|
|
34
|
-
|
|
35
|
-
array.on('push', ({ items }) => {
|
|
36
|
-
let anchor = slot.anchor();
|
|
37
|
-
|
|
38
|
-
elements = slot.nodes;
|
|
39
|
-
|
|
40
|
-
root(() => array.map(template, array.length - items.length));
|
|
41
|
-
|
|
42
|
-
anchor.after(fragment);
|
|
43
|
-
reset();
|
|
44
|
-
});
|
|
45
|
-
array.on('splice', ({ deleteCount: d, items: i, start: s }) => {
|
|
46
|
-
if (array.length === 0) {
|
|
47
|
-
slot.clear();
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
root(() => array.map(template, s, i.length))
|
|
52
|
-
|
|
53
|
-
slot.splice(s, d, fragment, ...elements);
|
|
54
|
-
reset();
|
|
55
|
-
});
|
|
56
|
-
array.on('unshift', ({ items }) => {
|
|
57
|
-
root(() => array.map(template, 0, items.length))
|
|
58
|
-
|
|
59
|
-
slot.unshift(fragment, ...elements);
|
|
60
|
-
reset();
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
root(() => array.map(template));
|
|
64
|
-
reset();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function hydrate<T>(elements: Elements[] | null, fragment: Fragment, renderable: RenderableTemplate<T>) {
|
|
68
|
-
let { fragment: frag, slots } = cache.get(renderable.literals),
|
|
69
|
-
clone = cloneNode.call(frag, true);
|
|
8
|
+
if (slots === null) {
|
|
9
|
+
return clone as Fragment;
|
|
10
|
+
}
|
|
70
11
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
parentPath,
|
|
76
|
-
values = renderable.values;
|
|
12
|
+
let node,
|
|
13
|
+
nodePath,
|
|
14
|
+
parent,
|
|
15
|
+
parentPath;
|
|
77
16
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
17
|
+
for (let i = 0, n = slots.length; i < n; i++) {
|
|
18
|
+
let { fn, path, slot } = slots[i],
|
|
19
|
+
pp = path.parent,
|
|
20
|
+
pr = path.relative;
|
|
82
21
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
22
|
+
if (pp !== parentPath) {
|
|
23
|
+
if (pp === nodePath) {
|
|
24
|
+
parent = node;
|
|
25
|
+
parentPath = nodePath;
|
|
87
26
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
27
|
+
nodePath = undefined;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
parent = clone;
|
|
31
|
+
parentPath = pp;
|
|
93
32
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
33
|
+
for (let i = 0, n = pp.length; i < n; i++) {
|
|
34
|
+
parent = pp[i].call(parent);
|
|
97
35
|
}
|
|
98
36
|
}
|
|
37
|
+
}
|
|
99
38
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
39
|
+
if (pr !== nodePath) {
|
|
40
|
+
node = parent;
|
|
41
|
+
nodePath = path.absolute;
|
|
103
42
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
43
|
+
for (let i = 0, n = pr.length; i < n; i++) {
|
|
44
|
+
node = pr[i].call(node);
|
|
107
45
|
}
|
|
108
|
-
|
|
109
|
-
// @ts-ignore
|
|
110
|
-
fn(node, values[slot]);
|
|
111
46
|
}
|
|
112
|
-
}
|
|
113
47
|
|
|
114
|
-
|
|
115
|
-
|
|
48
|
+
// @ts-ignore
|
|
49
|
+
fn(node, values[slot]);
|
|
116
50
|
}
|
|
117
51
|
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
export default {
|
|
123
|
-
reactive,
|
|
124
|
-
static: hydrate
|
|
125
|
-
};
|
|
52
|
+
return clone as Fragment;
|
|
53
|
+
}
|
package/src/html/index.ts
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { ReactiveArray } from '@esportsplus/reactivity';
|
|
2
|
-
import { RENDERABLE,
|
|
3
|
-
import { RenderableReactive, RenderableTemplate } from '~/types';
|
|
2
|
+
import { RENDERABLE, RENDERABLE_HTML_FRAGMENT, RENDERABLE_HTML_REACTIVE_ARRAY } from '~/constants';
|
|
3
|
+
import { RenderableReactive, RenderableTemplate, RenderableValues } from '~/types';
|
|
4
4
|
import hydrate from './hydrate';
|
|
5
|
+
import parser from './parser';
|
|
5
6
|
|
|
6
7
|
|
|
7
|
-
const html =
|
|
8
|
-
return {
|
|
8
|
+
const html = (literals: TemplateStringsArray, ...values: RenderableValues[]): RenderableTemplate => {
|
|
9
|
+
return {
|
|
10
|
+
[RENDERABLE]: RENDERABLE_HTML_FRAGMENT,
|
|
11
|
+
fragment: hydrate(parser.parse(literals), values),
|
|
12
|
+
literals
|
|
13
|
+
};
|
|
9
14
|
};
|
|
10
15
|
|
|
11
|
-
html.reactive = <T>(array: ReactiveArray<T>, template: RenderableReactive
|
|
12
|
-
return {
|
|
16
|
+
html.reactive = <T>(array: ReactiveArray<T[]>, template: RenderableReactive['template']) => {
|
|
17
|
+
return {
|
|
18
|
+
[RENDERABLE]: RENDERABLE_HTML_REACTIVE_ARRAY,
|
|
19
|
+
array,
|
|
20
|
+
template
|
|
21
|
+
};
|
|
13
22
|
};
|
|
14
23
|
|
|
15
24
|
|
|
16
|
-
export default html;
|
|
17
|
-
export { hydrate };
|
|
25
|
+
export default html;
|
|
@@ -3,7 +3,9 @@ import {
|
|
|
3
3
|
REGEX_SLOT_NODES, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH
|
|
4
4
|
} from '~/constants';
|
|
5
5
|
import { Template } from '~/types';
|
|
6
|
-
import {
|
|
6
|
+
import { firstElementChild, nextElementSibling } from '~/utilities/element';
|
|
7
|
+
import { fragment } from '~/utilities/fragment';
|
|
8
|
+
import { firstChild, nextSibling } from '~/utilities/node';
|
|
7
9
|
import { spread } from '~/attributes';
|
|
8
10
|
import s from '~/slot';
|
|
9
11
|
|
|
@@ -137,9 +139,9 @@ function set(literals: TemplateStringsArray, html: string, slots: Template['slot
|
|
|
137
139
|
}
|
|
138
140
|
|
|
139
141
|
|
|
140
|
-
const
|
|
142
|
+
const parse = (literals: TemplateStringsArray) => {
|
|
141
143
|
return cache.get(literals) || build(literals);
|
|
142
144
|
};
|
|
143
145
|
|
|
144
146
|
|
|
145
|
-
export default {
|
|
147
|
+
export default { parse };
|
package/src/render.ts
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
import { isInstanceOf } from '@esportsplus/utilities';
|
|
2
1
|
import { SLOT_HTML } from './constants';
|
|
3
|
-
import slot, { Slot } from './slot';
|
|
4
2
|
import { Renderable } from './types';
|
|
5
|
-
import {
|
|
3
|
+
import { fragment } from './utilities/fragment';
|
|
4
|
+
import { firstChild, nodeValue } from './utilities/node';
|
|
5
|
+
import slot from './slot';
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
let
|
|
9
|
-
|
|
8
|
+
let anchor,
|
|
9
|
+
marker = firstChild.call( fragment(SLOT_HTML) );
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
export default (
|
|
13
|
-
if (isInstanceOf(parent, Slot)) {
|
|
14
|
-
return parent.render(renderable);
|
|
15
|
-
}
|
|
16
|
-
|
|
12
|
+
export default (parent: HTMLElement, renderable: Renderable) => {
|
|
17
13
|
nodeValue.call(parent, '');
|
|
18
|
-
|
|
14
|
+
parent.append(anchor = marker.cloneNode());
|
|
19
15
|
|
|
20
|
-
|
|
16
|
+
slot(anchor, renderable);
|
|
21
17
|
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import queue from '@esportsplus/queue';
|
|
2
|
+
import { CLEANUP } from '~/constants';
|
|
3
|
+
import { microtask } from '~/utilities/queue';
|
|
4
|
+
import { previousSibling } from '~/utilities/node';
|
|
5
|
+
import { SlotGroup } from '~/types';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
let cleanup = queue<VoidFunction[]>(64),
|
|
9
|
+
scheduled = false;
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
function schedule() {
|
|
13
|
+
if (!cleanup.length || scheduled) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
scheduled = true;
|
|
18
|
+
microtask.add(task);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function task() {
|
|
22
|
+
try {
|
|
23
|
+
let fns,
|
|
24
|
+
fn,
|
|
25
|
+
n = cleanup.length;
|
|
26
|
+
|
|
27
|
+
while ((fns = cleanup.next()) && n--) {
|
|
28
|
+
while (fn = fns.pop()) {
|
|
29
|
+
fn();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch { }
|
|
34
|
+
|
|
35
|
+
if (cleanup.length) {
|
|
36
|
+
microtask.add(task);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
scheduled = false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
const ondisconnect = (element: Element, fn: VoidFunction) => {
|
|
45
|
+
((element as any)[CLEANUP] ??= []).push(fn);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const remove = (groups: SlotGroup[]) => {
|
|
49
|
+
let group, head, tail;
|
|
50
|
+
|
|
51
|
+
while (group = groups.pop()) {
|
|
52
|
+
head = group.head;
|
|
53
|
+
tail = group.tail || head;
|
|
54
|
+
|
|
55
|
+
for (let node = tail; node; node = previousSibling.call(node)) {
|
|
56
|
+
if (CLEANUP in node) {
|
|
57
|
+
cleanup.add( node[CLEANUP] as VoidFunction[] );
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
node.remove();
|
|
61
|
+
|
|
62
|
+
if (head === node) {
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!scheduled && cleanup.length) {
|
|
69
|
+
schedule();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
export { ondisconnect, remove };
|