@esportsplus/template 0.13.0 → 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/constants.d.ts +2 -1
- package/build/constants.js +2 -1
- package/build/event.d.ts +1 -1
- package/build/event.js +53 -28
- package/build/slot.d.ts +2 -2
- package/build/slot.js +31 -34
- package/build/types.d.ts +4 -2
- package/build/types.js +1 -1
- package/package.json +1 -1
- package/src/attributes.ts +2 -1
- package/src/constants.ts +3 -0
- package/src/event.ts +76 -37
- package/src/slot.ts +38 -44
- package/src/types.ts +2 -2
package/build/constants.d.ts
CHANGED
|
@@ -9,7 +9,8 @@ declare const RENDERABLE: unique symbol;
|
|
|
9
9
|
declare const RENDERABLE_REACTIVE: unique symbol;
|
|
10
10
|
declare const RENDERABLE_TEMPLATE: unique symbol;
|
|
11
11
|
declare const SLOT: unique symbol;
|
|
12
|
+
declare const SLOT_CLEANUP: unique symbol;
|
|
12
13
|
declare const SLOT_HTML = "<!--$-->";
|
|
13
14
|
declare const SLOT_MARKER = "{{$}}";
|
|
14
15
|
declare const SLOT_MARKER_LENGTH: number;
|
|
15
|
-
export { NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH };
|
|
16
|
+
export { NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT, SLOT_CLEANUP, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH };
|
package/build/constants.js
CHANGED
|
@@ -29,7 +29,8 @@ const RENDERABLE = Symbol();
|
|
|
29
29
|
const RENDERABLE_REACTIVE = Symbol();
|
|
30
30
|
const RENDERABLE_TEMPLATE = Symbol();
|
|
31
31
|
const SLOT = Symbol();
|
|
32
|
+
const SLOT_CLEANUP = Symbol();
|
|
32
33
|
const SLOT_HTML = '<!--$-->';
|
|
33
34
|
const SLOT_MARKER = '{{$}}';
|
|
34
35
|
const SLOT_MARKER_LENGTH = SLOT_MARKER.length;
|
|
35
|
-
export { NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH };
|
|
36
|
+
export { NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT, SLOT_CLEANUP, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH };
|
package/build/event.d.ts
CHANGED
package/build/event.js
CHANGED
|
@@ -1,33 +1,15 @@
|
|
|
1
1
|
import { root } from '@esportsplus/reactivity';
|
|
2
|
+
import { SLOT_CLEANUP } from './constants.js';
|
|
2
3
|
import { addEventListener, defineProperty, parentElement } from './utilities.js';
|
|
3
|
-
let capture = new Set(['
|
|
4
|
-
'
|
|
5
|
-
'
|
|
6
|
-
'
|
|
7
|
-
'
|
|
4
|
+
let capture = new Set(['onblur', 'onfocus', 'onscroll']), controllers = new Map(), keys = {}, passive = new Set([
|
|
5
|
+
'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel',
|
|
6
|
+
'onscroll',
|
|
7
|
+
'ontouchcancel', 'ontouchend', 'ontouchleave', 'ontouchmove', 'ontouchstart',
|
|
8
|
+
'onwheel'
|
|
8
9
|
]);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
let node = e.target;
|
|
13
|
-
defineProperty(e, 'currentTarget', {
|
|
14
|
-
configurable: true,
|
|
15
|
-
get() {
|
|
16
|
-
return node || window.document;
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
while (node) {
|
|
20
|
-
if (key in node) {
|
|
21
|
-
return node[key].call(node, e);
|
|
22
|
-
}
|
|
23
|
-
node = parentElement.call(node);
|
|
24
|
-
}
|
|
25
|
-
}, {
|
|
26
|
-
capture: capture.has(type),
|
|
27
|
-
passive: passive.has(type)
|
|
28
|
-
});
|
|
29
|
-
return key;
|
|
30
|
-
}
|
|
10
|
+
['onmousemove', 'onmousewheel', 'onscroll', 'ontouchend', 'ontouchmove', 'ontouchstart', 'onwheel'].map(event => {
|
|
11
|
+
controllers.set(event, null);
|
|
12
|
+
});
|
|
31
13
|
export default (element, event, listener) => {
|
|
32
14
|
if (event === 'onmount') {
|
|
33
15
|
let interval = setInterval(() => {
|
|
@@ -45,5 +27,48 @@ export default (element, event, listener) => {
|
|
|
45
27
|
else if (event === 'onrender') {
|
|
46
28
|
return root(() => listener(element));
|
|
47
29
|
}
|
|
48
|
-
|
|
30
|
+
let controller = controllers.get(event), signal;
|
|
31
|
+
if (controller === null) {
|
|
32
|
+
let { abort, signal } = new AbortController();
|
|
33
|
+
controllers.set(event, controller = {
|
|
34
|
+
abort,
|
|
35
|
+
signal,
|
|
36
|
+
listeners: 0,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (controller) {
|
|
40
|
+
controller.listeners++;
|
|
41
|
+
(element[SLOT_CLEANUP] ??= []).push(() => {
|
|
42
|
+
if (--controller.listeners) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
controller.abort();
|
|
46
|
+
controllers.set(event, null);
|
|
47
|
+
});
|
|
48
|
+
signal = controller.signal;
|
|
49
|
+
}
|
|
50
|
+
let key = keys[event];
|
|
51
|
+
if (!key) {
|
|
52
|
+
key = keys[event] = Symbol();
|
|
53
|
+
addEventListener.call(window.document, event.slice(2), (e) => {
|
|
54
|
+
let node = e.target;
|
|
55
|
+
defineProperty(e, 'currentTarget', {
|
|
56
|
+
configurable: true,
|
|
57
|
+
get() {
|
|
58
|
+
return node || window.document;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
while (node) {
|
|
62
|
+
if (key in node) {
|
|
63
|
+
return node[key].call(node, e);
|
|
64
|
+
}
|
|
65
|
+
node = parentElement.call(node);
|
|
66
|
+
}
|
|
67
|
+
}, {
|
|
68
|
+
capture: capture.has(event),
|
|
69
|
+
passive: passive.has(event),
|
|
70
|
+
signal
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
element[key] = listener;
|
|
49
74
|
};
|
package/build/slot.d.ts
CHANGED
|
@@ -10,10 +10,10 @@ declare class Slot {
|
|
|
10
10
|
set length(n: number);
|
|
11
11
|
anchor(index?: number): Element;
|
|
12
12
|
clear(): void;
|
|
13
|
-
pop(): Elements | undefined;
|
|
13
|
+
pop(): Elements[] | undefined;
|
|
14
14
|
push(...groups: Elements[]): number;
|
|
15
15
|
render(input: unknown): this;
|
|
16
|
-
shift(): Elements | undefined;
|
|
16
|
+
shift(): Elements[] | undefined;
|
|
17
17
|
splice(start: number, stop?: number, ...groups: Elements[]): Elements[];
|
|
18
18
|
unshift(...groups: Elements[]): number;
|
|
19
19
|
}
|
package/build/slot.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { effect, root, DIRTY } from '@esportsplus/reactivity';
|
|
2
|
-
import { RENDERABLE, RENDERABLE_REACTIVE, SLOT } from './constants.js';
|
|
2
|
+
import { RENDERABLE, RENDERABLE_REACTIVE, SLOT, SLOT_CLEANUP } from './constants.js';
|
|
3
3
|
import { hydrate } from './html/index.js';
|
|
4
4
|
import { firstChild, isArray, isObject, nextSibling, nodeValue, raf, text } from './utilities.js';
|
|
5
|
-
let cleanup = [],
|
|
6
|
-
function
|
|
5
|
+
let cleanup = [], scheduled = false;
|
|
6
|
+
function after(anchor, groups) {
|
|
7
7
|
for (let i = 0, n = groups.length; i < n; i++) {
|
|
8
8
|
let group = groups[i];
|
|
9
9
|
if (group.length) {
|
|
@@ -13,29 +13,13 @@ function afterGroups(anchor, groups) {
|
|
|
13
13
|
}
|
|
14
14
|
return groups;
|
|
15
15
|
}
|
|
16
|
-
function
|
|
17
|
-
if (group === undefined) {
|
|
18
|
-
return group;
|
|
19
|
-
}
|
|
20
|
-
for (let i = 0, n = group.length; i < n; i++) {
|
|
21
|
-
let item = group[i];
|
|
22
|
-
if (key in item) {
|
|
23
|
-
cleanup.push(item[key]);
|
|
24
|
-
}
|
|
25
|
-
item.remove();
|
|
26
|
-
}
|
|
27
|
-
if (!scheduled && cleanup.length) {
|
|
28
|
-
schedule();
|
|
29
|
-
}
|
|
30
|
-
return group;
|
|
31
|
-
}
|
|
32
|
-
function removeGroups(groups) {
|
|
16
|
+
function remove(...groups) {
|
|
33
17
|
for (let i = 0, n = groups.length; i < n; i++) {
|
|
34
18
|
let group = groups[i];
|
|
35
19
|
for (let j = 0, o = group.length; j < o; j++) {
|
|
36
20
|
let item = group[j];
|
|
37
|
-
if (
|
|
38
|
-
cleanup.push(item[
|
|
21
|
+
if (item[SLOT_CLEANUP]) {
|
|
22
|
+
cleanup.push(...item[SLOT_CLEANUP]);
|
|
39
23
|
}
|
|
40
24
|
item.remove();
|
|
41
25
|
}
|
|
@@ -55,12 +39,12 @@ function render(anchor, input, slot) {
|
|
|
55
39
|
for (let i = 0, n = input.length; i < n; i++) {
|
|
56
40
|
groups.push(render(null, input[i]));
|
|
57
41
|
}
|
|
58
|
-
return anchor ?
|
|
42
|
+
return anchor ? after(anchor, groups) : groups;
|
|
59
43
|
}
|
|
60
44
|
let nodes = [];
|
|
61
45
|
if (RENDERABLE in input) {
|
|
62
46
|
if (input[RENDERABLE] === RENDERABLE_REACTIVE) {
|
|
63
|
-
return
|
|
47
|
+
return after(anchor, hydrate.reactive(input, slot));
|
|
64
48
|
}
|
|
65
49
|
else {
|
|
66
50
|
nodes = hydrate.static(input);
|
|
@@ -98,13 +82,16 @@ function schedule() {
|
|
|
98
82
|
scheduled = true;
|
|
99
83
|
raf.add(() => {
|
|
100
84
|
try {
|
|
101
|
-
let
|
|
102
|
-
while (
|
|
103
|
-
|
|
85
|
+
let fn;
|
|
86
|
+
while (fn = cleanup.pop()) {
|
|
87
|
+
fn();
|
|
104
88
|
}
|
|
105
89
|
}
|
|
106
90
|
catch (e) { }
|
|
107
91
|
scheduled = false;
|
|
92
|
+
if (cleanup.length) {
|
|
93
|
+
schedule();
|
|
94
|
+
}
|
|
108
95
|
});
|
|
109
96
|
}
|
|
110
97
|
class Slot {
|
|
@@ -113,7 +100,9 @@ class Slot {
|
|
|
113
100
|
nodes;
|
|
114
101
|
text = null;
|
|
115
102
|
constructor(marker) {
|
|
116
|
-
marker[
|
|
103
|
+
(marker[SLOT_CLEANUP] ??= []).push(() => {
|
|
104
|
+
this.clear();
|
|
105
|
+
});
|
|
117
106
|
this.marker = marker;
|
|
118
107
|
this.nodes = [];
|
|
119
108
|
}
|
|
@@ -131,14 +120,18 @@ class Slot {
|
|
|
131
120
|
return node || this.marker;
|
|
132
121
|
}
|
|
133
122
|
clear() {
|
|
134
|
-
|
|
123
|
+
remove(...this.nodes);
|
|
135
124
|
this.text = null;
|
|
136
125
|
}
|
|
137
126
|
pop() {
|
|
138
|
-
|
|
127
|
+
let group = this.nodes.pop();
|
|
128
|
+
if (!group) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
return remove(group);
|
|
139
132
|
}
|
|
140
133
|
push(...groups) {
|
|
141
|
-
|
|
134
|
+
after(this.anchor(), groups);
|
|
142
135
|
for (let i = 0, n = groups.length; i < n; i++) {
|
|
143
136
|
this.nodes.push(groups[i]);
|
|
144
137
|
}
|
|
@@ -182,13 +175,17 @@ class Slot {
|
|
|
182
175
|
return this;
|
|
183
176
|
}
|
|
184
177
|
shift() {
|
|
185
|
-
|
|
178
|
+
let group = this.nodes.shift();
|
|
179
|
+
if (!group) {
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
return remove(group);
|
|
186
183
|
}
|
|
187
184
|
splice(start, stop = this.nodes.length, ...groups) {
|
|
188
|
-
return
|
|
185
|
+
return remove(...this.nodes.splice(start, stop, ...after(this.anchor(start), groups)));
|
|
189
186
|
}
|
|
190
187
|
unshift(...groups) {
|
|
191
|
-
return this.nodes.unshift(...
|
|
188
|
+
return this.nodes.unshift(...after(this.marker, groups));
|
|
192
189
|
}
|
|
193
190
|
}
|
|
194
191
|
export default (marker, value) => {
|
package/build/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactiveArray } from '@esportsplus/reactivity';
|
|
2
|
-
import { RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE } from './constants.js';
|
|
2
|
+
import { RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT_CLEANUP } from './constants.js';
|
|
3
3
|
import { firstChild } from './utilities.js';
|
|
4
4
|
import attributes from './attributes.js';
|
|
5
5
|
import event from './event.js';
|
|
@@ -17,7 +17,9 @@ type Attributes = {
|
|
|
17
17
|
} & Record<PropertyKey, unknown>;
|
|
18
18
|
type Effect<T> = () => EffectResponse<T>;
|
|
19
19
|
type EffectResponse<T> = T extends [] ? EffectResponse<T[number]>[] : Primitive | Renderable<T>;
|
|
20
|
-
type Element = HTMLElement & Attributes &
|
|
20
|
+
type Element = HTMLElement & Attributes & {
|
|
21
|
+
[SLOT_CLEANUP]?: VoidFunction[];
|
|
22
|
+
} & Record<PropertyKey, unknown>;
|
|
21
23
|
type Elements = Element[];
|
|
22
24
|
type Primitive = bigint | boolean | null | number | string | undefined;
|
|
23
25
|
type Renderable<T = unknown> = RenderableReactive<T> | RenderableTemplate<T>;
|
package/build/types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import { RENDERABLE } from './constants.js';
|
|
1
|
+
import { RENDERABLE, SLOT_CLEANUP } from './constants.js';
|
package/package.json
CHANGED
package/src/attributes.ts
CHANGED
|
@@ -67,7 +67,7 @@ function set(element: Element, value: unknown, name: string) {
|
|
|
67
67
|
}
|
|
68
68
|
else if (typeof value === 'function') {
|
|
69
69
|
if (name.startsWith('on')) {
|
|
70
|
-
event(element, name
|
|
70
|
+
event(element, name as `on${string}`, value);
|
|
71
71
|
}
|
|
72
72
|
else {
|
|
73
73
|
reactive(element, ('e' + store(element)[key]++), name, value, true);
|
|
@@ -197,5 +197,6 @@ const spread = function (element: Element, attributes: Attributes | Attributes[]
|
|
|
197
197
|
}
|
|
198
198
|
};
|
|
199
199
|
|
|
200
|
+
|
|
200
201
|
export default { apply, spread };
|
|
201
202
|
export { apply, spread }
|
package/src/constants.ts
CHANGED
|
@@ -45,6 +45,8 @@ const RENDERABLE_TEMPLATE = Symbol();
|
|
|
45
45
|
|
|
46
46
|
const SLOT = Symbol();
|
|
47
47
|
|
|
48
|
+
const SLOT_CLEANUP = Symbol();
|
|
49
|
+
|
|
48
50
|
const SLOT_HTML = '<!--$-->';
|
|
49
51
|
|
|
50
52
|
const SLOT_MARKER = '{{$}}';
|
|
@@ -64,6 +66,7 @@ export {
|
|
|
64
66
|
RENDERABLE_REACTIVE,
|
|
65
67
|
RENDERABLE_TEMPLATE,
|
|
66
68
|
SLOT,
|
|
69
|
+
SLOT_CLEANUP,
|
|
67
70
|
SLOT_HTML,
|
|
68
71
|
SLOT_MARKER,
|
|
69
72
|
SLOT_MARKER_LENGTH
|
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/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
|
-
let cleanup:
|
|
9
|
-
// Using a private symbol since 'SLOT' is used as a different flag in 'render.ts'
|
|
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
|
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactiveArray } from '@esportsplus/reactivity';
|
|
2
|
-
import { RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE } from './constants';
|
|
2
|
+
import { RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT_CLEANUP } from './constants';
|
|
3
3
|
import { firstChild } from './utilities';
|
|
4
4
|
import attributes from './attributes';
|
|
5
5
|
import event from './event';
|
|
@@ -26,7 +26,7 @@ type Effect<T> = () => EffectResponse<T>;
|
|
|
26
26
|
|
|
27
27
|
type EffectResponse<T> = T extends [] ? EffectResponse<T[number]>[] : Primitive | Renderable<T>;
|
|
28
28
|
|
|
29
|
-
type Element = HTMLElement & Attributes & Record<PropertyKey, unknown>;
|
|
29
|
+
type Element = HTMLElement & Attributes & { [SLOT_CLEANUP]?: VoidFunction[] } & Record<PropertyKey, unknown>;
|
|
30
30
|
|
|
31
31
|
type Elements = Element[];
|
|
32
32
|
|