@esportsplus/template 0.23.2 → 0.23.4
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 -4
- package/build/attributes.js +50 -28
- package/build/event/index.js +3 -3
- package/build/slot/{reactive.js → array.js} +14 -26
- package/build/slot/cleanup.d.ts +1 -1
- package/build/slot/cleanup.js +7 -35
- package/build/slot/effect.js +59 -51
- package/build/slot/render.js +4 -4
- package/build/types.d.ts +3 -3
- package/build/utilities/fragment.js +4 -5
- package/build/utilities/queue.d.ts +1 -2
- package/build/utilities/queue.js +2 -3
- package/package.json +2 -2
- package/src/attributes.ts +66 -31
- package/src/event/index.ts +4 -4
- package/src/slot/{reactive.ts → array.ts} +15 -41
- package/src/slot/cleanup.ts +10 -47
- package/src/slot/effect.ts +69 -57
- package/src/slot/render.ts +5 -5
- package/src/types.ts +3 -7
- package/src/utilities/fragment.ts +4 -6
- package/src/utilities/queue.ts +2 -4
- /package/build/slot/{reactive.d.ts → array.d.ts} +0 -0
package/build/attributes.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { STATE_HYDRATING, STATE_NONE, STATE_WAITING } from './constants.js';
|
|
2
1
|
import { Attributes, Element } from './types.js';
|
|
3
|
-
|
|
4
|
-
declare const set: (element: Element, name: string, value: unknown, state?: State) => void;
|
|
2
|
+
declare const set: (element: Element, name: string, value: unknown) => void;
|
|
5
3
|
declare const spread: (element: Element, value: Attributes | Attributes[]) => void;
|
|
6
4
|
declare const _default: {
|
|
7
|
-
set: (element: Element, name: string, value: unknown
|
|
5
|
+
set: (element: Element, name: string, value: unknown) => void;
|
|
8
6
|
spread: (element: Element, value: Attributes | Attributes[]) => void;
|
|
9
7
|
};
|
|
10
8
|
export default _default;
|
package/build/attributes.js
CHANGED
|
@@ -31,28 +31,51 @@ function list(ctx, element, id, name, state, value) {
|
|
|
31
31
|
if (value == null || value === false || value === '') {
|
|
32
32
|
value = '';
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
store[name] = (element.getAttribute(name) || '').trim();
|
|
34
|
+
let base = name + '.static', delimiter = delimiters[name], store = (ctx ??= context(element)).store ??= {}, dynamic = store[name], type = typeof value;
|
|
35
|
+
if (dynamic === undefined) {
|
|
36
|
+
let value = (element.getAttribute(name) || '').trim();
|
|
37
|
+
store[base] = value;
|
|
38
|
+
store[name] = dynamic = new Set();
|
|
40
39
|
}
|
|
41
|
-
let current = value ? (delimiter + value) : '';
|
|
42
40
|
if (id === null) {
|
|
43
|
-
|
|
41
|
+
if (value && type === 'string') {
|
|
42
|
+
store[base] += (store[base] ? delimiter : '') + value;
|
|
43
|
+
}
|
|
44
44
|
}
|
|
45
45
|
else {
|
|
46
|
-
let
|
|
47
|
-
if (
|
|
48
|
-
|
|
46
|
+
let hot = {};
|
|
47
|
+
if (value && type === 'string') {
|
|
48
|
+
let part, parts = value.split(delimiter);
|
|
49
|
+
for (let i = 0, n = parts.length; i < n; i++) {
|
|
50
|
+
part = parts[i].trim();
|
|
51
|
+
if (part === '') {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
dynamic.add(part);
|
|
55
|
+
hot[part] = null;
|
|
56
|
+
}
|
|
49
57
|
}
|
|
50
|
-
|
|
51
|
-
|
|
58
|
+
let cold = store[id];
|
|
59
|
+
if (cold !== undefined) {
|
|
60
|
+
for (let part in cold) {
|
|
61
|
+
if (part in hot) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
dynamic.delete(part);
|
|
65
|
+
}
|
|
52
66
|
}
|
|
53
|
-
store[id] =
|
|
67
|
+
store[id] = hot;
|
|
68
|
+
}
|
|
69
|
+
value = store[base];
|
|
70
|
+
for (let key of dynamic) {
|
|
71
|
+
value += (value ? delimiter : '') + key;
|
|
72
|
+
}
|
|
73
|
+
if (state === STATE_HYDRATING) {
|
|
74
|
+
apply(element, name, value);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
schedule(ctx, element, name, state, value);
|
|
54
78
|
}
|
|
55
|
-
schedule(ctx, element, name, state, store[name]);
|
|
56
79
|
}
|
|
57
80
|
function property(ctx, element, id, name, state, value) {
|
|
58
81
|
if (value == null || value === false || value === '') {
|
|
@@ -65,13 +88,14 @@ function property(ctx, element, id, name, state, value) {
|
|
|
65
88
|
}
|
|
66
89
|
ctx[name] = value;
|
|
67
90
|
}
|
|
68
|
-
schedule(ctx, element, name, state, value);
|
|
69
|
-
}
|
|
70
|
-
function schedule(ctx, element, name, state, value) {
|
|
71
91
|
if (state === STATE_HYDRATING) {
|
|
72
92
|
apply(element, name, value);
|
|
73
|
-
return;
|
|
74
93
|
}
|
|
94
|
+
else {
|
|
95
|
+
schedule(ctx, element, name, state, value);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function schedule(ctx, element, name, state, value) {
|
|
75
99
|
ctx ??= context(element);
|
|
76
100
|
(ctx.updates ??= {})[name] = value;
|
|
77
101
|
if (state === STATE_NONE && !ctx.updating) {
|
|
@@ -101,12 +125,11 @@ function task() {
|
|
|
101
125
|
scheduled = false;
|
|
102
126
|
}
|
|
103
127
|
}
|
|
104
|
-
const set = (element, name, value
|
|
105
|
-
let fn = name === 'class' || name === 'style' ? list : property, type = typeof value;
|
|
128
|
+
const set = (element, name, value) => {
|
|
129
|
+
let fn = name === 'class' || name === 'style' ? list : property, state = STATE_HYDRATING, type = typeof value;
|
|
106
130
|
if (type === 'function') {
|
|
107
131
|
if (name.startsWith('on')) {
|
|
108
|
-
event(element, name, value);
|
|
109
|
-
return;
|
|
132
|
+
return event(element, name, value);
|
|
110
133
|
}
|
|
111
134
|
let ctx = context(element);
|
|
112
135
|
ctx.effect ??= 0;
|
|
@@ -129,16 +152,14 @@ const set = (element, name, value, state = STATE_HYDRATING) => {
|
|
|
129
152
|
return;
|
|
130
153
|
}
|
|
131
154
|
if (type !== 'object') {
|
|
132
|
-
fn(null, element, null, name, state, value);
|
|
133
|
-
return;
|
|
134
155
|
}
|
|
135
|
-
if (isArray(value)) {
|
|
156
|
+
else if (isArray(value)) {
|
|
136
157
|
for (let i = 0, n = value.length; i < n; i++) {
|
|
137
158
|
let v = value[i];
|
|
138
159
|
if (v == null || v === false || v === '') {
|
|
139
160
|
continue;
|
|
140
161
|
}
|
|
141
|
-
set(element, name, v
|
|
162
|
+
set(element, name, v);
|
|
142
163
|
}
|
|
143
164
|
return;
|
|
144
165
|
}
|
|
@@ -146,7 +167,8 @@ const set = (element, name, value, state = STATE_HYDRATING) => {
|
|
|
146
167
|
};
|
|
147
168
|
const spread = function (element, value) {
|
|
148
169
|
if (isObject(value)) {
|
|
149
|
-
|
|
170
|
+
let names = Object.keys(value), name;
|
|
171
|
+
while (name = names.pop()) {
|
|
150
172
|
let v = value[name];
|
|
151
173
|
if (v == null || v === false || v === '') {
|
|
152
174
|
continue;
|
package/build/event/index.js
CHANGED
|
@@ -66,15 +66,15 @@ export default (element, event, listener) => {
|
|
|
66
66
|
case 'ondisconnect':
|
|
67
67
|
ondisconnect(element, () => listener(element));
|
|
68
68
|
return;
|
|
69
|
+
case 'onrender':
|
|
70
|
+
root(() => listener(element));
|
|
71
|
+
return;
|
|
69
72
|
case 'onresize':
|
|
70
73
|
onresize(element, listener);
|
|
71
74
|
return;
|
|
72
75
|
case 'ontick':
|
|
73
76
|
ontick(element, listener);
|
|
74
77
|
return;
|
|
75
|
-
case 'onrender':
|
|
76
|
-
root(() => listener(element));
|
|
77
|
-
return;
|
|
78
78
|
default:
|
|
79
79
|
element[keys[event] || register(element, event)] = listener;
|
|
80
80
|
return;
|
|
@@ -3,7 +3,7 @@ import { EMPTY_FRAGMENT } from '../constants.js';
|
|
|
3
3
|
import { append } from '../utilities/fragment.js';
|
|
4
4
|
import { cloneNode, firstChild, lastChild } from '../utilities/node.js';
|
|
5
5
|
import { ondisconnect, remove } from './cleanup.js';
|
|
6
|
-
class
|
|
6
|
+
class ArraySlot {
|
|
7
7
|
array;
|
|
8
8
|
fragment;
|
|
9
9
|
marker;
|
|
@@ -16,7 +16,7 @@ class ReactiveArraySlot {
|
|
|
16
16
|
this.template = function (data, i) {
|
|
17
17
|
let dispose, frag = root((d) => {
|
|
18
18
|
dispose = d;
|
|
19
|
-
return template
|
|
19
|
+
return template(data, i);
|
|
20
20
|
}), group = {
|
|
21
21
|
head: firstChild.call(frag),
|
|
22
22
|
tail: lastChild.call(frag)
|
|
@@ -66,58 +66,46 @@ class ReactiveArraySlot {
|
|
|
66
66
|
return this.marker;
|
|
67
67
|
}
|
|
68
68
|
clear() {
|
|
69
|
-
remove(this.nodes);
|
|
69
|
+
remove(...this.nodes.splice(0));
|
|
70
70
|
}
|
|
71
71
|
pop() {
|
|
72
72
|
let group = this.nodes.pop();
|
|
73
73
|
if (group) {
|
|
74
|
-
remove(
|
|
74
|
+
remove(group);
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
push(items) {
|
|
78
|
-
let anchor = this.anchor()
|
|
79
|
-
this.nodes.
|
|
80
|
-
for (let i = 0, n = items.length; i < n; i++) {
|
|
81
|
-
this.nodes[i + length] = this.template.call(array, items[i], i);
|
|
82
|
-
}
|
|
78
|
+
let anchor = this.anchor();
|
|
79
|
+
this.nodes.push(...items.map(this.template));
|
|
83
80
|
anchor.after(this.fragment);
|
|
84
81
|
}
|
|
85
82
|
render() {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
remove(nodes);
|
|
83
|
+
if (this.nodes.length) {
|
|
84
|
+
remove(...this.nodes.splice(0));
|
|
89
85
|
}
|
|
90
|
-
nodes = this.array.map(this.template);
|
|
86
|
+
this.nodes = this.array.map(this.template);
|
|
91
87
|
this.marker.after(this.fragment);
|
|
92
88
|
}
|
|
93
89
|
shift() {
|
|
94
90
|
let group = this.nodes.shift();
|
|
95
91
|
if (group) {
|
|
96
|
-
remove(
|
|
92
|
+
remove(group);
|
|
97
93
|
}
|
|
98
94
|
}
|
|
99
95
|
splice(start, stop = this.nodes.length, ...items) {
|
|
100
96
|
if (!items.length) {
|
|
101
|
-
return remove(this.nodes.splice(start, stop));
|
|
102
|
-
}
|
|
103
|
-
let array = this.array, n = items.length, nodes = new Array(n);
|
|
104
|
-
for (let i = 0; i < n; i++) {
|
|
105
|
-
nodes[i] = this.template.call(array, items[i], i);
|
|
97
|
+
return remove(...this.nodes.splice(start, stop));
|
|
106
98
|
}
|
|
107
|
-
remove(this.nodes.splice(start, stop, ...
|
|
99
|
+
remove(...this.nodes.splice(start, stop, ...items.map(this.template)));
|
|
108
100
|
this.anchor(start - 1).after(this.fragment);
|
|
109
101
|
}
|
|
110
102
|
unshift(items) {
|
|
111
|
-
|
|
112
|
-
for (let i = 0; i < n; i++) {
|
|
113
|
-
nodes[i] = this.template.call(array, items[i], i);
|
|
114
|
-
}
|
|
115
|
-
this.nodes.unshift(...nodes);
|
|
103
|
+
this.nodes.unshift(...items.map(this.template));
|
|
116
104
|
this.marker.after(this.fragment);
|
|
117
105
|
}
|
|
118
106
|
}
|
|
119
107
|
export default (anchor, renderable) => {
|
|
120
|
-
let { array, template } = renderable, slot = new
|
|
108
|
+
let { array, template } = renderable, slot = new ArraySlot(anchor, array, template);
|
|
121
109
|
if (array.length) {
|
|
122
110
|
root(() => {
|
|
123
111
|
slot.nodes = array.map(slot.template);
|
package/build/slot/cleanup.d.ts
CHANGED
package/build/slot/cleanup.js
CHANGED
|
@@ -1,42 +1,17 @@
|
|
|
1
|
-
import queue from '@esportsplus/queue';
|
|
2
1
|
import { CLEANUP } from '../constants.js';
|
|
3
|
-
import { microtask } from '../utilities/queue.js';
|
|
4
2
|
import { previousSibling } from '../utilities/node.js';
|
|
5
|
-
let cleanup = queue(64), scheduled = false;
|
|
6
|
-
function schedule() {
|
|
7
|
-
if (!cleanup.length || scheduled) {
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
|
-
scheduled = true;
|
|
11
|
-
microtask.add(task);
|
|
12
|
-
}
|
|
13
|
-
function task() {
|
|
14
|
-
try {
|
|
15
|
-
let fns, fn, n = cleanup.length;
|
|
16
|
-
while ((fns = cleanup.next()) && n--) {
|
|
17
|
-
while (fn = fns.pop()) {
|
|
18
|
-
fn();
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
catch { }
|
|
23
|
-
if (cleanup.length) {
|
|
24
|
-
microtask.add(task);
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
scheduled = false;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
3
|
const ondisconnect = (element, fn) => {
|
|
31
4
|
(element[CLEANUP] ??= []).push(fn);
|
|
32
5
|
};
|
|
33
|
-
const remove = (groups) => {
|
|
34
|
-
let
|
|
35
|
-
|
|
36
|
-
let head = group.head, next, tail = group.tail || head;
|
|
6
|
+
const remove = (...groups) => {
|
|
7
|
+
for (let i = 0, n = groups.length; i < n; i++) {
|
|
8
|
+
let fns, fn, group = groups[i], head = group.head, next, tail = group.tail || head;
|
|
37
9
|
while (tail) {
|
|
38
10
|
if (CLEANUP in tail) {
|
|
39
|
-
|
|
11
|
+
fns = tail[CLEANUP];
|
|
12
|
+
while (fn = fns.pop()) {
|
|
13
|
+
fn();
|
|
14
|
+
}
|
|
40
15
|
}
|
|
41
16
|
next = previousSibling.call(tail);
|
|
42
17
|
tail.remove();
|
|
@@ -46,8 +21,5 @@ const remove = (groups) => {
|
|
|
46
21
|
tail = next;
|
|
47
22
|
}
|
|
48
23
|
}
|
|
49
|
-
if (!scheduled && cleanup.length) {
|
|
50
|
-
schedule();
|
|
51
|
-
}
|
|
52
24
|
};
|
|
53
25
|
export { ondisconnect, remove };
|
package/build/slot/effect.js
CHANGED
|
@@ -5,67 +5,75 @@ import { raf } from '../utilities/queue.js';
|
|
|
5
5
|
import { remove } from './cleanup.js';
|
|
6
6
|
import text from '../utilities/text.js';
|
|
7
7
|
import render from './render.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
anchor.after(textnode);
|
|
8
|
+
class EffectSlot {
|
|
9
|
+
anchor;
|
|
10
|
+
disposer;
|
|
11
|
+
group = null;
|
|
12
|
+
textnode = null;
|
|
13
|
+
constructor(anchor, fn) {
|
|
14
|
+
let dispose = fn.length ? () => this.dispose() : undefined, state = STATE_HYDRATING;
|
|
15
|
+
this.anchor = anchor;
|
|
16
|
+
this.disposer = effect(() => {
|
|
17
|
+
let value = fn(dispose);
|
|
18
|
+
if (state === STATE_HYDRATING) {
|
|
19
|
+
state = STATE_NONE;
|
|
20
|
+
this.update(value);
|
|
22
21
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
let fragment = render(anchor, value), head = firstChild.call(fragment);
|
|
30
|
-
if (textnode && textnode.isConnected) {
|
|
31
|
-
remove([{ head: textnode, tail: textnode }]);
|
|
32
|
-
}
|
|
33
|
-
if (head) {
|
|
34
|
-
this.group = {
|
|
35
|
-
head,
|
|
36
|
-
tail: lastChild.call(fragment)
|
|
37
|
-
};
|
|
38
|
-
anchor.after(fragment);
|
|
39
|
-
}
|
|
22
|
+
else {
|
|
23
|
+
raf.add(() => {
|
|
24
|
+
this.update(value);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
});
|
|
40
28
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
let context = {
|
|
44
|
-
group: undefined,
|
|
45
|
-
textnode: undefined
|
|
46
|
-
}, dispose = fn.length ? () => {
|
|
47
|
-
let { group, textnode } = context;
|
|
29
|
+
dispose() {
|
|
30
|
+
let { anchor, group, textnode } = this;
|
|
48
31
|
if (textnode) {
|
|
49
32
|
group = { head: anchor, tail: textnode };
|
|
50
33
|
}
|
|
51
34
|
else if (group) {
|
|
52
35
|
group.head = anchor;
|
|
53
36
|
}
|
|
54
|
-
|
|
37
|
+
this.disposer();
|
|
55
38
|
if (group) {
|
|
56
|
-
remove(
|
|
39
|
+
remove(group);
|
|
57
40
|
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
41
|
+
}
|
|
42
|
+
update(value) {
|
|
43
|
+
if (this.group) {
|
|
44
|
+
remove(this.group);
|
|
45
|
+
this.group = null;
|
|
46
|
+
}
|
|
47
|
+
if (value == null || value === false) {
|
|
48
|
+
value = '';
|
|
64
49
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
50
|
+
let { anchor, textnode } = this;
|
|
51
|
+
if (typeof value !== 'object') {
|
|
52
|
+
if (textnode) {
|
|
53
|
+
nodeValue.call(textnode, String(value));
|
|
54
|
+
if (!textnode.isConnected) {
|
|
55
|
+
anchor.after(textnode);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
anchor.after(this.textnode = text(String(value)));
|
|
60
|
+
}
|
|
69
61
|
}
|
|
70
|
-
|
|
62
|
+
else {
|
|
63
|
+
let fragment = render(anchor, value), head = firstChild.call(fragment);
|
|
64
|
+
if (textnode?.isConnected) {
|
|
65
|
+
remove({ head: textnode, tail: textnode });
|
|
66
|
+
}
|
|
67
|
+
if (head) {
|
|
68
|
+
this.group = {
|
|
69
|
+
head,
|
|
70
|
+
tail: lastChild.call(fragment)
|
|
71
|
+
};
|
|
72
|
+
anchor.after(fragment);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export default (anchor, fn) => {
|
|
78
|
+
new EffectSlot(anchor, fn);
|
|
71
79
|
};
|
package/build/slot/render.js
CHANGED
|
@@ -3,7 +3,7 @@ import { EMPTY_FRAGMENT, RENDERABLE } from '../constants.js';
|
|
|
3
3
|
import { cloneNode, lastChild } from '../utilities/node.js';
|
|
4
4
|
import { append } from '../utilities/fragment.js';
|
|
5
5
|
import text from '../utilities/text.js';
|
|
6
|
-
import
|
|
6
|
+
import array from './array.js';
|
|
7
7
|
export default function render(anchor, input) {
|
|
8
8
|
if (input == null || input === false || input === '') {
|
|
9
9
|
return EMPTY_FRAGMENT;
|
|
@@ -11,12 +11,12 @@ export default function render(anchor, input) {
|
|
|
11
11
|
if (typeof input !== 'object') {
|
|
12
12
|
return text(input);
|
|
13
13
|
}
|
|
14
|
+
if (RENDERABLE in input) {
|
|
15
|
+
return array(anchor, input);
|
|
16
|
+
}
|
|
14
17
|
if ('nodeType' in input) {
|
|
15
18
|
return input;
|
|
16
19
|
}
|
|
17
|
-
if (RENDERABLE in input) {
|
|
18
|
-
return reactive(anchor, input);
|
|
19
|
-
}
|
|
20
20
|
if (isArray(input)) {
|
|
21
21
|
let fragment = cloneNode.call(EMPTY_FRAGMENT);
|
|
22
22
|
for (let i = 0, n = input.length; i < n; i++) {
|
package/build/types.d.ts
CHANGED
|
@@ -6,14 +6,14 @@ import slot from './slot/index.js';
|
|
|
6
6
|
import html from './html/index.js';
|
|
7
7
|
type Attribute = Effect<Primitive | Primitive[]> | ((...args: any[]) => void) | Primitive;
|
|
8
8
|
type Attributes<T extends HTMLElement = Element> = {
|
|
9
|
-
[key: `aria-${string}`]: string | number | boolean | undefined;
|
|
10
|
-
[key: `data-${string}`]: string | undefined;
|
|
11
9
|
class?: Attribute | Attribute[];
|
|
12
10
|
onconnect?: (element: T) => void;
|
|
13
11
|
ondisconnect?: (element: T) => void;
|
|
14
12
|
onrender?: (element: T) => void;
|
|
15
13
|
ontick?: (dispose: VoidFunction, element: T) => void;
|
|
16
14
|
style?: Attribute | Attribute[];
|
|
15
|
+
[key: `aria-${string}`]: string | number | boolean | undefined;
|
|
16
|
+
[key: `data-${string}`]: string | undefined;
|
|
17
17
|
} & {
|
|
18
18
|
[K in keyof GlobalEventHandlersEventMap as `on${string & K}`]?: (this: T, event: GlobalEventHandlersEventMap[K]) => void;
|
|
19
19
|
} & Record<PropertyKey, unknown>;
|
|
@@ -24,7 +24,7 @@ type Renderable<T> = DocumentFragment | Effect<T> | Node | NodeList | Primitive
|
|
|
24
24
|
type RenderableReactive<T> = Readonly<{
|
|
25
25
|
[RENDERABLE]: typeof RENDERABLE_HTML_REACTIVE_ARRAY;
|
|
26
26
|
array: ReactiveArray<T>;
|
|
27
|
-
template: (
|
|
27
|
+
template: (value: T, i: number) => ReturnType<typeof html>;
|
|
28
28
|
}>;
|
|
29
29
|
type SlotGroup = {
|
|
30
30
|
head: Element;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { innerHTML } from './element.js';
|
|
2
2
|
import { cloneNode } from './node.js';
|
|
3
|
-
let
|
|
3
|
+
let template = document.createElement('template');
|
|
4
4
|
const append = DocumentFragment.prototype.append;
|
|
5
5
|
const fragment = (html) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return content;
|
|
6
|
+
let element = cloneNode.call(template);
|
|
7
|
+
innerHTML.call(element, html);
|
|
8
|
+
return element.content;
|
|
10
9
|
};
|
|
11
10
|
export { append, fragment };
|
package/build/utilities/queue.js
CHANGED
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"author": "ICJR",
|
|
3
3
|
"dependencies": {
|
|
4
4
|
"@esportsplus/queue": "^0.1.0",
|
|
5
|
-
"@esportsplus/reactivity": "^0.
|
|
5
|
+
"@esportsplus/reactivity": "^0.17.1",
|
|
6
6
|
"@esportsplus/tasks": "^0.2.1",
|
|
7
7
|
"@esportsplus/utilities": "^0.22.1"
|
|
8
8
|
},
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"private": false,
|
|
15
15
|
"type": "module",
|
|
16
16
|
"types": "./build/index.d.ts",
|
|
17
|
-
"version": "0.23.
|
|
17
|
+
"version": "0.23.4",
|
|
18
18
|
"scripts": {
|
|
19
19
|
"build": "tsc && tsc-alias",
|
|
20
20
|
"-": "-"
|
package/src/attributes.ts
CHANGED
|
@@ -61,36 +61,70 @@ function list(
|
|
|
61
61
|
value = '';
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
let base = name + '.static',
|
|
65
|
+
delimiter = delimiters[name],
|
|
66
|
+
store = (ctx ??= context(element)).store ??= {},
|
|
67
|
+
dynamic = store[name] as Set<string> | undefined,
|
|
68
|
+
type = typeof value;
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
if (dynamic === undefined) {
|
|
71
|
+
let value = (element.getAttribute(name) || '').trim();
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
store[name] =
|
|
73
|
+
store[base] = value;
|
|
74
|
+
store[name] = dynamic = new Set();
|
|
73
75
|
}
|
|
74
76
|
|
|
75
|
-
let current = value ? (delimiter + value) : '';
|
|
76
|
-
|
|
77
77
|
if (id === null) {
|
|
78
|
-
|
|
78
|
+
if (value && type === 'string') {
|
|
79
|
+
store[base] += (store[base] ? delimiter : '') + value;
|
|
80
|
+
}
|
|
79
81
|
}
|
|
80
82
|
else {
|
|
81
|
-
let
|
|
83
|
+
let hot: Attributes = {};
|
|
82
84
|
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
+
if (value && type === 'string') {
|
|
86
|
+
let part: string,
|
|
87
|
+
parts = (value as string).split(delimiter);
|
|
88
|
+
|
|
89
|
+
for (let i = 0, n = parts.length; i < n; i++) {
|
|
90
|
+
part = parts[i].trim();
|
|
91
|
+
|
|
92
|
+
if (part === '') {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
dynamic.add(part);
|
|
97
|
+
hot[part] = null;
|
|
98
|
+
}
|
|
85
99
|
}
|
|
86
|
-
|
|
87
|
-
|
|
100
|
+
|
|
101
|
+
let cold = store[id] as Attributes | undefined;
|
|
102
|
+
|
|
103
|
+
if (cold !== undefined) {
|
|
104
|
+
for (let part in cold) {
|
|
105
|
+
if (part in hot) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
dynamic.delete(part);
|
|
110
|
+
}
|
|
88
111
|
}
|
|
89
112
|
|
|
90
|
-
store[id] =
|
|
113
|
+
store[id] = hot;
|
|
91
114
|
}
|
|
92
115
|
|
|
93
|
-
|
|
116
|
+
value = store[base];
|
|
117
|
+
|
|
118
|
+
for (let key of dynamic) {
|
|
119
|
+
value += (value ? delimiter : '') + key;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (state === STATE_HYDRATING) {
|
|
123
|
+
apply(element, name, value);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
schedule(ctx, element, name, state, value);
|
|
127
|
+
}
|
|
94
128
|
}
|
|
95
129
|
|
|
96
130
|
function property(
|
|
@@ -115,15 +149,15 @@ function property(
|
|
|
115
149
|
ctx[name] = value as string;
|
|
116
150
|
}
|
|
117
151
|
|
|
118
|
-
schedule(ctx, element, name, state, value);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function schedule(ctx: Context | null, element: Element, name: string, state: State, value: unknown) {
|
|
122
152
|
if (state === STATE_HYDRATING) {
|
|
123
153
|
apply(element, name, value);
|
|
124
|
-
return;
|
|
125
154
|
}
|
|
155
|
+
else {
|
|
156
|
+
schedule(ctx, element, name, state, value);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
126
159
|
|
|
160
|
+
function schedule(ctx: Context | null, element: Element, name: string, state: State, value: unknown) {
|
|
127
161
|
ctx ??= context(element);
|
|
128
162
|
(ctx.updates ??= {})[name] = value;
|
|
129
163
|
|
|
@@ -164,14 +198,14 @@ function task() {
|
|
|
164
198
|
}
|
|
165
199
|
|
|
166
200
|
|
|
167
|
-
const set = (element: Element, name: string, value: unknown
|
|
201
|
+
const set = (element: Element, name: string, value: unknown) => {
|
|
168
202
|
let fn = name === 'class' || name === 'style' ? list : property,
|
|
203
|
+
state: State = STATE_HYDRATING,
|
|
169
204
|
type = typeof value;
|
|
170
205
|
|
|
171
206
|
if (type === 'function') {
|
|
172
207
|
if (name.startsWith('on')) {
|
|
173
|
-
event(element, name as `on${string}`, value as Function);
|
|
174
|
-
return;
|
|
208
|
+
return event(element, name as `on${string}`, value as Function);
|
|
175
209
|
}
|
|
176
210
|
|
|
177
211
|
let ctx = context(element);
|
|
@@ -210,11 +244,9 @@ const set = (element: Element, name: string, value: unknown, state: State = STAT
|
|
|
210
244
|
}
|
|
211
245
|
|
|
212
246
|
if (type !== 'object') {
|
|
213
|
-
|
|
214
|
-
return;
|
|
247
|
+
// Skip isArray when possible
|
|
215
248
|
}
|
|
216
|
-
|
|
217
|
-
if (isArray(value)) {
|
|
249
|
+
else if (isArray(value)) {
|
|
218
250
|
for (let i = 0, n = value.length; i < n; i++) {
|
|
219
251
|
let v = value[i];
|
|
220
252
|
|
|
@@ -222,7 +254,7 @@ const set = (element: Element, name: string, value: unknown, state: State = STAT
|
|
|
222
254
|
continue;
|
|
223
255
|
}
|
|
224
256
|
|
|
225
|
-
set(element, name, v
|
|
257
|
+
set(element, name, v);
|
|
226
258
|
}
|
|
227
259
|
return;
|
|
228
260
|
}
|
|
@@ -232,7 +264,10 @@ const set = (element: Element, name: string, value: unknown, state: State = STAT
|
|
|
232
264
|
|
|
233
265
|
const spread = function (element: Element, value: Attributes | Attributes[]) {
|
|
234
266
|
if (isObject(value)) {
|
|
235
|
-
|
|
267
|
+
let names = Object.keys(value),
|
|
268
|
+
name;
|
|
269
|
+
|
|
270
|
+
while (name = names.pop()) {
|
|
236
271
|
let v = value[name];
|
|
237
272
|
|
|
238
273
|
if (v == null || v === false || v === '') {
|
package/src/event/index.ts
CHANGED
|
@@ -98,6 +98,10 @@ export default (element: Element, event: `on${string}`, listener: Function): voi
|
|
|
98
98
|
ondisconnect(element, () => listener(element));
|
|
99
99
|
return;
|
|
100
100
|
|
|
101
|
+
case 'onrender':
|
|
102
|
+
root(() => listener(element));
|
|
103
|
+
return;
|
|
104
|
+
|
|
101
105
|
case 'onresize':
|
|
102
106
|
onresize(element, listener);
|
|
103
107
|
return;
|
|
@@ -106,10 +110,6 @@ export default (element: Element, event: `on${string}`, listener: Function): voi
|
|
|
106
110
|
ontick(element, listener);
|
|
107
111
|
return;
|
|
108
112
|
|
|
109
|
-
case 'onrender':
|
|
110
|
-
root(() => listener(element));
|
|
111
|
-
return;
|
|
112
|
-
|
|
113
113
|
default:
|
|
114
114
|
element[ keys[event] || register(element, event) ] = listener;
|
|
115
115
|
return;
|
|
@@ -6,15 +6,12 @@ import { cloneNode, firstChild, lastChild } from '~/utilities/node';
|
|
|
6
6
|
import { ondisconnect, remove } from './cleanup';
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class ArraySlot<T> {
|
|
10
10
|
array: ReactiveArray<T>;
|
|
11
11
|
fragment: Node;
|
|
12
12
|
marker: Element;
|
|
13
13
|
nodes: SlotGroup[] = [];
|
|
14
|
-
template: (
|
|
15
|
-
this: ReactiveArray<T>,
|
|
16
|
-
...args: Parameters< Parameters<ReactiveArray<T>['map']>[0] >
|
|
17
|
-
) => SlotGroup;
|
|
14
|
+
template: (...args: Parameters< RenderableReactive<T>['template'] >) => SlotGroup;
|
|
18
15
|
|
|
19
16
|
|
|
20
17
|
constructor(anchor: Element, array: ReactiveArray<T>, template: RenderableReactive<T>['template']) {
|
|
@@ -26,7 +23,7 @@ class ReactiveArraySlot<T> {
|
|
|
26
23
|
let dispose: VoidFunction,
|
|
27
24
|
frag = root((d) => {
|
|
28
25
|
dispose = d;
|
|
29
|
-
return template
|
|
26
|
+
return template(data, i);
|
|
30
27
|
}),
|
|
31
28
|
group = {
|
|
32
29
|
head: firstChild.call(frag),
|
|
@@ -88,38 +85,31 @@ class ReactiveArraySlot<T> {
|
|
|
88
85
|
}
|
|
89
86
|
|
|
90
87
|
clear() {
|
|
91
|
-
remove(this.nodes);
|
|
88
|
+
remove(...this.nodes.splice(0));
|
|
92
89
|
}
|
|
93
90
|
|
|
94
91
|
pop() {
|
|
95
92
|
let group = this.nodes.pop();
|
|
96
93
|
|
|
97
94
|
if (group) {
|
|
98
|
-
remove(
|
|
95
|
+
remove(group);
|
|
99
96
|
}
|
|
100
97
|
}
|
|
101
98
|
|
|
102
99
|
push(items: T[]) {
|
|
103
|
-
let anchor = this.anchor()
|
|
104
|
-
array = this.array,
|
|
105
|
-
length = this.nodes.length;
|
|
100
|
+
let anchor = this.anchor();
|
|
106
101
|
|
|
107
|
-
this.nodes.
|
|
102
|
+
this.nodes.push( ...items.map(this.template) );
|
|
108
103
|
|
|
109
|
-
for (let i = 0, n = items.length; i < n; i++) {
|
|
110
|
-
this.nodes[i + length] = this.template.call(array, items[i], i);
|
|
111
|
-
}
|
|
112
104
|
anchor.after(this.fragment);
|
|
113
105
|
}
|
|
114
106
|
|
|
115
107
|
render() {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (nodes.length) {
|
|
119
|
-
remove(nodes);
|
|
108
|
+
if (this.nodes.length) {
|
|
109
|
+
remove(...this.nodes.splice(0));
|
|
120
110
|
}
|
|
121
111
|
|
|
122
|
-
nodes = this.array.map(this.template);
|
|
112
|
+
this.nodes = this.array.map(this.template);
|
|
123
113
|
this.marker.after(this.fragment);
|
|
124
114
|
}
|
|
125
115
|
|
|
@@ -127,37 +117,21 @@ class ReactiveArraySlot<T> {
|
|
|
127
117
|
let group = this.nodes.shift();
|
|
128
118
|
|
|
129
119
|
if (group) {
|
|
130
|
-
remove(
|
|
120
|
+
remove(group);
|
|
131
121
|
}
|
|
132
122
|
}
|
|
133
123
|
|
|
134
124
|
splice(start: number, stop: number = this.nodes.length, ...items: T[]) {
|
|
135
125
|
if (!items.length) {
|
|
136
|
-
return remove(this.nodes.splice(start, stop));
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
let array = this.array,
|
|
140
|
-
n = items.length,
|
|
141
|
-
nodes = new Array(n);
|
|
142
|
-
|
|
143
|
-
for (let i = 0; i < n; i++) {
|
|
144
|
-
nodes[i] = this.template.call(array, items[i], i);
|
|
126
|
+
return remove(...this.nodes.splice(start, stop));
|
|
145
127
|
}
|
|
146
128
|
|
|
147
|
-
remove(this.nodes.splice(start, stop, ...
|
|
129
|
+
remove( ...this.nodes.splice(start, stop, ...items.map(this.template)) );
|
|
148
130
|
this.anchor(start - 1).after(this.fragment);
|
|
149
131
|
}
|
|
150
132
|
|
|
151
133
|
unshift(items: T[]) {
|
|
152
|
-
|
|
153
|
-
n = items.length,
|
|
154
|
-
nodes = new Array(n);
|
|
155
|
-
|
|
156
|
-
for (let i = 0; i < n; i++) {
|
|
157
|
-
nodes[i] = this.template.call(array, items[i], i);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
this.nodes.unshift(...nodes);
|
|
134
|
+
this.nodes.unshift(...items.map(this.template));
|
|
161
135
|
this.marker.after(this.fragment);
|
|
162
136
|
}
|
|
163
137
|
}
|
|
@@ -165,7 +139,7 @@ class ReactiveArraySlot<T> {
|
|
|
165
139
|
|
|
166
140
|
export default <T>(anchor: Element, renderable: RenderableReactive<T>) => {
|
|
167
141
|
let { array, template } = renderable,
|
|
168
|
-
slot = new
|
|
142
|
+
slot = new ArraySlot(anchor, array, template);
|
|
169
143
|
|
|
170
144
|
if (array.length) {
|
|
171
145
|
root(() => {
|
package/src/slot/cleanup.ts
CHANGED
|
@@ -1,60 +1,27 @@
|
|
|
1
|
-
import queue from '@esportsplus/queue';
|
|
2
1
|
import { CLEANUP } from '~/constants';
|
|
3
|
-
import { microtask } from '~/utilities/queue';
|
|
4
2
|
import { previousSibling } from '~/utilities/node';
|
|
5
3
|
import { SlotGroup } from '~/types';
|
|
6
4
|
|
|
7
5
|
|
|
8
|
-
let cleanup = queue<VoidFunction[]>(64),
|
|
9
|
-
scheduled = false;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
function schedule() {
|
|
13
|
-
if (!cleanup.length || scheduled) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
scheduled = true;
|
|
18
|
-
microtask.add(task);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function task() {
|
|
22
|
-
try {
|
|
23
|
-
let fns, fn,
|
|
24
|
-
n = cleanup.length;
|
|
25
|
-
|
|
26
|
-
while ((fns = cleanup.next()) && n--) {
|
|
27
|
-
while (fn = fns.pop()) {
|
|
28
|
-
fn();
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
catch { }
|
|
33
|
-
|
|
34
|
-
if (cleanup.length) {
|
|
35
|
-
microtask.add(task);
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
scheduled = false;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
6
|
const ondisconnect = (element: Element, fn: VoidFunction) => {
|
|
44
7
|
((element as any)[CLEANUP] ??= []).push(fn);
|
|
45
8
|
};
|
|
46
9
|
|
|
47
|
-
const remove = (groups: SlotGroup[]) => {
|
|
48
|
-
let
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
10
|
+
const remove = (...groups: SlotGroup[]) => {
|
|
11
|
+
for (let i = 0, n = groups.length; i < n; i++) {
|
|
12
|
+
let fns, fn,
|
|
13
|
+
group = groups[i],
|
|
14
|
+
head = group.head,
|
|
52
15
|
next,
|
|
53
16
|
tail = group.tail || head;
|
|
54
17
|
|
|
55
18
|
while (tail) {
|
|
56
19
|
if (CLEANUP in tail) {
|
|
57
|
-
|
|
20
|
+
fns = tail[CLEANUP] as VoidFunction[];
|
|
21
|
+
|
|
22
|
+
while (fn = fns.pop()) {
|
|
23
|
+
fn();
|
|
24
|
+
}
|
|
58
25
|
}
|
|
59
26
|
|
|
60
27
|
next = previousSibling.call(tail);
|
|
@@ -67,10 +34,6 @@ const remove = (groups: SlotGroup[]) => {
|
|
|
67
34
|
tail = next;
|
|
68
35
|
}
|
|
69
36
|
}
|
|
70
|
-
|
|
71
|
-
if (!scheduled && cleanup.length) {
|
|
72
|
-
schedule();
|
|
73
|
-
}
|
|
74
37
|
};
|
|
75
38
|
|
|
76
39
|
|
package/src/slot/effect.ts
CHANGED
|
@@ -8,84 +8,96 @@ import text from '~/utilities/text';
|
|
|
8
8
|
import render from './render';
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
class EffectSlot {
|
|
12
|
+
anchor: Element;
|
|
13
|
+
disposer: VoidFunction;
|
|
14
|
+
group: SlotGroup | null = null;
|
|
15
|
+
textnode: Node | null = null;
|
|
16
16
|
|
|
17
|
-
if (value == null || value === false) {
|
|
18
|
-
value = '';
|
|
19
|
-
}
|
|
20
17
|
|
|
21
|
-
|
|
18
|
+
constructor(anchor: Element, fn: (dispose?: VoidFunction) => Renderable<any>) {
|
|
19
|
+
let dispose = fn.length ? () => this.dispose() : undefined,
|
|
20
|
+
state = STATE_HYDRATING;
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
this.anchor = anchor;
|
|
23
|
+
this.disposer = effect(() => {
|
|
24
|
+
let value = fn(dispose);
|
|
26
25
|
|
|
27
|
-
if (
|
|
28
|
-
|
|
26
|
+
if (state === STATE_HYDRATING) {
|
|
27
|
+
state = STATE_NONE;
|
|
28
|
+
this.update(value);
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
else {
|
|
31
|
+
raf.add(() => {
|
|
32
|
+
this.update(value);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
});
|
|
34
36
|
}
|
|
35
|
-
else {
|
|
36
|
-
let fragment = render(anchor, value),
|
|
37
|
-
head = firstChild.call(fragment);
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
|
|
39
|
+
dispose() {
|
|
40
|
+
let { anchor, group, textnode } = this;
|
|
41
|
+
|
|
42
|
+
if (textnode) {
|
|
43
|
+
group = { head: anchor, tail: textnode as Element };
|
|
44
|
+
}
|
|
45
|
+
else if (group) {
|
|
46
|
+
group.head = anchor;
|
|
41
47
|
}
|
|
42
48
|
|
|
43
|
-
|
|
44
|
-
this.group = {
|
|
45
|
-
head,
|
|
46
|
-
tail: lastChild.call(fragment)
|
|
47
|
-
};
|
|
49
|
+
this.disposer();
|
|
48
50
|
|
|
49
|
-
|
|
51
|
+
if (group) {
|
|
52
|
+
remove(group);
|
|
50
53
|
}
|
|
51
54
|
}
|
|
52
|
-
}
|
|
53
55
|
|
|
56
|
+
update(value: unknown) {
|
|
57
|
+
if (this.group) {
|
|
58
|
+
remove(this.group);
|
|
59
|
+
this.group = null;
|
|
60
|
+
}
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
dispose = fn.length ? () => {
|
|
61
|
-
let { group, textnode } = context;
|
|
62
|
+
if (value == null || value === false) {
|
|
63
|
+
value = '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let { anchor, textnode } = this;
|
|
62
67
|
|
|
68
|
+
if (typeof value !== 'object') {
|
|
63
69
|
if (textnode) {
|
|
64
|
-
|
|
70
|
+
nodeValue.call(textnode, String(value));
|
|
71
|
+
|
|
72
|
+
if (!textnode.isConnected) {
|
|
73
|
+
anchor.after(textnode);
|
|
74
|
+
}
|
|
65
75
|
}
|
|
66
|
-
else
|
|
67
|
-
|
|
76
|
+
else {
|
|
77
|
+
anchor.after( this.textnode = text( String(value) ) );
|
|
68
78
|
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
let fragment = render(anchor, value),
|
|
82
|
+
head = firstChild.call(fragment);
|
|
69
83
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (group) {
|
|
73
|
-
remove([group]);
|
|
84
|
+
if (textnode?.isConnected) {
|
|
85
|
+
remove({ head: textnode as Element, tail: textnode as Element });
|
|
74
86
|
}
|
|
75
|
-
} : undefined,
|
|
76
|
-
state = STATE_HYDRATING;
|
|
77
87
|
|
|
78
|
-
|
|
79
|
-
|
|
88
|
+
if (head) {
|
|
89
|
+
this.group = {
|
|
90
|
+
head,
|
|
91
|
+
tail: lastChild.call(fragment)
|
|
92
|
+
};
|
|
80
93
|
|
|
81
|
-
|
|
82
|
-
update.call(context, anchor, value);
|
|
83
|
-
state = STATE_NONE;
|
|
94
|
+
anchor.after(fragment);
|
|
84
95
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
export default (anchor: Element, fn: (dispose?: VoidFunction) => Renderable<any>) => {
|
|
102
|
+
new EffectSlot(anchor, fn);
|
|
91
103
|
};
|
package/src/slot/render.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Element, RenderableReactive } from '~/types';
|
|
|
4
4
|
import { cloneNode, lastChild } from '~/utilities/node';
|
|
5
5
|
import { append } from '~/utilities/fragment';
|
|
6
6
|
import text from '~/utilities/text';
|
|
7
|
-
import
|
|
7
|
+
import array from './array';
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
export default function render(anchor: Element, input: unknown): Node {
|
|
@@ -16,12 +16,12 @@ export default function render(anchor: Element, input: unknown): Node {
|
|
|
16
16
|
return text(input as any);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
if (
|
|
20
|
-
return input as
|
|
19
|
+
if (RENDERABLE in input) {
|
|
20
|
+
return array(anchor, input as RenderableReactive<unknown>);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
if (
|
|
24
|
-
return
|
|
23
|
+
if ('nodeType' in input) {
|
|
24
|
+
return input as Node;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
if (isArray(input)) {
|
package/src/types.ts
CHANGED
|
@@ -9,14 +9,14 @@ import html from './html';
|
|
|
9
9
|
type Attribute = Effect<Primitive | Primitive[]> | ((...args: any[]) => void) | Primitive;
|
|
10
10
|
|
|
11
11
|
type Attributes<T extends HTMLElement = Element> = {
|
|
12
|
-
[key: `aria-${string}`]: string | number | boolean | undefined;
|
|
13
|
-
[key: `data-${string}`]: string | undefined;
|
|
14
12
|
class?: Attribute | Attribute[];
|
|
15
13
|
onconnect?: (element: T) => void;
|
|
16
14
|
ondisconnect?: (element: T) => void;
|
|
17
15
|
onrender?: (element: T) => void;
|
|
18
16
|
ontick?: (dispose: VoidFunction, element: T) => void;
|
|
19
17
|
style?: Attribute | Attribute[];
|
|
18
|
+
[key: `aria-${string}`]: string | number | boolean | undefined;
|
|
19
|
+
[key: `data-${string}`]: string | undefined;
|
|
20
20
|
} & {
|
|
21
21
|
[K in keyof GlobalEventHandlersEventMap as `on${string & K}`]?: (this: T, event: GlobalEventHandlersEventMap[K]) => void;
|
|
22
22
|
} & Record<PropertyKey, unknown>;
|
|
@@ -34,11 +34,7 @@ type Renderable<T> = DocumentFragment | Effect<T> | Node | NodeList | Primitive
|
|
|
34
34
|
type RenderableReactive<T> = Readonly<{
|
|
35
35
|
[RENDERABLE]: typeof RENDERABLE_HTML_REACTIVE_ARRAY;
|
|
36
36
|
array: ReactiveArray<T>;
|
|
37
|
-
template: (
|
|
38
|
-
this: ReactiveArray<T>,
|
|
39
|
-
value: T,
|
|
40
|
-
i: number
|
|
41
|
-
) => ReturnType<typeof html>;
|
|
37
|
+
template: (value: T, i: number) => ReturnType<typeof html>;
|
|
42
38
|
}>;
|
|
43
39
|
|
|
44
40
|
type SlotGroup = {
|
|
@@ -2,19 +2,17 @@ import { innerHTML } from './element';
|
|
|
2
2
|
import { cloneNode } from './node';
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
let
|
|
5
|
+
let template = document.createElement('template');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
const append = DocumentFragment.prototype.append;
|
|
9
9
|
|
|
10
10
|
const fragment = (html: string): DocumentFragment => {
|
|
11
|
-
|
|
11
|
+
let element = cloneNode.call(template) as HTMLTemplateElement;
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
innerHTML.call(element, html);
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return content;
|
|
15
|
+
return element.content;
|
|
18
16
|
};
|
|
19
17
|
|
|
20
18
|
|
package/src/utilities/queue.ts
CHANGED
|
File without changes
|