@esportsplus/template 0.15.20 → 0.17.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 +27 -23
- package/build/constants.d.ts +14 -3
- package/build/constants.js +15 -3
- package/build/event.js +7 -7
- package/build/html/cache.d.ts +2 -2
- package/build/html/cache.js +24 -16
- package/build/html/hydrate.d.ts +2 -6
- package/build/html/hydrate.js +29 -53
- package/build/html/index.d.ts +2106 -5
- package/build/html/index.js +12 -4
- package/build/render.d.ts +1 -2
- package/build/render.js +6 -10
- package/build/slot/cleanup.d.ts +4 -0
- package/build/slot/cleanup.js +51 -0
- package/build/slot/effect.d.ts +3 -0
- package/build/slot/effect.js +51 -0
- package/build/slot/index.d.ts +3 -0
- package/build/slot/index.js +15 -0
- package/build/slot/reactive.d.ts +3 -0
- package/build/slot/reactive.js +117 -0
- package/build/slot/render.d.ts +2 -0
- package/build/slot/render.js +58 -0
- package/build/svg.d.ts +1 -2
- package/build/types.d.ts +25 -21
- package/build/utilities.d.ts +3 -3
- package/build/utilities.js +12 -12
- package/package.json +2 -2
- package/src/attributes.ts +30 -28
- package/src/constants.ts +38 -4
- package/src/event.ts +8 -8
- package/src/html/cache.ts +28 -18
- package/src/html/hydrate.ts +38 -68
- package/src/html/index.ts +16 -8
- package/src/render.ts +8 -12
- package/src/slot/cleanup.ts +74 -0
- package/src/slot/effect.ts +73 -0
- package/src/slot/index.ts +23 -0
- package/src/slot/reactive.ts +167 -0
- package/src/slot/render.ts +81 -0
- package/src/svg.ts +1 -2
- package/src/types.ts +30 -20
- package/src/utilities.ts +15 -13
- package/build/slot.d.ts +0 -21
- package/build/slot.js +0 -200
- package/src/slot.ts +0 -287
package/src/constants.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import { fragment } from './utilities';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const CLEANUP = Symbol();
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const EMPTY_FRAGMENT = fragment('');
|
|
8
|
+
|
|
9
|
+
|
|
1
10
|
const NODE_CLOSING = 1;
|
|
2
11
|
|
|
3
12
|
const NODE_COMMENT = 2;
|
|
@@ -38,9 +47,21 @@ const REGEX_SLOT_NODES = /<([\w-]+|[\/!])(?:([^><]*{{\$}}[^><]*)|(?:[^><]*))?>|{
|
|
|
38
47
|
|
|
39
48
|
const RENDERABLE = Symbol();
|
|
40
49
|
|
|
41
|
-
const
|
|
50
|
+
const RENDERABLE_ARRAY = 0;
|
|
51
|
+
|
|
52
|
+
const RENDERABLE_FRAGMENT = 1;
|
|
53
|
+
|
|
54
|
+
const RENDERABLE_HTML_FRAGMENT = 2;
|
|
42
55
|
|
|
43
|
-
const
|
|
56
|
+
const RENDERABLE_HTML_REACTIVE_ARRAY = 3;
|
|
57
|
+
|
|
58
|
+
const RENDERABLE_NODE = 4;
|
|
59
|
+
|
|
60
|
+
const RENDERABLE_NODE_LIST = 5;
|
|
61
|
+
|
|
62
|
+
const RENDERABLE_TEXT = 6;
|
|
63
|
+
|
|
64
|
+
const RENDERABLE_VOID = 7;
|
|
44
65
|
|
|
45
66
|
|
|
46
67
|
const SLOT_HTML = '<!--$-->';
|
|
@@ -50,9 +71,22 @@ const SLOT_MARKER = '{{$}}';
|
|
|
50
71
|
const SLOT_MARKER_LENGTH = SLOT_MARKER.length;
|
|
51
72
|
|
|
52
73
|
|
|
74
|
+
const STATE_HYDRATING = 0;
|
|
75
|
+
|
|
76
|
+
const STATE_NONE = 1;
|
|
77
|
+
|
|
78
|
+
const STATE_WAITING = 2;
|
|
79
|
+
|
|
80
|
+
|
|
53
81
|
export {
|
|
82
|
+
CLEANUP,
|
|
83
|
+
EMPTY_FRAGMENT,
|
|
54
84
|
NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST,
|
|
55
85
|
REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES,
|
|
56
|
-
RENDERABLE,
|
|
57
|
-
|
|
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
|
|
58
92
|
};
|
package/src/event.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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
4
|
import { addEventListener, parentElement, raf } from './utilities';
|
|
5
|
+
import { ondisconnect } from './slot/cleanup';
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
let capture = new Set<`on${string}`>(['onblur', 'onfocus', 'onscroll']),
|
|
@@ -91,15 +91,15 @@ export default (element: Element, event: `on${string}`, listener: Function): voi
|
|
|
91
91
|
addEventListener.call(window.document, event.slice(2), (e) => {
|
|
92
92
|
let node = e.target as Element | null;
|
|
93
93
|
|
|
94
|
-
defineProperty(e, 'currentTarget', {
|
|
95
|
-
configurable: true,
|
|
96
|
-
get() {
|
|
97
|
-
return node || window.document;
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
94
|
while (node) {
|
|
102
95
|
if (key in node) {
|
|
96
|
+
defineProperty(e, 'currentTarget', {
|
|
97
|
+
configurable: true,
|
|
98
|
+
get() {
|
|
99
|
+
return node || window.document;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
103
|
return (node[key] as Function).call(node, e);
|
|
104
104
|
}
|
|
105
105
|
|
package/src/html/cache.ts
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES,
|
|
3
3
|
REGEX_SLOT_NODES, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH
|
|
4
4
|
} from '~/constants';
|
|
5
|
-
import {
|
|
5
|
+
import { Template } from '~/types';
|
|
6
6
|
import { firstChild, firstElementChild, fragment, nextElementSibling, nextSibling } from '~/utilities';
|
|
7
7
|
import { spread } from '~/attributes';
|
|
8
8
|
import s from '~/slot';
|
|
@@ -11,8 +11,10 @@ import s from '~/slot';
|
|
|
11
11
|
let cache = new WeakMap<TemplateStringsArray, Template>();
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
function build(literals: TemplateStringsArray
|
|
15
|
-
|
|
14
|
+
function build(literals: TemplateStringsArray) {
|
|
15
|
+
let n = literals.length - 1;
|
|
16
|
+
|
|
17
|
+
if (n === 0) {
|
|
16
18
|
return set(literals, literals[0]);
|
|
17
19
|
}
|
|
18
20
|
|
|
@@ -25,12 +27,11 @@ function build(literals: TemplateStringsArray, values: unknown[]) {
|
|
|
25
27
|
levels = [{
|
|
26
28
|
children: 0,
|
|
27
29
|
elements: 0,
|
|
28
|
-
path: [] as NonNullable<Template['slots']>[
|
|
30
|
+
path: [] as NonNullable<Template['slots']>[number]['path']['parent']
|
|
29
31
|
}],
|
|
30
32
|
parsed = html.split(SLOT_MARKER),
|
|
31
33
|
slot = 0,
|
|
32
|
-
slots: Template['slots'] = []
|
|
33
|
-
total = values.length;
|
|
34
|
+
slots: Template['slots'] = [];
|
|
34
35
|
|
|
35
36
|
for (let match of html.matchAll(REGEX_SLOT_NODES)) {
|
|
36
37
|
let parent = levels[level],
|
|
@@ -42,17 +43,23 @@ function build(literals: TemplateStringsArray, values: unknown[]) {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
if (type === NODE_ELEMENT || type === NODE_VOID) {
|
|
45
|
-
let attr = match[2]
|
|
46
|
+
let attr = match[2],
|
|
47
|
+
path = parent.path.length
|
|
48
|
+
? methods(parent.elements, parent.path, firstElementChild, nextElementSibling)
|
|
49
|
+
: methods(parent.children, [], firstChild, nextSibling);
|
|
46
50
|
|
|
47
51
|
if (attr) {
|
|
48
52
|
let i = attr.indexOf(SLOT_MARKER),
|
|
49
|
-
|
|
53
|
+
p = {
|
|
54
|
+
absolute: path,
|
|
55
|
+
parent: parent.path,
|
|
56
|
+
relative: path.slice(parent.path.length)
|
|
57
|
+
};
|
|
50
58
|
|
|
51
59
|
while (i !== -1) {
|
|
52
60
|
slots.push({
|
|
53
61
|
fn: spread,
|
|
54
|
-
|
|
55
|
-
path,
|
|
62
|
+
path: p,
|
|
56
63
|
slot
|
|
57
64
|
});
|
|
58
65
|
|
|
@@ -65,25 +72,28 @@ function build(literals: TemplateStringsArray, values: unknown[]) {
|
|
|
65
72
|
levels[++level] = {
|
|
66
73
|
children: 0,
|
|
67
74
|
elements: 0,
|
|
68
|
-
path
|
|
69
|
-
? methods(parent.elements, parent.path, firstElementChild, nextElementSibling)
|
|
70
|
-
: methods(parent.children, [], firstChild, nextSibling)
|
|
75
|
+
path
|
|
71
76
|
};
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
parent.elements++;
|
|
75
80
|
}
|
|
76
81
|
else if (type === NODE_SLOT) {
|
|
82
|
+
let relative = methods(parent.children, [], firstChild, nextSibling);
|
|
83
|
+
|
|
77
84
|
buffer += parsed[slot] + SLOT_HTML;
|
|
78
85
|
slots.push({
|
|
79
86
|
fn: s,
|
|
80
|
-
|
|
81
|
-
|
|
87
|
+
path: {
|
|
88
|
+
absolute: [...parent.path, ...relative],
|
|
89
|
+
parent: parent.path,
|
|
90
|
+
relative
|
|
91
|
+
},
|
|
82
92
|
slot: slot++
|
|
83
93
|
});
|
|
84
94
|
}
|
|
85
95
|
|
|
86
|
-
if (slot ===
|
|
96
|
+
if (slot === n) {
|
|
87
97
|
buffer += parsed[slot];
|
|
88
98
|
break;
|
|
89
99
|
}
|
|
@@ -127,8 +137,8 @@ function set(literals: TemplateStringsArray, html: string, slots: Template['slot
|
|
|
127
137
|
}
|
|
128
138
|
|
|
129
139
|
|
|
130
|
-
const get =
|
|
131
|
-
return cache.get(literals) || build(literals
|
|
140
|
+
const get = (literals: TemplateStringsArray) => {
|
|
141
|
+
return cache.get(literals) || build(literals);
|
|
132
142
|
};
|
|
133
143
|
|
|
134
144
|
|
package/src/html/hydrate.ts
CHANGED
|
@@ -1,83 +1,53 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { Slot } from '~/slot';
|
|
4
|
-
import { cloneNode, firstChild, nextSibling } from '~/utilities';
|
|
5
|
-
import cache from './cache';
|
|
1
|
+
import { Fragment, RenderableValues, Template } from '~/types';
|
|
2
|
+
import { cloneNode } from '~/utilities';
|
|
6
3
|
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
let
|
|
10
|
-
factory = renderable.template,
|
|
11
|
-
refresh = () => {
|
|
12
|
-
slot.render( root(() => array.map(template)) );
|
|
13
|
-
},
|
|
14
|
-
template = function(data, i) {
|
|
15
|
-
let renderable = factory.call(this, data, i);
|
|
5
|
+
export default ({ fragment, slots }: Template, values: RenderableValues[]): Fragment => {
|
|
6
|
+
let clone = cloneNode.call(fragment, true);
|
|
16
7
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
array.on('pop', () => {
|
|
21
|
-
slot.pop();
|
|
22
|
-
});
|
|
23
|
-
array.on('push', ({ items }) => {
|
|
24
|
-
slot.push( ...root(() => array.map(template, array.length - items.length)) );
|
|
25
|
-
});
|
|
26
|
-
array.on('reverse', refresh);
|
|
27
|
-
array.on('shift', () => {
|
|
28
|
-
slot.shift();
|
|
29
|
-
});
|
|
30
|
-
array.on('sort', refresh);
|
|
31
|
-
array.on('splice', ({ deleteCount: d, items: i, start: s }) => {
|
|
32
|
-
slot.splice(s, d, ...root(() => array.map(template, s, i.length)));
|
|
33
|
-
});
|
|
34
|
-
array.on('unshift', ({ items }) => {
|
|
35
|
-
slot.unshift( ...root(() => array.map(template, 0, items.length)) );
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
return array.map(template);
|
|
39
|
-
}
|
|
8
|
+
if (slots === null) {
|
|
9
|
+
return clone as Fragment;
|
|
10
|
+
}
|
|
40
11
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
12
|
+
let node,
|
|
13
|
+
nodePath,
|
|
14
|
+
parent,
|
|
15
|
+
parentPath;
|
|
45
16
|
|
|
46
|
-
|
|
47
|
-
let
|
|
48
|
-
|
|
49
|
-
|
|
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;
|
|
50
21
|
|
|
51
|
-
|
|
52
|
-
|
|
22
|
+
if (pp !== parentPath) {
|
|
23
|
+
if (pp === nodePath) {
|
|
24
|
+
parent = node;
|
|
25
|
+
parentPath = nodePath;
|
|
53
26
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
27
|
+
nodePath = undefined;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
parent = clone;
|
|
31
|
+
parentPath = pp;
|
|
57
32
|
|
|
58
|
-
for (let
|
|
59
|
-
|
|
33
|
+
for (let i = 0, n = pp.length; i < n; i++) {
|
|
34
|
+
parent = pp[i].call(parent);
|
|
60
35
|
}
|
|
61
36
|
}
|
|
62
|
-
|
|
63
|
-
// @ts-ignore
|
|
64
|
-
fn(node, values[slot]);
|
|
65
37
|
}
|
|
66
|
-
}
|
|
67
38
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return { elements, fragment };
|
|
73
|
-
}
|
|
39
|
+
if (pr !== nodePath) {
|
|
40
|
+
node = parent;
|
|
41
|
+
nodePath = path.absolute;
|
|
74
42
|
|
|
43
|
+
for (let i = 0, n = pr.length; i < n; i++) {
|
|
44
|
+
node = pr[i].call(node);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
75
47
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return reactive(renderable, slot);
|
|
79
|
-
},
|
|
80
|
-
static: <T>(renderable: RenderableTemplate<T>) => {
|
|
81
|
-
return hydrate(renderable, renderable.template || (renderable.template = cache.get(renderable)));
|
|
48
|
+
// @ts-ignore
|
|
49
|
+
fn(node, values[slot]);
|
|
82
50
|
}
|
|
83
|
-
|
|
51
|
+
|
|
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 cache from './cache';
|
|
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(cache.get(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;
|
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 { firstChild, fragment, nodeValue
|
|
3
|
+
import { firstChild, fragment, nodeValue } from './utilities';
|
|
4
|
+
import slot from './slot';
|
|
6
5
|
|
|
7
6
|
|
|
8
|
-
let
|
|
9
|
-
|
|
7
|
+
let anchor,
|
|
8
|
+
marker = firstChild.call( fragment(SLOT_HTML) );
|
|
10
9
|
|
|
11
10
|
|
|
12
|
-
export default (
|
|
13
|
-
|
|
14
|
-
return parent.render(renderable);
|
|
15
|
-
}
|
|
16
|
-
|
|
11
|
+
export default (parent: HTMLElement, renderable: Renderable) => {
|
|
12
|
+
// parent.nodeValue = '';
|
|
17
13
|
nodeValue.call(parent, '');
|
|
18
|
-
|
|
14
|
+
parent.append(anchor = marker.cloneNode());
|
|
19
15
|
|
|
20
|
-
return slot(
|
|
16
|
+
return slot(anchor, renderable);
|
|
21
17
|
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import queue from '@esportsplus/queue';
|
|
2
|
+
import { CLEANUP } from '~/constants';
|
|
3
|
+
import { microtask, previousSibling } from '~/utilities';
|
|
4
|
+
import { SlotGroup } from '~/types';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
let cleanup = queue<VoidFunction[]>(64),
|
|
8
|
+
scheduled = false;
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
function schedule() {
|
|
12
|
+
if (!cleanup.length || scheduled) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
scheduled = true;
|
|
17
|
+
microtask.add(task);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function task() {
|
|
21
|
+
try {
|
|
22
|
+
let fns,
|
|
23
|
+
fn,
|
|
24
|
+
n = cleanup.length;
|
|
25
|
+
|
|
26
|
+
while ((fns = cleanup.next()) && n--) {
|
|
27
|
+
while (fn = fns.pop()) {
|
|
28
|
+
fn();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch { }
|
|
33
|
+
|
|
34
|
+
if (cleanup.length) {
|
|
35
|
+
microtask.add(task);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
scheduled = false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
const ondisconnect = (element: Element, fn: VoidFunction) => {
|
|
44
|
+
((element as any)[CLEANUP] ??= []).push(fn);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const remove = (groups: SlotGroup[]) => {
|
|
48
|
+
let group, head, tail;
|
|
49
|
+
|
|
50
|
+
while (group = groups.pop()) {
|
|
51
|
+
head = group.head;
|
|
52
|
+
tail = group.tail || head;
|
|
53
|
+
|
|
54
|
+
// for (let node: Element | null = tail; node; node = node.previousSibling as Element | null) {
|
|
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 };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { effect } from '@esportsplus/reactivity';
|
|
2
|
+
import { EMPTY_FRAGMENT, STATE_HYDRATING, STATE_NONE } from '~/constants';
|
|
3
|
+
import { Element, Fragment, SlotGroup } from '~/types';
|
|
4
|
+
import { cloneNode, firstChild, lastChild, nodeValue, raf, text } from '~/utilities'
|
|
5
|
+
import { ondisconnect } from '~/slot/cleanup';
|
|
6
|
+
import { remove } from './cleanup';
|
|
7
|
+
import render from './render';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function update(this: { group?: SlotGroup, textnode?: Element }, anchor: Element, fragment: Fragment, value: unknown) {
|
|
11
|
+
let type = typeof value;
|
|
12
|
+
|
|
13
|
+
if (this.group) {
|
|
14
|
+
remove([this.group]);
|
|
15
|
+
this.group = undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (value == null || type !== 'object') {
|
|
19
|
+
let textnode = this.textnode;
|
|
20
|
+
|
|
21
|
+
if (textnode) {
|
|
22
|
+
// textnode.nodeValue = String(value);
|
|
23
|
+
nodeValue.call(textnode, String(value));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
textnode = this.textnode = text( String(value) );
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!textnode.isConnected) {
|
|
30
|
+
anchor.after(textnode);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
render(anchor, fragment, value);
|
|
35
|
+
|
|
36
|
+
this.group = {
|
|
37
|
+
// head: fragment.firstChild as Element,
|
|
38
|
+
// tail: fragment.lastChild as Element
|
|
39
|
+
head: firstChild.call(fragment),
|
|
40
|
+
tail: lastChild.call(fragment)
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
anchor.after(fragment);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
export default (anchor: Element, fn: Function) => {
|
|
49
|
+
let context = {
|
|
50
|
+
group: undefined as SlotGroup | undefined,
|
|
51
|
+
textnode: undefined as Element | undefined
|
|
52
|
+
},
|
|
53
|
+
// fragment = EMPTY_FRAGMENT.cloneNode() as Fragment,
|
|
54
|
+
fragment = cloneNode.call(EMPTY_FRAGMENT) as Fragment,
|
|
55
|
+
state = STATE_HYDRATING;
|
|
56
|
+
|
|
57
|
+
ondisconnect(
|
|
58
|
+
anchor,
|
|
59
|
+
effect(() => {
|
|
60
|
+
let value = fn();
|
|
61
|
+
|
|
62
|
+
if (state === STATE_HYDRATING) {
|
|
63
|
+
update.call(context, anchor, fragment, value);
|
|
64
|
+
state = STATE_NONE;
|
|
65
|
+
}
|
|
66
|
+
else if (state === STATE_NONE) {
|
|
67
|
+
raf.add(() => {
|
|
68
|
+
update.call(context, anchor, fragment, value);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { EMPTY_FRAGMENT } from '~/constants';
|
|
2
|
+
import { Element, Fragment } from '~/types';
|
|
3
|
+
import { cloneNode } from '~/utilities';
|
|
4
|
+
import effect from './effect';
|
|
5
|
+
import render from './render';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
function slot(anchor: Element, input: unknown) {
|
|
9
|
+
let fragment = cloneNode.call(EMPTY_FRAGMENT) as Fragment;
|
|
10
|
+
|
|
11
|
+
render(anchor, fragment, input);
|
|
12
|
+
|
|
13
|
+
anchor.after(fragment);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export default (anchor: Element, value: unknown) => {
|
|
18
|
+
if (typeof value === 'function') {
|
|
19
|
+
return effect(anchor, value as Function);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return slot(anchor, value);
|
|
23
|
+
};
|