@esportsplus/template 0.12.12 → 0.14.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.
Potentially problematic release.
This version of @esportsplus/template might be problematic. Click here for more details.
- package/build/attributes.d.ts +3 -1
- package/build/attributes.js +37 -40
- package/build/constants.d.ts +3 -5
- package/build/constants.js +3 -5
- package/build/event.d.ts +1 -1
- package/build/event.js +53 -28
- package/build/html/cache.js +14 -70
- package/build/html/hydrate.js +6 -4
- package/build/slot.d.ts +2 -2
- package/build/slot.js +31 -34
- package/build/types.d.ts +7 -4
- package/build/types.js +1 -1
- package/package.json +2 -2
- package/src/attributes.ts +45 -44
- package/src/constants.ts +7 -14
- package/src/event.ts +76 -37
- package/src/html/cache.ts +16 -88
- package/src/html/hydrate.ts +6 -4
- package/src/slot.ts +38 -44
- package/src/types.ts +9 -4
package/src/attributes.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { effect, root } from '@esportsplus/reactivity';
|
|
2
|
-
import { ATTRIBUTES } from './constants';
|
|
3
2
|
import { Attributes, Element } from './types';
|
|
4
3
|
import { className, isArray, isObject, raf, removeAttribute, setAttribute } from './utilities';
|
|
5
4
|
import event from './event';
|
|
@@ -9,7 +8,8 @@ let attributes: Record<string, unknown> = {},
|
|
|
9
8
|
delimiters: Record<string, string> = {
|
|
10
9
|
class: ' ',
|
|
11
10
|
style: ';'
|
|
12
|
-
}
|
|
11
|
+
},
|
|
12
|
+
key = Symbol();
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
function attribute(element: Element, name: string, value: unknown) {
|
|
@@ -55,12 +55,22 @@ function reactive(element: Element, id: string, name: string, value: unknown, wa
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
function set(element: Element, value: unknown, name: string) {
|
|
58
|
-
if (
|
|
58
|
+
if (name === 'style' && isObject(value)) {
|
|
59
|
+
for (let key in value) {
|
|
60
|
+
set(element, value[key], name);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else if (isArray(value)) {
|
|
64
|
+
for (let i = 0, n = value.length; i < n; i++) {
|
|
65
|
+
set(element, value[i], name);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (typeof value === 'function') {
|
|
59
69
|
if (name.startsWith('on')) {
|
|
60
|
-
event(element, name
|
|
70
|
+
event(element, name as `on${string}`, value);
|
|
61
71
|
}
|
|
62
72
|
else {
|
|
63
|
-
reactive(element, ('e' + store(element)[
|
|
73
|
+
reactive(element, ('e' + store(element)[key]++), name, value, true);
|
|
64
74
|
}
|
|
65
75
|
}
|
|
66
76
|
else {
|
|
@@ -70,8 +80,8 @@ function set(element: Element, value: unknown, name: string) {
|
|
|
70
80
|
|
|
71
81
|
function store(element: Element) {
|
|
72
82
|
return (
|
|
73
|
-
element[
|
|
74
|
-
) as Attributes & { [
|
|
83
|
+
element[key] || (element[key] = { [key]: 0 })
|
|
84
|
+
) as Attributes & { [key]: number };
|
|
75
85
|
}
|
|
76
86
|
|
|
77
87
|
function update(element: Element, id: null | string, name: string, value: unknown, wait = false) {
|
|
@@ -155,47 +165,38 @@ function update(element: Element, id: null | string, name: string, value: unknow
|
|
|
155
165
|
}
|
|
156
166
|
|
|
157
167
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
168
|
+
const apply = (element: Element) => {
|
|
169
|
+
for (let key in attributes) {
|
|
170
|
+
attribute(element, key, attributes[key]);
|
|
171
|
+
}
|
|
163
172
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
else if (isArray(value)) {
|
|
173
|
-
for (let i = 0, n = value.length; i < n; i++) {
|
|
174
|
-
set(element, value[i], name);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
set(element, value, name);
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
spread: function (element: Element, attributes: Attributes | Attributes[]) {
|
|
182
|
-
if (isObject(attributes)) {
|
|
183
|
-
for (let name in attributes) {
|
|
184
|
-
this.set(element, attributes[name], name);
|
|
185
|
-
}
|
|
173
|
+
attributes = {};
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const spread = function (element: Element, attributes: Attributes | Attributes[]) {
|
|
177
|
+
if (isObject(attributes)) {
|
|
178
|
+
for (let name in attributes) {
|
|
179
|
+
set(element, attributes[name], name);
|
|
186
180
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
181
|
+
}
|
|
182
|
+
else if (isArray(attributes)) {
|
|
183
|
+
for (let i = 0, n = attributes.length; i < n; i++) {
|
|
184
|
+
let attrs = attributes[i];
|
|
190
185
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
186
|
+
if (!isObject(attrs)) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
194
189
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
190
|
+
for (let name in attrs) {
|
|
191
|
+
set(element, attrs[name], name);
|
|
198
192
|
}
|
|
199
193
|
}
|
|
200
194
|
}
|
|
201
|
-
|
|
195
|
+
else {
|
|
196
|
+
throw new Error('@esportsplus/template: attributes must be of type `Attributes` or `Attributes[]`; Received ' + JSON.stringify(attributes));
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
export default { apply, spread };
|
|
202
|
+
export { apply, spread }
|
package/src/constants.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
const ATTRIBUTES = Symbol();
|
|
2
|
-
|
|
3
|
-
|
|
4
1
|
const NODE_CLOSING = 1;
|
|
5
2
|
|
|
6
3
|
const NODE_COMMENT = 2;
|
|
@@ -36,12 +33,6 @@ const NODE_WHITELIST: Record<string, number> = {
|
|
|
36
33
|
|
|
37
34
|
const REGEX_EMPTY_TEXT_NODES = /(>|})\s+(<|{)/g;
|
|
38
35
|
|
|
39
|
-
const REGEX_EVENTS = /(?:\s*on[\w-:]+\s*=\s*["'][^"']*["'])/g;
|
|
40
|
-
|
|
41
|
-
const REGEX_EXTRA_WHITESPACE = /\s\s+/g;
|
|
42
|
-
|
|
43
|
-
const REGEX_SLOT_ATTRIBUTES = /<[\w-]+([^><]*{{\$}}[^><]*)>/g;
|
|
44
|
-
|
|
45
36
|
const REGEX_SLOT_NODES = /<([\w-]+|[\/!])(?:([^><]*{{\$}}[^><]*)|(?:[^><]*))?>|{{\$}}/g;
|
|
46
37
|
|
|
47
38
|
|
|
@@ -54,27 +45,29 @@ const RENDERABLE_TEMPLATE = Symbol();
|
|
|
54
45
|
|
|
55
46
|
const SLOT = Symbol();
|
|
56
47
|
|
|
48
|
+
const SLOT_CLEANUP = Symbol();
|
|
49
|
+
|
|
57
50
|
const SLOT_HTML = '<!--$-->';
|
|
58
51
|
|
|
59
52
|
const SLOT_MARKER = '{{$}}';
|
|
60
53
|
|
|
54
|
+
const SLOT_MARKER_LENGTH = SLOT_MARKER.length;
|
|
55
|
+
|
|
61
56
|
|
|
62
57
|
export {
|
|
63
|
-
ATTRIBUTES,
|
|
64
58
|
NODE_CLOSING,
|
|
65
59
|
NODE_ELEMENT,
|
|
66
60
|
NODE_SLOT,
|
|
67
61
|
NODE_VOID,
|
|
68
62
|
NODE_WHITELIST,
|
|
69
63
|
REGEX_EMPTY_TEXT_NODES,
|
|
70
|
-
REGEX_EVENTS,
|
|
71
|
-
REGEX_EXTRA_WHITESPACE,
|
|
72
|
-
REGEX_SLOT_ATTRIBUTES,
|
|
73
64
|
REGEX_SLOT_NODES,
|
|
74
65
|
RENDERABLE,
|
|
75
66
|
RENDERABLE_REACTIVE,
|
|
76
67
|
RENDERABLE_TEMPLATE,
|
|
77
68
|
SLOT,
|
|
69
|
+
SLOT_CLEANUP,
|
|
78
70
|
SLOT_HTML,
|
|
79
|
-
SLOT_MARKER
|
|
71
|
+
SLOT_MARKER,
|
|
72
|
+
SLOT_MARKER_LENGTH
|
|
80
73
|
};
|
package/src/event.ts
CHANGED
|
@@ -1,51 +1,31 @@
|
|
|
1
1
|
import { root } from '@esportsplus/reactivity';
|
|
2
|
+
import { SLOT_CLEANUP } from './constants';
|
|
2
3
|
import { Element } from './types';
|
|
3
4
|
import { addEventListener, defineProperty, parentElement } from './utilities';
|
|
4
5
|
|
|
5
6
|
|
|
6
|
-
let capture = new Set(['
|
|
7
|
+
let capture = new Set<`on${string}`>(['onblur', 'onfocus', 'onscroll']),
|
|
8
|
+
controllers = new Map<
|
|
9
|
+
`on${string}`,
|
|
10
|
+
(AbortController & { listeners: number }) | null
|
|
11
|
+
>(),
|
|
7
12
|
keys: Record<string, symbol> = {},
|
|
8
|
-
passive = new Set([
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
+
passive = new Set<`on${string}`>([
|
|
14
|
+
'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel',
|
|
15
|
+
'onscroll',
|
|
16
|
+
'ontouchcancel', 'ontouchend', 'ontouchleave', 'ontouchmove', 'ontouchstart',
|
|
17
|
+
'onwheel'
|
|
13
18
|
]);
|
|
14
19
|
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
(['onmousemove', 'onmousewheel', 'onscroll', 'ontouchend', 'ontouchmove', 'ontouchstart', 'onwheel'] as `on${string}`[]).map(event => {
|
|
22
|
+
controllers.set(event, null);
|
|
23
|
+
});
|
|
19
24
|
|
|
20
|
-
addEventListener.call(window.document, type, (e) => {
|
|
21
|
-
let node = e.target as Element | null;
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
configurable: true,
|
|
25
|
-
get() {
|
|
26
|
-
return node || window.document;
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
while (node) {
|
|
31
|
-
if (key in node) {
|
|
32
|
-
return (node[key] as Function).call(node, e);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
node = parentElement.call(node);
|
|
36
|
-
}
|
|
37
|
-
}, {
|
|
38
|
-
capture: capture.has(type),
|
|
39
|
-
passive: passive.has(type)
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
return key;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
export default (element: Element, event: string, listener: Function): void => {
|
|
26
|
+
export default (element: Element, event: `on${string}`, listener: Function) => {
|
|
47
27
|
if (event === 'onmount') {
|
|
48
|
-
let interval
|
|
28
|
+
let interval = setInterval(() => {
|
|
49
29
|
retry--;
|
|
50
30
|
|
|
51
31
|
if (element.isConnected) {
|
|
@@ -66,5 +46,64 @@ export default (element: Element, event: string, listener: Function): void => {
|
|
|
66
46
|
return root(() => listener(element));
|
|
67
47
|
}
|
|
68
48
|
|
|
69
|
-
|
|
49
|
+
let controller = controllers.get(event),
|
|
50
|
+
signal: AbortController['signal'] | undefined;
|
|
51
|
+
|
|
52
|
+
if (controller === null) {
|
|
53
|
+
let { abort, signal } = new AbortController();
|
|
54
|
+
|
|
55
|
+
controllers.set(
|
|
56
|
+
event,
|
|
57
|
+
controller = {
|
|
58
|
+
abort,
|
|
59
|
+
signal,
|
|
60
|
+
listeners: 0,
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (controller) {
|
|
66
|
+
controller.listeners++;
|
|
67
|
+
|
|
68
|
+
( element[SLOT_CLEANUP] ??= [] ).push(() => {
|
|
69
|
+
if (--controller.listeners) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
controller.abort();
|
|
74
|
+
controllers.set(event, null);
|
|
75
|
+
})
|
|
76
|
+
signal = controller.signal;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let key = keys[event];
|
|
80
|
+
|
|
81
|
+
if (!key) {
|
|
82
|
+
key = keys[event] = Symbol();
|
|
83
|
+
|
|
84
|
+
addEventListener.call(window.document, event.slice(2), (e) => {
|
|
85
|
+
let node = e.target as Element | null;
|
|
86
|
+
|
|
87
|
+
defineProperty(e, 'currentTarget', {
|
|
88
|
+
configurable: true,
|
|
89
|
+
get() {
|
|
90
|
+
return node || window.document;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
while (node) {
|
|
95
|
+
if (key in node) {
|
|
96
|
+
return (node[key] as Function).call(node, e);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
node = parentElement.call(node);
|
|
100
|
+
}
|
|
101
|
+
}, {
|
|
102
|
+
capture: capture.has(event),
|
|
103
|
+
passive: passive.has(event),
|
|
104
|
+
signal
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
element[key] = listener;
|
|
70
109
|
};
|
package/src/html/cache.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES,
|
|
3
|
-
|
|
2
|
+
NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES,
|
|
3
|
+
REGEX_SLOT_NODES, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH
|
|
4
4
|
} from '~/constants';
|
|
5
5
|
import { RenderableTemplate, Template } from '~/types';
|
|
6
6
|
import { firstChild, firstElementChild, fragment, nextElementSibling, nextSibling } from '~/utilities';
|
|
7
|
-
import
|
|
7
|
+
import { spread } from '~/attributes';
|
|
8
8
|
import s from '~/slot';
|
|
9
9
|
|
|
10
10
|
|
|
@@ -16,9 +16,10 @@ function build(literals: TemplateStringsArray, values: unknown[]) {
|
|
|
16
16
|
return set(literals, literals[0]);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
let
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
let buffer = '',
|
|
20
|
+
html = literals.join(SLOT_MARKER)
|
|
21
|
+
.replace(REGEX_EMPTY_TEXT_NODES, '$1$2')
|
|
22
|
+
.trim(),
|
|
22
23
|
index = 0,
|
|
23
24
|
level = 0,
|
|
24
25
|
levels = [{
|
|
@@ -26,73 +27,11 @@ function build(literals: TemplateStringsArray, values: unknown[]) {
|
|
|
26
27
|
elements: 0,
|
|
27
28
|
path: [] as NonNullable<Template['slots']>[0]['path']
|
|
28
29
|
}],
|
|
30
|
+
parsed = html.split(SLOT_MARKER),
|
|
29
31
|
slot = 0,
|
|
30
32
|
slots: Template['slots'] = [],
|
|
31
33
|
total = values.length;
|
|
32
34
|
|
|
33
|
-
{
|
|
34
|
-
let attribute = '',
|
|
35
|
-
buffer = '',
|
|
36
|
-
char = '',
|
|
37
|
-
quote = '';
|
|
38
|
-
|
|
39
|
-
for (let match of html.matchAll(REGEX_SLOT_ATTRIBUTES)) {
|
|
40
|
-
let found = match[1],
|
|
41
|
-
metadata = attributes[found];
|
|
42
|
-
|
|
43
|
-
if (metadata) {
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
metadata = attributes[found] = [];
|
|
48
|
-
|
|
49
|
-
for (let i = 0, n = found.length; i < n; i++) {
|
|
50
|
-
char = found[i];
|
|
51
|
-
|
|
52
|
-
if (char === ' ') {
|
|
53
|
-
buffer = '';
|
|
54
|
-
}
|
|
55
|
-
else if (char === '=') {
|
|
56
|
-
attribute = buffer;
|
|
57
|
-
buffer = '';
|
|
58
|
-
}
|
|
59
|
-
else if (char === '"' || char === "'") {
|
|
60
|
-
if (!attribute) {
|
|
61
|
-
}
|
|
62
|
-
else if (!quote) {
|
|
63
|
-
quote = char;
|
|
64
|
-
}
|
|
65
|
-
else if (quote === char) {
|
|
66
|
-
attribute = '';
|
|
67
|
-
buffer = '';
|
|
68
|
-
quote = '';
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
else if (char === '{' && char !== buffer) {
|
|
72
|
-
buffer = char;
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
buffer += char;
|
|
76
|
-
|
|
77
|
-
if (buffer === SLOT_MARKER) {
|
|
78
|
-
buffer = '';
|
|
79
|
-
|
|
80
|
-
if (attribute) {
|
|
81
|
-
metadata.push(attribute);
|
|
82
|
-
|
|
83
|
-
if (!quote) {
|
|
84
|
-
attribute = '';
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
metadata.push(null);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
35
|
for (let match of html.matchAll(REGEX_SLOT_NODES)) {
|
|
97
36
|
let parent = levels[level],
|
|
98
37
|
type = match[1] === undefined ? NODE_SLOT : (NODE_WHITELIST[match[1].toLowerCase()] || NODE_ELEMENT);
|
|
@@ -106,24 +45,18 @@ function build(literals: TemplateStringsArray, values: unknown[]) {
|
|
|
106
45
|
let attr = match[2];
|
|
107
46
|
|
|
108
47
|
if (attr) {
|
|
109
|
-
let
|
|
48
|
+
let i = attr.indexOf(SLOT_MARKER),
|
|
110
49
|
path = methods(parent.children, parent.path, firstChild, nextSibling);
|
|
111
50
|
|
|
112
|
-
|
|
113
|
-
throw new Error(`Template: attribute metadata could not be found for '${attr}'`);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
for (let i = 0, n = metadata.length; i < n; i++) {
|
|
117
|
-
let name = metadata[i];
|
|
118
|
-
|
|
51
|
+
while (i !== -1) {
|
|
119
52
|
slots.push({
|
|
120
|
-
fn:
|
|
121
|
-
name,
|
|
53
|
+
fn: spread,
|
|
122
54
|
path,
|
|
123
55
|
slot
|
|
124
56
|
});
|
|
125
57
|
|
|
126
|
-
buffer +=
|
|
58
|
+
buffer += parsed[slot++];
|
|
59
|
+
i = attr.indexOf(SLOT_MARKER, i + SLOT_MARKER_LENGTH);
|
|
127
60
|
}
|
|
128
61
|
}
|
|
129
62
|
|
|
@@ -140,17 +73,16 @@ function build(literals: TemplateStringsArray, values: unknown[]) {
|
|
|
140
73
|
parent.elements++;
|
|
141
74
|
}
|
|
142
75
|
else if (type === NODE_SLOT) {
|
|
143
|
-
buffer +=
|
|
76
|
+
buffer += parsed[slot] + SLOT_HTML;
|
|
144
77
|
slots.push({
|
|
145
78
|
fn: s,
|
|
146
|
-
name: null,
|
|
147
79
|
path: methods(parent.children, parent.path, firstChild, nextSibling),
|
|
148
80
|
slot: slot++
|
|
149
81
|
});
|
|
150
82
|
}
|
|
151
83
|
|
|
152
84
|
if (slot === total) {
|
|
153
|
-
buffer +=
|
|
85
|
+
buffer += parsed[slot];
|
|
154
86
|
break;
|
|
155
87
|
}
|
|
156
88
|
|
|
@@ -164,7 +96,7 @@ function build(literals: TemplateStringsArray, values: unknown[]) {
|
|
|
164
96
|
index = (match.index || 0) + match[0].length;
|
|
165
97
|
}
|
|
166
98
|
|
|
167
|
-
return set(literals,
|
|
99
|
+
return set(literals, buffer, slots);
|
|
168
100
|
}
|
|
169
101
|
|
|
170
102
|
function methods(children: number, copy: (typeof firstChild)[], first: (typeof firstChild), next: (typeof firstChild)) {
|
|
@@ -179,10 +111,6 @@ function methods(children: number, copy: (typeof firstChild)[], first: (typeof f
|
|
|
179
111
|
return methods;
|
|
180
112
|
}
|
|
181
113
|
|
|
182
|
-
function minify(html: string) {
|
|
183
|
-
return html.replace(REGEX_EMPTY_TEXT_NODES, '$1$2').replace(REGEX_EXTRA_WHITESPACE, ' ').trim();
|
|
184
|
-
}
|
|
185
|
-
|
|
186
114
|
function set(literals: TemplateStringsArray, html: string, slots: Template['slots'] = null) {
|
|
187
115
|
let template = {
|
|
188
116
|
fragment: fragment(html),
|
package/src/html/hydrate.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { root, ReactiveArray } from '@esportsplus/reactivity';
|
|
|
2
2
|
import { Element, Elements, Renderable, RenderableReactive, RenderableTemplate, Template } from '~/types';
|
|
3
3
|
import { Slot } from '~/slot';
|
|
4
4
|
import { cloneNode, firstChild, nextSibling } from '~/utilities';
|
|
5
|
-
import
|
|
5
|
+
import { apply } from '~/attributes';
|
|
6
6
|
import cache from './cache';
|
|
7
7
|
|
|
8
8
|
|
|
@@ -41,11 +41,11 @@ function render<T>(renderable: Renderable<T>, template: Template) {
|
|
|
41
41
|
values = renderable.values;
|
|
42
42
|
|
|
43
43
|
for (let i = slots.length - 1; i >= 0; i--) {
|
|
44
|
-
let { fn,
|
|
44
|
+
let { fn, path, slot } = slots[i];
|
|
45
45
|
|
|
46
46
|
if (path === previous) {}
|
|
47
47
|
else {
|
|
48
|
-
|
|
48
|
+
apply(node);
|
|
49
49
|
|
|
50
50
|
node = fragment;
|
|
51
51
|
previous = path;
|
|
@@ -59,7 +59,9 @@ function render<T>(renderable: Renderable<T>, template: Template) {
|
|
|
59
59
|
fn(node, values[slot], name);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
if (node) {
|
|
63
|
+
apply(node);
|
|
64
|
+
}
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
for (let element = firstChild.call(fragment as Element); element; element = nextSibling.call(element)) {
|
package/src/slot.ts
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { effect, root, DIRTY } from '@esportsplus/reactivity';
|
|
2
|
-
import { RENDERABLE, RENDERABLE_REACTIVE, SLOT } from './constants';
|
|
2
|
+
import { RENDERABLE, RENDERABLE_REACTIVE, SLOT, SLOT_CLEANUP } from './constants';
|
|
3
3
|
import { hydrate } from './html';
|
|
4
4
|
import { Element, Elements, RenderableReactive, RenderableTemplate } from './types';
|
|
5
5
|
import { firstChild, isArray, isObject, nextSibling, nodeValue, raf, text } from './utilities'
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
let cleanup: Slot[] = [],
|
|
10
|
-
key = Symbol(),
|
|
8
|
+
let cleanup: VoidFunction[] = [],
|
|
11
9
|
scheduled = false;
|
|
12
10
|
|
|
13
11
|
|
|
14
|
-
function
|
|
12
|
+
function after(anchor: Element, groups: Elements[]) {
|
|
15
13
|
for (let i = 0, n = groups.length; i < n; i++) {
|
|
16
14
|
let group = groups[i];
|
|
17
15
|
|
|
@@ -24,37 +22,15 @@ function afterGroups(anchor: Element, groups: Elements[]) {
|
|
|
24
22
|
return groups;
|
|
25
23
|
}
|
|
26
24
|
|
|
27
|
-
function
|
|
28
|
-
if (group === undefined) {
|
|
29
|
-
return group;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
for (let i = 0, n = group.length; i < n; i++) {
|
|
33
|
-
let item = group[i];
|
|
34
|
-
|
|
35
|
-
if (key in item) {
|
|
36
|
-
cleanup.push(item[key] as Slot);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
item.remove();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (!scheduled && cleanup.length) {
|
|
43
|
-
schedule();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return group;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function removeGroups(groups: Elements[]) {
|
|
25
|
+
function remove(...groups: Elements[]) {
|
|
50
26
|
for (let i = 0, n = groups.length; i < n; i++) {
|
|
51
27
|
let group = groups[i];
|
|
52
28
|
|
|
53
29
|
for (let j = 0, o = group.length; j < o; j++) {
|
|
54
30
|
let item = group[j];
|
|
55
31
|
|
|
56
|
-
if (
|
|
57
|
-
cleanup.push(item[
|
|
32
|
+
if (item[SLOT_CLEANUP]) {
|
|
33
|
+
cleanup.push(...item[SLOT_CLEANUP]);
|
|
58
34
|
}
|
|
59
35
|
|
|
60
36
|
item.remove();
|
|
@@ -80,14 +56,14 @@ function render(anchor: Element | null, input: unknown, slot?: Slot): Elements |
|
|
|
80
56
|
groups.push( render(null, input[i]) as Elements );
|
|
81
57
|
}
|
|
82
58
|
|
|
83
|
-
return anchor ?
|
|
59
|
+
return anchor ? after(anchor, groups) : groups;
|
|
84
60
|
}
|
|
85
61
|
|
|
86
62
|
let nodes: Elements = [];
|
|
87
63
|
|
|
88
64
|
if (RENDERABLE in input) {
|
|
89
65
|
if (input[RENDERABLE] === RENDERABLE_REACTIVE) {
|
|
90
|
-
return
|
|
66
|
+
return after(anchor!, hydrate.reactive(input as RenderableReactive, slot!));
|
|
91
67
|
}
|
|
92
68
|
else {
|
|
93
69
|
nodes = hydrate.static(input as RenderableTemplate<unknown>);
|
|
@@ -135,15 +111,19 @@ function schedule() {
|
|
|
135
111
|
|
|
136
112
|
raf.add(() => {
|
|
137
113
|
try {
|
|
138
|
-
let
|
|
114
|
+
let fn;
|
|
139
115
|
|
|
140
|
-
while (
|
|
141
|
-
|
|
116
|
+
while (fn = cleanup.pop()) {
|
|
117
|
+
fn();
|
|
142
118
|
}
|
|
143
119
|
}
|
|
144
|
-
catch(e) {}
|
|
120
|
+
catch(e) { }
|
|
145
121
|
|
|
146
122
|
scheduled = false;
|
|
123
|
+
|
|
124
|
+
if (cleanup.length) {
|
|
125
|
+
schedule();
|
|
126
|
+
}
|
|
147
127
|
});
|
|
148
128
|
}
|
|
149
129
|
|
|
@@ -157,7 +137,9 @@ class Slot {
|
|
|
157
137
|
|
|
158
138
|
|
|
159
139
|
constructor(marker: Element) {
|
|
160
|
-
marker[
|
|
140
|
+
( marker[SLOT_CLEANUP] ??= [] ).push(() => {
|
|
141
|
+
this.clear();
|
|
142
|
+
});
|
|
161
143
|
|
|
162
144
|
this.marker = marker;
|
|
163
145
|
this.nodes = [];
|
|
@@ -185,16 +167,22 @@ class Slot {
|
|
|
185
167
|
}
|
|
186
168
|
|
|
187
169
|
clear() {
|
|
188
|
-
|
|
170
|
+
remove(...this.nodes);
|
|
189
171
|
this.text = null;
|
|
190
172
|
}
|
|
191
173
|
|
|
192
174
|
pop() {
|
|
193
|
-
|
|
175
|
+
let group = this.nodes.pop();
|
|
176
|
+
|
|
177
|
+
if (!group) {
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return remove(group);
|
|
194
182
|
}
|
|
195
183
|
|
|
196
184
|
push(...groups: Elements[]) {
|
|
197
|
-
|
|
185
|
+
after(this.anchor(), groups);
|
|
198
186
|
|
|
199
187
|
for (let i = 0, n = groups.length; i < n; i++) {
|
|
200
188
|
this.nodes.push(groups[i]);
|
|
@@ -252,17 +240,23 @@ class Slot {
|
|
|
252
240
|
}
|
|
253
241
|
|
|
254
242
|
shift() {
|
|
255
|
-
|
|
243
|
+
let group = this.nodes.shift();
|
|
244
|
+
|
|
245
|
+
if (!group) {
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return remove(group);
|
|
256
250
|
}
|
|
257
251
|
|
|
258
252
|
splice(start: number, stop: number = this.nodes.length, ...groups: Elements[]) {
|
|
259
|
-
return
|
|
260
|
-
this.nodes.splice(start, stop, ...
|
|
253
|
+
return remove(
|
|
254
|
+
...this.nodes.splice(start, stop, ...after(this.anchor(start), groups))
|
|
261
255
|
);
|
|
262
256
|
}
|
|
263
257
|
|
|
264
258
|
unshift(...groups: Elements[]) {
|
|
265
|
-
return this.nodes.unshift(...
|
|
259
|
+
return this.nodes.unshift(...after(this.marker, groups));
|
|
266
260
|
}
|
|
267
261
|
}
|
|
268
262
|
|