@esportsplus/template 0.26.6 → 0.28.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/.github/workflows/publish.yml +2 -2
- package/build/attributes.js +6 -6
- package/build/constants.d.ts +2 -3
- package/build/constants.js +3 -4
- package/build/event/index.js +7 -4
- package/build/event/ontick.js +3 -3
- package/build/html/index.d.ts +4 -3
- package/build/html/index.js +3 -7
- package/build/html/parser.js +60 -52
- package/build/index.d.ts +1 -0
- package/build/render.js +4 -5
- package/build/slot/array.d.ts +25 -3
- package/build/slot/array.js +123 -48
- package/build/slot/cleanup.js +2 -2
- package/build/slot/effect.d.ts +12 -3
- package/build/slot/effect.js +17 -13
- package/build/slot/index.js +2 -2
- package/build/slot/render.js +15 -7
- package/build/types.d.ts +3 -10
- package/build/types.js +1 -1
- package/build/utilities/marker.d.ts +2 -0
- package/build/utilities/marker.js +4 -0
- package/build/utilities/raf.d.ts +2 -0
- package/build/utilities/raf.js +1 -0
- package/package.json +7 -4
- package/src/attributes.ts +6 -6
- package/src/constants.ts +6 -8
- package/src/event/index.ts +9 -4
- package/src/event/ontick.ts +3 -3
- package/src/html/index.ts +5 -9
- package/src/html/parser.ts +21 -7
- package/src/index.ts +1 -0
- package/src/render.ts +5 -8
- package/src/slot/array.ts +172 -65
- package/src/slot/cleanup.ts +2 -2
- package/src/slot/effect.ts +20 -13
- package/src/slot/index.ts +2 -2
- package/src/slot/render.ts +20 -9
- package/src/types.ts +3 -11
- package/src/utilities/marker.ts +6 -0
- package/src/utilities/raf.ts +1 -0
- package/build/utilities/queue.d.ts +0 -2
- package/build/utilities/queue.js +0 -3
- package/src/utilities/queue.ts +0 -7
package/build/attributes.js
CHANGED
|
@@ -2,8 +2,8 @@ import { effect } from '@esportsplus/reactivity';
|
|
|
2
2
|
import { isArray, isObject } from '@esportsplus/utilities';
|
|
3
3
|
import { STATE_HYDRATING, STATE_NONE, STATE_WAITING } from './constants.js';
|
|
4
4
|
import { className, removeAttribute, setAttribute } from './utilities/element.js';
|
|
5
|
-
import { raf } from './utilities/queue.js';
|
|
6
5
|
import q from '@esportsplus/queue';
|
|
6
|
+
import raf from './utilities/raf.js';
|
|
7
7
|
import event from './event/index.js';
|
|
8
8
|
const STORE = Symbol();
|
|
9
9
|
let delimiters = {
|
|
@@ -17,7 +17,7 @@ function apply(element, name, value) {
|
|
|
17
17
|
else if (name === 'class') {
|
|
18
18
|
className.call(element, value);
|
|
19
19
|
}
|
|
20
|
-
else if (name === 'style' || name.startsWith('data-') || 'ownerSVGElement'
|
|
20
|
+
else if (name === 'style' || (name[0] === 'd' && name.startsWith('data-')) || element['ownerSVGElement']) {
|
|
21
21
|
setAttribute.call(element, name, value);
|
|
22
22
|
}
|
|
23
23
|
else {
|
|
@@ -58,7 +58,7 @@ function list(ctx, element, id, name, state, value) {
|
|
|
58
58
|
let cold = store[id];
|
|
59
59
|
if (cold !== undefined) {
|
|
60
60
|
for (let part in cold) {
|
|
61
|
-
if (part
|
|
61
|
+
if (hot[part] === undefined) {
|
|
62
62
|
continue;
|
|
63
63
|
}
|
|
64
64
|
dynamic.delete(part);
|
|
@@ -106,7 +106,7 @@ function schedule(ctx, element, name, state, value) {
|
|
|
106
106
|
return;
|
|
107
107
|
}
|
|
108
108
|
scheduled = true;
|
|
109
|
-
raf
|
|
109
|
+
raf(task);
|
|
110
110
|
}
|
|
111
111
|
function task() {
|
|
112
112
|
let context, n = queue.length;
|
|
@@ -119,7 +119,7 @@ function task() {
|
|
|
119
119
|
context.updating = false;
|
|
120
120
|
}
|
|
121
121
|
if (queue.length) {
|
|
122
|
-
raf
|
|
122
|
+
raf(task);
|
|
123
123
|
}
|
|
124
124
|
else {
|
|
125
125
|
scheduled = false;
|
|
@@ -128,7 +128,7 @@ function task() {
|
|
|
128
128
|
const set = (element, name, value) => {
|
|
129
129
|
let fn = name === 'class' || name === 'style' ? list : property, state = STATE_HYDRATING;
|
|
130
130
|
if (typeof value === 'function') {
|
|
131
|
-
if (name
|
|
131
|
+
if (name[0] === 'o' && name[1] === 'n') {
|
|
132
132
|
return event(element, name, value);
|
|
133
133
|
}
|
|
134
134
|
let ctx = context(element);
|
package/build/constants.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
declare const ARRAY_SLOT: unique symbol;
|
|
1
2
|
declare const CLEANUP: unique symbol;
|
|
2
3
|
declare const EMPTY_FRAGMENT: DocumentFragment;
|
|
3
4
|
declare const NODE_CLOSING = 1;
|
|
@@ -9,11 +10,9 @@ declare const REGEX_EMPTY_TEXT_NODES: RegExp;
|
|
|
9
10
|
declare const REGEX_EVENTS: RegExp;
|
|
10
11
|
declare const REGEX_SLOT_ATTRIBUTES: RegExp;
|
|
11
12
|
declare const REGEX_SLOT_NODES: RegExp;
|
|
12
|
-
declare const RENDERABLE: unique symbol;
|
|
13
|
-
declare const RENDERABLE_HTML_REACTIVE_ARRAY = 1;
|
|
14
13
|
declare const SLOT_HTML = "<!--$-->";
|
|
15
14
|
declare const SLOT_MARKER = "{{$}}";
|
|
16
15
|
declare const STATE_HYDRATING = 0;
|
|
17
16
|
declare const STATE_NONE = 1;
|
|
18
17
|
declare const STATE_WAITING = 2;
|
|
19
|
-
export { CLEANUP, EMPTY_FRAGMENT, NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_EVENTS, REGEX_SLOT_ATTRIBUTES, REGEX_SLOT_NODES,
|
|
18
|
+
export { ARRAY_SLOT, CLEANUP, EMPTY_FRAGMENT, NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_EVENTS, REGEX_SLOT_ATTRIBUTES, REGEX_SLOT_NODES, SLOT_HTML, STATE_HYDRATING, SLOT_MARKER, STATE_NONE, STATE_WAITING };
|
package/build/constants.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { fragment } from './utilities/fragment.js';
|
|
2
|
-
const
|
|
2
|
+
const ARRAY_SLOT = Symbol('template.array.slot');
|
|
3
|
+
const CLEANUP = Symbol('template.cleanup');
|
|
3
4
|
const EMPTY_FRAGMENT = fragment('');
|
|
4
5
|
const NODE_CLOSING = 1;
|
|
5
6
|
const NODE_COMMENT = 2;
|
|
@@ -30,11 +31,9 @@ const REGEX_EMPTY_TEXT_NODES = /(>|}|\s)\s+(<|{|\s)/g;
|
|
|
30
31
|
const REGEX_EVENTS = /(?:\s*on[\w-:]+\s*=(?:\s*["'][^"']*["'])*)/g;
|
|
31
32
|
const REGEX_SLOT_ATTRIBUTES = /<[\w-]+([^><]*{{\$}}[^><]*)>/g;
|
|
32
33
|
const REGEX_SLOT_NODES = /<([\w-]+|[\/!])(?:([^><]*{{\$}}[^><]*)|(?:[^><]*))?>|{{\$}}/g;
|
|
33
|
-
const RENDERABLE = Symbol();
|
|
34
|
-
const RENDERABLE_HTML_REACTIVE_ARRAY = 1;
|
|
35
34
|
const SLOT_HTML = '<!--$-->';
|
|
36
35
|
const SLOT_MARKER = '{{$}}';
|
|
37
36
|
const STATE_HYDRATING = 0;
|
|
38
37
|
const STATE_NONE = 1;
|
|
39
38
|
const STATE_WAITING = 2;
|
|
40
|
-
export { CLEANUP, EMPTY_FRAGMENT, NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_EVENTS, REGEX_SLOT_ATTRIBUTES, REGEX_SLOT_NODES,
|
|
39
|
+
export { ARRAY_SLOT, CLEANUP, EMPTY_FRAGMENT, NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_EVENTS, REGEX_SLOT_ATTRIBUTES, REGEX_SLOT_NODES, SLOT_HTML, STATE_HYDRATING, SLOT_MARKER, STATE_NONE, STATE_WAITING };
|
package/build/event/index.js
CHANGED
|
@@ -7,9 +7,11 @@ import onconnect from './onconnect.js';
|
|
|
7
7
|
import onresize from './onresize.js';
|
|
8
8
|
import ontick from './ontick.js';
|
|
9
9
|
let capture = new Set(['onblur', 'onfocus', 'onscroll']), controllers = new Map(), keys = {}, passive = new Set([
|
|
10
|
+
'onanimationend', 'onanimationiteration', 'onanimationstart',
|
|
10
11
|
'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel',
|
|
12
|
+
'onpointerenter', 'onpointerleave', 'onpointermove', 'onpointerout', 'onpointerover',
|
|
11
13
|
'onscroll',
|
|
12
|
-
'ontouchcancel', 'ontouchend', 'ontouchleave', 'ontouchmove', 'ontouchstart',
|
|
14
|
+
'ontouchcancel', 'ontouchend', 'ontouchleave', 'ontouchmove', 'ontouchstart', 'ontransitionend',
|
|
13
15
|
'onwheel'
|
|
14
16
|
]);
|
|
15
17
|
['onmousemove', 'onmousewheel', 'onscroll', 'ontouchend', 'ontouchmove', 'ontouchstart', 'onwheel'].map(event => {
|
|
@@ -38,16 +40,17 @@ function register(element, event) {
|
|
|
38
40
|
}
|
|
39
41
|
let key = keys[event] = Symbol();
|
|
40
42
|
addEventListener.call(window.document, event.slice(2), (e) => {
|
|
41
|
-
let node = e.target;
|
|
43
|
+
let fn, node = e.target;
|
|
42
44
|
while (node) {
|
|
43
|
-
|
|
45
|
+
fn = node[key];
|
|
46
|
+
if (typeof fn === 'function') {
|
|
44
47
|
defineProperty(e, 'currentTarget', {
|
|
45
48
|
configurable: true,
|
|
46
49
|
get() {
|
|
47
50
|
return node || window.document;
|
|
48
51
|
}
|
|
49
52
|
});
|
|
50
|
-
return
|
|
53
|
+
return fn.call(node, e);
|
|
51
54
|
}
|
|
52
55
|
node = parentElement.call(node);
|
|
53
56
|
}
|
package/build/event/ontick.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { STATE_HYDRATING, STATE_NONE } from '../constants.js';
|
|
2
|
-
import
|
|
2
|
+
import raf from '../utilities/raf.js';
|
|
3
3
|
let tasks = Object.assign(new Set(), { running: false });
|
|
4
4
|
function tick() {
|
|
5
5
|
if (tasks.size === 0) {
|
|
@@ -9,13 +9,13 @@ function tick() {
|
|
|
9
9
|
for (let task of tasks) {
|
|
10
10
|
task();
|
|
11
11
|
}
|
|
12
|
-
raf
|
|
12
|
+
raf(tick);
|
|
13
13
|
}
|
|
14
14
|
const add = (task) => {
|
|
15
15
|
tasks.add(task);
|
|
16
16
|
if (!tasks.running) {
|
|
17
17
|
tasks.running = true;
|
|
18
|
-
raf
|
|
18
|
+
raf(tick);
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
21
|
const remove = (task) => {
|
package/build/html/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { ReactiveArray } from '@esportsplus/reactivity';
|
|
2
|
-
import { Attribute, Attributes, Renderable
|
|
3
|
-
|
|
2
|
+
import { Attribute, Attributes, Renderable } from '../types.js';
|
|
3
|
+
import { ArraySlot } from '../slot/array.js';
|
|
4
|
+
type Values<T> = Attribute | Attributes<any> | ArraySlot<T> | Renderable<T>;
|
|
4
5
|
declare const html: {
|
|
5
6
|
<T>(literals: TemplateStringsArray, ...values: (Values<T> | Values<T>[])[]): Node;
|
|
6
|
-
reactive<T>(
|
|
7
|
+
reactive<T>(arr: ReactiveArray<T>, template: (value: T) => ReturnType<typeof html>): ArraySlot<T>;
|
|
7
8
|
};
|
|
8
9
|
export default html;
|
package/build/html/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { RENDERABLE, RENDERABLE_HTML_REACTIVE_ARRAY } from '../constants.js';
|
|
2
1
|
import { cloneNode } from '../utilities/node.js';
|
|
2
|
+
import { ArraySlot } from '../slot/array.js';
|
|
3
3
|
import parser from './parser.js';
|
|
4
4
|
const html = (literals, ...values) => {
|
|
5
5
|
let { fragment, slots } = parser.parse(literals), clone = cloneNode.call(fragment, true);
|
|
@@ -23,11 +23,7 @@ const html = (literals, ...values) => {
|
|
|
23
23
|
}
|
|
24
24
|
return clone;
|
|
25
25
|
};
|
|
26
|
-
html.reactive = (
|
|
27
|
-
return
|
|
28
|
-
[RENDERABLE]: RENDERABLE_HTML_REACTIVE_ARRAY,
|
|
29
|
-
array,
|
|
30
|
-
template
|
|
31
|
-
};
|
|
26
|
+
html.reactive = (arr, template) => {
|
|
27
|
+
return new ArraySlot(arr, template);
|
|
32
28
|
};
|
|
33
29
|
export default html;
|
package/build/html/parser.js
CHANGED
|
@@ -18,8 +18,9 @@ function build(literals) {
|
|
|
18
18
|
path: []
|
|
19
19
|
}], parsed = html.split(SLOT_MARKER), slot = 0, slots = [];
|
|
20
20
|
{
|
|
21
|
-
let attribute = '', buffer = '', char = '', quote = '';
|
|
22
|
-
|
|
21
|
+
let attribute = '', buffer = '', char = '', match, quote = '';
|
|
22
|
+
REGEX_SLOT_ATTRIBUTES.lastIndex = 0;
|
|
23
|
+
while (match = REGEX_SLOT_ATTRIBUTES.exec(html)) {
|
|
23
24
|
let found = match[1], metadata = attributes[found];
|
|
24
25
|
if (metadata) {
|
|
25
26
|
continue;
|
|
@@ -71,58 +72,62 @@ function build(literals) {
|
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
75
|
+
REGEX_SLOT_NODES.lastIndex = 0;
|
|
76
|
+
{
|
|
77
|
+
let match;
|
|
78
|
+
while (match = REGEX_SLOT_NODES.exec(html)) {
|
|
79
|
+
let parent = levels[level], type = match[1] === undefined ? NODE_SLOT : (NODE_WHITELIST[match[1].toLowerCase()] || NODE_ELEMENT);
|
|
80
|
+
if ((match.index || 1) - 1 > index) {
|
|
81
|
+
parent.children++;
|
|
82
|
+
}
|
|
83
|
+
if (type === NODE_ELEMENT || type === NODE_VOID) {
|
|
84
|
+
let attr = match[2], path = parent.path.length
|
|
85
|
+
? methods(parent.elements, parent.path, firstElementChild, nextElementSibling)
|
|
86
|
+
: methods(parent.children, [], firstChild, nextSibling);
|
|
87
|
+
if (attr) {
|
|
88
|
+
let metadata = attributes[attr];
|
|
89
|
+
if (!metadata) {
|
|
90
|
+
throw new Error(`Template: attribute metadata could not be found for '${attr}'`);
|
|
91
|
+
}
|
|
92
|
+
for (let i = 0, n = metadata.length; i < n; i++) {
|
|
93
|
+
let name = metadata[i];
|
|
94
|
+
slots.push({
|
|
95
|
+
fn: name === null ? a.spread : a.set,
|
|
96
|
+
name,
|
|
97
|
+
path
|
|
98
|
+
});
|
|
99
|
+
buffer += parsed[slot++];
|
|
100
|
+
}
|
|
87
101
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
name,
|
|
102
|
+
if (type === NODE_ELEMENT) {
|
|
103
|
+
levels[++level] = {
|
|
104
|
+
children: 0,
|
|
105
|
+
elements: 0,
|
|
93
106
|
path
|
|
94
|
-
}
|
|
95
|
-
buffer += parsed[slot++];
|
|
107
|
+
};
|
|
96
108
|
}
|
|
109
|
+
parent.elements++;
|
|
97
110
|
}
|
|
98
|
-
if (type ===
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
111
|
+
else if (type === NODE_SLOT) {
|
|
112
|
+
buffer += parsed[slot++] + SLOT_HTML;
|
|
113
|
+
slots.push({
|
|
114
|
+
fn: s,
|
|
115
|
+
name: null,
|
|
116
|
+
path: methods(parent.children, parent.path, firstChild, nextSibling)
|
|
117
|
+
});
|
|
104
118
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
buffer += parsed[slot];
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
119
|
-
if (type === NODE_CLOSING) {
|
|
120
|
-
level--;
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
parent.children++;
|
|
119
|
+
if (n === slot) {
|
|
120
|
+
buffer += parsed[slot];
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
if (type === NODE_CLOSING) {
|
|
124
|
+
level--;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
parent.children++;
|
|
128
|
+
}
|
|
129
|
+
index = (match.index || 0) + match[0].length;
|
|
124
130
|
}
|
|
125
|
-
index = (match.index || 0) + match[0].length;
|
|
126
131
|
}
|
|
127
132
|
if (events) {
|
|
128
133
|
buffer = buffer.replace(REGEX_EVENTS, '');
|
|
@@ -130,10 +135,13 @@ function build(literals) {
|
|
|
130
135
|
return set(literals, buffer, slots);
|
|
131
136
|
}
|
|
132
137
|
function methods(children, copy, first, next) {
|
|
133
|
-
let
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
138
|
+
let length = copy.length, methods = new Array(length + 1 + children);
|
|
139
|
+
for (let i = 0, n = length; i < n; i++) {
|
|
140
|
+
methods[i] = copy[i];
|
|
141
|
+
}
|
|
142
|
+
methods[length] = first;
|
|
143
|
+
for (let i = 0, n = children; i < n; i++) {
|
|
144
|
+
methods[length + 1 + i] = next;
|
|
137
145
|
}
|
|
138
146
|
return methods;
|
|
139
147
|
}
|
package/build/index.d.ts
CHANGED
package/build/render.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import { firstChild, nodeValue } from './utilities/node.js';
|
|
1
|
+
import { nodeValue } from './utilities/node.js';
|
|
2
|
+
import marker from './utilities/marker.js';
|
|
4
3
|
import slot from './slot/index.js';
|
|
5
|
-
let anchor, marker = firstChild.call(fragment(SLOT_HTML));
|
|
6
4
|
export default (parent, renderable) => {
|
|
5
|
+
let anchor = marker.cloneNode();
|
|
7
6
|
nodeValue.call(parent, '');
|
|
8
|
-
parent.append(anchor
|
|
7
|
+
parent.append(anchor);
|
|
9
8
|
slot(anchor, renderable);
|
|
10
9
|
};
|
package/build/slot/array.d.ts
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { ReactiveArray } from '@esportsplus/reactivity';
|
|
2
|
+
import html from '../html/index.js';
|
|
3
|
+
declare class ArraySlot<T> {
|
|
4
|
+
private array;
|
|
5
|
+
private queue;
|
|
6
|
+
private marker;
|
|
7
|
+
private nodes;
|
|
8
|
+
private scheduled;
|
|
9
|
+
private signal;
|
|
10
|
+
private template;
|
|
11
|
+
readonly fragment: Node;
|
|
12
|
+
constructor(array: ReactiveArray<T>, template: ((value: T) => ReturnType<typeof html>));
|
|
13
|
+
private anchor;
|
|
14
|
+
private clear;
|
|
15
|
+
private pop;
|
|
16
|
+
private push;
|
|
17
|
+
private schedule;
|
|
18
|
+
private shift;
|
|
19
|
+
private sort;
|
|
20
|
+
private splice;
|
|
21
|
+
private sync;
|
|
22
|
+
private unshift;
|
|
23
|
+
get length(): number;
|
|
24
|
+
}
|
|
25
|
+
export { ArraySlot };
|
package/build/slot/array.js
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
1
|
-
import { root } from '@esportsplus/reactivity';
|
|
2
|
-
import { EMPTY_FRAGMENT } from '../constants.js';
|
|
1
|
+
import { read, root, set, signal } from '@esportsplus/reactivity';
|
|
2
|
+
import { ARRAY_SLOT, EMPTY_FRAGMENT } from '../constants.js';
|
|
3
3
|
import { append } from '../utilities/fragment.js';
|
|
4
|
-
import { cloneNode, firstChild, lastChild } from '../utilities/node.js';
|
|
4
|
+
import { cloneNode, firstChild, lastChild, nextSibling } from '../utilities/node.js';
|
|
5
5
|
import { ondisconnect, remove } from './cleanup.js';
|
|
6
|
+
import marker from '../utilities/marker.js';
|
|
7
|
+
import raf from '../utilities/raf.js';
|
|
6
8
|
class ArraySlot {
|
|
7
9
|
array;
|
|
8
|
-
|
|
10
|
+
queue = [];
|
|
9
11
|
marker;
|
|
10
12
|
nodes = [];
|
|
13
|
+
scheduled = false;
|
|
14
|
+
signal;
|
|
11
15
|
template;
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
fragment;
|
|
17
|
+
constructor(array, template) {
|
|
14
18
|
this.array = array;
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
19
|
+
let fragment = this.fragment = cloneNode.call(EMPTY_FRAGMENT);
|
|
20
|
+
this.marker = marker.cloneNode();
|
|
21
|
+
this.signal = signal(array.length);
|
|
22
|
+
this.template = function (data) {
|
|
17
23
|
let dispose, frag = root((d) => {
|
|
18
24
|
dispose = d;
|
|
19
|
-
return template(data
|
|
25
|
+
return template(data);
|
|
20
26
|
}), group = {
|
|
21
27
|
head: firstChild.call(frag),
|
|
22
28
|
tail: lastChild.call(frag)
|
|
@@ -25,39 +31,41 @@ class ArraySlot {
|
|
|
25
31
|
ondisconnect(group.head, dispose);
|
|
26
32
|
return group;
|
|
27
33
|
};
|
|
28
|
-
|
|
29
|
-
array.
|
|
30
|
-
root(() =>
|
|
34
|
+
append.call(fragment, this.marker);
|
|
35
|
+
if (array.length) {
|
|
36
|
+
root(() => {
|
|
37
|
+
this.nodes = array.map(this.template);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
array.on('clear', () => {
|
|
41
|
+
this.queue.length = 0;
|
|
42
|
+
this.schedule({ op: 'clear' });
|
|
43
|
+
});
|
|
44
|
+
array.on('concat', ({ items }) => {
|
|
45
|
+
this.schedule({ items, op: 'concat' });
|
|
46
|
+
});
|
|
47
|
+
array.on('pop', () => {
|
|
48
|
+
this.schedule({ op: 'pop' });
|
|
31
49
|
});
|
|
32
|
-
array.on('pop', () => this.pop());
|
|
33
50
|
array.on('push', ({ items }) => {
|
|
34
|
-
|
|
51
|
+
this.schedule({ items, op: 'push' });
|
|
35
52
|
});
|
|
36
|
-
array.on('
|
|
37
|
-
|
|
38
|
-
|
|
53
|
+
array.on('reverse', () => {
|
|
54
|
+
this.schedule({ op: 'reverse' });
|
|
55
|
+
});
|
|
56
|
+
array.on('shift', () => {
|
|
57
|
+
this.schedule({ op: 'shift' });
|
|
58
|
+
});
|
|
59
|
+
array.on('sort', ({ order }) => {
|
|
60
|
+
this.schedule({ op: 'sort', order });
|
|
39
61
|
});
|
|
40
62
|
array.on('splice', ({ deleteCount, items, start }) => {
|
|
41
|
-
|
|
63
|
+
this.schedule({ deleteCount, items, op: 'splice', start });
|
|
42
64
|
});
|
|
43
65
|
array.on('unshift', ({ items }) => {
|
|
44
|
-
|
|
66
|
+
this.schedule({ items, op: 'unshift' });
|
|
45
67
|
});
|
|
46
68
|
}
|
|
47
|
-
get length() {
|
|
48
|
-
return this.nodes.length;
|
|
49
|
-
}
|
|
50
|
-
set length(n) {
|
|
51
|
-
if (n >= this.nodes.length) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
else if (n === 0) {
|
|
55
|
-
this.clear();
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
this.splice(n);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
69
|
anchor(index = this.nodes.length - 1) {
|
|
62
70
|
let node = this.nodes[index];
|
|
63
71
|
if (node) {
|
|
@@ -79,12 +87,53 @@ class ArraySlot {
|
|
|
79
87
|
this.nodes.push(...items.map(this.template));
|
|
80
88
|
anchor.after(this.fragment);
|
|
81
89
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
90
|
+
schedule(op) {
|
|
91
|
+
this.queue.push(op);
|
|
92
|
+
if (this.scheduled) {
|
|
93
|
+
return;
|
|
85
94
|
}
|
|
86
|
-
this.
|
|
87
|
-
|
|
95
|
+
this.scheduled = true;
|
|
96
|
+
raf(() => {
|
|
97
|
+
let queue = this.queue;
|
|
98
|
+
this.queue.length = 0;
|
|
99
|
+
root(() => {
|
|
100
|
+
for (let i = 0, n = queue.length; i < n; i++) {
|
|
101
|
+
let op = queue[i];
|
|
102
|
+
switch (op.op) {
|
|
103
|
+
case 'clear':
|
|
104
|
+
this.clear();
|
|
105
|
+
break;
|
|
106
|
+
case 'concat':
|
|
107
|
+
this.push(op.items);
|
|
108
|
+
break;
|
|
109
|
+
case 'pop':
|
|
110
|
+
this.pop();
|
|
111
|
+
break;
|
|
112
|
+
case 'push':
|
|
113
|
+
this.push(op.items);
|
|
114
|
+
break;
|
|
115
|
+
case 'reverse':
|
|
116
|
+
this.nodes.reverse();
|
|
117
|
+
this.sync();
|
|
118
|
+
break;
|
|
119
|
+
case 'shift':
|
|
120
|
+
this.shift();
|
|
121
|
+
break;
|
|
122
|
+
case 'sort':
|
|
123
|
+
this.sort(op.order);
|
|
124
|
+
break;
|
|
125
|
+
case 'splice':
|
|
126
|
+
this.splice(op.start, op.deleteCount, op.items);
|
|
127
|
+
break;
|
|
128
|
+
case 'unshift':
|
|
129
|
+
this.unshift(op.items);
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
set(this.signal, this.nodes.length);
|
|
135
|
+
this.scheduled = false;
|
|
136
|
+
});
|
|
88
137
|
}
|
|
89
138
|
shift() {
|
|
90
139
|
let group = this.nodes.shift();
|
|
@@ -92,7 +141,22 @@ class ArraySlot {
|
|
|
92
141
|
remove(group);
|
|
93
142
|
}
|
|
94
143
|
}
|
|
95
|
-
|
|
144
|
+
sort(order) {
|
|
145
|
+
let nodes = this.nodes, n = nodes.length;
|
|
146
|
+
if (n !== order.length) {
|
|
147
|
+
remove(...nodes.splice(0));
|
|
148
|
+
this.nodes = this.array.map(this.template);
|
|
149
|
+
this.marker.after(this.fragment);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
let sorted = new Array(n);
|
|
153
|
+
for (let i = 0; i < n; i++) {
|
|
154
|
+
sorted[i] = nodes[order[i]];
|
|
155
|
+
}
|
|
156
|
+
this.nodes = sorted;
|
|
157
|
+
this.sync();
|
|
158
|
+
}
|
|
159
|
+
splice(start, stop = this.nodes.length, items) {
|
|
96
160
|
if (!items.length) {
|
|
97
161
|
remove(...this.nodes.splice(start, stop));
|
|
98
162
|
return;
|
|
@@ -100,17 +164,28 @@ class ArraySlot {
|
|
|
100
164
|
remove(...this.nodes.splice(start, stop, ...items.map(this.template)));
|
|
101
165
|
this.anchor(start - 1).after(this.fragment);
|
|
102
166
|
}
|
|
167
|
+
sync() {
|
|
168
|
+
let nodes = this.nodes, n = nodes.length;
|
|
169
|
+
if (!n) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
for (let i = 0; i < n; i++) {
|
|
173
|
+
let group = nodes[i], next, node = group.head;
|
|
174
|
+
while (node) {
|
|
175
|
+
next = node === group.tail ? null : nextSibling.call(node);
|
|
176
|
+
append.call(this.fragment, node);
|
|
177
|
+
node = next;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
this.marker.after(this.fragment);
|
|
181
|
+
}
|
|
103
182
|
unshift(items) {
|
|
104
183
|
this.nodes.unshift(...items.map(this.template));
|
|
105
184
|
this.marker.after(this.fragment);
|
|
106
185
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
let array = renderable.array, slot = new ArraySlot(anchor, array, renderable.template);
|
|
110
|
-
if (array.length) {
|
|
111
|
-
root(() => {
|
|
112
|
-
slot.nodes = array.map(slot.template);
|
|
113
|
-
});
|
|
186
|
+
get length() {
|
|
187
|
+
return read(this.signal);
|
|
114
188
|
}
|
|
115
|
-
|
|
116
|
-
};
|
|
189
|
+
}
|
|
190
|
+
Object.defineProperty(ArraySlot.prototype, ARRAY_SLOT, { value: true });
|
|
191
|
+
export { ArraySlot };
|
package/build/slot/cleanup.js
CHANGED
|
@@ -7,8 +7,8 @@ const remove = (...groups) => {
|
|
|
7
7
|
for (let i = 0, n = groups.length; i < n; i++) {
|
|
8
8
|
let fns, fn, group = groups[i], head = group.head, next, tail = group.tail || head;
|
|
9
9
|
while (tail) {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
fns = tail[CLEANUP];
|
|
11
|
+
if (fns !== undefined) {
|
|
12
12
|
while (fn = fns.pop()) {
|
|
13
13
|
fn();
|
|
14
14
|
}
|
package/build/slot/effect.d.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
-
import { Element, Renderable } from '../types.js';
|
|
2
|
-
declare
|
|
3
|
-
|
|
1
|
+
import { Element, Renderable, SlotGroup } from '../types.js';
|
|
2
|
+
declare class EffectSlot {
|
|
3
|
+
anchor: Element;
|
|
4
|
+
disposer: VoidFunction;
|
|
5
|
+
group: SlotGroup | null;
|
|
6
|
+
scheduled: boolean;
|
|
7
|
+
textnode: Node | null;
|
|
8
|
+
constructor(anchor: Element, fn: (dispose?: VoidFunction) => Renderable<any>);
|
|
9
|
+
dispose(): void;
|
|
10
|
+
update(value: unknown): void;
|
|
11
|
+
}
|
|
12
|
+
export { EffectSlot };
|