@esportsplus/template 0.15.16 → 0.15.17
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.d.ts +2 -2
- package/build/attributes.js +87 -47
- package/build/constants.d.ts +2 -1
- package/build/constants.js +2 -1
- package/build/event.js +6 -5
- package/build/html/hydrate.d.ts +6 -3
- package/build/html/hydrate.js +26 -23
- package/build/slot.d.ts +4 -4
- package/build/slot.js +62 -61
- package/build/types.d.ts +8 -4
- package/build/types.js +1 -1
- package/build/utilities.d.ts +2 -1
- package/build/utilities.js +2 -1
- package/package.json +3 -3
- package/src/attributes.ts +131 -52
- package/src/constants.ts +8 -14
- package/src/event.ts +8 -6
- package/src/html/hydrate.ts +33 -34
- package/src/slot.ts +85 -79
- package/src/types.ts +13 -5
- package/src/utilities.ts +3 -1
package/package.json
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"author": "ICJR",
|
|
3
3
|
"dependencies": {
|
|
4
4
|
"@esportsplus/queue": "^0.1.0",
|
|
5
|
-
"@esportsplus/reactivity": "^0.12.
|
|
5
|
+
"@esportsplus/reactivity": "^0.12.4",
|
|
6
6
|
"@esportsplus/tasks": "^0.2.1",
|
|
7
|
-
"@esportsplus/utilities": "^0.
|
|
7
|
+
"@esportsplus/utilities": "^0.22.1"
|
|
8
8
|
},
|
|
9
9
|
"devDependencies": {
|
|
10
10
|
"@esportsplus/typescript": "^0.9.2"
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"private": false,
|
|
15
15
|
"type": "module",
|
|
16
16
|
"types": "./build/index.d.ts",
|
|
17
|
-
"version": "0.15.
|
|
17
|
+
"version": "0.15.17",
|
|
18
18
|
"scripts": {
|
|
19
19
|
"build": "tsc && tsc-alias",
|
|
20
20
|
"-": "-"
|
package/src/attributes.ts
CHANGED
|
@@ -1,17 +1,41 @@
|
|
|
1
1
|
import { effect } from '@esportsplus/reactivity';
|
|
2
2
|
import { isArray, isFunction, isObject, isString } from '@esportsplus/utilities';
|
|
3
|
+
import { ATTRIBUTE_STORE } from '~/constants';
|
|
3
4
|
import { ondisconnect } from './slot';
|
|
4
5
|
import { Attributes, Element } from './types';
|
|
5
6
|
import { className, raf, removeAttribute, setAttribute } from './utilities';
|
|
7
|
+
import q from '@esportsplus/queue';
|
|
6
8
|
import event from './event';
|
|
7
9
|
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
const EFFECT_KEY = Symbol();
|
|
12
|
+
|
|
13
|
+
const UPDATES_KEY = Symbol();
|
|
14
|
+
|
|
15
|
+
const STATE_HYDRATING = 0;
|
|
16
|
+
|
|
17
|
+
const STATE_NONE = 1;
|
|
18
|
+
|
|
19
|
+
const STATE_WAITING = 2;
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
type Context = {
|
|
23
|
+
element: Element;
|
|
24
|
+
store: Record<PropertyKey, unknown>
|
|
25
|
+
updates: Record<PropertyKey, unknown>;
|
|
26
|
+
updating: boolean;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
type State = typeof STATE_HYDRATING | typeof STATE_NONE | typeof STATE_WAITING;
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
let delimiters: Record<string, string> = {
|
|
11
33
|
class: ' ',
|
|
12
34
|
style: ';'
|
|
13
35
|
},
|
|
14
|
-
|
|
36
|
+
hydrating: Record<string, unknown> = {},
|
|
37
|
+
queue = q<Context>(64),
|
|
38
|
+
scheduled = false;
|
|
15
39
|
|
|
16
40
|
|
|
17
41
|
function attribute(element: Element, name: string, value: unknown) {
|
|
@@ -29,73 +53,115 @@ function attribute(element: Element, name: string, value: unknown) {
|
|
|
29
53
|
}
|
|
30
54
|
}
|
|
31
55
|
|
|
32
|
-
function
|
|
56
|
+
function schedule() {
|
|
57
|
+
if (scheduled) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
scheduled = true;
|
|
62
|
+
raf.add(task);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function set(context: Context, name: string, value: unknown, state: State) {
|
|
33
66
|
if (isArray(value)) {
|
|
34
67
|
for (let i = 0, n = value.length; i < n; i++) {
|
|
35
|
-
set(
|
|
68
|
+
set(context, name, value[i], state);
|
|
36
69
|
}
|
|
37
70
|
}
|
|
38
71
|
else if (isFunction(value)) {
|
|
39
72
|
if (name.startsWith('on')) {
|
|
40
|
-
event(element, name as `on${string}`, value);
|
|
73
|
+
event(context.element, name as `on${string}`, value);
|
|
41
74
|
}
|
|
42
75
|
else {
|
|
43
|
-
|
|
76
|
+
context.store[EFFECT_KEY] ??= 0;
|
|
77
|
+
|
|
78
|
+
let id = (context.store[EFFECT_KEY] as number)++;
|
|
44
79
|
|
|
45
80
|
ondisconnect(
|
|
46
|
-
element,
|
|
81
|
+
context.element,
|
|
47
82
|
effect(() => {
|
|
48
|
-
let v = (value as Function)(element);
|
|
83
|
+
let v = (value as Function)(context.element);
|
|
49
84
|
|
|
50
85
|
if (isArray(v)) {
|
|
51
86
|
let last = v.length - 1;
|
|
52
87
|
|
|
53
88
|
for (let i = 0, n = v.length; i < n; i++) {
|
|
54
|
-
update(
|
|
89
|
+
update(
|
|
90
|
+
context,
|
|
91
|
+
id,
|
|
92
|
+
name,
|
|
93
|
+
v[i],
|
|
94
|
+
state === STATE_HYDRATING
|
|
95
|
+
? state
|
|
96
|
+
: i !== last ? STATE_WAITING : state
|
|
97
|
+
);
|
|
55
98
|
}
|
|
56
99
|
}
|
|
57
100
|
else {
|
|
58
|
-
update(
|
|
101
|
+
update(context, id, name, v, state);
|
|
59
102
|
}
|
|
60
103
|
})
|
|
61
104
|
);
|
|
62
105
|
|
|
63
|
-
|
|
106
|
+
state = STATE_NONE;
|
|
64
107
|
}
|
|
65
108
|
}
|
|
66
109
|
else {
|
|
67
|
-
update(
|
|
110
|
+
update(context, null, name, value, state);
|
|
68
111
|
}
|
|
69
112
|
}
|
|
70
113
|
|
|
71
|
-
function
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
114
|
+
function task() {
|
|
115
|
+
let context,
|
|
116
|
+
n = queue.length;
|
|
117
|
+
|
|
118
|
+
while ((context = queue.next()) && n--) {
|
|
119
|
+
let { element, updates } = context;
|
|
120
|
+
|
|
121
|
+
for (let name in updates) {
|
|
122
|
+
attribute(element, name, updates[name]);
|
|
123
|
+
delete updates[name];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
context.updating = false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (queue.length) {
|
|
130
|
+
raf.add(task);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
scheduled = false;
|
|
134
|
+
}
|
|
75
135
|
}
|
|
76
136
|
|
|
77
|
-
function update(
|
|
137
|
+
function update(
|
|
138
|
+
context: Context,
|
|
139
|
+
id: null | number,
|
|
140
|
+
name: string,
|
|
141
|
+
value: unknown,
|
|
142
|
+
state: State
|
|
143
|
+
) {
|
|
78
144
|
if (value === false || value == null) {
|
|
79
145
|
value = '';
|
|
80
146
|
}
|
|
81
147
|
|
|
82
|
-
let
|
|
148
|
+
let store = context.store;
|
|
83
149
|
|
|
84
150
|
if (name in delimiters) {
|
|
85
151
|
let cache = name + '.static',
|
|
86
152
|
delimiter = delimiters[name],
|
|
87
|
-
dynamic =
|
|
153
|
+
dynamic = store[name] as Attributes | undefined;
|
|
88
154
|
|
|
89
155
|
if (dynamic === undefined) {
|
|
90
|
-
let value = (element.getAttribute(name) || '').trim();
|
|
156
|
+
let value = (context.element.getAttribute(name) || '').trim();
|
|
91
157
|
|
|
92
|
-
|
|
93
|
-
|
|
158
|
+
store[cache] = value;
|
|
159
|
+
store[name] = dynamic = {};
|
|
94
160
|
}
|
|
95
161
|
|
|
96
162
|
if (id === null) {
|
|
97
163
|
if (value && isString(value)) {
|
|
98
|
-
|
|
164
|
+
store[cache] += (store[cache] ? delimiter : '') + value;
|
|
99
165
|
}
|
|
100
166
|
}
|
|
101
167
|
else {
|
|
@@ -117,7 +183,7 @@ function update(element: Element, id: null | string, name: string, value: unknow
|
|
|
117
183
|
}
|
|
118
184
|
}
|
|
119
185
|
|
|
120
|
-
let cold =
|
|
186
|
+
let cold = store[id] as Attributes | undefined;
|
|
121
187
|
|
|
122
188
|
if (cold !== undefined) {
|
|
123
189
|
for (let part in cold) {
|
|
@@ -129,61 +195,74 @@ function update(element: Element, id: null | string, name: string, value: unknow
|
|
|
129
195
|
}
|
|
130
196
|
}
|
|
131
197
|
|
|
132
|
-
|
|
198
|
+
store[id] = hot;
|
|
133
199
|
}
|
|
134
200
|
|
|
135
|
-
value =
|
|
201
|
+
value = store[cache];
|
|
136
202
|
|
|
137
203
|
for (let key in dynamic) {
|
|
138
204
|
value += (value ? delimiter : '') + key;
|
|
139
205
|
}
|
|
140
206
|
}
|
|
141
|
-
else if (
|
|
142
|
-
if (
|
|
207
|
+
else if (id !== null) {
|
|
208
|
+
if (store[name] === value) {
|
|
143
209
|
return;
|
|
144
210
|
}
|
|
145
211
|
|
|
146
|
-
|
|
212
|
+
store[name] = value as string;
|
|
147
213
|
}
|
|
148
214
|
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
attributes[name] = value;
|
|
152
|
-
}
|
|
215
|
+
if (state === STATE_HYDRATING) {
|
|
216
|
+
hydrating[name] = value;
|
|
153
217
|
}
|
|
154
218
|
else {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
219
|
+
context.updates[name] = value;
|
|
220
|
+
|
|
221
|
+
if (state === STATE_NONE && !context.updating) {
|
|
222
|
+
context.updating = true;
|
|
223
|
+
queue.add(context);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (!scheduled) {
|
|
227
|
+
schedule();
|
|
228
|
+
}
|
|
158
229
|
}
|
|
159
230
|
}
|
|
160
231
|
|
|
161
232
|
|
|
162
233
|
const apply = (element: Element) => {
|
|
163
|
-
for (let key in
|
|
164
|
-
attribute(element, key,
|
|
234
|
+
for (let key in hydrating) {
|
|
235
|
+
attribute(element, key, hydrating[key]);
|
|
165
236
|
}
|
|
166
237
|
|
|
167
|
-
|
|
238
|
+
hydrating = {};
|
|
168
239
|
};
|
|
169
240
|
|
|
170
|
-
const spread = function (element: Element,
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
let attrs = attributes[i];
|
|
241
|
+
const spread = function (element: Element, value: Attributes | Attributes[]) {
|
|
242
|
+
let cache = element[ATTRIBUTE_STORE] ??= { [UPDATES_KEY]: {} },
|
|
243
|
+
context = {
|
|
244
|
+
element,
|
|
245
|
+
store: cache,
|
|
246
|
+
updates: cache[UPDATES_KEY] as Record<PropertyKey, unknown>,
|
|
247
|
+
updating: false
|
|
248
|
+
};
|
|
179
249
|
|
|
180
|
-
|
|
181
|
-
|
|
250
|
+
if (isArray(value)) {
|
|
251
|
+
for (let i = 0, n = value.length; i < n; i++) {
|
|
252
|
+
let v = value[i];
|
|
253
|
+
|
|
254
|
+
for (let name in v) {
|
|
255
|
+
set(context, name, v[name], STATE_HYDRATING);
|
|
182
256
|
}
|
|
183
257
|
}
|
|
184
258
|
}
|
|
259
|
+
else if (isObject(value)) {
|
|
260
|
+
for (let name in value) {
|
|
261
|
+
set(context, name, value[name], STATE_HYDRATING);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
185
264
|
};
|
|
186
265
|
|
|
187
266
|
|
|
188
267
|
export default { apply, spread };
|
|
189
|
-
export { apply, spread }
|
|
268
|
+
export { apply, spread };
|
package/src/constants.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
const ATTRIBUTE_STORE = Symbol();
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
const NODE_CLOSING = 1;
|
|
2
5
|
|
|
3
6
|
const NODE_COMMENT = 2;
|
|
@@ -53,18 +56,9 @@ const SLOT_MARKER_LENGTH = SLOT_MARKER.length;
|
|
|
53
56
|
|
|
54
57
|
|
|
55
58
|
export {
|
|
56
|
-
|
|
57
|
-
NODE_ELEMENT,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
REGEX_EMPTY_TEXT_NODES,
|
|
62
|
-
REGEX_SLOT_NODES,
|
|
63
|
-
RENDERABLE,
|
|
64
|
-
RENDERABLE_REACTIVE,
|
|
65
|
-
RENDERABLE_TEMPLATE,
|
|
66
|
-
SLOT_CLEANUP,
|
|
67
|
-
SLOT_HTML,
|
|
68
|
-
SLOT_MARKER,
|
|
69
|
-
SLOT_MARKER_LENGTH
|
|
59
|
+
ATTRIBUTE_STORE,
|
|
60
|
+
NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST,
|
|
61
|
+
REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES,
|
|
62
|
+
RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE,
|
|
63
|
+
SLOT_CLEANUP, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH
|
|
70
64
|
};
|
package/src/event.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { root } from '@esportsplus/reactivity';
|
|
|
2
2
|
import { defineProperty } from '@esportsplus/utilities';
|
|
3
3
|
import { ondisconnect } from './slot';
|
|
4
4
|
import { Element } from './types';
|
|
5
|
-
import { addEventListener, parentElement } from './utilities';
|
|
5
|
+
import { addEventListener, parentElement, raf } from './utilities';
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
let capture = new Set<`on${string}`>(['onblur', 'onfocus', 'onscroll']),
|
|
@@ -26,7 +26,8 @@ let capture = new Set<`on${string}`>(['onblur', 'onfocus', 'onscroll']),
|
|
|
26
26
|
|
|
27
27
|
export default (element: Element, event: `on${string}`, listener: Function): void => {
|
|
28
28
|
if (event === 'onconnect') {
|
|
29
|
-
let
|
|
29
|
+
let retry = 60,
|
|
30
|
+
task = () => {
|
|
30
31
|
retry--;
|
|
31
32
|
|
|
32
33
|
if (element.isConnected) {
|
|
@@ -34,11 +35,12 @@ export default (element: Element, event: `on${string}`, listener: Function): voi
|
|
|
34
35
|
root(() => listener(element));
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
if (
|
|
38
|
-
|
|
38
|
+
if (retry) {
|
|
39
|
+
raf.add(task);
|
|
39
40
|
}
|
|
40
|
-
}
|
|
41
|
-
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
raf.add(task);
|
|
42
44
|
|
|
43
45
|
return;
|
|
44
46
|
}
|
package/src/html/hydrate.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { root
|
|
2
|
-
import { Element, Elements, Renderable, RenderableReactive, RenderableTemplate, Template } from '~/types';
|
|
1
|
+
import { root } from '@esportsplus/reactivity';
|
|
2
|
+
import { Element, Elements, Renderable, RenderableReactive, RenderableTemplate, RenderedGroup, Template } from '~/types';
|
|
3
3
|
import { Slot } from '~/slot';
|
|
4
4
|
import { cloneNode, firstChild, nextSibling } from '~/utilities';
|
|
5
5
|
import { apply } from '~/attributes';
|
|
@@ -10,23 +10,38 @@ function reactive<T>(renderable: RenderableReactive<T>, slot: Slot) {
|
|
|
10
10
|
let array = renderable.values,
|
|
11
11
|
factory = renderable.template,
|
|
12
12
|
refresh = () => {
|
|
13
|
-
slot.
|
|
14
|
-
|
|
13
|
+
slot.render(
|
|
14
|
+
root(() => array.map(template))
|
|
15
|
+
);
|
|
15
16
|
},
|
|
16
|
-
|
|
17
|
-
return root(() => template
|
|
17
|
+
renderer = (i: number, n?: number) => {
|
|
18
|
+
return root(() => array.map(template, i, n)) as RenderedGroup[];
|
|
18
19
|
},
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
template = function(data, i) {
|
|
21
|
+
let renderable = factory.call(this, data, i);
|
|
22
|
+
|
|
23
|
+
return render(renderable, cache.get(renderable));
|
|
24
|
+
} as Parameters<typeof array['map']>[0];
|
|
25
|
+
|
|
26
|
+
array.on('pop', () => {
|
|
27
|
+
slot.pop();
|
|
28
|
+
});
|
|
29
|
+
array.on('push', ({ items }) => {
|
|
30
|
+
slot.push(...renderer(array.length - items.length));
|
|
31
|
+
});
|
|
23
32
|
array.on('reverse', refresh);
|
|
24
|
-
array.on('shift', () =>
|
|
33
|
+
array.on('shift', () => {
|
|
34
|
+
slot.shift();
|
|
35
|
+
});
|
|
25
36
|
array.on('sort', refresh);
|
|
26
|
-
array.on('splice', ({ deleteCount: d, items: i, start: s }) =>
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
array.on('splice', ({ deleteCount: d, items: i, start: s }) => {
|
|
38
|
+
slot.splice(s, d, ...renderer(s, i.length));
|
|
39
|
+
});
|
|
40
|
+
array.on('unshift', ({ items }) => {
|
|
41
|
+
slot.unshift(...renderer(0, items.length));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return array.map(template) as RenderedGroup[];
|
|
30
45
|
}
|
|
31
46
|
|
|
32
47
|
function render<T>(renderable: Renderable<T>, template: Template) {
|
|
@@ -42,8 +57,7 @@ function render<T>(renderable: Renderable<T>, template: Template) {
|
|
|
42
57
|
for (let i = slots.length - 1; i >= 0; i--) {
|
|
43
58
|
let { fn, path, slot } = slots[i];
|
|
44
59
|
|
|
45
|
-
if (path
|
|
46
|
-
else {
|
|
60
|
+
if (path !== previous) {
|
|
47
61
|
apply(node);
|
|
48
62
|
|
|
49
63
|
node = fragment;
|
|
@@ -55,7 +69,7 @@ function render<T>(renderable: Renderable<T>, template: Template) {
|
|
|
55
69
|
}
|
|
56
70
|
|
|
57
71
|
// @ts-ignore
|
|
58
|
-
fn(node, values[slot]
|
|
72
|
+
fn(node, values[slot]);
|
|
59
73
|
}
|
|
60
74
|
|
|
61
75
|
apply(node);
|
|
@@ -65,22 +79,7 @@ function render<T>(renderable: Renderable<T>, template: Template) {
|
|
|
65
79
|
elements.push(element);
|
|
66
80
|
}
|
|
67
81
|
|
|
68
|
-
return elements;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function template<T>(array: ReactiveArray<T>, template: RenderableReactive<T>['template'], i: number, n?: number) {
|
|
72
|
-
let groups: Elements[] = [],
|
|
73
|
-
renderables = array.map< RenderableTemplate<T> >(template, i, n);
|
|
74
|
-
|
|
75
|
-
for (let i = 0, n = renderables.length; i < n; i++) {
|
|
76
|
-
let renderable = renderables[i];
|
|
77
|
-
|
|
78
|
-
groups.push(
|
|
79
|
-
render(renderable, cache.get(renderable))
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return groups;
|
|
82
|
+
return { elements, fragment };
|
|
84
83
|
}
|
|
85
84
|
|
|
86
85
|
|