subay 0.0.12 → 0.1.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/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 +131 -318
- package/lib/h.js.map +1 -1
- package/lib/index.js +3 -1
- package/lib/s.d.ts +1 -1
- package/lib/s.d.ts.map +1 -1
- package/lib/s.js +1 -1
- package/lib/s.js.map +1 -1
- package/package.json +2 -6
- 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 +138 -365
- package/src/s.ts +1 -1
- package/lib/index.dev.js +0 -96
- package/lib/name.d.ts +0 -2
- package/lib/name.d.ts.map +0 -1
- package/lib/name.js +0 -58
- package/lib/name.js.map +0 -1
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;
|
|
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 [];
|
|
159
110
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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;
|
|
172
126
|
}
|
|
173
|
-
|
|
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
|
+
});
|
|
134
|
+
}
|
|
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,17 @@ 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
|
-
const nodified = carr.map((c) => {
|
|
259
|
-
if (c instanceof Node) {
|
|
260
|
-
return c;
|
|
261
|
-
}
|
|
262
|
-
const node = new Text(c);
|
|
263
|
-
cleanup(() => node.remove());
|
|
264
|
-
return node;
|
|
265
|
-
});
|
|
266
|
-
|
|
193
|
+
const nodified = carr.map((c) => (c instanceof Node ? c : deferRemove(new Text(c))));
|
|
267
194
|
if (firstNode.isConnected) {
|
|
268
195
|
firstNode.after(...nodified);
|
|
269
196
|
} else {
|
|
@@ -273,218 +200,82 @@ const fragment = (child: () => Node[]): Node[] => {
|
|
|
273
200
|
|
|
274
201
|
return tempArray;
|
|
275
202
|
};
|
|
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
203
|
interface MapState<T> {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
204
|
+
_killer: Array<() => void>;
|
|
205
|
+
_nodes: Node[][];
|
|
206
|
+
_arr: T[];
|
|
390
207
|
}
|
|
391
208
|
export const map = <T>(array: () => T[], f: (item: T) => Node[]): Node[] => {
|
|
392
209
|
return fragment(() => {
|
|
393
210
|
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
|
-
};
|
|
211
|
+
cleanup(() => mapState()._killer.forEach((f) => f()));
|
|
212
|
+
const createItem = (item: T) => root((destroyItem) => [destroyItem, f(item)] as [() => void, Node[]]);
|
|
403
213
|
const mapState = S<MapState<T>>(
|
|
404
214
|
(
|
|
405
215
|
preState = {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
216
|
+
_arr: [] as T[],
|
|
217
|
+
_nodes: [],
|
|
218
|
+
_killer: [],
|
|
409
219
|
},
|
|
410
220
|
) => {
|
|
411
221
|
const currentArrayValue = array();
|
|
412
|
-
const preArrayValue = preState.
|
|
222
|
+
const preArrayValue = preState._arr;
|
|
413
223
|
const newState: MapState<T> = {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
224
|
+
_arr: currentArrayValue,
|
|
225
|
+
_nodes: [],
|
|
226
|
+
_killer: [],
|
|
417
227
|
};
|
|
418
|
-
|
|
228
|
+
arr(
|
|
419
229
|
preArrayValue,
|
|
420
230
|
currentArrayValue,
|
|
231
|
+
(oldItem) => preState._killer[preArrayValue.indexOf(oldItem)]!(),
|
|
421
232
|
(newItem) => {
|
|
422
233
|
const [destroyer, domNodes] = createItem(newItem);
|
|
423
|
-
const preNode = newState.
|
|
424
|
-
|
|
425
|
-
newState.
|
|
426
|
-
newState.
|
|
234
|
+
const preNode = (newState._nodes.at(-1)?.at(-1) ?? headNode) as Element | Comment;
|
|
235
|
+
preNode.after(...domNodes);
|
|
236
|
+
newState._nodes.push(domNodes);
|
|
237
|
+
newState._killer.push(destroyer);
|
|
427
238
|
},
|
|
428
239
|
(oldItem) => {
|
|
429
240
|
const i = preArrayValue.indexOf(oldItem);
|
|
430
|
-
preState.
|
|
241
|
+
newState._nodes.push(preState._nodes[i]!);
|
|
242
|
+
newState._killer.push(preState._killer[i]!);
|
|
431
243
|
},
|
|
432
244
|
(oldItem) => {
|
|
433
245
|
const i = preArrayValue.indexOf(oldItem);
|
|
434
|
-
newState.
|
|
435
|
-
|
|
246
|
+
const preNode = (newState._nodes.at(-1)?.at(-1) ?? headNode) as Element | Comment;
|
|
247
|
+
const domNodes = preState._nodes[i]!;
|
|
248
|
+
preNode.after(...domNodes);
|
|
249
|
+
newState._nodes.push(domNodes);
|
|
250
|
+
newState._killer.push(preState._killer[i]!);
|
|
436
251
|
},
|
|
437
252
|
);
|
|
438
253
|
return newState;
|
|
439
254
|
},
|
|
440
255
|
);
|
|
441
|
-
return sample(() => mapState().
|
|
256
|
+
return sample(() => mapState()._nodes.flat());
|
|
442
257
|
});
|
|
443
258
|
};
|
|
444
259
|
|
|
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;
|
|
260
|
+
const isPropertyOf = (el: HTMLElement | SVGElement, key: string) => {
|
|
261
|
+
if (!(key in el)) return false;
|
|
262
|
+
let d;
|
|
263
|
+
while (el && !(d = Object.getOwnPropertyDescriptor(el, key))) el = Object.getPrototypeOf(el);
|
|
264
|
+
return d && d.writable !== false && !!(!d.get || d.set);
|
|
461
265
|
};
|
|
462
266
|
|
|
463
267
|
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];
|
|
268
|
+
if (!(node instanceof Element)) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (!isFun(key) && !isReactive(val)) {
|
|
272
|
+
if (isPropertyOf(node as any, key)) {
|
|
474
273
|
(node as any)[key] = val;
|
|
475
|
-
cleanup(() => {
|
|
476
|
-
(node as any)[key] = preValue;
|
|
477
|
-
});
|
|
478
274
|
} else {
|
|
479
275
|
node.setAttribute(key, String(val));
|
|
480
|
-
cleanup(() => {
|
|
481
|
-
node.removeAttribute(key);
|
|
482
|
-
});
|
|
483
276
|
}
|
|
484
277
|
} else {
|
|
485
|
-
S(() =>
|
|
486
|
-
attribute(node, typeof key === 'function' ? key() : key, isReactive(val) ? val() : val);
|
|
487
|
-
});
|
|
278
|
+
S(() => attribute(node, callif(key), isReactive(val) ? val() : val));
|
|
488
279
|
}
|
|
489
280
|
};
|
|
490
281
|
|
|
@@ -500,31 +291,29 @@ const spreadAttrs = (node: Element, value: () => any) => {
|
|
|
500
291
|
const tree2dom = (ns: string | null, node: InterTreeNode, values: any[]): Node[] => {
|
|
501
292
|
if (node._type === InterNodeType.Text) {
|
|
502
293
|
return node._data.flatMap((part) => {
|
|
503
|
-
if (
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
return fragment(() => value());
|
|
512
|
-
} else {
|
|
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;
|
|
294
|
+
if (isStr(part)) {
|
|
295
|
+
return deferRemove(new Text(part));
|
|
296
|
+
}
|
|
297
|
+
const value = values[part];
|
|
298
|
+
if (isFun(value)) {
|
|
299
|
+
return fragment(value);
|
|
300
|
+
} else if (typeof value !== 'object') {
|
|
301
|
+
return deferRemove(new Text(String(value)));
|
|
521
302
|
}
|
|
303
|
+
return value;
|
|
522
304
|
});
|
|
523
305
|
} else {
|
|
524
306
|
const { _attrs: attrs, _content: content, _tag: tag, _sourceNode: sourceNode } = node;
|
|
525
307
|
const tagValue = unparseText(tag, values);
|
|
526
308
|
const currentNs = node._ns ?? ns;
|
|
527
|
-
return element(currentNs, sourceNode, tagValue,
|
|
309
|
+
return element(currentNs, sourceNode, tagValue, (el) => {
|
|
310
|
+
for (const { _name: name, _value: value } of attrs) {
|
|
311
|
+
if (name[0] === '...' && isNum(name[1])) {
|
|
312
|
+
spreadAttrs(el, unparseText(name.slice(1), values));
|
|
313
|
+
} else {
|
|
314
|
+
attribute(el, unparseText(name, values), unparseText(value, values));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
528
317
|
return content.flatMap((t) => tree2dom(currentNs, t, values));
|
|
529
318
|
});
|
|
530
319
|
}
|
|
@@ -550,35 +339,19 @@ const cloneTreeNode = (treeNode: InterTreeNode, parentNode: Element | null): Int
|
|
|
550
339
|
};
|
|
551
340
|
|
|
552
341
|
const tag =
|
|
553
|
-
(
|
|
554
|
-
ns: string | null,
|
|
555
|
-
docType: DOMParserSupportedType,
|
|
556
|
-
wrapSource: (source: string) => string,
|
|
557
|
-
unwrapNode: (node: Document) => NodeList,
|
|
558
|
-
) =>
|
|
342
|
+
(ns: string | null, markup2nodeList: (markup: string) => NodeList) =>
|
|
559
343
|
(segments: TemplateStringsArray, ...values: any[]): Node[] => {
|
|
560
344
|
const tree = parse(
|
|
561
|
-
|
|
562
|
-
wrapSource,
|
|
563
|
-
unwrapNode,
|
|
345
|
+
markup2nodeList,
|
|
564
346
|
segments,
|
|
565
347
|
values.map((it) => values.indexOf(it)),
|
|
566
348
|
);
|
|
567
349
|
const clonedTree = tree.map((tree) => cloneTreeNode(tree, null));
|
|
568
|
-
return fragment(() =>
|
|
569
|
-
return clonedTree.flatMap((item) => tree2dom(ns, item, values));
|
|
570
|
-
});
|
|
350
|
+
return fragment(() => clonedTree.flatMap((item) => tree2dom(ns, item, values)));
|
|
571
351
|
};
|
|
572
352
|
const svgns = 'http://www.w3.org/2000/svg';
|
|
573
353
|
export const svg = tag(
|
|
574
354
|
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,
|
|
355
|
+
(svgStr) => domParser.parseFromString(`<svg xmlns="${svgns}">${svgStr}</svg>`, 'text/xml').firstChild!.childNodes,
|
|
584
356
|
);
|
|
357
|
+
export const html = tag(null, (htmlStr) => domParser.parseFromString(htmlStr, 'text/html').body.childNodes);
|
package/src/s.ts
CHANGED