@esportsplus/template 0.15.19 → 0.16.1
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 +1 -3
- package/build/attributes.js +3 -14
- package/build/constants.d.ts +2 -1
- package/build/constants.js +3 -1
- package/build/html/cache.d.ts +2 -2
- package/build/html/cache.js +23 -13
- package/build/html/hydrate.d.ts +5 -3
- package/build/html/hydrate.js +62 -40
- package/build/render.d.ts +1 -1
- package/build/slot.d.ts +9 -9
- package/build/slot.js +105 -100
- package/build/types.d.ts +8 -7
- package/package.json +2 -2
- package/src/attributes.ts +3 -19
- package/src/constants.ts +7 -0
- package/src/html/cache.ts +28 -16
- package/src/html/hydrate.ts +80 -45
- package/src/slot.ts +119 -128
- package/src/types.ts +8 -4
package/src/slot.ts
CHANGED
|
@@ -1,56 +1,32 @@
|
|
|
1
|
-
import { effect
|
|
2
|
-
import { isArray,
|
|
3
|
-
import { RENDERABLE, RENDERABLE_REACTIVE } from './constants';
|
|
1
|
+
import { effect } from '@esportsplus/reactivity';
|
|
2
|
+
import { isArray, isInstanceOf } from '@esportsplus/utilities';
|
|
3
|
+
import { EMPTY_FRAGMENT, RENDERABLE, RENDERABLE_REACTIVE } from './constants';
|
|
4
4
|
import { hydrate } from './html';
|
|
5
|
-
import { Element, Elements,
|
|
6
|
-
import { append, cloneNode,
|
|
5
|
+
import { Element, Elements, Fragment, RenderableReactive, RenderableTemplate } from './types';
|
|
6
|
+
import { append, cloneNode, microtask, nodeValue, raf, text } from './utilities'
|
|
7
7
|
import queue from '@esportsplus/queue';
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
const CLEANUP_KEY = Symbol();
|
|
11
11
|
|
|
12
|
+
const CONNECTED = 0;
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
scheduled = false,
|
|
15
|
-
template = fragment('');
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
function after(anchor: Element, groups: HydrateResult[]) {
|
|
19
|
-
let n = groups.length;
|
|
20
|
-
|
|
21
|
-
if (n === 0) {
|
|
22
|
-
return [];
|
|
23
|
-
}
|
|
14
|
+
const HYDRATING = 1;
|
|
24
15
|
|
|
25
|
-
let elements: Elements[] = [],
|
|
26
|
-
fragment = groups[0].fragment || cloneNode.call(template);
|
|
27
16
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
for (let i = 1; i < n; i++) {
|
|
33
|
-
let { elements: e, fragment: f } = groups[i];
|
|
34
|
-
|
|
35
|
-
append.call(fragment, f);
|
|
36
|
-
elements.push(e);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
anchor.after(fragment);
|
|
41
|
-
|
|
42
|
-
return elements;
|
|
43
|
-
}
|
|
17
|
+
let cleanup = queue<VoidFunction[]>(64),
|
|
18
|
+
scheduled = false;
|
|
44
19
|
|
|
45
|
-
function remove(...groups: Elements[]) {
|
|
46
|
-
for (let i = 0, n = groups.length; i < n; i++) {
|
|
47
|
-
let group = groups[i];
|
|
48
20
|
|
|
49
|
-
|
|
50
|
-
|
|
21
|
+
function remove(groups: Elements[]) {
|
|
22
|
+
let group,
|
|
23
|
+
item;
|
|
51
24
|
|
|
25
|
+
while (group = groups.pop()) {
|
|
26
|
+
while (item = group.pop()) {
|
|
52
27
|
if (CLEANUP_KEY in item) {
|
|
53
28
|
cleanup.add(item[CLEANUP_KEY] as VoidFunction[]);
|
|
29
|
+
item[CLEANUP_KEY] = null;
|
|
54
30
|
}
|
|
55
31
|
|
|
56
32
|
item.remove();
|
|
@@ -60,61 +36,55 @@ function remove(...groups: Elements[]) {
|
|
|
60
36
|
if (!scheduled && cleanup.length) {
|
|
61
37
|
schedule();
|
|
62
38
|
}
|
|
63
|
-
|
|
64
|
-
return groups;
|
|
65
39
|
}
|
|
66
40
|
|
|
67
|
-
function render(elements: Elements[], fragment:
|
|
41
|
+
function render(anchor: Element, elements: Elements[] | null, fragment: Fragment, input: unknown, slot?: Slot) {
|
|
68
42
|
if (input === false || input == null || input === '') {
|
|
69
43
|
return;
|
|
70
44
|
}
|
|
71
45
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
46
|
+
let type = typeof input;
|
|
47
|
+
|
|
48
|
+
if (type === 'object' && RENDERABLE in (input as Record<PropertyKey, unknown>)) {
|
|
49
|
+
if ((input as Record<PropertyKey, unknown>)[RENDERABLE] === RENDERABLE_REACTIVE) {
|
|
50
|
+
slot ??= new Slot(anchor);
|
|
51
|
+
hydrate.reactive(slot.nodes, fragment, input as RenderableReactive, slot);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
hydrate.static(elements, fragment, input as RenderableTemplate);
|
|
75
55
|
}
|
|
76
56
|
}
|
|
77
|
-
else if (
|
|
78
|
-
|
|
79
|
-
let response = hydrate.reactive(input as RenderableReactive, slot!);
|
|
80
|
-
|
|
81
|
-
for (let i = 0, n = response.length; i < n; i++) {
|
|
82
|
-
let { elements: e, fragment: f } = response[i];
|
|
57
|
+
else if (type === 'string' || type === 'number') {
|
|
58
|
+
let element = text( type === 'string' ? input as string : String(input) );
|
|
83
59
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
60
|
+
if (slot) {
|
|
61
|
+
slot.text = element;
|
|
87
62
|
}
|
|
88
|
-
else {
|
|
89
|
-
let { elements: e, fragment: f } = hydrate.static(input as RenderableTemplate<unknown>);
|
|
90
63
|
|
|
91
|
-
|
|
92
|
-
|
|
64
|
+
append.call(fragment, element);
|
|
65
|
+
|
|
66
|
+
if (elements) {
|
|
67
|
+
elements.push([element]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (isArray(input)) {
|
|
71
|
+
for (let i = 0, n = input.length; i < n; i++) {
|
|
72
|
+
render(anchor, elements, fragment, input[i], slot);
|
|
93
73
|
}
|
|
94
74
|
}
|
|
95
75
|
else if (isInstanceOf(input, NodeList)) {
|
|
96
|
-
|
|
76
|
+
append.call(fragment, ...input);
|
|
97
77
|
|
|
98
|
-
|
|
99
|
-
|
|
78
|
+
if (elements) {
|
|
79
|
+
elements.push([...input] as Elements);
|
|
100
80
|
}
|
|
101
|
-
|
|
102
|
-
append.call(fragment, ...e);
|
|
103
|
-
elements.push(e);
|
|
104
81
|
}
|
|
105
82
|
else if (isInstanceOf(input, Node)) {
|
|
106
83
|
append.call(fragment, input);
|
|
107
|
-
elements.push([ input as Element ]);
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
let element = text( typeof input === 'string' ? input : String(input) );
|
|
111
84
|
|
|
112
|
-
if (
|
|
113
|
-
|
|
85
|
+
if (elements) {
|
|
86
|
+
elements.push([ input as Element ]);
|
|
114
87
|
}
|
|
115
|
-
|
|
116
|
-
append.call(fragment, element);
|
|
117
|
-
elements.push([ element ]);
|
|
118
88
|
}
|
|
119
89
|
}
|
|
120
90
|
|
|
@@ -166,54 +136,68 @@ class Slot {
|
|
|
166
136
|
}
|
|
167
137
|
|
|
168
138
|
set length(n: number) {
|
|
169
|
-
this.
|
|
139
|
+
if (n >= this.nodes.length) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
else if (n === 0) {
|
|
143
|
+
this.clear();
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
this.splice(n);
|
|
147
|
+
}
|
|
170
148
|
}
|
|
171
149
|
|
|
172
150
|
|
|
173
151
|
anchor(index: number = this.nodes.length - 1) {
|
|
174
|
-
let
|
|
175
|
-
nodes = this.nodes[index];
|
|
152
|
+
let nodes = this.nodes[index];
|
|
176
153
|
|
|
177
|
-
|
|
178
|
-
node = nodes[nodes.length - 1];
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return node || this.marker;
|
|
154
|
+
return nodes ? nodes[nodes.length - 1] : this.marker;
|
|
182
155
|
}
|
|
183
156
|
|
|
184
157
|
clear() {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
158
|
+
if (this.text) {
|
|
159
|
+
this.nodes.push([this.text]);
|
|
160
|
+
this.text = null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
remove(this.nodes);
|
|
164
|
+
|
|
165
|
+
return this;
|
|
188
166
|
}
|
|
189
167
|
|
|
190
168
|
pop() {
|
|
191
169
|
let group = this.nodes.pop();
|
|
192
170
|
|
|
193
|
-
if (
|
|
194
|
-
|
|
171
|
+
if (group) {
|
|
172
|
+
remove([group]);
|
|
195
173
|
}
|
|
196
174
|
|
|
197
|
-
return
|
|
175
|
+
return this;
|
|
198
176
|
}
|
|
199
177
|
|
|
200
|
-
push(...
|
|
201
|
-
|
|
178
|
+
push(fragment: Fragment, ...nodes: Elements[]) {
|
|
179
|
+
this.anchor().after(fragment);
|
|
180
|
+
this.nodes.push( ...nodes );
|
|
181
|
+
|
|
182
|
+
return this;
|
|
202
183
|
}
|
|
203
184
|
|
|
204
|
-
render(input: unknown) {
|
|
205
|
-
|
|
185
|
+
render(input: unknown, state = HYDRATING) {
|
|
186
|
+
let type = typeof input;
|
|
187
|
+
|
|
188
|
+
if (type === 'function') {
|
|
206
189
|
ondisconnect(
|
|
207
190
|
this.marker,
|
|
208
191
|
effect(() => {
|
|
209
|
-
let v = input();
|
|
192
|
+
let v = (input as Function)();
|
|
210
193
|
|
|
211
|
-
if (
|
|
212
|
-
|
|
194
|
+
if (state === HYDRATING) {
|
|
195
|
+
this.render(v, state);
|
|
196
|
+
state = CONNECTED;
|
|
213
197
|
}
|
|
214
|
-
else {
|
|
198
|
+
else if (state === CONNECTED) {
|
|
215
199
|
raf.add(() => {
|
|
216
|
-
this.render(v);
|
|
200
|
+
this.render(v, state);
|
|
217
201
|
});
|
|
218
202
|
}
|
|
219
203
|
})
|
|
@@ -222,29 +206,20 @@ class Slot {
|
|
|
222
206
|
return this;
|
|
223
207
|
}
|
|
224
208
|
|
|
225
|
-
|
|
226
|
-
let type = typeof input;
|
|
209
|
+
let text = this.text;
|
|
227
210
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
else if (this.text.isConnected) {
|
|
231
|
-
nodeValue.call(
|
|
232
|
-
this.text,
|
|
233
|
-
(type === 'string' || type === 'number') ? input : ''
|
|
234
|
-
);
|
|
235
|
-
return this;
|
|
236
|
-
}
|
|
211
|
+
if (text && text.isConnected && (input == null || type !== 'object')) {
|
|
212
|
+
nodeValue.call(text, (type === 'number' || type === 'string') ? input : '');
|
|
237
213
|
}
|
|
214
|
+
else {
|
|
215
|
+
this.clear();
|
|
238
216
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
let fragment = cloneNode.call(template),
|
|
242
|
-
nodes: Elements[] = [];
|
|
217
|
+
let fragment = cloneNode.call(EMPTY_FRAGMENT);
|
|
243
218
|
|
|
244
|
-
|
|
219
|
+
render(this.marker, this.nodes, fragment, input, this);
|
|
245
220
|
|
|
246
|
-
|
|
247
|
-
|
|
221
|
+
this.marker.after(fragment);
|
|
222
|
+
}
|
|
248
223
|
|
|
249
224
|
return this;
|
|
250
225
|
}
|
|
@@ -252,25 +227,30 @@ class Slot {
|
|
|
252
227
|
shift() {
|
|
253
228
|
let group = this.nodes.shift();
|
|
254
229
|
|
|
255
|
-
if (
|
|
256
|
-
|
|
230
|
+
if (group) {
|
|
231
|
+
remove([group]);
|
|
257
232
|
}
|
|
258
233
|
|
|
259
|
-
return
|
|
234
|
+
return this;
|
|
260
235
|
}
|
|
261
236
|
|
|
262
|
-
splice(start: number, stop: number = this.nodes.length, ...
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
)
|
|
269
|
-
|
|
237
|
+
splice(start: number, stop: number = this.nodes.length, fragment?: Fragment, ...nodes: Elements[]) {
|
|
238
|
+
if (!fragment) {
|
|
239
|
+
remove( this.nodes.splice(start, stop) );
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
this.anchor(start).after(fragment);
|
|
243
|
+
remove( this.nodes.splice(start, stop, ...nodes) )
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return this;
|
|
270
247
|
}
|
|
271
248
|
|
|
272
|
-
unshift(...
|
|
273
|
-
|
|
249
|
+
unshift(fragment: Fragment, ...nodes: Elements[]) {
|
|
250
|
+
this.marker.after(fragment);
|
|
251
|
+
this.nodes.unshift( ...nodes );
|
|
252
|
+
|
|
253
|
+
return this;
|
|
274
254
|
}
|
|
275
255
|
}
|
|
276
256
|
|
|
@@ -281,6 +261,17 @@ const ondisconnect = (element: Element, fn: VoidFunction) => {
|
|
|
281
261
|
|
|
282
262
|
|
|
283
263
|
export default (marker: Element, value: unknown) => {
|
|
284
|
-
|
|
264
|
+
let type = typeof value;
|
|
265
|
+
|
|
266
|
+
if (type === 'function') {
|
|
267
|
+
new Slot(marker).render(value);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
let fragment = cloneNode.call(EMPTY_FRAGMENT);
|
|
271
|
+
|
|
272
|
+
render(marker, null, fragment, value);
|
|
273
|
+
|
|
274
|
+
marker.after(fragment);
|
|
275
|
+
};
|
|
285
276
|
};
|
|
286
277
|
export { ondisconnect, Slot };
|
package/src/types.ts
CHANGED
|
@@ -30,7 +30,7 @@ type Element = HTMLElement & Attributes & Record<PropertyKey, unknown>;
|
|
|
30
30
|
|
|
31
31
|
type Elements = Element[];
|
|
32
32
|
|
|
33
|
-
type
|
|
33
|
+
type Fragment = DocumentFragment | Node;
|
|
34
34
|
|
|
35
35
|
// Copied from '@esportsplus/utilities'
|
|
36
36
|
// - Importing from ^ causes 'cannot be named without a reference to...' error
|
|
@@ -48,7 +48,7 @@ type RenderableReactive<T = unknown> = Readonly<{
|
|
|
48
48
|
values: ReactiveArray<T>;
|
|
49
49
|
}>;
|
|
50
50
|
|
|
51
|
-
type RenderableTemplate<T> = {
|
|
51
|
+
type RenderableTemplate<T = unknown> = {
|
|
52
52
|
[RENDERABLE]: typeof RENDERABLE_TEMPLATE;
|
|
53
53
|
literals: TemplateStringsArray;
|
|
54
54
|
template: Template | null;
|
|
@@ -63,7 +63,11 @@ type Template = {
|
|
|
63
63
|
literals: TemplateStringsArray;
|
|
64
64
|
slots: {
|
|
65
65
|
fn: typeof attributes.spread | typeof slot;
|
|
66
|
-
path:
|
|
66
|
+
path: {
|
|
67
|
+
absolute: typeof firstChild[],
|
|
68
|
+
parent: typeof firstChild[],
|
|
69
|
+
relative: typeof firstChild[]
|
|
70
|
+
};
|
|
67
71
|
slot: number;
|
|
68
72
|
}[] | null;
|
|
69
73
|
};
|
|
@@ -72,7 +76,7 @@ type Template = {
|
|
|
72
76
|
export type {
|
|
73
77
|
Attributes,
|
|
74
78
|
Effect, Element, Elements,
|
|
75
|
-
|
|
79
|
+
Fragment,
|
|
76
80
|
Renderable, RenderableReactive, RenderableTemplate, RenderableValue,
|
|
77
81
|
Template
|
|
78
82
|
};
|