subay 0.0.13 → 0.1.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/README.md +14 -18
- package/README.zh.md +677 -682
- package/examples/todo-app.html +5 -20
- package/lib/arr.d.ts +2 -0
- package/lib/arr.d.ts.map +1 -0
- package/lib/arr.js +122 -0
- package/lib/arr.js.map +1 -0
- package/lib/h.d.ts.map +1 -1
- package/lib/h.js +136 -318
- package/lib/h.js.map +1 -1
- package/package.json +1 -1
- package/skills/subay-application/SKILL.md +122 -0
- package/skills/subay-component/SKILL.md +53 -0
- package/skills/subay-expert/SKILL.md +37 -0
- package/skills/subay-state/SKILL.md +162 -0
- package/skills/subay-template/SKILL.md +39 -0
- package/src/arr.ts +428 -0
- package/src/h.ts +144 -365
package/src/h.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// deno-lint-ignore-file no-explicit-any no-sloppy-imports
|
|
2
|
+
import { arr } from './arr.js';
|
|
2
3
|
import { cleanup, isReactive, root, S, sample } from './s.js';
|
|
3
4
|
|
|
4
5
|
const enum InterNodeType {
|
|
@@ -28,88 +29,61 @@ type InterAttributeNode = {
|
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
const cache = new Map<TemplateStringsArray, InterTreeNode[]>();
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const min = 10 * power5;
|
|
35
|
-
const length = 26 * power5;
|
|
36
|
-
return Math.floor(Math.random() * length + min).toString(BASE);
|
|
37
|
-
}
|
|
38
|
-
const MarkStart = random6LetterString();
|
|
39
|
-
const MarkEnd = random6LetterString();
|
|
32
|
+
const rnds = () => String.fromCharCode((97 + Math.random() * 26) | 0) + Math.random().toString(36).substring(2, 5);
|
|
33
|
+
const MarkStart = rnds();
|
|
34
|
+
const MarkEnd = rnds();
|
|
40
35
|
const MarkReg = new RegExp(`([\\s\\S]*?)${MarkStart}(\\d+)${MarkEnd}([\\s\\S]*?)(?=${MarkStart}\\d+${MarkEnd}|$)`, 'g');
|
|
41
36
|
const BlankText = /\s*\n\s*/g;
|
|
42
37
|
const domParser = new DOMParser();
|
|
38
|
+
const isNum = (d: any): d is number => typeof d === 'number';
|
|
39
|
+
const isFun = (d: any): d is (...args: any[]) => any => typeof d === 'function';
|
|
40
|
+
const isStr = (d: any): d is string => typeof d === 'string';
|
|
41
|
+
const deferRemove = <T extends Element | Comment | Text>(d: T) => (cleanup(() => d.remove()), d);
|
|
42
|
+
const callif = <T>(fun: (() => T) | T): T => (isFun(fun) ? fun() : fun);
|
|
43
43
|
|
|
44
44
|
const parseText = (data: string) => {
|
|
45
|
-
const result: Array<string | number> = [];
|
|
46
45
|
data = data.replace(BlankText, '');
|
|
46
|
+
if (!data) return [];
|
|
47
47
|
if (data.includes(MarkStart) && data.includes(MarkEnd)) {
|
|
48
|
+
const result: Array<string | number> = [];
|
|
48
49
|
for (const res of data.matchAll(MarkReg)) {
|
|
49
50
|
const [, prefix, index, suffix] = res;
|
|
50
51
|
if (prefix) {
|
|
51
52
|
result.push(prefix);
|
|
52
53
|
}
|
|
53
54
|
if (index) {
|
|
54
|
-
result.push(
|
|
55
|
+
result.push(+index);
|
|
55
56
|
}
|
|
56
57
|
if (suffix) {
|
|
57
58
|
result.push(suffix);
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
-
result.push(data);
|
|
61
|
+
return result;
|
|
62
62
|
}
|
|
63
|
-
return
|
|
63
|
+
return [data];
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
const unparseText = (data: Array<string | number | boolean>, values: any[]) => {
|
|
67
67
|
if (data.length === 1) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
return data[0];
|
|
68
|
+
const head = data[0];
|
|
69
|
+
return isNum(head) ? values[head] : head;
|
|
72
70
|
}
|
|
73
|
-
return S(() =>
|
|
74
|
-
return data
|
|
75
|
-
.map((it) => {
|
|
76
|
-
if (typeof it === 'number') {
|
|
77
|
-
const value = values[it];
|
|
78
|
-
if (typeof value === 'function') {
|
|
79
|
-
return value();
|
|
80
|
-
}
|
|
81
|
-
return value;
|
|
82
|
-
}
|
|
83
|
-
return it;
|
|
84
|
-
})
|
|
85
|
-
.join('');
|
|
86
|
-
});
|
|
71
|
+
return S(() => data.map((it) => (isNum(it) ? callif(values[it]) : it)).join(''));
|
|
87
72
|
};
|
|
88
73
|
|
|
89
74
|
const parse = (
|
|
90
|
-
|
|
91
|
-
wrapSource: (source: string) => string,
|
|
92
|
-
unwrapNode: (node: Document) => NodeList,
|
|
75
|
+
markup2nodeList: (markup: string) => NodeList,
|
|
93
76
|
segments: TemplateStringsArray,
|
|
94
77
|
indexMap: number[],
|
|
95
78
|
): InterTreeNode[] => {
|
|
96
79
|
if (cache.has(segments)) {
|
|
97
80
|
return cache.get(segments)!;
|
|
98
81
|
}
|
|
99
|
-
|
|
100
82
|
const htmlStr = segments
|
|
101
|
-
.flatMap((segment, index, { length }) =>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
return [segment, MarkStart, indexMap[index], MarkEnd];
|
|
106
|
-
})
|
|
83
|
+
.flatMap((segment, index, { length }) =>
|
|
84
|
+
index === length - 1 ? [segment] : [segment, MarkStart, indexMap[index], MarkEnd],
|
|
85
|
+
)
|
|
107
86
|
.join('');
|
|
108
|
-
|
|
109
|
-
const wrappedHtmlStr = wrapSource(htmlStr);
|
|
110
|
-
|
|
111
|
-
const document = domParser.parseFromString(wrappedHtmlStr, docType);
|
|
112
|
-
|
|
113
87
|
const dom2treenode = (domNodeList: NodeList) => {
|
|
114
88
|
const nodeList: InterTreeNode[] = [];
|
|
115
89
|
for (
|
|
@@ -117,115 +91,79 @@ const parse = (
|
|
|
117
91
|
item != null;
|
|
118
92
|
item = nextSibling, nextSibling = nextSibling?.nextSibling, index++
|
|
119
93
|
) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
typeof name[0] === 'number' ||
|
|
137
|
-
value.length > 1 ||
|
|
138
|
-
typeof value[0] === 'number';
|
|
139
|
-
|
|
140
|
-
if (!isVariableElem && !isVariableAttr) {
|
|
141
|
-
return [];
|
|
142
|
-
} else {
|
|
143
|
-
el.removeAttribute(attr.nodeName);
|
|
144
|
-
}
|
|
145
|
-
return [
|
|
146
|
-
{
|
|
147
|
-
_name: name,
|
|
148
|
-
_value: value.length === 0 ? [true] : value,
|
|
149
|
-
} satisfies InterAttributeNode,
|
|
150
|
-
];
|
|
151
|
-
}),
|
|
152
|
-
_content: el.childNodes.length > 0 ? dom2treenode(el.childNodes) : [],
|
|
153
|
-
_sourceNode: isVariableElem ? null : el,
|
|
154
|
-
_sourceIndex: index,
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
if (!isVariableElem) {
|
|
158
|
-
break remove_and_dec_index;
|
|
159
|
-
}
|
|
160
|
-
} else if (item.nodeType === 3 /*Node.TEXT_NODE*/) {
|
|
161
|
-
const data = (item as Text).data;
|
|
162
|
-
const result = parseText(data);
|
|
163
|
-
if (result.length > 0) {
|
|
164
|
-
nodeList.push({
|
|
165
|
-
_type: InterNodeType.Text,
|
|
166
|
-
_data: result,
|
|
167
|
-
});
|
|
94
|
+
if (item.nodeType === 1 /* Node.ELEMENT_NODE*/) {
|
|
95
|
+
const el = item as Element;
|
|
96
|
+
const tag = parseText(el.tagName.toLowerCase());
|
|
97
|
+
const isVariableElem = tag.length > 1 || isNum(tag[0]);
|
|
98
|
+
|
|
99
|
+
nodeList.push({
|
|
100
|
+
_ns: el.namespaceURI,
|
|
101
|
+
_type: InterNodeType.Element,
|
|
102
|
+
_tag: tag,
|
|
103
|
+
_attrs: Array.from(el.attributes).flatMap((attr) => {
|
|
104
|
+
const name = parseText(attr.nodeName);
|
|
105
|
+
const value = parseText(attr.value);
|
|
106
|
+
const isVariableAttr = name.length > 1 || isNum(name[0]) || value.length > 1 || isNum(value[0]);
|
|
107
|
+
|
|
108
|
+
if (!isVariableElem && !isVariableAttr) {
|
|
109
|
+
return [];
|
|
168
110
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
111
|
+
el.removeAttribute(attr.nodeName);
|
|
112
|
+
return [
|
|
113
|
+
{
|
|
114
|
+
_name: name,
|
|
115
|
+
_value: value.length === 0 ? [true] : value,
|
|
116
|
+
} satisfies InterAttributeNode,
|
|
117
|
+
];
|
|
118
|
+
}),
|
|
119
|
+
_content: el.childNodes.length > 0 ? dom2treenode(el.childNodes) : [],
|
|
120
|
+
_sourceNode: isVariableElem ? null : el,
|
|
121
|
+
_sourceIndex: index,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (!isVariableElem) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
} else if (item.nodeType === 3 /*Node.TEXT_NODE*/) {
|
|
128
|
+
const result = parseText((item as Text).data);
|
|
129
|
+
if (result.length > 0) {
|
|
130
|
+
nodeList.push({
|
|
131
|
+
_type: InterNodeType.Text,
|
|
132
|
+
_data: result,
|
|
133
|
+
});
|
|
172
134
|
}
|
|
173
|
-
|
|
135
|
+
}
|
|
136
|
+
(item as Comment | Text | Element).remove();
|
|
137
|
+
index--;
|
|
174
138
|
}
|
|
175
139
|
return nodeList;
|
|
176
140
|
};
|
|
177
|
-
|
|
178
|
-
const rootList = unwrapNode(document);
|
|
179
|
-
|
|
180
|
-
const treeNode = dom2treenode(rootList);
|
|
181
|
-
|
|
141
|
+
const treeNode = dom2treenode(markup2nodeList(htmlStr));
|
|
182
142
|
cache.set(segments, treeNode);
|
|
183
|
-
|
|
184
143
|
return treeNode;
|
|
185
144
|
};
|
|
186
145
|
|
|
187
146
|
const element = (
|
|
188
147
|
ns: string | null,
|
|
189
148
|
sourceNode: Element | null,
|
|
190
|
-
tagName: string | (() => string)
|
|
191
|
-
|
|
192
|
-
values: any[],
|
|
193
|
-
child: () => Node[],
|
|
149
|
+
tagName: string | (() => string),
|
|
150
|
+
child: (el: Element) => Node[],
|
|
194
151
|
): Node[] => {
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
return tagName(
|
|
204
|
-
...attrs.flatMap(({ _name: name, _value: value }) => {
|
|
205
|
-
if (name[0] === '...' && typeof name[1] === 'number') {
|
|
206
|
-
return unparseText(name.slice(1), values);
|
|
207
|
-
} else {
|
|
208
|
-
return unparseText(value, values);
|
|
209
|
-
}
|
|
210
|
-
}),
|
|
211
|
-
child,
|
|
212
|
-
) as Node[];
|
|
213
|
-
});
|
|
214
|
-
}
|
|
152
|
+
if (isFun(tagName)) {
|
|
153
|
+
return fragment(() => {
|
|
154
|
+
const realTagName = tagName();
|
|
155
|
+
if (!isStr(realTagName)) {
|
|
156
|
+
throw new Error(`Expecting string returned from ${tagName} but get ${realTagName}`, { cause: tagName });
|
|
157
|
+
}
|
|
158
|
+
return element(ns, null, realTagName, child);
|
|
159
|
+
});
|
|
215
160
|
}
|
|
216
|
-
if (
|
|
161
|
+
if (!isStr(tagName)) {
|
|
217
162
|
return [];
|
|
218
163
|
}
|
|
219
|
-
const el = sourceNode || (ns ? document.createElementNS(ns, tagName) : document.createElement(tagName));
|
|
164
|
+
const el = deferRemove(sourceNode || (ns ? document.createElementNS(ns, tagName) : document.createElement(tagName)));
|
|
220
165
|
S(() => {
|
|
221
|
-
|
|
222
|
-
if (name[0] === '...' && typeof name[1] === 'number') {
|
|
223
|
-
spreadAttrs(el, unparseText(name.slice(1), values));
|
|
224
|
-
} else {
|
|
225
|
-
attribute(el, unparseText(name, values), unparseText(value, values));
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
const subnodes = child();
|
|
166
|
+
const subnodes = child(el);
|
|
229
167
|
if (subnodes.length === 0) {
|
|
230
168
|
return;
|
|
231
169
|
}
|
|
@@ -242,28 +180,21 @@ const element = (
|
|
|
242
180
|
}
|
|
243
181
|
}
|
|
244
182
|
});
|
|
245
|
-
cleanup(() => el.remove());
|
|
246
183
|
return [el];
|
|
247
184
|
};
|
|
248
185
|
let fragmentHeadNode: null | Comment = null;
|
|
249
186
|
const fragment = (child: () => Node[]): Node[] => {
|
|
250
|
-
const firstNode = new
|
|
251
|
-
cleanup(() => firstNode.remove());
|
|
187
|
+
const firstNode = deferRemove(new Text(''));
|
|
252
188
|
const tempArray: Node[] = [firstNode];
|
|
253
189
|
S(() => {
|
|
254
190
|
fragmentHeadNode = firstNode;
|
|
255
191
|
const c = child();
|
|
256
192
|
const carr = Array.isArray(c) ? c : [c];
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const node = new Text(c);
|
|
263
|
-
cleanup(() => node.remove());
|
|
264
|
-
return node;
|
|
265
|
-
});
|
|
266
|
-
|
|
193
|
+
const nodified = carr.flatMap((c) =>
|
|
194
|
+
c == null ? []
|
|
195
|
+
: c instanceof Node ? c
|
|
196
|
+
: deferRemove(new Text(c)),
|
|
197
|
+
);
|
|
267
198
|
if (firstNode.isConnected) {
|
|
268
199
|
firstNode.after(...nodified);
|
|
269
200
|
} else {
|
|
@@ -273,218 +204,82 @@ const fragment = (child: () => Node[]): Node[] => {
|
|
|
273
204
|
|
|
274
205
|
return tempArray;
|
|
275
206
|
};
|
|
276
|
-
function arrTrans<T>(a: readonly T[], b: readonly T[], a2b: (a: T) => void, dela: (a: T) => void, keep: (a: T) => void): void {
|
|
277
|
-
const aLen = a.length;
|
|
278
|
-
const bLen = b.length;
|
|
279
|
-
|
|
280
|
-
if (a === b || (aLen === 0 && bLen === 0)) {
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
let start = 0;
|
|
285
|
-
const minLen = Math.min(aLen, bLen);
|
|
286
|
-
while (start < minLen && a[start] === b[start]) {
|
|
287
|
-
start++;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
let end = 0;
|
|
291
|
-
while (end < minLen - start && a[aLen - 1 - end] === b[bLen - 1 - end]) {
|
|
292
|
-
end++;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const aMidLen = aLen - start - end;
|
|
296
|
-
const bMidLen = bLen - start - end;
|
|
297
|
-
|
|
298
|
-
if (aMidLen === 0 && bMidLen === 0) {
|
|
299
|
-
for (let i = 0; i < start; i++) {
|
|
300
|
-
keep(a[i]!);
|
|
301
|
-
}
|
|
302
|
-
for (let i = 0; i < end; i++) {
|
|
303
|
-
keep(a[aLen - end + i]!);
|
|
304
|
-
}
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const bIndexMap = new Map<T, number>();
|
|
309
|
-
for (let i = 0; i < bMidLen; i++) {
|
|
310
|
-
bIndexMap.set(b[start + i]!, i);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const indices: number[] = [];
|
|
314
|
-
for (let i = 0; i < aMidLen; i++) {
|
|
315
|
-
const val = a[start + i]!;
|
|
316
|
-
const bIdx = bIndexMap.get(val);
|
|
317
|
-
if (bIdx !== undefined) {
|
|
318
|
-
indices.push(bIdx);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
const tails: number[] = [];
|
|
323
|
-
const parents: number[] = new Array(indices.length).fill(-1);
|
|
324
|
-
const tailIndices: number[] = [];
|
|
325
|
-
|
|
326
|
-
for (let i = 0; i < indices.length; i++) {
|
|
327
|
-
const idx = indices[i]!;
|
|
328
|
-
|
|
329
|
-
let left = 0;
|
|
330
|
-
let right = tails.length;
|
|
331
|
-
while (left < right) {
|
|
332
|
-
const mid = (left + right) >> 1;
|
|
333
|
-
if (tails[mid]! < idx) {
|
|
334
|
-
left = mid + 1;
|
|
335
|
-
} else {
|
|
336
|
-
right = mid;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const pos = left;
|
|
341
|
-
if (pos === tails.length) {
|
|
342
|
-
tails.push(idx);
|
|
343
|
-
tailIndices.push(i);
|
|
344
|
-
} else {
|
|
345
|
-
tails[pos] = idx;
|
|
346
|
-
tailIndices[pos] = i;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
if (pos > 0) {
|
|
350
|
-
parents[i] = tailIndices[pos - 1]!;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
const keepSet = new Set<T>();
|
|
355
|
-
let curr = tailIndices.length > 0 ? tailIndices[tailIndices.length - 1]! : -1;
|
|
356
|
-
while (curr !== -1) {
|
|
357
|
-
const bIdx = indices[curr]!;
|
|
358
|
-
keepSet.add(b[start + bIdx]!);
|
|
359
|
-
curr = parents[curr]!;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
for (let i = 0; i < start; i++) {
|
|
363
|
-
keep(a[i]!);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
for (let i = 0; i < aMidLen; i++) {
|
|
367
|
-
const val = a[start + i]!;
|
|
368
|
-
if (!keepSet.has(val)) {
|
|
369
|
-
dela(val);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
for (let i = 0; i < bMidLen; i++) {
|
|
374
|
-
const val = b[start + i]!;
|
|
375
|
-
if (keepSet.has(val)) {
|
|
376
|
-
keep(val);
|
|
377
|
-
} else {
|
|
378
|
-
a2b(val);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
for (let i = 0; i < end; i++) {
|
|
383
|
-
keep(a[aLen - end + i]!);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
207
|
interface MapState<T> {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
208
|
+
_killer: Array<() => void>;
|
|
209
|
+
_nodes: Node[][];
|
|
210
|
+
_arr: T[];
|
|
390
211
|
}
|
|
391
212
|
export const map = <T>(array: () => T[], f: (item: T) => Node[]): Node[] => {
|
|
392
213
|
return fragment(() => {
|
|
393
214
|
const headNode = fragmentHeadNode;
|
|
394
|
-
cleanup(() =>
|
|
395
|
-
|
|
396
|
-
});
|
|
397
|
-
const createItem = (item: T) => {
|
|
398
|
-
return root((destroyItem) => {
|
|
399
|
-
const domNodes = f(item);
|
|
400
|
-
return [destroyItem, domNodes] as [() => void, Node[]];
|
|
401
|
-
});
|
|
402
|
-
};
|
|
215
|
+
cleanup(() => mapState()._killer.forEach((f) => f()));
|
|
216
|
+
const createItem = (item: T) => root((destroyItem) => [destroyItem, f(item)] as [() => void, Node[]]);
|
|
403
217
|
const mapState = S<MapState<T>>(
|
|
404
218
|
(
|
|
405
219
|
preState = {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
220
|
+
_arr: [] as T[],
|
|
221
|
+
_nodes: [],
|
|
222
|
+
_killer: [],
|
|
409
223
|
},
|
|
410
224
|
) => {
|
|
411
225
|
const currentArrayValue = array();
|
|
412
|
-
const preArrayValue = preState.
|
|
226
|
+
const preArrayValue = preState._arr;
|
|
413
227
|
const newState: MapState<T> = {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
228
|
+
_arr: currentArrayValue,
|
|
229
|
+
_nodes: [],
|
|
230
|
+
_killer: [],
|
|
417
231
|
};
|
|
418
|
-
|
|
232
|
+
arr(
|
|
419
233
|
preArrayValue,
|
|
420
234
|
currentArrayValue,
|
|
235
|
+
(oldItem) => preState._killer[preArrayValue.indexOf(oldItem)]!(),
|
|
421
236
|
(newItem) => {
|
|
422
237
|
const [destroyer, domNodes] = createItem(newItem);
|
|
423
|
-
const preNode = newState.
|
|
424
|
-
|
|
425
|
-
newState.
|
|
426
|
-
newState.
|
|
238
|
+
const preNode = (newState._nodes.at(-1)?.at(-1) ?? headNode) as Element | Comment;
|
|
239
|
+
preNode.after(...domNodes);
|
|
240
|
+
newState._nodes.push(domNodes);
|
|
241
|
+
newState._killer.push(destroyer);
|
|
427
242
|
},
|
|
428
243
|
(oldItem) => {
|
|
429
244
|
const i = preArrayValue.indexOf(oldItem);
|
|
430
|
-
preState.
|
|
245
|
+
newState._nodes.push(preState._nodes[i]!);
|
|
246
|
+
newState._killer.push(preState._killer[i]!);
|
|
431
247
|
},
|
|
432
248
|
(oldItem) => {
|
|
433
249
|
const i = preArrayValue.indexOf(oldItem);
|
|
434
|
-
newState.
|
|
435
|
-
|
|
250
|
+
const preNode = (newState._nodes.at(-1)?.at(-1) ?? headNode) as Element | Comment;
|
|
251
|
+
const domNodes = preState._nodes[i]!;
|
|
252
|
+
preNode.after(...domNodes);
|
|
253
|
+
newState._nodes.push(domNodes);
|
|
254
|
+
newState._killer.push(preState._killer[i]!);
|
|
436
255
|
},
|
|
437
256
|
);
|
|
438
257
|
return newState;
|
|
439
258
|
},
|
|
440
259
|
);
|
|
441
|
-
return sample(() => mapState().
|
|
260
|
+
return sample(() => mapState()._nodes.flat());
|
|
442
261
|
});
|
|
443
262
|
};
|
|
444
263
|
|
|
445
|
-
const isPropertyOf = (
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
} while (domEl && !descriptor);
|
|
451
|
-
if (!descriptor) {
|
|
452
|
-
return false;
|
|
453
|
-
}
|
|
454
|
-
if (false === descriptor.writable) {
|
|
455
|
-
return false;
|
|
456
|
-
}
|
|
457
|
-
if (descriptor.get && !descriptor.set) {
|
|
458
|
-
return false;
|
|
459
|
-
}
|
|
460
|
-
return true;
|
|
264
|
+
const isPropertyOf = (el: HTMLElement | SVGElement, key: string) => {
|
|
265
|
+
if (!(key in el)) return false;
|
|
266
|
+
let d;
|
|
267
|
+
while (el && !(d = Object.getOwnPropertyDescriptor(el, key))) el = Object.getPrototypeOf(el);
|
|
268
|
+
return d && d.writable !== false && !!(!d.get || d.set);
|
|
461
269
|
};
|
|
462
270
|
|
|
463
271
|
const attribute = (node: Element, key: string | (() => string), val: string | (() => any)) => {
|
|
464
|
-
if (
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
if (
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
const isProp = isPropertyOf(node as any, key);
|
|
472
|
-
if (isProp) {
|
|
473
|
-
const preValue = (node as any)[key];
|
|
272
|
+
if (!(node instanceof Element)) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (!isFun(key) && !isReactive(val)) {
|
|
276
|
+
if (isPropertyOf(node as any, key)) {
|
|
474
277
|
(node as any)[key] = val;
|
|
475
|
-
cleanup(() => {
|
|
476
|
-
(node as any)[key] = preValue;
|
|
477
|
-
});
|
|
478
278
|
} else {
|
|
479
279
|
node.setAttribute(key, String(val));
|
|
480
|
-
cleanup(() => {
|
|
481
|
-
node.removeAttribute(key);
|
|
482
|
-
});
|
|
483
280
|
}
|
|
484
281
|
} else {
|
|
485
|
-
S(() =>
|
|
486
|
-
attribute(node, typeof key === 'function' ? key() : key, isReactive(val) ? val() : val);
|
|
487
|
-
});
|
|
282
|
+
S(() => attribute(node, callif(key), isReactive(val) ? val() : val));
|
|
488
283
|
}
|
|
489
284
|
};
|
|
490
285
|
|
|
@@ -500,31 +295,31 @@ const spreadAttrs = (node: Element, value: () => any) => {
|
|
|
500
295
|
const tree2dom = (ns: string | null, node: InterTreeNode, values: any[]): Node[] => {
|
|
501
296
|
if (node._type === InterNodeType.Text) {
|
|
502
297
|
return node._data.flatMap((part) => {
|
|
503
|
-
if (
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
return fragment(value);
|
|
514
|
-
}
|
|
515
|
-
} else if (typeof value !== 'object') {
|
|
516
|
-
const textNode = new Text(String(value));
|
|
517
|
-
cleanup(() => textNode.remove());
|
|
518
|
-
return textNode;
|
|
519
|
-
}
|
|
520
|
-
return value;
|
|
298
|
+
if (isStr(part)) {
|
|
299
|
+
return deferRemove(new Text(part));
|
|
300
|
+
}
|
|
301
|
+
const value = values[part];
|
|
302
|
+
if (isFun(value)) {
|
|
303
|
+
return fragment(value);
|
|
304
|
+
} else if (null == value) {
|
|
305
|
+
return [];
|
|
306
|
+
} else if (typeof value !== 'object') {
|
|
307
|
+
return deferRemove(new Text(String(value)));
|
|
521
308
|
}
|
|
309
|
+
return value;
|
|
522
310
|
});
|
|
523
311
|
} else {
|
|
524
312
|
const { _attrs: attrs, _content: content, _tag: tag, _sourceNode: sourceNode } = node;
|
|
525
313
|
const tagValue = unparseText(tag, values);
|
|
526
314
|
const currentNs = node._ns ?? ns;
|
|
527
|
-
return element(currentNs, sourceNode, tagValue,
|
|
315
|
+
return element(currentNs, sourceNode, tagValue, (el) => {
|
|
316
|
+
for (const { _name: name, _value: value } of attrs) {
|
|
317
|
+
if (name[0] === '...' && isNum(name[1])) {
|
|
318
|
+
spreadAttrs(el, unparseText(name.slice(1), values));
|
|
319
|
+
} else {
|
|
320
|
+
attribute(el, unparseText(name, values), unparseText(value, values));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
528
323
|
return content.flatMap((t) => tree2dom(currentNs, t, values));
|
|
529
324
|
});
|
|
530
325
|
}
|
|
@@ -550,35 +345,19 @@ const cloneTreeNode = (treeNode: InterTreeNode, parentNode: Element | null): Int
|
|
|
550
345
|
};
|
|
551
346
|
|
|
552
347
|
const tag =
|
|
553
|
-
(
|
|
554
|
-
ns: string | null,
|
|
555
|
-
docType: DOMParserSupportedType,
|
|
556
|
-
wrapSource: (source: string) => string,
|
|
557
|
-
unwrapNode: (node: Document) => NodeList,
|
|
558
|
-
) =>
|
|
348
|
+
(ns: string | null, markup2nodeList: (markup: string) => NodeList) =>
|
|
559
349
|
(segments: TemplateStringsArray, ...values: any[]): Node[] => {
|
|
560
350
|
const tree = parse(
|
|
561
|
-
|
|
562
|
-
wrapSource,
|
|
563
|
-
unwrapNode,
|
|
351
|
+
markup2nodeList,
|
|
564
352
|
segments,
|
|
565
353
|
values.map((it) => values.indexOf(it)),
|
|
566
354
|
);
|
|
567
355
|
const clonedTree = tree.map((tree) => cloneTreeNode(tree, null));
|
|
568
|
-
return fragment(() =>
|
|
569
|
-
return clonedTree.flatMap((item) => tree2dom(ns, item, values));
|
|
570
|
-
});
|
|
356
|
+
return fragment(() => clonedTree.flatMap((item) => tree2dom(ns, item, values)));
|
|
571
357
|
};
|
|
572
358
|
const svgns = 'http://www.w3.org/2000/svg';
|
|
573
359
|
export const svg = tag(
|
|
574
360
|
svgns,
|
|
575
|
-
'text/xml',
|
|
576
|
-
(svg) => `<svg xmlns="${svgns}">${svg}</svg>`,
|
|
577
|
-
(doc) => doc.firstChild!.childNodes,
|
|
578
|
-
);
|
|
579
|
-
export const html = tag(
|
|
580
|
-
null,
|
|
581
|
-
'text/html',
|
|
582
|
-
(html) => html,
|
|
583
|
-
(doc) => doc.body.childNodes,
|
|
361
|
+
(svgStr) => domParser.parseFromString(`<svg xmlns="${svgns}">${svgStr}</svg>`, 'text/xml').firstChild!.childNodes,
|
|
584
362
|
);
|
|
363
|
+
export const html = tag(null, (htmlStr) => domParser.parseFromString(htmlStr, 'text/html').body.childNodes);
|