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