creo 0.1.0 → 0.2.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 +275 -4
- package/dist/functional/lis.d.ts +11 -0
- package/dist/functional/maybe.d.ts +2 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +763 -779
- package/dist/index.js.map +16 -17
- package/dist/internal/engine.d.ts +14 -8
- package/dist/internal/internal_view.d.ts +22 -31
- package/dist/internal/orchestrator.d.ts +1 -1
- package/dist/public/primitives/primitives.d.ts +1 -1
- package/dist/public/view.d.ts +2 -6
- package/dist/render/html_render.d.ts +9 -22
- package/dist/render/json_render.d.ts +6 -4
- package/dist/render/render_interface.d.ts +6 -4
- package/dist/render/string_render.d.ts +14 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,354 +1,30 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
delete() {
|
|
24
|
-
this[$owner]?.delete(this);
|
|
25
|
-
}
|
|
26
|
-
clearFields() {
|
|
27
|
-
this[$next] = null;
|
|
28
|
-
this[$prev] = null;
|
|
29
|
-
this[$owner] = null;
|
|
30
|
-
}
|
|
31
|
-
insertNext(value) {
|
|
32
|
-
const owner = this[$owner];
|
|
33
|
-
if (!owner) {
|
|
34
|
-
throw new Error("The item is detached from DataContainer");
|
|
35
|
-
}
|
|
36
|
-
return owner.insertNext(this, value);
|
|
37
|
-
}
|
|
38
|
-
insertPrev(value) {
|
|
39
|
-
const owner = this[$owner];
|
|
40
|
-
if (!owner) {
|
|
41
|
-
throw new Error("The item is detached from DataContainer");
|
|
42
|
-
}
|
|
43
|
-
return owner.insertPrev(this, value);
|
|
44
|
-
}
|
|
45
|
-
getNext() {
|
|
46
|
-
return this[$next];
|
|
47
|
-
}
|
|
48
|
-
getPrev() {
|
|
49
|
-
return this[$prev];
|
|
50
|
-
}
|
|
51
|
-
getList() {
|
|
52
|
-
return this[$owner];
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
class InternalList {
|
|
57
|
-
#head;
|
|
58
|
-
#tail;
|
|
59
|
-
#size = 0;
|
|
60
|
-
#cursorNode;
|
|
61
|
-
#cursorIndex = -1;
|
|
62
|
-
#invalidateCursor() {
|
|
63
|
-
this.#cursorNode = null;
|
|
64
|
-
this.#cursorIndex = -1;
|
|
65
|
-
}
|
|
66
|
-
insertStart(value) {
|
|
67
|
-
const node = new ListNode(value, null, this.#head, this);
|
|
68
|
-
if (this.#head != null) {
|
|
69
|
-
this.#head[$prev] = node;
|
|
70
|
-
} else {
|
|
71
|
-
this.#tail = node;
|
|
72
|
-
}
|
|
73
|
-
this.#head = node;
|
|
74
|
-
this.#size++;
|
|
75
|
-
this.#invalidateCursor();
|
|
76
|
-
return this.#head;
|
|
77
|
-
}
|
|
78
|
-
delete(node) {
|
|
79
|
-
const n = node;
|
|
80
|
-
const prev = n[$prev];
|
|
81
|
-
const next = n[$next];
|
|
82
|
-
if (next) {
|
|
83
|
-
next[$prev] = prev;
|
|
84
|
-
}
|
|
85
|
-
if (prev) {
|
|
86
|
-
prev[$next] = next;
|
|
87
|
-
}
|
|
88
|
-
if (node === this.#head) {
|
|
89
|
-
this.#head = next;
|
|
90
|
-
}
|
|
91
|
-
if (node === this.#tail) {
|
|
92
|
-
this.#tail = prev;
|
|
93
|
-
}
|
|
94
|
-
n.clearFields();
|
|
95
|
-
this.#size--;
|
|
96
|
-
this.#invalidateCursor();
|
|
97
|
-
}
|
|
98
|
-
at(n) {
|
|
99
|
-
if (n < 0)
|
|
100
|
-
n = this.#size + n;
|
|
101
|
-
if (n < 0 || n >= this.#size)
|
|
102
|
-
return;
|
|
103
|
-
let current;
|
|
104
|
-
let pos;
|
|
105
|
-
const distFromHead = n;
|
|
106
|
-
const distFromTail = this.#size - 1 - n;
|
|
107
|
-
const distFromCursor = this.#cursorNode != null ? Math.abs(n - this.#cursorIndex) : Infinity;
|
|
108
|
-
if (distFromCursor <= distFromHead && distFromCursor <= distFromTail) {
|
|
109
|
-
current = this.#cursorNode;
|
|
110
|
-
pos = this.#cursorIndex;
|
|
111
|
-
} else if (distFromHead <= distFromTail) {
|
|
112
|
-
current = this.#head;
|
|
113
|
-
pos = 0;
|
|
114
|
-
} else {
|
|
115
|
-
current = this.#tail;
|
|
116
|
-
pos = this.#size - 1;
|
|
117
|
-
}
|
|
118
|
-
while (pos < n && current != null) {
|
|
119
|
-
current = current[$next];
|
|
120
|
-
pos++;
|
|
121
|
-
}
|
|
122
|
-
while (pos > n && current != null) {
|
|
123
|
-
current = current[$prev];
|
|
124
|
-
pos--;
|
|
125
|
-
}
|
|
126
|
-
if (current != null) {
|
|
127
|
-
this.#cursorNode = current;
|
|
128
|
-
this.#cursorIndex = pos;
|
|
129
|
-
}
|
|
130
|
-
return current;
|
|
131
|
-
}
|
|
132
|
-
get size() {
|
|
133
|
-
return this.#size;
|
|
134
|
-
}
|
|
135
|
-
clear() {
|
|
136
|
-
this.#head = null;
|
|
137
|
-
this.#tail = null;
|
|
138
|
-
this.#size = 0;
|
|
139
|
-
this.#invalidateCursor();
|
|
140
|
-
}
|
|
141
|
-
first() {
|
|
142
|
-
return this.#head;
|
|
143
|
-
}
|
|
144
|
-
last() {
|
|
145
|
-
return this.#tail;
|
|
146
|
-
}
|
|
147
|
-
insertEnd(value) {
|
|
148
|
-
const node = new ListNode(value, this.#tail, null, this);
|
|
149
|
-
if (this.#tail != null) {
|
|
150
|
-
this.#tail[$next] = node;
|
|
151
|
-
} else {
|
|
152
|
-
this.#head = node;
|
|
153
|
-
}
|
|
154
|
-
this.#tail = node;
|
|
155
|
-
this.#size++;
|
|
156
|
-
return this.#tail;
|
|
157
|
-
}
|
|
158
|
-
insertNext(ref, value) {
|
|
159
|
-
const r = ref;
|
|
160
|
-
if (r[$owner] != this) {
|
|
161
|
-
throw new TypeError("The reference node does not belong to the current list");
|
|
162
|
-
}
|
|
163
|
-
const node = new ListNode(value, r, r[$next], this);
|
|
164
|
-
if (r[$next]) {
|
|
165
|
-
r[$next][$prev] = node;
|
|
166
|
-
} else {
|
|
167
|
-
this.#tail = node;
|
|
168
|
-
}
|
|
169
|
-
r[$next] = node;
|
|
170
|
-
this.#size++;
|
|
171
|
-
this.#invalidateCursor();
|
|
172
|
-
return node;
|
|
173
|
-
}
|
|
174
|
-
insertPrev(ref, value) {
|
|
175
|
-
const r = ref;
|
|
176
|
-
if (r[$owner] != this) {
|
|
177
|
-
throw new TypeError("The reference node does not belong to the current list");
|
|
178
|
-
}
|
|
179
|
-
const node = new ListNode(value, r[$prev], r, this);
|
|
180
|
-
if (r[$prev]) {
|
|
181
|
-
r[$prev][$next] = node;
|
|
182
|
-
} else {
|
|
183
|
-
this.#head = node;
|
|
184
|
-
}
|
|
185
|
-
r[$prev] = node;
|
|
186
|
-
this.#size++;
|
|
187
|
-
this.#invalidateCursor();
|
|
188
|
-
return node;
|
|
189
|
-
}
|
|
190
|
-
*[Symbol.iterator]() {
|
|
191
|
-
let current = this.#head;
|
|
192
|
-
while (current) {
|
|
193
|
-
const next = current[$next];
|
|
194
|
-
yield current;
|
|
195
|
-
current = next;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// src/structures/indexed_list.ts
|
|
201
|
-
class IndexedList {
|
|
202
|
-
#list = new InternalList;
|
|
203
|
-
#map = new Map;
|
|
204
|
-
push(item) {
|
|
205
|
-
const existing = this.#map.get(item);
|
|
206
|
-
if (existing)
|
|
207
|
-
return existing;
|
|
208
|
-
const node = this.#list.insertEnd(item);
|
|
209
|
-
this.#map.set(item, node);
|
|
210
|
-
return node;
|
|
211
|
-
}
|
|
212
|
-
unshift(item) {
|
|
213
|
-
if (this.#map.has(item))
|
|
214
|
-
return;
|
|
215
|
-
const node = this.#list.insertStart(item);
|
|
216
|
-
this.#map.set(item, node);
|
|
217
|
-
}
|
|
218
|
-
insertAfter(ref, item) {
|
|
219
|
-
if (this.#map.has(item))
|
|
220
|
-
return;
|
|
221
|
-
const refNode = this.#map.get(ref);
|
|
222
|
-
if (!refNode) {
|
|
223
|
-
this.push(item);
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
const node = refNode.insertNext(item);
|
|
227
|
-
this.#map.set(item, node);
|
|
228
|
-
}
|
|
229
|
-
delete(item) {
|
|
230
|
-
const node = this.#map.get(item);
|
|
231
|
-
if (!node)
|
|
232
|
-
return;
|
|
233
|
-
this.#map.delete(item);
|
|
234
|
-
node.delete();
|
|
235
|
-
}
|
|
236
|
-
has(item) {
|
|
237
|
-
return this.#map.has(item);
|
|
238
|
-
}
|
|
239
|
-
get length() {
|
|
240
|
-
return this.#map.size;
|
|
241
|
-
}
|
|
242
|
-
first() {
|
|
243
|
-
return this.#list.first()?.v;
|
|
244
|
-
}
|
|
245
|
-
last() {
|
|
246
|
-
return this.#list.last()?.v;
|
|
247
|
-
}
|
|
248
|
-
getNode(item) {
|
|
249
|
-
return this.#map.get(item);
|
|
250
|
-
}
|
|
251
|
-
at(index) {
|
|
252
|
-
return this.#list.at(index)?.v;
|
|
253
|
-
}
|
|
254
|
-
upsert(pos, item) {
|
|
255
|
-
const existing = this.#list.at(pos);
|
|
256
|
-
if (!existing) {
|
|
257
|
-
return this.push(item);
|
|
258
|
-
}
|
|
259
|
-
this.#map.delete(existing.v);
|
|
260
|
-
existing.v = item;
|
|
261
|
-
this.#map.set(item, existing);
|
|
262
|
-
return existing;
|
|
263
|
-
}
|
|
264
|
-
swap(a, b) {
|
|
265
|
-
const nodeA = this.#map.get(a);
|
|
266
|
-
const nodeB = this.#map.get(b);
|
|
267
|
-
if (!nodeA || !nodeB)
|
|
268
|
-
return;
|
|
269
|
-
nodeA.v = b;
|
|
270
|
-
nodeB.v = a;
|
|
271
|
-
this.#map.set(a, nodeB);
|
|
272
|
-
this.#map.set(b, nodeA);
|
|
273
|
-
}
|
|
274
|
-
clear() {
|
|
275
|
-
this.#list.clear();
|
|
276
|
-
this.#map.clear();
|
|
277
|
-
}
|
|
278
|
-
*[Symbol.iterator]() {
|
|
279
|
-
for (const node of this.#list) {
|
|
280
|
-
yield node.v;
|
|
281
|
-
}
|
|
1
|
+
// src/internal/internal_view.ts
|
|
2
|
+
var F_PENDING = 1;
|
|
3
|
+
var F_DIRTY = 1 << 1;
|
|
4
|
+
var F_MOVED = 1 << 2;
|
|
5
|
+
var F_QUICK_RERENDER = 1 << 3;
|
|
6
|
+
var F_PRIMITIVE = 1 << 4;
|
|
7
|
+
var F_TEXT_CONTENT = 1 << 5;
|
|
8
|
+
function hasScStructuralChange(prev, next) {
|
|
9
|
+
const prevLen = prev?.length ?? 0;
|
|
10
|
+
const nextLen = next?.length ?? 0;
|
|
11
|
+
if (prevLen === 0 && nextLen === 0)
|
|
12
|
+
return false;
|
|
13
|
+
if (!prev || !next)
|
|
14
|
+
return true;
|
|
15
|
+
if (prev.length !== next.length)
|
|
16
|
+
return true;
|
|
17
|
+
for (let i = 0;i < next.length; i++) {
|
|
18
|
+
if (next[i].viewFn !== prev[i].viewFn)
|
|
19
|
+
return true;
|
|
20
|
+
if (next[i].userKey !== prev[i].userKey)
|
|
21
|
+
return true;
|
|
282
22
|
}
|
|
23
|
+
return false;
|
|
283
24
|
}
|
|
284
25
|
|
|
285
|
-
// src/
|
|
286
|
-
|
|
287
|
-
renderer;
|
|
288
|
-
dirty = new IndexedList;
|
|
289
|
-
#scheduler;
|
|
290
|
-
#renderScheduled = false;
|
|
291
|
-
#collector;
|
|
292
|
-
constructor(renderer, scheduler) {
|
|
293
|
-
this.renderer = renderer;
|
|
294
|
-
this.#scheduler = scheduler ?? "scheduler" in globalThis ? (cb) => window.scheduler.postTask(cb) : (cb) => queueMicrotask(cb);
|
|
295
|
-
}
|
|
296
|
-
disposeView(view) {
|
|
297
|
-
this.dirty.delete(view);
|
|
298
|
-
}
|
|
299
|
-
schedule() {
|
|
300
|
-
if (this.#renderScheduled) {
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
this.#renderScheduled = true;
|
|
304
|
-
this.#scheduler(() => {
|
|
305
|
-
this.#renderScheduled = false;
|
|
306
|
-
this.render();
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
markDirty(view) {
|
|
310
|
-
this.dirty.push(view);
|
|
311
|
-
this.schedule();
|
|
312
|
-
}
|
|
313
|
-
pendingView(view) {
|
|
314
|
-
this.#collector?.(view);
|
|
315
|
-
}
|
|
316
|
-
collect(slot) {
|
|
317
|
-
const list = [];
|
|
318
|
-
const prev = this.#collector;
|
|
319
|
-
this.#collector = list.push.bind(list);
|
|
320
|
-
slot();
|
|
321
|
-
this.#collector = prev;
|
|
322
|
-
return list;
|
|
323
|
-
}
|
|
324
|
-
#rendering = false;
|
|
325
|
-
render() {
|
|
326
|
-
if (this.#rendering)
|
|
327
|
-
return;
|
|
328
|
-
this.#rendering = true;
|
|
329
|
-
try {
|
|
330
|
-
const cbs = [];
|
|
331
|
-
while (this.dirty.length > 0) {
|
|
332
|
-
const view = this.dirty.first();
|
|
333
|
-
const isNew = view.renderRef == null;
|
|
334
|
-
if (!isNew)
|
|
335
|
-
view.onUpdateBefore();
|
|
336
|
-
this.renderer.render(view);
|
|
337
|
-
if (view.dirty) {
|
|
338
|
-
view.reconsile();
|
|
339
|
-
cbs.push(isNew ? view.onMount : view.onUpdateAfter);
|
|
340
|
-
view.dirty = false;
|
|
341
|
-
}
|
|
342
|
-
this.dirty.delete(view);
|
|
343
|
-
}
|
|
344
|
-
for (const cb of cbs) {
|
|
345
|
-
cb();
|
|
346
|
-
}
|
|
347
|
-
} finally {
|
|
348
|
-
this.#rendering = false;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
26
|
+
// src/public/primitive.ts
|
|
27
|
+
var $primitive = Symbol("primitive");
|
|
352
28
|
|
|
353
29
|
// src/public/state.ts
|
|
354
30
|
class State {
|
|
@@ -436,219 +112,484 @@ function shallowEqual(a, b) {
|
|
|
436
112
|
if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
|
|
437
113
|
return false;
|
|
438
114
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
const key = keysA[i];
|
|
444
|
-
if (!Object.is(a[key], b[key])) {
|
|
115
|
+
let countA = 0;
|
|
116
|
+
for (const key in a) {
|
|
117
|
+
countA++;
|
|
118
|
+
if (!Object.is(a[key], b[key]))
|
|
445
119
|
return false;
|
|
120
|
+
}
|
|
121
|
+
let countB = 0;
|
|
122
|
+
for (const _ in b)
|
|
123
|
+
countB++;
|
|
124
|
+
return countA === countB;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/functional/lis.ts
|
|
128
|
+
function lis(arr) {
|
|
129
|
+
const len = arr.length;
|
|
130
|
+
if (len === 0)
|
|
131
|
+
return new Set;
|
|
132
|
+
const tails = [];
|
|
133
|
+
const prev = new Array(len).fill(-1);
|
|
134
|
+
for (let i = 0;i < len; i++) {
|
|
135
|
+
if (arr[i] < 0)
|
|
136
|
+
continue;
|
|
137
|
+
const val = arr[i];
|
|
138
|
+
let lo = 0;
|
|
139
|
+
let hi = tails.length;
|
|
140
|
+
while (lo < hi) {
|
|
141
|
+
const mid = lo + hi >> 1;
|
|
142
|
+
if (arr[tails[mid]] < val)
|
|
143
|
+
lo = mid + 1;
|
|
144
|
+
else
|
|
145
|
+
hi = mid;
|
|
446
146
|
}
|
|
147
|
+
if (lo > 0)
|
|
148
|
+
prev[i] = tails[lo - 1];
|
|
149
|
+
tails[lo] = i;
|
|
150
|
+
}
|
|
151
|
+
const result = new Set;
|
|
152
|
+
if (tails.length === 0)
|
|
153
|
+
return result;
|
|
154
|
+
let idx = tails[tails.length - 1];
|
|
155
|
+
for (let k = tails.length - 1;k >= 0; k--) {
|
|
156
|
+
result.add(idx);
|
|
157
|
+
idx = prev[idx];
|
|
447
158
|
}
|
|
448
|
-
return
|
|
159
|
+
return result;
|
|
449
160
|
}
|
|
450
161
|
|
|
451
|
-
// src/internal/
|
|
452
|
-
class
|
|
453
|
-
|
|
454
|
-
engine
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
#
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
this.
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
162
|
+
// src/internal/orchestrator.ts
|
|
163
|
+
class Orchestrator {
|
|
164
|
+
#currentEngine;
|
|
165
|
+
setCurrentEngine(engine) {
|
|
166
|
+
this.#currentEngine = engine;
|
|
167
|
+
}
|
|
168
|
+
currentEngine() {
|
|
169
|
+
return this.#currentEngine;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
var orchestrator = new Orchestrator;
|
|
173
|
+
|
|
174
|
+
// src/internal/engine.ts
|
|
175
|
+
class Engine {
|
|
176
|
+
renderer;
|
|
177
|
+
#dirtyQueue = new Set;
|
|
178
|
+
#collector;
|
|
179
|
+
#collectFor;
|
|
180
|
+
#scheduler;
|
|
181
|
+
#renderScheduled = false;
|
|
182
|
+
#rendering = false;
|
|
183
|
+
constructor(renderer, scheduler) {
|
|
184
|
+
this.renderer = renderer;
|
|
185
|
+
this.#scheduler = scheduler ?? ((cb) => queueMicrotask(cb));
|
|
186
|
+
renderer.engine = this;
|
|
187
|
+
}
|
|
188
|
+
newView(viewFn, parent, props, slot, userKey) {
|
|
189
|
+
const res = {
|
|
190
|
+
viewFn,
|
|
191
|
+
userKey,
|
|
192
|
+
props,
|
|
193
|
+
slot,
|
|
194
|
+
body: null,
|
|
195
|
+
sc: null,
|
|
196
|
+
renderRef: null,
|
|
197
|
+
flags: F_PENDING | (viewFn[$primitive] != null ? F_PRIMITIVE : 0),
|
|
198
|
+
children: null,
|
|
199
|
+
keyToView: null,
|
|
200
|
+
unsubscribe: null,
|
|
201
|
+
parent,
|
|
202
|
+
scHost: null
|
|
203
|
+
};
|
|
204
|
+
if (slot) {
|
|
205
|
+
res.sc = this.#collect(slot, [], res);
|
|
206
|
+
}
|
|
207
|
+
return res;
|
|
208
|
+
}
|
|
209
|
+
view(viewFn, props, slot, userKey) {
|
|
210
|
+
const view = this.newView(viewFn, this.#collectFor, props, slot, userKey);
|
|
211
|
+
this.#collector?.push(view);
|
|
212
|
+
return view;
|
|
213
|
+
}
|
|
214
|
+
views(views) {
|
|
215
|
+
if (views)
|
|
216
|
+
this.#collector?.push(...views);
|
|
217
|
+
}
|
|
218
|
+
initViewBody(view) {
|
|
219
|
+
if (!(view.flags & F_PENDING))
|
|
220
|
+
return;
|
|
221
|
+
view.flags &= ~F_PENDING;
|
|
222
|
+
if (view.flags & F_PRIMITIVE) {
|
|
223
|
+
const engine = this;
|
|
224
|
+
view.body = {
|
|
225
|
+
render() {
|
|
226
|
+
engine.views(view.sc);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
view.body = view.viewFn({
|
|
232
|
+
props: () => view.props,
|
|
233
|
+
use: (storeOrState) => {
|
|
234
|
+
if (!isStore(storeOrState)) {
|
|
235
|
+
return new State(storeOrState, () => this.markDirty(view));
|
|
478
236
|
}
|
|
479
|
-
const s =
|
|
480
|
-
|
|
237
|
+
const s = storeOrState;
|
|
238
|
+
const unsub = s.subscribe(() => this.markDirty(view));
|
|
239
|
+
if (!view.unsubscribe)
|
|
240
|
+
view.unsubscribe = [];
|
|
241
|
+
view.unsubscribe.push(unsub);
|
|
481
242
|
return s;
|
|
482
243
|
},
|
|
483
244
|
slot: () => {
|
|
484
|
-
if (!
|
|
245
|
+
if (!view.sc)
|
|
485
246
|
return;
|
|
486
|
-
|
|
487
|
-
for (const child of
|
|
488
|
-
this
|
|
247
|
+
view.scHost = this.#collectFor ?? view;
|
|
248
|
+
for (const child of view.sc) {
|
|
249
|
+
child.parent = this.#collectFor ?? view;
|
|
250
|
+
this.#collector?.push(child);
|
|
489
251
|
}
|
|
490
252
|
}
|
|
491
253
|
});
|
|
492
|
-
this.slotChildren = slot && this.engine.collect(slot);
|
|
493
|
-
this.markDirty();
|
|
494
254
|
}
|
|
495
|
-
|
|
496
|
-
this
|
|
497
|
-
this.
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
255
|
+
createRoot(children, props) {
|
|
256
|
+
orchestrator.setCurrentEngine(this);
|
|
257
|
+
const view = this.newView(({ slot }) => ({
|
|
258
|
+
render() {
|
|
259
|
+
slot();
|
|
260
|
+
}
|
|
261
|
+
}), null, props, children, null);
|
|
262
|
+
this.markDirty(view);
|
|
263
|
+
return view;
|
|
502
264
|
}
|
|
503
|
-
|
|
504
|
-
if (
|
|
505
|
-
return
|
|
265
|
+
markDirty(view) {
|
|
266
|
+
if (view.flags & F_DIRTY) {
|
|
267
|
+
return;
|
|
506
268
|
}
|
|
507
|
-
|
|
269
|
+
view.flags |= F_DIRTY;
|
|
270
|
+
this.#dirtyQueue.add(view);
|
|
271
|
+
this.schedule();
|
|
508
272
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
this.
|
|
273
|
+
markMoved(view) {
|
|
274
|
+
if (view.flags & F_MOVED) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
view.flags |= F_MOVED;
|
|
278
|
+
this.#dirtyQueue.add(view);
|
|
279
|
+
this.schedule();
|
|
515
280
|
}
|
|
516
|
-
|
|
517
|
-
this
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
281
|
+
schedule() {
|
|
282
|
+
if (this.#renderScheduled)
|
|
283
|
+
return;
|
|
284
|
+
this.#renderScheduled = true;
|
|
285
|
+
this.#scheduler(() => {
|
|
286
|
+
this.#renderScheduled = false;
|
|
287
|
+
this.render();
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
#collect(render, collector, parent) {
|
|
291
|
+
const before = this.#collector;
|
|
292
|
+
const beforeParent = this.#collectFor;
|
|
293
|
+
this.#collector = collector;
|
|
294
|
+
this.#collectFor = parent;
|
|
295
|
+
render();
|
|
296
|
+
this.#collector = before;
|
|
297
|
+
this.#collectFor = beforeParent;
|
|
298
|
+
return collector;
|
|
299
|
+
}
|
|
300
|
+
nextProps(view, nextProps, nextSlot, preCollectedSc) {
|
|
301
|
+
const prevSc = view.sc;
|
|
302
|
+
view.slot = nextSlot;
|
|
303
|
+
if (preCollectedSc) {
|
|
304
|
+
for (const child of preCollectedSc)
|
|
305
|
+
child.parent = view;
|
|
306
|
+
view.sc = preCollectedSc;
|
|
307
|
+
} else {
|
|
308
|
+
view.sc = nextSlot ? this.#collect(nextSlot, [], view) : null;
|
|
309
|
+
}
|
|
310
|
+
const structChanged = hasScStructuralChange(prevSc, view.sc);
|
|
311
|
+
const shouldUpdate = view.body?.shouldUpdate ? view.body.shouldUpdate(nextProps) : !shallowEqual(view.props, nextProps);
|
|
312
|
+
if (shouldUpdate || structChanged) {
|
|
313
|
+
view.props = nextProps;
|
|
314
|
+
this.markDirty(view);
|
|
315
|
+
} else if (view.sc && view.scHost?.children) {
|
|
316
|
+
this.#propagateScProps(view);
|
|
317
|
+
} else if (view.sc && prevSc) {
|
|
318
|
+
for (let i = 0;i < view.sc.length; i++) {
|
|
319
|
+
if (view.sc[i].props !== prevSc[i].props) {
|
|
320
|
+
this.markDirty(view);
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
526
324
|
}
|
|
527
325
|
}
|
|
528
|
-
|
|
529
|
-
const
|
|
530
|
-
|
|
326
|
+
#propagateScProps(owner) {
|
|
327
|
+
const host = owner.scHost;
|
|
328
|
+
const sc = owner.sc;
|
|
329
|
+
if (!host?.children || !sc)
|
|
531
330
|
return;
|
|
532
|
-
if (
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
const expectedNext = vdom.at(i);
|
|
539
|
-
if (pending.userKey == null) {
|
|
540
|
-
if (expectedNext?.viewFn === pending.viewFn) {
|
|
541
|
-
this.quickRerender = false;
|
|
542
|
-
expectedNext.nextProps(pending.props, pending.slot);
|
|
543
|
-
} else {
|
|
544
|
-
if (expectedNext) {
|
|
545
|
-
if (expectedNext.userKey)
|
|
546
|
-
this.keyToIndex?.delete(expectedNext.userKey);
|
|
547
|
-
expectedNext[Symbol.dispose]();
|
|
331
|
+
if (host.keyToView) {
|
|
332
|
+
for (const item of sc) {
|
|
333
|
+
if (item.userKey != null) {
|
|
334
|
+
const live = host.keyToView.get(item.userKey);
|
|
335
|
+
if (live && live.viewFn === item.viewFn) {
|
|
336
|
+
this.nextProps(live, item.props, item.slot);
|
|
548
337
|
}
|
|
549
|
-
vdom.upsert(i, new View(pending.viewFn, pending.props, pending.slot, this.engine, this, null));
|
|
550
338
|
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
if (matched !== expectedNext) {
|
|
559
|
-
matched.markMove();
|
|
560
|
-
expectedNext.markMove();
|
|
561
|
-
vdom.swap(matched, expectedNext);
|
|
562
|
-
if (this.keyToIndex) {
|
|
563
|
-
if (matched.userKey != null)
|
|
564
|
-
this.keyToIndex.set(matched.userKey, i);
|
|
565
|
-
if (expectedNext.userKey != null)
|
|
566
|
-
this.keyToIndex.set(expectedNext.userKey, matchedIndex);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
} else {
|
|
570
|
-
matched.markMove();
|
|
571
|
-
vdom.upsert(i, matched);
|
|
572
|
-
if (this.keyToIndex && matched.userKey != null) {
|
|
573
|
-
this.keyToIndex.set(matched.userKey, i);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
} else {
|
|
577
|
-
if (expectedNext) {
|
|
578
|
-
if (expectedNext.userKey)
|
|
579
|
-
this.keyToIndex?.delete(expectedNext.userKey);
|
|
580
|
-
expectedNext[Symbol.dispose]();
|
|
581
|
-
}
|
|
582
|
-
vdom.upsert(i, new View(pending.viewFn, pending.props, pending.slot, this.engine, this, pending.userKey));
|
|
583
|
-
if (!this.keyToIndex)
|
|
584
|
-
this.keyToIndex = new Map;
|
|
585
|
-
this.keyToIndex.set(pending.userKey, i);
|
|
339
|
+
}
|
|
340
|
+
} else {
|
|
341
|
+
const len = Math.min(sc.length, host.children.length);
|
|
342
|
+
for (let i = 0;i < len; i++) {
|
|
343
|
+
const live = host.children[i];
|
|
344
|
+
if (live.viewFn === sc[i].viewFn) {
|
|
345
|
+
this.nextProps(live, sc[i].props, sc[i].slot);
|
|
586
346
|
}
|
|
587
347
|
}
|
|
588
348
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
349
|
+
}
|
|
350
|
+
reconcile(view) {
|
|
351
|
+
let pendingChildren = null;
|
|
352
|
+
if (view.body?.render) {
|
|
353
|
+
pendingChildren = this.#collect(view.body.render, [], view);
|
|
354
|
+
}
|
|
355
|
+
if (pendingChildren == null || pendingChildren.length === 0) {
|
|
356
|
+
if (view.children)
|
|
357
|
+
for (const child of view.children)
|
|
358
|
+
this.dispose(child);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
if (view.children == null) {
|
|
362
|
+
view.children = pendingChildren;
|
|
363
|
+
for (const child of pendingChildren) {
|
|
364
|
+
this.initViewBody(child);
|
|
365
|
+
this.markDirty(child);
|
|
366
|
+
if (child.userKey != null) {
|
|
367
|
+
if (!view.keyToView)
|
|
368
|
+
view.keyToView = new Map;
|
|
369
|
+
view.keyToView.set(child.userKey, child);
|
|
597
370
|
}
|
|
598
371
|
}
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const hasKeys = pendingChildren.some((c) => c.userKey != null);
|
|
375
|
+
if (hasKeys) {
|
|
376
|
+
this.#reconcileKeyed(view, view.children, pendingChildren);
|
|
377
|
+
} else {
|
|
378
|
+
this.#reconcileNonKeyed(view, view.children, pendingChildren);
|
|
599
379
|
}
|
|
600
380
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
381
|
+
#reconcileNonKeyed(view, oldChildren, pending) {
|
|
382
|
+
const oldLen = oldChildren.length;
|
|
383
|
+
const newLen = pending.length;
|
|
384
|
+
const minLen = Math.min(oldLen, newLen);
|
|
385
|
+
for (let i = 0;i < minLen; i++) {
|
|
386
|
+
const old = oldChildren[i];
|
|
387
|
+
const pend = pending[i];
|
|
388
|
+
this.#patchOrReplace(view, oldChildren, i, old, pend);
|
|
389
|
+
}
|
|
390
|
+
for (let i = oldLen;i < newLen; i++) {
|
|
391
|
+
oldChildren[i] = pending[i];
|
|
392
|
+
this.initViewBody(pending[i]);
|
|
393
|
+
this.markDirty(pending[i]);
|
|
394
|
+
}
|
|
395
|
+
for (let i = newLen;i < oldLen; i++) {
|
|
396
|
+
this.dispose(oldChildren[i]);
|
|
397
|
+
}
|
|
398
|
+
oldChildren.length = newLen;
|
|
399
|
+
}
|
|
400
|
+
#reconcileKeyed(view, oldChildren, pending) {
|
|
401
|
+
let i = 0;
|
|
402
|
+
let oldEnd = oldChildren.length - 1;
|
|
403
|
+
let newEnd = pending.length - 1;
|
|
404
|
+
while (i <= oldEnd && i <= newEnd) {
|
|
405
|
+
const oldView = oldChildren[i];
|
|
406
|
+
const pendView = pending[i];
|
|
407
|
+
if (oldView.userKey !== pendView.userKey)
|
|
408
|
+
break;
|
|
409
|
+
this.#patchOrReplace(view, oldChildren, i, oldView, pendView);
|
|
410
|
+
i++;
|
|
411
|
+
}
|
|
412
|
+
while (oldEnd >= i && newEnd >= i) {
|
|
413
|
+
const oldView = oldChildren[oldEnd];
|
|
414
|
+
const pendView = pending[newEnd];
|
|
415
|
+
if (oldView.userKey !== pendView.userKey)
|
|
416
|
+
break;
|
|
417
|
+
this.#patchOrReplace(view, oldChildren, oldEnd, oldView, pendView);
|
|
418
|
+
oldEnd--;
|
|
419
|
+
newEnd--;
|
|
420
|
+
}
|
|
421
|
+
if (i > oldEnd) {
|
|
422
|
+
for (let j = i;j <= newEnd; j++) {
|
|
423
|
+
this.initViewBody(pending[j]);
|
|
424
|
+
this.markDirty(pending[j]);
|
|
425
|
+
if (pending[j].userKey != null) {
|
|
426
|
+
if (!view.keyToView)
|
|
427
|
+
view.keyToView = new Map;
|
|
428
|
+
view.keyToView.set(pending[j].userKey, pending[j]);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
view.children = [
|
|
432
|
+
...oldChildren.slice(0, i),
|
|
433
|
+
...pending.slice(i, newEnd + 1),
|
|
434
|
+
...oldChildren.slice(i, oldEnd + 1)
|
|
435
|
+
];
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
if (i > newEnd) {
|
|
439
|
+
for (let j = i;j <= oldEnd; j++) {
|
|
440
|
+
this.dispose(oldChildren[j]);
|
|
605
441
|
}
|
|
442
|
+
view.children = [
|
|
443
|
+
...oldChildren.slice(0, i),
|
|
444
|
+
...oldChildren.slice(oldEnd + 1)
|
|
445
|
+
];
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
const newKeyToIndex = new Map;
|
|
449
|
+
for (let j = i;j <= newEnd; j++) {
|
|
450
|
+
const key = pending[j].userKey;
|
|
451
|
+
if (key != null)
|
|
452
|
+
newKeyToIndex.set(key, j);
|
|
606
453
|
}
|
|
607
|
-
|
|
608
|
-
|
|
454
|
+
const middleLen = newEnd - i + 1;
|
|
455
|
+
const newIdxToOldIdx = new Array(middleLen).fill(-1);
|
|
456
|
+
const matched = new Set;
|
|
457
|
+
for (let j = i;j <= oldEnd; j++) {
|
|
458
|
+
const oldView = oldChildren[j];
|
|
459
|
+
const key = oldView.userKey;
|
|
460
|
+
if (key != null && newKeyToIndex.has(key)) {
|
|
461
|
+
const newIdx = newKeyToIndex.get(key);
|
|
462
|
+
newIdxToOldIdx[newIdx - i] = j;
|
|
463
|
+
matched.add(j);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
for (let j = i;j <= oldEnd; j++) {
|
|
467
|
+
if (!matched.has(j)) {
|
|
468
|
+
this.dispose(oldChildren[j]);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const stable = lis(newIdxToOldIdx);
|
|
472
|
+
const newChildren = new Array(middleLen);
|
|
473
|
+
for (let j = middleLen - 1;j >= 0; j--) {
|
|
474
|
+
const newIdx = i + j;
|
|
475
|
+
const pendView = pending[newIdx];
|
|
476
|
+
if (newIdxToOldIdx[j] === -1) {
|
|
477
|
+
this.initViewBody(pendView);
|
|
478
|
+
this.markDirty(pendView);
|
|
479
|
+
if (pendView.userKey != null) {
|
|
480
|
+
if (!view.keyToView)
|
|
481
|
+
view.keyToView = new Map;
|
|
482
|
+
view.keyToView.set(pendView.userKey, pendView);
|
|
483
|
+
}
|
|
484
|
+
newChildren[j] = pendView;
|
|
485
|
+
} else {
|
|
486
|
+
const oldView = oldChildren[newIdxToOldIdx[j]];
|
|
487
|
+
this.#patchOrReplace(view, oldChildren, newIdxToOldIdx[j], oldView, pendView);
|
|
488
|
+
if (!stable.has(j)) {
|
|
489
|
+
this.markMoved(oldView);
|
|
490
|
+
}
|
|
491
|
+
newChildren[j] = oldView;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
const head = oldChildren.slice(0, i);
|
|
495
|
+
const tail = oldChildren.slice(oldEnd + 1);
|
|
496
|
+
view.children = [...head, ...newChildren, ...tail];
|
|
609
497
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
return true;
|
|
625
|
-
if (next[i].props !== prev[i].props)
|
|
626
|
-
return true;
|
|
498
|
+
#patchOrReplace(parent, oldChildren, idx, oldView, pendView) {
|
|
499
|
+
if (oldView.viewFn === pendView.viewFn) {
|
|
500
|
+
this.nextProps(oldView, pendView.props, pendView.slot, pendView.sc);
|
|
501
|
+
} else {
|
|
502
|
+
this.dispose(oldView);
|
|
503
|
+
oldChildren[idx] = pendView;
|
|
504
|
+
this.initViewBody(pendView);
|
|
505
|
+
this.markDirty(pendView);
|
|
506
|
+
if (pendView.userKey != null) {
|
|
507
|
+
if (!parent.keyToView)
|
|
508
|
+
parent.keyToView = new Map;
|
|
509
|
+
parent.keyToView.set(pendView.userKey, pendView);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
627
512
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
513
|
+
dispose(view) {
|
|
514
|
+
const isPrimitiveWithDom = view.flags & F_PRIMITIVE && view.renderRef;
|
|
515
|
+
if (view.children) {
|
|
516
|
+
for (const child of view.children) {
|
|
517
|
+
if (isPrimitiveWithDom) {
|
|
518
|
+
this.#disposeVirtual(child);
|
|
519
|
+
} else {
|
|
520
|
+
this.dispose(child);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (view.unsubscribe)
|
|
525
|
+
for (const unsub of view.unsubscribe)
|
|
526
|
+
unsub();
|
|
527
|
+
if (view.userKey != null)
|
|
528
|
+
view.parent?.keyToView?.delete(view.userKey);
|
|
529
|
+
this.renderer.unmount(view);
|
|
530
|
+
this.#dirtyQueue.delete(view);
|
|
531
|
+
}
|
|
532
|
+
#disposeVirtual(view) {
|
|
533
|
+
if (view.children) {
|
|
534
|
+
for (const child of view.children)
|
|
535
|
+
this.#disposeVirtual(child);
|
|
536
|
+
}
|
|
537
|
+
if (view.unsubscribe)
|
|
538
|
+
for (const unsub of view.unsubscribe)
|
|
539
|
+
unsub();
|
|
540
|
+
if (view.userKey != null)
|
|
541
|
+
view.parent?.keyToView?.delete(view.userKey);
|
|
542
|
+
view.renderRef = undefined;
|
|
543
|
+
this.#dirtyQueue.delete(view);
|
|
636
544
|
}
|
|
637
|
-
|
|
638
|
-
|
|
545
|
+
render() {
|
|
546
|
+
if (this.#rendering)
|
|
547
|
+
return;
|
|
548
|
+
orchestrator.setCurrentEngine(this);
|
|
549
|
+
this.#rendering = true;
|
|
550
|
+
try {
|
|
551
|
+
const cbs = [];
|
|
552
|
+
for (const view of this.#dirtyQueue) {
|
|
553
|
+
this.initViewBody(view);
|
|
554
|
+
const isNew = !view.renderRef;
|
|
555
|
+
if (!isNew)
|
|
556
|
+
view.body?.onUpdateBefore?.();
|
|
557
|
+
if (view.flags & F_DIRTY) {
|
|
558
|
+
this.reconcile(view);
|
|
559
|
+
this.renderer.render(view);
|
|
560
|
+
view.flags &= ~F_DIRTY;
|
|
561
|
+
view.flags &= ~F_MOVED;
|
|
562
|
+
if (isNew && view.body?.onMount) {
|
|
563
|
+
const b = view.body;
|
|
564
|
+
cbs.push(() => b.onMount());
|
|
565
|
+
} else if (!isNew && view.body?.onUpdateAfter) {
|
|
566
|
+
const b = view.body;
|
|
567
|
+
cbs.push(() => b.onUpdateAfter());
|
|
568
|
+
}
|
|
569
|
+
} else {
|
|
570
|
+
this.renderer.render(view);
|
|
571
|
+
view.flags &= ~F_MOVED;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
this.#dirtyQueue.clear();
|
|
575
|
+
orchestrator.setCurrentEngine(null);
|
|
576
|
+
this.#rendering = false;
|
|
577
|
+
for (const cb of cbs)
|
|
578
|
+
cb();
|
|
579
|
+
} catch (e) {
|
|
580
|
+
orchestrator.setCurrentEngine(null);
|
|
581
|
+
this.#rendering = false;
|
|
582
|
+
throw e;
|
|
583
|
+
}
|
|
639
584
|
}
|
|
640
585
|
}
|
|
641
|
-
var orchestrator = new Orchestrator;
|
|
642
586
|
|
|
643
587
|
// src/public/app.ts
|
|
644
588
|
function createApp(slot, renderer, options) {
|
|
645
589
|
return {
|
|
646
590
|
mount(props) {
|
|
647
591
|
const engine = new Engine(renderer, options?.scheduler);
|
|
648
|
-
|
|
649
|
-
new View(() => ({ render() {
|
|
650
|
-
slot();
|
|
651
|
-
} }), props ?? {}, null, engine, null, null);
|
|
592
|
+
engine.createRoot(slot, props ?? {});
|
|
652
593
|
engine.render();
|
|
653
594
|
return { engine };
|
|
654
595
|
}
|
|
@@ -657,12 +598,7 @@ function createApp(slot, renderer, options) {
|
|
|
657
598
|
// src/public/view.ts
|
|
658
599
|
function view(body) {
|
|
659
600
|
return (props, slot) => {
|
|
660
|
-
orchestrator.currentEngine().
|
|
661
|
-
viewFn: body,
|
|
662
|
-
props,
|
|
663
|
-
slot,
|
|
664
|
-
userKey: maybeGetUserKey(props)
|
|
665
|
-
});
|
|
601
|
+
orchestrator.currentEngine().view(body, props, slot, maybeGetUserKey(props));
|
|
666
602
|
};
|
|
667
603
|
}
|
|
668
604
|
function maybeGetUserKey(params) {
|
|
@@ -670,8 +606,6 @@ function maybeGetUserKey(params) {
|
|
|
670
606
|
return params.key;
|
|
671
607
|
}
|
|
672
608
|
}
|
|
673
|
-
// src/public/primitive.ts
|
|
674
|
-
var $primitive = Symbol("primitive");
|
|
675
609
|
// src/public/primitives/primitives.ts
|
|
676
610
|
function html(tag) {
|
|
677
611
|
const fn = ({
|
|
@@ -684,7 +618,10 @@ function html(tag) {
|
|
|
684
618
|
fn[$primitive] = tag;
|
|
685
619
|
return view(fn);
|
|
686
620
|
}
|
|
687
|
-
var
|
|
621
|
+
var textViewFn = Object.assign(() => ({ render() {} }), { [$primitive]: "text" });
|
|
622
|
+
function text(content) {
|
|
623
|
+
orchestrator.currentEngine().view(textViewFn, content, null, null);
|
|
624
|
+
}
|
|
688
625
|
var div = html("div");
|
|
689
626
|
var span = html("span");
|
|
690
627
|
var section = html("section");
|
|
@@ -792,20 +729,85 @@ var track = html("track");
|
|
|
792
729
|
var map = html("map");
|
|
793
730
|
var area = html("area");
|
|
794
731
|
// src/render/html_render.ts
|
|
732
|
+
function isEventProp(key) {
|
|
733
|
+
return key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && key.charCodeAt(2) >= 65 && key.charCodeAt(2) <= 90;
|
|
734
|
+
}
|
|
795
735
|
var DOM_EVENT = {
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
736
|
+
Click: "click",
|
|
737
|
+
Dblclick: "dblclick",
|
|
738
|
+
PointerDown: "pointerdown",
|
|
739
|
+
PointerUp: "pointerup",
|
|
740
|
+
PointerMove: "pointermove",
|
|
741
|
+
Input: "input",
|
|
742
|
+
Change: "change",
|
|
743
|
+
KeyDown: "keydown",
|
|
744
|
+
KeyUp: "keyup",
|
|
745
|
+
Focus: "focus",
|
|
746
|
+
Blur: "blur"
|
|
807
747
|
};
|
|
808
|
-
var
|
|
748
|
+
var $EV = Symbol.for("creo.ev");
|
|
749
|
+
var containerState = new WeakMap;
|
|
750
|
+
function getState(container) {
|
|
751
|
+
let state = containerState.get(container);
|
|
752
|
+
if (!state) {
|
|
753
|
+
state = {
|
|
754
|
+
counts: new Map,
|
|
755
|
+
handler(e) {
|
|
756
|
+
const domEvent = e.type;
|
|
757
|
+
let dom = e.target;
|
|
758
|
+
while (dom && dom !== container) {
|
|
759
|
+
const evObj = dom[$EV];
|
|
760
|
+
if (evObj) {
|
|
761
|
+
const handler = evObj[domEvent];
|
|
762
|
+
if (handler) {
|
|
763
|
+
handler(mapEventData(domEvent, e));
|
|
764
|
+
if (e.cancelBubble)
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
dom = dom.parentElement;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
containerState.set(container, state);
|
|
773
|
+
}
|
|
774
|
+
return state;
|
|
775
|
+
}
|
|
776
|
+
function ensureDelegated(container, domEvent) {
|
|
777
|
+
const state = getState(container);
|
|
778
|
+
const count = state.counts.get(domEvent) ?? 0;
|
|
779
|
+
if (count === 0) {
|
|
780
|
+
container.addEventListener(domEvent, state.handler);
|
|
781
|
+
}
|
|
782
|
+
state.counts.set(domEvent, count + 1);
|
|
783
|
+
}
|
|
784
|
+
function removeDelegated(container, domEvent) {
|
|
785
|
+
const state = getState(container);
|
|
786
|
+
const count = state.counts.get(domEvent) ?? 0;
|
|
787
|
+
if (count <= 1) {
|
|
788
|
+
state.counts.delete(domEvent);
|
|
789
|
+
container.removeEventListener(domEvent, state.handler);
|
|
790
|
+
} else {
|
|
791
|
+
state.counts.set(domEvent, count - 1);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
function mapEventData(domEvent, e) {
|
|
795
|
+
let data2;
|
|
796
|
+
if (domEvent === "click" || domEvent === "dblclick" || domEvent === "pointerdown" || domEvent === "pointerup" || domEvent === "pointermove") {
|
|
797
|
+
const pe = e;
|
|
798
|
+
data2 = { x: pe.clientX, y: pe.clientY };
|
|
799
|
+
} else if (domEvent === "input" || domEvent === "change") {
|
|
800
|
+
data2 = { value: e.target.value };
|
|
801
|
+
} else if (domEvent === "keydown" || domEvent === "keyup") {
|
|
802
|
+
const ke = e;
|
|
803
|
+
data2 = { key: ke.key, code: ke.code };
|
|
804
|
+
} else {
|
|
805
|
+
data2 = {};
|
|
806
|
+
}
|
|
807
|
+
data2.stopPropagation = () => e.stopPropagation();
|
|
808
|
+
data2.preventDefault = () => e.preventDefault();
|
|
809
|
+
return data2;
|
|
810
|
+
}
|
|
809
811
|
var DOM_PROPERTIES = new Set([
|
|
810
812
|
"value",
|
|
811
813
|
"checked",
|
|
@@ -815,52 +817,62 @@ var DOM_PROPERTIES = new Set([
|
|
|
815
817
|
|
|
816
818
|
class HtmlRender {
|
|
817
819
|
container;
|
|
820
|
+
engine;
|
|
818
821
|
constructor(container) {
|
|
819
822
|
this.container = container;
|
|
820
823
|
}
|
|
821
824
|
render(view2) {
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
825
|
+
if (!view2.renderRef) {
|
|
826
|
+
if (view2.flags & F_PRIMITIVE) {
|
|
827
|
+
const parentNode = this.findParentDom(view2);
|
|
828
|
+
const refNode = this.findInsertionPoint(view2);
|
|
829
|
+
const tag = view2.viewFn[$primitive];
|
|
830
|
+
if (tag === "text") {
|
|
831
|
+
const textNode = document.createTextNode(String(view2.props));
|
|
832
|
+
view2.renderRef = { element: textNode, prevProps: null };
|
|
833
|
+
parentNode.insertBefore(textNode, refNode);
|
|
834
|
+
} else {
|
|
835
|
+
const element = document.createElement(tag);
|
|
836
|
+
const props = view2.props;
|
|
837
|
+
const domRef = { element, prevProps: null };
|
|
838
|
+
view2.renderRef = domRef;
|
|
839
|
+
this.setAttributes(element, props);
|
|
840
|
+
if (view2.children?.length === 1) {
|
|
841
|
+
const child = view2.children[0];
|
|
842
|
+
if (child.flags & F_PRIMITIVE && child.viewFn[$primitive] === "text") {
|
|
843
|
+
element.textContent = String(child.props);
|
|
844
|
+
child.renderRef = { element, prevProps: null };
|
|
845
|
+
child.flags |= F_TEXT_CONTENT;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
parentNode.insertBefore(element, refNode);
|
|
832
849
|
}
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
if (newRef?.kind === "primitive" && newRef.element instanceof HTMLElement && view2.props?.autofocus) {
|
|
836
|
-
newRef.element.focus();
|
|
850
|
+
} else {
|
|
851
|
+
view2.renderRef = true;
|
|
837
852
|
}
|
|
838
853
|
return;
|
|
839
854
|
}
|
|
840
|
-
if (view2.
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
const
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
if (ref.kind === "primitive") {
|
|
849
|
-
parentNode.insertBefore(ref.element, expectedStart);
|
|
850
|
-
} else {
|
|
851
|
-
if (view2.virtualDom) {
|
|
852
|
-
for (const child of view2.virtualDom) {
|
|
853
|
-
this.moveDomNodes(child, parentNode, expectedStart);
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
parentNode.insertBefore(ref.endComment, expectedStart);
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
}
|
|
855
|
+
if (view2.flags & F_MOVED) {
|
|
856
|
+
const parentNode = this.findParentDom(view2);
|
|
857
|
+
const refNode = this.findInsertionPoint(view2);
|
|
858
|
+
if (view2.flags & F_PRIMITIVE) {
|
|
859
|
+
const ref2 = view2.renderRef;
|
|
860
|
+
parentNode.insertBefore(ref2.element, refNode);
|
|
861
|
+
} else {
|
|
862
|
+
this.moveDomNodes(view2, parentNode, refNode);
|
|
860
863
|
}
|
|
861
864
|
}
|
|
862
|
-
if (
|
|
865
|
+
if (!(view2.flags & F_PRIMITIVE))
|
|
866
|
+
return;
|
|
867
|
+
const ref = view2.renderRef;
|
|
868
|
+
if (view2.flags & F_TEXT_CONTENT) {
|
|
869
|
+
const parentEl = ref.element;
|
|
870
|
+
const nextText = String(view2.props);
|
|
871
|
+
if (parentEl.textContent !== nextText) {
|
|
872
|
+
parentEl.textContent = nextText;
|
|
873
|
+
}
|
|
863
874
|
return;
|
|
875
|
+
}
|
|
864
876
|
if (ref.element instanceof Text) {
|
|
865
877
|
const nextText = String(view2.props);
|
|
866
878
|
if (ref.element.textContent !== nextText) {
|
|
@@ -870,74 +882,70 @@ class HtmlRender {
|
|
|
870
882
|
}
|
|
871
883
|
const nextProps = view2.props;
|
|
872
884
|
if (!ref.prevProps) {
|
|
873
|
-
this.setAttributes(ref
|
|
874
|
-
} else {
|
|
875
|
-
this.diffAttributes(ref
|
|
885
|
+
this.setAttributes(ref.element, nextProps);
|
|
886
|
+
} else if (ref.prevProps !== nextProps) {
|
|
887
|
+
this.diffAttributes(ref.element, ref.prevProps, nextProps);
|
|
876
888
|
}
|
|
877
|
-
ref.prevProps =
|
|
889
|
+
ref.prevProps = nextProps;
|
|
878
890
|
}
|
|
879
891
|
unmount(view2) {
|
|
880
|
-
|
|
892
|
+
if (view2.flags & F_PRIMITIVE) {
|
|
893
|
+
this.removeDomNodes(view2);
|
|
894
|
+
}
|
|
881
895
|
view2.renderRef = undefined;
|
|
882
896
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
if (
|
|
887
|
-
const
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
element: textNode,
|
|
891
|
-
prevProps: null,
|
|
892
|
-
listeners: null
|
|
893
|
-
};
|
|
894
|
-
view2.renderRef = ref2;
|
|
895
|
-
return textNode;
|
|
897
|
+
findParentDom(view2) {
|
|
898
|
+
let parent = view2.parent;
|
|
899
|
+
while (parent) {
|
|
900
|
+
if (parent.flags & F_PRIMITIVE) {
|
|
901
|
+
const ref = parent.renderRef;
|
|
902
|
+
if (ref && ref.element instanceof HTMLElement)
|
|
903
|
+
return ref.element;
|
|
896
904
|
}
|
|
897
|
-
|
|
898
|
-
const props = view2.props;
|
|
899
|
-
const ref = {
|
|
900
|
-
kind: "primitive",
|
|
901
|
-
element,
|
|
902
|
-
prevProps: null,
|
|
903
|
-
listeners: null
|
|
904
|
-
};
|
|
905
|
-
view2.renderRef = ref;
|
|
906
|
-
this.setAttributes(ref, element, props);
|
|
907
|
-
return element;
|
|
905
|
+
parent = parent.parent;
|
|
908
906
|
}
|
|
909
|
-
|
|
910
|
-
view2.renderRef = {
|
|
911
|
-
kind: "composite",
|
|
912
|
-
endComment
|
|
913
|
-
};
|
|
914
|
-
return endComment;
|
|
907
|
+
return this.container;
|
|
915
908
|
}
|
|
916
|
-
|
|
917
|
-
|
|
909
|
+
findInsertionPoint(view2) {
|
|
910
|
+
const parent = view2.parent;
|
|
911
|
+
if (!parent?.children)
|
|
912
|
+
return null;
|
|
913
|
+
const children = parent.children;
|
|
914
|
+
if (children[children.length - 1] === view2) {
|
|
915
|
+
return this.#parentEndAnchor(parent);
|
|
916
|
+
}
|
|
917
|
+
const idx = children.indexOf(view2);
|
|
918
|
+
for (let i2 = idx + 1;i2 < children.length; i2++) {
|
|
919
|
+
const dom = this.getFirstDomNode(children[i2]);
|
|
920
|
+
if (dom)
|
|
921
|
+
return dom;
|
|
922
|
+
}
|
|
923
|
+
return this.#parentEndAnchor(parent);
|
|
918
924
|
}
|
|
919
|
-
|
|
920
|
-
|
|
925
|
+
#parentEndAnchor(parent) {
|
|
926
|
+
if (parent.flags & F_PRIMITIVE)
|
|
927
|
+
return null;
|
|
928
|
+
return this.findInsertionPoint(parent);
|
|
921
929
|
}
|
|
922
|
-
setAttributes(
|
|
930
|
+
setAttributes(element, props) {
|
|
923
931
|
for (const key in props) {
|
|
924
932
|
const value = props[key];
|
|
925
|
-
if (
|
|
933
|
+
if (key === "key" || value == null)
|
|
926
934
|
continue;
|
|
927
|
-
if (
|
|
928
|
-
this.bindEvent(
|
|
935
|
+
if (isEventProp(key)) {
|
|
936
|
+
this.bindEvent(element, key, value);
|
|
929
937
|
continue;
|
|
930
938
|
}
|
|
931
939
|
this.setAttribute(element, key, value);
|
|
932
940
|
}
|
|
933
941
|
}
|
|
934
|
-
diffAttributes(
|
|
935
|
-
for (const key
|
|
936
|
-
if (
|
|
942
|
+
diffAttributes(element, prev, next) {
|
|
943
|
+
for (const key in prev) {
|
|
944
|
+
if (key === "key")
|
|
937
945
|
continue;
|
|
938
946
|
if (!(key in next) || next[key] == null) {
|
|
939
|
-
if (
|
|
940
|
-
this.unbindEvent(
|
|
947
|
+
if (isEventProp(key)) {
|
|
948
|
+
this.unbindEvent(element, key);
|
|
941
949
|
} else {
|
|
942
950
|
this.removeAttribute(element, key);
|
|
943
951
|
}
|
|
@@ -945,54 +953,39 @@ class HtmlRender {
|
|
|
945
953
|
}
|
|
946
954
|
for (const key in next) {
|
|
947
955
|
const value = next[key];
|
|
948
|
-
if (
|
|
956
|
+
if (key === "key" || value == null)
|
|
949
957
|
continue;
|
|
950
958
|
if (prev[key] === value)
|
|
951
959
|
continue;
|
|
952
|
-
if (
|
|
953
|
-
|
|
954
|
-
|
|
960
|
+
if (isEventProp(key)) {
|
|
961
|
+
const creoName = key.slice(2);
|
|
962
|
+
const domEvent = DOM_EVENT[creoName] ?? creoName.toLowerCase();
|
|
963
|
+
const evObj = element[$EV];
|
|
964
|
+
if (evObj) {
|
|
965
|
+
evObj[domEvent] = value;
|
|
966
|
+
} else {
|
|
967
|
+
this.bindEvent(element, key, value);
|
|
968
|
+
}
|
|
955
969
|
} else {
|
|
956
970
|
this.setAttribute(element, key, value);
|
|
957
971
|
}
|
|
958
972
|
}
|
|
959
973
|
}
|
|
960
|
-
bindEvent(
|
|
961
|
-
const
|
|
962
|
-
const domEvent = DOM_EVENT[
|
|
963
|
-
const
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
}
|
|
974
|
-
unbindEvent(ref, element, prop) {
|
|
975
|
-
const wrapped = ref.listeners?.get(prop);
|
|
976
|
-
if (!wrapped)
|
|
977
|
-
return;
|
|
978
|
-
const creoEvent = this.eventPropToCreoName(prop);
|
|
979
|
-
const domEvent = DOM_EVENT[creoEvent] ?? creoEvent.toLowerCase();
|
|
980
|
-
element.removeEventListener(domEvent, wrapped);
|
|
981
|
-
ref.listeners.delete(prop);
|
|
982
|
-
}
|
|
983
|
-
mapEventData(creoEvent, e) {
|
|
984
|
-
if (creoEvent === "click" || creoEvent === "dblclick" || creoEvent.startsWith("pointer")) {
|
|
985
|
-
const pe = e;
|
|
986
|
-
return { x: pe.clientX, y: pe.clientY };
|
|
987
|
-
}
|
|
988
|
-
if (creoEvent === "input" || creoEvent === "change") {
|
|
989
|
-
return { value: e.target.value };
|
|
990
|
-
}
|
|
991
|
-
if (creoEvent.startsWith("key")) {
|
|
992
|
-
const ke = e;
|
|
993
|
-
return { key: ke.key, code: ke.code };
|
|
974
|
+
bindEvent(element, prop, handler) {
|
|
975
|
+
const creoName = prop.slice(2);
|
|
976
|
+
const domEvent = DOM_EVENT[creoName] ?? creoName.toLowerCase();
|
|
977
|
+
const evObj = element[$EV] ?? (element[$EV] = {});
|
|
978
|
+
evObj[domEvent] = handler;
|
|
979
|
+
ensureDelegated(this.container, domEvent);
|
|
980
|
+
}
|
|
981
|
+
unbindEvent(element, prop) {
|
|
982
|
+
const creoName = prop.slice(2);
|
|
983
|
+
const domEvent = DOM_EVENT[creoName] ?? creoName.toLowerCase();
|
|
984
|
+
const evObj = element[$EV];
|
|
985
|
+
if (evObj) {
|
|
986
|
+
delete evObj[domEvent];
|
|
994
987
|
}
|
|
995
|
-
|
|
988
|
+
removeDelegated(this.container, domEvent);
|
|
996
989
|
}
|
|
997
990
|
setAttribute(element, key, value) {
|
|
998
991
|
if (key === "class") {
|
|
@@ -1002,11 +995,10 @@ class HtmlRender {
|
|
|
1002
995
|
} else if (DOM_PROPERTIES.has(key)) {
|
|
1003
996
|
element[key] = value;
|
|
1004
997
|
} else if (typeof value === "boolean") {
|
|
1005
|
-
if (value)
|
|
998
|
+
if (value)
|
|
1006
999
|
element.setAttribute(key, "");
|
|
1007
|
-
|
|
1000
|
+
else
|
|
1008
1001
|
element.removeAttribute(key);
|
|
1009
|
-
}
|
|
1010
1002
|
} else {
|
|
1011
1003
|
element.setAttribute(key, String(value));
|
|
1012
1004
|
}
|
|
@@ -1022,94 +1014,52 @@ class HtmlRender {
|
|
|
1022
1014
|
element.removeAttribute(key);
|
|
1023
1015
|
}
|
|
1024
1016
|
}
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
if (vdom && vdom.length > 0) {
|
|
1028
|
-
const node = vdom.getNode(view2);
|
|
1029
|
-
if (node) {
|
|
1030
|
-
if (node.isLast() || parent.quickRerender) {
|
|
1031
|
-
const ref2 = parent.renderRef;
|
|
1032
|
-
if (ref2?.kind === "composite") {
|
|
1033
|
-
return ref2.endComment;
|
|
1034
|
-
} else {
|
|
1035
|
-
return null;
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
const prev = node.getPrev();
|
|
1039
|
-
if (prev) {
|
|
1040
|
-
const prevRef = prev.v.renderRef;
|
|
1041
|
-
if (prevRef) {
|
|
1042
|
-
return (prevRef.kind === "composite" ? prevRef.endComment : prevRef.element).nextSibling;
|
|
1043
|
-
}
|
|
1044
|
-
let cur = prev.getPrev();
|
|
1045
|
-
while (cur) {
|
|
1046
|
-
const curRef = cur.v.renderRef;
|
|
1047
|
-
if (curRef) {
|
|
1048
|
-
return (curRef.kind === "composite" ? curRef.endComment : curRef.element).nextSibling;
|
|
1049
|
-
}
|
|
1050
|
-
cur = cur.getPrev();
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
const ref = parent.renderRef;
|
|
1056
|
-
if (!ref)
|
|
1057
|
-
return null;
|
|
1058
|
-
return ref.kind === "composite" ? ref.endComment : ref.element.firstChild;
|
|
1059
|
-
}
|
|
1060
|
-
getParentDomNode(parent) {
|
|
1061
|
-
const ref = parent.renderRef;
|
|
1062
|
-
if (!ref)
|
|
1017
|
+
getFirstDomNode(view2) {
|
|
1018
|
+
if (!view2.renderRef)
|
|
1063
1019
|
return null;
|
|
1064
|
-
if (
|
|
1065
|
-
return
|
|
1020
|
+
if (view2.flags & F_PRIMITIVE)
|
|
1021
|
+
return view2.renderRef.element;
|
|
1022
|
+
if (view2.children) {
|
|
1023
|
+
for (const child of view2.children) {
|
|
1024
|
+
const dom = this.getFirstDomNode(child);
|
|
1025
|
+
if (dom)
|
|
1026
|
+
return dom;
|
|
1027
|
+
}
|
|
1066
1028
|
}
|
|
1067
|
-
return
|
|
1029
|
+
return null;
|
|
1068
1030
|
}
|
|
1069
1031
|
moveDomNodes(view2, parentNode, insertBefore) {
|
|
1070
|
-
|
|
1071
|
-
if (!ref)
|
|
1032
|
+
if (!view2.renderRef)
|
|
1072
1033
|
return;
|
|
1073
|
-
if (
|
|
1074
|
-
parentNode.insertBefore(
|
|
1075
|
-
} else {
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
this.moveDomNodes(child, parentNode, insertBefore);
|
|
1079
|
-
}
|
|
1034
|
+
if (view2.flags & F_PRIMITIVE) {
|
|
1035
|
+
parentNode.insertBefore(view2.renderRef.element, insertBefore);
|
|
1036
|
+
} else if (view2.children) {
|
|
1037
|
+
for (const child of view2.children) {
|
|
1038
|
+
this.moveDomNodes(child, parentNode, insertBefore);
|
|
1080
1039
|
}
|
|
1081
|
-
parentNode.insertBefore(ref.endComment, insertBefore);
|
|
1082
1040
|
}
|
|
1083
1041
|
}
|
|
1084
|
-
getFirstDomNode(view2) {
|
|
1085
|
-
const ref = view2.renderRef;
|
|
1086
|
-
if (!ref)
|
|
1087
|
-
return null;
|
|
1088
|
-
if (ref.kind === "primitive")
|
|
1089
|
-
return ref.element;
|
|
1090
|
-
if (view2.virtualDom) {
|
|
1091
|
-
for (const child of view2.virtualDom) {
|
|
1092
|
-
const node = this.getFirstDomNode(child);
|
|
1093
|
-
if (node)
|
|
1094
|
-
return node;
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
return ref.endComment;
|
|
1098
|
-
}
|
|
1099
1042
|
removeDomNodes(view2) {
|
|
1100
1043
|
const ref = view2.renderRef;
|
|
1101
|
-
if (!ref)
|
|
1102
|
-
return;
|
|
1103
|
-
if (ref.kind === "primitive") {
|
|
1104
|
-
ref.element.parentNode?.removeChild(ref.element);
|
|
1044
|
+
if (!ref || !(view2.flags & F_PRIMITIVE))
|
|
1105
1045
|
return;
|
|
1046
|
+
const evObj = ref.element[$EV];
|
|
1047
|
+
if (evObj) {
|
|
1048
|
+
for (const domEvent in evObj) {
|
|
1049
|
+
removeDelegated(this.container, domEvent);
|
|
1050
|
+
}
|
|
1051
|
+
delete ref.element[$EV];
|
|
1106
1052
|
}
|
|
1107
|
-
ref.
|
|
1053
|
+
ref.element.parentNode?.removeChild(ref.element);
|
|
1108
1054
|
}
|
|
1109
1055
|
}
|
|
1110
1056
|
// src/render/json_render.ts
|
|
1111
1057
|
class JsonRender {
|
|
1112
1058
|
root;
|
|
1059
|
+
engine;
|
|
1060
|
+
constructor() {
|
|
1061
|
+
this.root = null;
|
|
1062
|
+
}
|
|
1113
1063
|
render(view2) {
|
|
1114
1064
|
const existing = view2.renderRef;
|
|
1115
1065
|
if (!existing) {
|
|
@@ -1118,23 +1068,25 @@ class JsonRender {
|
|
|
1118
1068
|
this.root = node;
|
|
1119
1069
|
return;
|
|
1120
1070
|
}
|
|
1121
|
-
const
|
|
1122
|
-
if (
|
|
1123
|
-
|
|
1071
|
+
const parentNode = view2.parent.renderRef;
|
|
1072
|
+
if (parentNode) {
|
|
1073
|
+
const idx = view2.parent.children ? view2.parent.children.indexOf(view2) : -1;
|
|
1074
|
+
if (idx >= 0 && idx < parentNode.children.length) {
|
|
1075
|
+
parentNode.children.splice(idx, 0, node);
|
|
1076
|
+
} else {
|
|
1077
|
+
parentNode.children.push(node);
|
|
1078
|
+
}
|
|
1124
1079
|
}
|
|
1125
1080
|
return;
|
|
1126
1081
|
}
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
const
|
|
1132
|
-
|
|
1133
|
-
const expectedIdx = nextNode ? parentNode.children.indexOf(nextNode) : parentNode.children.length;
|
|
1134
|
-
if (oldIdx !== expectedIdx && oldIdx !== expectedIdx - 1) {
|
|
1082
|
+
if (view2.parent) {
|
|
1083
|
+
const parentNode = view2.parent.renderRef;
|
|
1084
|
+
if (parentNode) {
|
|
1085
|
+
const oldIdx = parentNode.children.indexOf(existing);
|
|
1086
|
+
const targetIdx = view2.parent.children ? view2.parent.children.indexOf(view2) : -1;
|
|
1087
|
+
if (oldIdx !== -1 && targetIdx !== -1 && oldIdx !== targetIdx) {
|
|
1135
1088
|
parentNode.children.splice(oldIdx, 1);
|
|
1136
|
-
|
|
1137
|
-
parentNode.children.splice(insertIdx, 0, existing);
|
|
1089
|
+
parentNode.children.splice(Math.min(targetIdx, parentNode.children.length), 0, existing);
|
|
1138
1090
|
}
|
|
1139
1091
|
}
|
|
1140
1092
|
}
|
|
@@ -1143,16 +1095,15 @@ class JsonRender {
|
|
|
1143
1095
|
}
|
|
1144
1096
|
unmount(view2) {
|
|
1145
1097
|
const childNode = view2.renderRef;
|
|
1146
|
-
|
|
1147
|
-
|
|
1098
|
+
if (!childNode || !view2.parent)
|
|
1099
|
+
return;
|
|
1100
|
+
const parentNode = view2.parent.renderRef;
|
|
1101
|
+
if (parentNode) {
|
|
1148
1102
|
const idx = parentNode.children.indexOf(childNode);
|
|
1149
1103
|
if (idx !== -1)
|
|
1150
1104
|
parentNode.children.splice(idx, 1);
|
|
1151
1105
|
}
|
|
1152
1106
|
}
|
|
1153
|
-
getNextSibling(view2) {
|
|
1154
|
-
return view2.parent?.virtualDom?.getNode(view2)?.getNext()?.v;
|
|
1155
|
-
}
|
|
1156
1107
|
buildNode(view2) {
|
|
1157
1108
|
const tag = view2.viewFn[$primitive];
|
|
1158
1109
|
const props = tag === "text" ? { content: view2.props } : { ...view2.props };
|
|
@@ -1180,52 +1131,84 @@ var VOID_TAGS = new Set([
|
|
|
1180
1131
|
"col",
|
|
1181
1132
|
"wbr"
|
|
1182
1133
|
]);
|
|
1134
|
+
var DOM_PROPERTIES2 = new Set(["value", "checked", "selected", "indeterminate"]);
|
|
1135
|
+
function isEventProp2(key) {
|
|
1136
|
+
return key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && key.charCodeAt(2) >= 65 && key.charCodeAt(2) <= 90;
|
|
1137
|
+
}
|
|
1138
|
+
function escapeHtml(str) {
|
|
1139
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1140
|
+
}
|
|
1141
|
+
function escapeAttr(str) {
|
|
1142
|
+
return str.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
1143
|
+
}
|
|
1183
1144
|
|
|
1184
|
-
class
|
|
1185
|
-
|
|
1145
|
+
class HtmlStringRender {
|
|
1146
|
+
root = null;
|
|
1147
|
+
engine;
|
|
1186
1148
|
render(view2) {
|
|
1187
1149
|
if (!view2.parent) {
|
|
1188
|
-
this.
|
|
1150
|
+
this.root = view2;
|
|
1189
1151
|
}
|
|
1190
1152
|
}
|
|
1191
1153
|
unmount(_view) {}
|
|
1192
1154
|
renderToString() {
|
|
1193
|
-
if (!this.
|
|
1155
|
+
if (!this.root)
|
|
1194
1156
|
return "";
|
|
1195
|
-
return this.buildString(this.
|
|
1157
|
+
return this.buildString(this.root);
|
|
1196
1158
|
}
|
|
1197
|
-
buildString(
|
|
1198
|
-
const tag =
|
|
1159
|
+
buildString(rec) {
|
|
1160
|
+
const tag = rec.viewFn[$primitive];
|
|
1199
1161
|
if (tag != null) {
|
|
1200
1162
|
if (tag === "text") {
|
|
1201
|
-
return
|
|
1163
|
+
return escapeHtml(String(rec.props));
|
|
1202
1164
|
}
|
|
1165
|
+
const attrs = this.buildAttrs(rec.props);
|
|
1203
1166
|
if (VOID_TAGS.has(tag)) {
|
|
1204
|
-
return
|
|
1167
|
+
return `<${tag}${attrs}>`;
|
|
1205
1168
|
}
|
|
1206
|
-
return `<${tag}>${this.buildChildren(
|
|
1169
|
+
return `<${tag}${attrs}>${this.buildChildren(rec)}</${tag}>`;
|
|
1207
1170
|
}
|
|
1208
|
-
return this.buildChildren(
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
const props = view2.props;
|
|
1212
|
-
let attrs = "";
|
|
1213
|
-
if (props.src)
|
|
1214
|
-
attrs += ` src="${props.src}"`;
|
|
1215
|
-
if (props.alt)
|
|
1216
|
-
attrs += ` alt="${props.alt}"`;
|
|
1217
|
-
return `<${tag}${attrs} />`;
|
|
1218
|
-
}
|
|
1219
|
-
buildChildren(view2) {
|
|
1171
|
+
return this.buildChildren(rec);
|
|
1172
|
+
}
|
|
1173
|
+
buildAttrs(props) {
|
|
1220
1174
|
let result = "";
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1175
|
+
for (const key in props) {
|
|
1176
|
+
const value = props[key];
|
|
1177
|
+
if (key === "key" || value == null)
|
|
1178
|
+
continue;
|
|
1179
|
+
if (isEventProp2(key))
|
|
1180
|
+
continue;
|
|
1181
|
+
if (DOM_PROPERTIES2.has(key))
|
|
1182
|
+
continue;
|
|
1183
|
+
if (key === "style") {
|
|
1184
|
+
let css = String(value);
|
|
1185
|
+
css = css.trim();
|
|
1186
|
+
if (css && !css.endsWith(";"))
|
|
1187
|
+
css += ";";
|
|
1188
|
+
result += ` style="${escapeAttr(css)}"`;
|
|
1189
|
+
continue;
|
|
1190
|
+
}
|
|
1191
|
+
if (typeof value === "boolean") {
|
|
1192
|
+
if (value)
|
|
1193
|
+
result += ` ${key}=""`;
|
|
1194
|
+
} else {
|
|
1195
|
+
const attrName = key === "class" ? "class" : key;
|
|
1196
|
+
result += ` ${attrName}="${escapeAttr(String(value))}"`;
|
|
1224
1197
|
}
|
|
1225
1198
|
}
|
|
1226
1199
|
return result;
|
|
1227
1200
|
}
|
|
1201
|
+
buildChildren(rec) {
|
|
1202
|
+
if (!rec.children)
|
|
1203
|
+
return "";
|
|
1204
|
+
let result = "";
|
|
1205
|
+
for (const child of rec.children) {
|
|
1206
|
+
result += this.buildString(child);
|
|
1207
|
+
}
|
|
1208
|
+
return result;
|
|
1209
|
+
}
|
|
1228
1210
|
}
|
|
1211
|
+
var StringRender = HtmlStringRender;
|
|
1229
1212
|
// src/functional/maybe.ts
|
|
1230
1213
|
function just(maybe, errorMessage) {
|
|
1231
1214
|
if (maybe == null) {
|
|
@@ -1365,9 +1348,10 @@ export {
|
|
|
1365
1348
|
Store,
|
|
1366
1349
|
State,
|
|
1367
1350
|
JsonRender,
|
|
1351
|
+
HtmlStringRender,
|
|
1368
1352
|
HtmlRender,
|
|
1369
1353
|
Engine,
|
|
1370
1354
|
$primitive
|
|
1371
1355
|
};
|
|
1372
1356
|
|
|
1373
|
-
//# debugId=
|
|
1357
|
+
//# debugId=3138F663141CF77564756E2164756E21
|