tutuca 0.13.3 → 0.15.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/dist/tutuca-cli.js +1079 -1254
- package/dist/tutuca-dev.ext.js +1086 -1216
- package/dist/tutuca-dev.js +1095 -1225
- package/dist/tutuca-dev.min.js +4 -4
- package/dist/tutuca-extra.ext.js +1055 -1133
- package/dist/tutuca-extra.js +1061 -1139
- package/dist/tutuca-extra.min.js +3 -3
- package/dist/tutuca.ext.js +1054 -1116
- package/dist/tutuca.js +1060 -1122
- package/dist/tutuca.min.js +3 -3
- package/package.json +1 -1
- package/skill/tutuca/SKILL.md +2 -2
- package/skill/tutuca/advanced.md +48 -17
- package/skill/tutuca/component-design.md +2 -2
- package/skill/tutuca/core.md +5 -4
- package/skill/tutuca/messages-and-intents.md +9 -11
- package/skill/tutuca/patterns/README.md +1 -1
- package/skill/tutuca/patterns/edit-through-a-dynamic-target.md +11 -6
- package/skill/tutuca/patterns/share-state-across-the-tree.md +18 -3
- package/skill/tutuca/semantics.md +63 -39
- package/skill/tutuca-source/tutuca.ext.js +1054 -1116
package/dist/tutuca.ext.js
CHANGED
|
@@ -51,12 +51,6 @@ enableMapSet();
|
|
|
51
51
|
|
|
52
52
|
// src/path.js
|
|
53
53
|
var NONE = /* @__PURE__ */ Symbol("NONE");
|
|
54
|
-
var readKey = (value, key, dval = null) => {
|
|
55
|
-
if (value == null) return dval;
|
|
56
|
-
if (value instanceof Map) return value.has(key) ? value.get(key) : dval;
|
|
57
|
-
if (value instanceof Set) return value.has(key) ? key : dval;
|
|
58
|
-
return Object.hasOwn(value, key) ? value[key] : dval;
|
|
59
|
-
};
|
|
60
54
|
var writeKey = (value, key, next) => {
|
|
61
55
|
if (value instanceof Map) value.set(key, next);
|
|
62
56
|
else if (value instanceof Set) {
|
|
@@ -64,20 +58,15 @@ var writeKey = (value, key, next) => {
|
|
|
64
58
|
value.add(next);
|
|
65
59
|
} else value[key] = next;
|
|
66
60
|
};
|
|
67
|
-
var writeSeqKey = (value, key, next) =>
|
|
68
|
-
if (value instanceof Map || value instanceof Set || Array.isArray(value) || Object.hasOwn(value, key))
|
|
69
|
-
writeKey(value, key, next);
|
|
70
|
-
else if (typeof value?.set === "function") value.set(key, next);
|
|
71
|
-
else writeKey(value, key, next);
|
|
72
|
-
};
|
|
61
|
+
var writeSeqKey = (value, key, next) => !(value instanceof Map || value instanceof Set || Array.isArray(value)) && !Object.hasOwn(value, key) && typeof value?.set === "function" ? value.set(key, next) : writeKey(value, key, next);
|
|
73
62
|
var Step = class {
|
|
74
|
-
lookup(_v, dval = null) {
|
|
75
|
-
return dval;
|
|
76
|
-
}
|
|
77
63
|
setDraftValue(_root, _v) {
|
|
78
64
|
}
|
|
79
|
-
|
|
80
|
-
|
|
65
|
+
// Re-enter this step while rebuilding a stack. `renderPath` is the dispatch
|
|
66
|
+
// position AFTER this step: a rebuilt frame must carry the same render path
|
|
67
|
+
// the renderer had there, or the provides it publishes would be located wrong.
|
|
68
|
+
enterFrame(stack, next, renderPath) {
|
|
69
|
+
return stack.enter(next, {}, true, renderPath);
|
|
81
70
|
}
|
|
82
71
|
toAbstractPathStep() {
|
|
83
72
|
return this;
|
|
@@ -94,41 +83,29 @@ var Step = class {
|
|
|
94
83
|
return null;
|
|
95
84
|
}
|
|
96
85
|
};
|
|
97
|
-
var BindStep = class
|
|
86
|
+
var BindStep = class extends Step {
|
|
98
87
|
constructor(binds) {
|
|
99
88
|
super();
|
|
100
89
|
this.binds = binds;
|
|
101
90
|
}
|
|
102
|
-
lookup(v
|
|
91
|
+
lookup(v) {
|
|
103
92
|
return v;
|
|
104
93
|
}
|
|
105
|
-
enterFrame(stack, next) {
|
|
106
|
-
return stack.enter(next, { ...this.binds }, false);
|
|
107
|
-
}
|
|
108
|
-
withIndex(i) {
|
|
109
|
-
return new _BindStep({ ...this.binds, key: i });
|
|
110
|
-
}
|
|
111
|
-
withKey(key) {
|
|
112
|
-
return new _BindStep({ ...this.binds, key });
|
|
94
|
+
enterFrame(stack, next, renderPath) {
|
|
95
|
+
return stack.enter(next, { ...this.binds }, false, renderPath);
|
|
113
96
|
}
|
|
114
97
|
toAbstractPathStep() {
|
|
115
98
|
return null;
|
|
116
99
|
}
|
|
117
100
|
};
|
|
118
|
-
var ScopeBindStep = class
|
|
101
|
+
var ScopeBindStep = class extends BindStep {
|
|
119
102
|
constructor(val, binds = {}) {
|
|
120
103
|
super(binds);
|
|
121
104
|
this.val = val;
|
|
122
105
|
}
|
|
123
|
-
enterFrame(stack, next) {
|
|
106
|
+
enterFrame(stack, next, renderPath) {
|
|
124
107
|
const dyn = this.val.evalAsHandler(stack)?.call(stack.it) ?? {};
|
|
125
|
-
return stack.enter(next, { ...this.binds, ...dyn }, false);
|
|
126
|
-
}
|
|
127
|
-
withIndex(i) {
|
|
128
|
-
return new _ScopeBindStep(this.val, { ...this.binds, key: i });
|
|
129
|
-
}
|
|
130
|
-
withKey(key) {
|
|
131
|
-
return new _ScopeBindStep(this.val, { ...this.binds, key });
|
|
108
|
+
return stack.enter(next, { ...this.binds, ...dyn }, false, renderPath);
|
|
132
109
|
}
|
|
133
110
|
};
|
|
134
111
|
var FieldStep = class extends Step {
|
|
@@ -136,18 +113,12 @@ var FieldStep = class extends Step {
|
|
|
136
113
|
super();
|
|
137
114
|
this.field = field;
|
|
138
115
|
}
|
|
139
|
-
lookup(v
|
|
140
|
-
return
|
|
116
|
+
lookup(v) {
|
|
117
|
+
return seqGet(v, this.field, NONE);
|
|
141
118
|
}
|
|
142
119
|
setDraftValue(root, v) {
|
|
143
120
|
writeKey(root, this.field, v);
|
|
144
121
|
}
|
|
145
|
-
withIndex(i) {
|
|
146
|
-
return new SeqStep(this.field, i);
|
|
147
|
-
}
|
|
148
|
-
withKey(k) {
|
|
149
|
-
return new SeqStep(this.field, k);
|
|
150
|
-
}
|
|
151
122
|
toKey() {
|
|
152
123
|
return { field: this.field };
|
|
153
124
|
}
|
|
@@ -158,15 +129,15 @@ var SeqStep = class extends Step {
|
|
|
158
129
|
this.field = field;
|
|
159
130
|
this.key = key;
|
|
160
131
|
}
|
|
161
|
-
lookup(v
|
|
162
|
-
return seqGet(
|
|
132
|
+
lookup(v) {
|
|
133
|
+
return seqGet(seqGet(v, this.field, null), this.key, NONE);
|
|
163
134
|
}
|
|
164
135
|
setDraftValue(root, v) {
|
|
165
|
-
const seq =
|
|
136
|
+
const seq = seqGet(root, this.field, null);
|
|
166
137
|
if (seq != null) writeSeqKey(seq, this.key, v);
|
|
167
138
|
}
|
|
168
|
-
enterFrame(stack, next) {
|
|
169
|
-
return stack.enter(next, { key: this.key }, true);
|
|
139
|
+
enterFrame(stack, next, renderPath) {
|
|
140
|
+
return stack.enter(next, { key: this.key }, true, renderPath);
|
|
170
141
|
}
|
|
171
142
|
toKey() {
|
|
172
143
|
return { field: this.field, key: this.key };
|
|
@@ -178,20 +149,20 @@ var SeqAccessStep = class extends Step {
|
|
|
178
149
|
this.seqField = seqField;
|
|
179
150
|
this.keyField = keyField;
|
|
180
151
|
}
|
|
181
|
-
lookup(v
|
|
182
|
-
const seq =
|
|
183
|
-
const key =
|
|
184
|
-
return key !== NONE && seq !== NONE ? seqGet(seq, key,
|
|
152
|
+
lookup(v) {
|
|
153
|
+
const seq = seqGet(v, this.seqField, NONE);
|
|
154
|
+
const key = seqGet(v, this.keyField, NONE);
|
|
155
|
+
return key !== NONE && seq !== NONE ? seqGet(seq, key, NONE) : NONE;
|
|
185
156
|
}
|
|
186
157
|
setDraftValue(root, v) {
|
|
187
|
-
const seq =
|
|
188
|
-
const key =
|
|
158
|
+
const seq = seqGet(root, this.seqField, NONE);
|
|
159
|
+
const key = seqGet(root, this.keyField, NONE);
|
|
189
160
|
if (seq !== NONE && key !== NONE) writeSeqKey(seq, key, v);
|
|
190
161
|
}
|
|
191
162
|
// Resolve `keyField` against `v` now and freeze it as a literal-key `SeqStep`, so a
|
|
192
163
|
// later lookup/setValue lands on this same item even if `keyField` changes meanwhile.
|
|
193
164
|
pinKey(v) {
|
|
194
|
-
const key =
|
|
165
|
+
const key = seqGet(v, this.keyField, NONE);
|
|
195
166
|
return key === NONE ? this : new SeqStep(this.seqField, key);
|
|
196
167
|
}
|
|
197
168
|
// The key is a *field reference* resolved live, so it is unknown without a value;
|
|
@@ -202,70 +173,62 @@ var SeqAccessStep = class extends Step {
|
|
|
202
173
|
}
|
|
203
174
|
};
|
|
204
175
|
var EachBindStep = class extends Step {
|
|
205
|
-
constructor(
|
|
176
|
+
constructor(eachNode, key) {
|
|
206
177
|
super();
|
|
207
|
-
this.
|
|
178
|
+
this.eachNode = eachNode;
|
|
208
179
|
this.key = key;
|
|
209
180
|
}
|
|
210
|
-
lookup(v
|
|
181
|
+
lookup(v) {
|
|
211
182
|
return v;
|
|
212
183
|
}
|
|
213
184
|
// Replay the renderer's per-item binds (key, value + any @enrich-with binds)
|
|
214
185
|
// so a rebuilt stack matches the one @each rendered with.
|
|
215
|
-
enterFrame(stack, next) {
|
|
216
|
-
return stack.enter(next, this.
|
|
186
|
+
enterFrame(stack, next, renderPath) {
|
|
187
|
+
return stack.enter(next, this.eachNode.enrichBinds(stack, this.key), false, renderPath);
|
|
217
188
|
}
|
|
218
189
|
toAbstractPathStep() {
|
|
219
190
|
return null;
|
|
220
191
|
}
|
|
221
192
|
};
|
|
222
193
|
var EachRenderItStep = class extends SeqStep {
|
|
223
|
-
enterFrame(stack, next) {
|
|
224
|
-
return stack.enter(next, { key: this.key, value: next }, false).enter(next, {}, true);
|
|
194
|
+
enterFrame(stack, next, renderPath) {
|
|
195
|
+
return stack.enter(next, { key: this.key, value: next }, false, renderPath).enter(next, {}, true, renderPath);
|
|
225
196
|
}
|
|
226
197
|
toAbstractPathStep() {
|
|
227
198
|
return new SeqStep(this.field, this.key);
|
|
228
199
|
}
|
|
229
200
|
};
|
|
230
|
-
function
|
|
231
|
-
|
|
201
|
+
function stepToJson(step) {
|
|
202
|
+
if (step instanceof SeqStep) return { f: step.field, k: step.key };
|
|
203
|
+
if (step instanceof FieldStep) return { f: step.field };
|
|
204
|
+
if (step instanceof SeqAccessStep) return { f: step.seqField, a: step.keyField };
|
|
205
|
+
return null;
|
|
232
206
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
warnRawDynStep("lookup", this);
|
|
246
|
-
return dval;
|
|
247
|
-
}
|
|
248
|
-
enterFrame(stack, _next) {
|
|
249
|
-
warnRawDynStep("enterFrame", this);
|
|
250
|
-
return stack;
|
|
251
|
-
}
|
|
252
|
-
};
|
|
253
|
-
var DynEachStep = class extends DynStep {
|
|
254
|
-
constructor(producerCompId, producerSteps, key) {
|
|
255
|
-
super(producerCompId, producerSteps);
|
|
256
|
-
this.key = key;
|
|
257
|
-
}
|
|
258
|
-
teleportSteps() {
|
|
259
|
-
const { producerSteps, key } = this;
|
|
260
|
-
if (producerSteps.length === 0) return producerSteps;
|
|
261
|
-
const last = producerSteps[producerSteps.length - 1];
|
|
262
|
-
if (!(last instanceof FieldStep)) {
|
|
263
|
-
console.warn("DynEachStep: seq-access dynamic cannot be iterated", this);
|
|
264
|
-
return producerSteps;
|
|
265
|
-
}
|
|
266
|
-
return producerSteps.slice(0, -1).concat(new SeqStep(last.field, key));
|
|
207
|
+
function stepFromJson(j) {
|
|
208
|
+
if (j == null || typeof j.f !== "string") return null;
|
|
209
|
+
if (j.k !== void 0) return new SeqStep(j.f, j.k);
|
|
210
|
+
if (j.a !== void 0) return new SeqAccessStep(j.f, j.a);
|
|
211
|
+
return new FieldStep(j.f);
|
|
212
|
+
}
|
|
213
|
+
function pathToJson(path2) {
|
|
214
|
+
const out = [];
|
|
215
|
+
for (const step of path2.steps) {
|
|
216
|
+
const j = stepToJson(step);
|
|
217
|
+
if (j === null) return null;
|
|
218
|
+
out.push(j);
|
|
267
219
|
}
|
|
268
|
-
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
222
|
+
function pathFromJson(j) {
|
|
223
|
+
if (!Array.isArray(j)) return null;
|
|
224
|
+
const steps = [];
|
|
225
|
+
for (const item of j) {
|
|
226
|
+
const step = stepFromJson(item);
|
|
227
|
+
if (step === null) return null;
|
|
228
|
+
steps.push(step);
|
|
229
|
+
}
|
|
230
|
+
return new Path(steps);
|
|
231
|
+
}
|
|
269
232
|
var Path = class _Path {
|
|
270
233
|
constructor(steps = []) {
|
|
271
234
|
this.steps = steps;
|
|
@@ -273,43 +236,13 @@ var Path = class _Path {
|
|
|
273
236
|
concat(steps) {
|
|
274
237
|
return new _Path(this.steps.concat(steps));
|
|
275
238
|
}
|
|
276
|
-
popStep
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
// The dispatch path: frame-only steps removed, one step per crossed component
|
|
280
|
-
// (DynStep included). `popStep` over it bubbles through every component.
|
|
239
|
+
// Frame-only steps removed, one step per crossed component: `popStep` over the
|
|
240
|
+
// result bubbles through every component.
|
|
281
241
|
compact() {
|
|
282
242
|
const out = [];
|
|
283
243
|
for (const step of this.steps) {
|
|
284
244
|
const s = step.toAbstractPathStep();
|
|
285
|
-
if (s !== null)
|
|
286
|
-
if (s !== step) s._originCid = step._originCid;
|
|
287
|
-
out.push(s);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
return new _Path(out);
|
|
291
|
-
}
|
|
292
|
-
// The abstract path used to apply a transaction: every DynStep is teleported —
|
|
293
|
-
// the steps interior to its producer..consumer span are dropped and the
|
|
294
|
-
// producer's own path spliced in — so a mutation lands on the data's real
|
|
295
|
-
// location. A path with no DynStep is returned unchanged.
|
|
296
|
-
toTransactionPath() {
|
|
297
|
-
let hasDyn = false;
|
|
298
|
-
for (const step of this.steps)
|
|
299
|
-
if (step instanceof DynStep) {
|
|
300
|
-
hasDyn = true;
|
|
301
|
-
break;
|
|
302
|
-
}
|
|
303
|
-
if (!hasDyn) return this;
|
|
304
|
-
const out = [];
|
|
305
|
-
for (const step of this.steps) {
|
|
306
|
-
if (step instanceof DynStep) {
|
|
307
|
-
while (out.length > 0 && step.interiorCids.has(out[out.length - 1]._originCid)) out.pop();
|
|
308
|
-
for (const ts of step.teleportSteps()) {
|
|
309
|
-
ts._originCid = step.producerCompId;
|
|
310
|
-
out.push(ts);
|
|
311
|
-
}
|
|
312
|
-
} else out.push(step);
|
|
245
|
+
if (s !== null) out.push(s);
|
|
313
246
|
}
|
|
314
247
|
return new _Path(out);
|
|
315
248
|
}
|
|
@@ -317,7 +250,6 @@ var Path = class _Path {
|
|
|
317
250
|
// key as it is *now* so a later lookup/setValue lands on the same item even if the
|
|
318
251
|
// keyField changed meanwhile (e.g. the selected tab moved while an intent was in
|
|
319
252
|
// flight). Returns a new Path with those steps replaced; `this` if nothing pinned.
|
|
320
|
-
// Must be called on a transaction path (no DynSteps — call toTransactionPath first).
|
|
321
253
|
pinKeys(root) {
|
|
322
254
|
let curVal = root;
|
|
323
255
|
let out = null;
|
|
@@ -325,7 +257,7 @@ var Path = class _Path {
|
|
|
325
257
|
const step = this.steps[i];
|
|
326
258
|
const pinned = step.pinKey(curVal);
|
|
327
259
|
if (pinned !== step) (out ??= this.steps.slice())[i] = pinned;
|
|
328
|
-
curVal = step.lookup(curVal
|
|
260
|
+
curVal = step.lookup(curVal);
|
|
329
261
|
if (curVal === NONE) break;
|
|
330
262
|
}
|
|
331
263
|
return out ? new _Path(out) : this;
|
|
@@ -333,20 +265,20 @@ var Path = class _Path {
|
|
|
333
265
|
lookup(v, dval = null) {
|
|
334
266
|
let curVal = v;
|
|
335
267
|
for (const step of this.steps) {
|
|
336
|
-
curVal = step.lookup(curVal
|
|
268
|
+
curVal = step.lookup(curVal);
|
|
337
269
|
if (curVal === NONE) return dval;
|
|
338
270
|
}
|
|
339
271
|
return curVal;
|
|
340
272
|
}
|
|
341
273
|
// The values entered along the path, root→leaf (root included): index 0 is `root`,
|
|
342
274
|
// the last entry is the leaf this path resolves to. Stops early at the first
|
|
343
|
-
// unresolvable step.
|
|
344
|
-
//
|
|
275
|
+
// unresolvable step. Used to walk the component instances on a dispatch path
|
|
276
|
+
// (filter via Components.getCompFor).
|
|
345
277
|
resolveChain(root) {
|
|
346
278
|
const out = [root];
|
|
347
279
|
let curVal = root;
|
|
348
280
|
for (const step of this.steps) {
|
|
349
|
-
curVal = step.lookup(curVal
|
|
281
|
+
curVal = step.lookup(curVal);
|
|
350
282
|
if (curVal === NONE) break;
|
|
351
283
|
out.push(curVal);
|
|
352
284
|
}
|
|
@@ -355,7 +287,7 @@ var Path = class _Path {
|
|
|
355
287
|
// A flat `[{ field, key? }]` list of the addressing steps, skipping frame-only
|
|
356
288
|
// steps (binds). Generic path introspection so tooling (e.g. the storybook
|
|
357
289
|
// activity log) can identify which subtree a transaction touched without
|
|
358
|
-
// depending on Step internals.
|
|
290
|
+
// depending on Step internals.
|
|
359
291
|
toKeys() {
|
|
360
292
|
const out = [];
|
|
361
293
|
for (const step of this.steps) {
|
|
@@ -369,69 +301,146 @@ var Path = class _Path {
|
|
|
369
301
|
return (0, immer_exports.produce)(root, (draft) => {
|
|
370
302
|
let parent = draft;
|
|
371
303
|
for (let i = 0; i < this.steps.length - 1; i++) {
|
|
372
|
-
parent = this.steps[i].lookup(parent
|
|
304
|
+
parent = this.steps[i].lookup(parent);
|
|
373
305
|
if (parent === NONE) return;
|
|
374
306
|
}
|
|
375
307
|
this.steps.at(-1).setDraftValue(parent, v);
|
|
376
308
|
});
|
|
377
309
|
}
|
|
310
|
+
};
|
|
311
|
+
function walkItems(stack, items, renderPath, path2) {
|
|
312
|
+
let prev = stack.it;
|
|
313
|
+
for (const step of items) {
|
|
314
|
+
const next = step.lookup(prev);
|
|
315
|
+
if (next === NONE) {
|
|
316
|
+
console.warn("bad PathItem", { root: stack.it, step, path: path2 });
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
renderPath = renderPath.pushItem(step);
|
|
320
|
+
stack = step.enterFrame(stack, next, renderPath);
|
|
321
|
+
prev = next;
|
|
322
|
+
}
|
|
323
|
+
return [stack, renderPath];
|
|
324
|
+
}
|
|
325
|
+
var EMPTY_PATH = new Path([]);
|
|
326
|
+
var DispatchPath = class _DispatchPath {
|
|
327
|
+
constructor(frames = [{ base: EMPTY_PATH, items: [] }]) {
|
|
328
|
+
this.frames = frames;
|
|
329
|
+
}
|
|
330
|
+
// Plain addressing steps in one frame based at the root: what a caller means
|
|
331
|
+
// by "this position" when it has no continuation of its own.
|
|
332
|
+
static ofSteps(steps) {
|
|
333
|
+
return new _DispatchPath([{ base: EMPTY_PATH, items: steps.slice() }]);
|
|
334
|
+
}
|
|
335
|
+
get top() {
|
|
336
|
+
return this.frames[this.frames.length - 1];
|
|
337
|
+
}
|
|
338
|
+
_withTopItems(items) {
|
|
339
|
+
const frames = this.frames.slice();
|
|
340
|
+
frames[frames.length - 1] = { base: this.top.base, items };
|
|
341
|
+
return new _DispatchPath(frames);
|
|
342
|
+
}
|
|
343
|
+
concat(steps) {
|
|
344
|
+
return this._withTopItems(this.top.items.concat(steps));
|
|
345
|
+
}
|
|
346
|
+
pushItem(step) {
|
|
347
|
+
return this.concat([step]);
|
|
348
|
+
}
|
|
349
|
+
pushFrame(base) {
|
|
350
|
+
return new _DispatchPath(this.frames.concat({ base, items: [] }));
|
|
351
|
+
}
|
|
352
|
+
// Whether bubbling has anywhere left to go: another step in this frame, or a
|
|
353
|
+
// visual caller underneath it. (A path always has at least one frame.)
|
|
354
|
+
canPop() {
|
|
355
|
+
return this.frames.length > 1 || this.frames[0].items.length > 0;
|
|
356
|
+
}
|
|
357
|
+
// One component closer to the root. At the top of a frame that is popping back
|
|
358
|
+
// to the visual caller, not to the producer's own parent — the caller is where
|
|
359
|
+
// the `*name` was written, and where an unhandled message should keep going.
|
|
360
|
+
popStep() {
|
|
361
|
+
const { top } = this;
|
|
362
|
+
if (top.items.length > 0) return this._withTopItems(top.items.slice(0, -1));
|
|
363
|
+
if (this.frames.length > 1) return new _DispatchPath(this.frames.slice(0, -1));
|
|
364
|
+
return this;
|
|
365
|
+
}
|
|
366
|
+
// Drop frame-only steps inside every frame independently; a frame's base is
|
|
367
|
+
// already addressing-only.
|
|
368
|
+
compact() {
|
|
369
|
+
return new _DispatchPath(
|
|
370
|
+
this.frames.map(({ base, items }) => ({ base, items: new Path(items).compact().steps }))
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
// A stable string for the ADDRESS this path denotes, for the render cache. The
|
|
374
|
+
// same immutable value can sit at two places in the tree, and a subtree rendered
|
|
375
|
+
// at one of them bakes that address in — the provides it publishes are located
|
|
376
|
+
// there. Frame-only steps address nothing, so they are compacted out: two sites
|
|
377
|
+
// that differ only in binds do render the same subtree.
|
|
378
|
+
get addressKey() {
|
|
379
|
+
this._addressKey ??= renderPathText(this.toTransactionPath().compact());
|
|
380
|
+
return this._addressKey;
|
|
381
|
+
}
|
|
382
|
+
// The active transaction address: the top frame's absolute base followed by
|
|
383
|
+
// its ordinary descendant steps. This is where a mutation lands.
|
|
384
|
+
toTransactionPath() {
|
|
385
|
+
const { base, items } = this.top;
|
|
386
|
+
return base.concat(items);
|
|
387
|
+
}
|
|
388
|
+
// Rebuild the render stack this path was dispatched from. A frame with a base
|
|
389
|
+
// re-enters at that absolute value first — replaying the resume a `*name`
|
|
390
|
+
// render performed — and then walks its ordinary items.
|
|
378
391
|
buildStack(stack) {
|
|
379
|
-
let
|
|
380
|
-
for (
|
|
381
|
-
const
|
|
382
|
-
if (
|
|
383
|
-
|
|
384
|
-
|
|
392
|
+
let renderPath = new _DispatchPath();
|
|
393
|
+
for (let i = 0; i < this.frames.length; i++) {
|
|
394
|
+
const { base, items } = this.frames[i];
|
|
395
|
+
if (i > 0 || base.steps.length > 0) {
|
|
396
|
+
const baseValue = base.lookup(stack.root, NONE);
|
|
397
|
+
if (baseValue === NONE) {
|
|
398
|
+
console.warn("bad frame base", { base, path: this });
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
renderPath = renderPath.pushFrame(base);
|
|
402
|
+
stack = stack.enter(baseValue, {}, true, renderPath);
|
|
385
403
|
}
|
|
386
|
-
|
|
387
|
-
|
|
404
|
+
const walked = walkItems(stack, items, renderPath, this);
|
|
405
|
+
if (walked === null) return null;
|
|
406
|
+
[stack, renderPath] = walked;
|
|
388
407
|
}
|
|
389
408
|
return stack;
|
|
390
409
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
410
|
+
// Reconstruct the dispatch position of a DOM event from the `data-*` stamps and
|
|
411
|
+
// `§…§` meta comments the renderer left on the way down. `stopOnNoEvent` is off for
|
|
412
|
+
// drag events, whose path is needed even where no handler is registered.
|
|
413
|
+
static fromNodeAndEventName(node, eventName, rootNode, comps, stopOnNoEvent = true) {
|
|
414
|
+
const parts = [];
|
|
394
415
|
const bubbles = BUBBLING_EVENTS.has(eventName);
|
|
395
|
-
let depth = 0;
|
|
396
416
|
let eventIds = [];
|
|
397
417
|
let handlers = null;
|
|
398
|
-
let
|
|
418
|
+
let nodeRefs = [];
|
|
399
419
|
let isLeafComponent = true;
|
|
400
420
|
const crossComponent = (cidNum, vid) => {
|
|
401
421
|
const comp = comps.getComponentForId(cidNum);
|
|
402
|
-
let
|
|
422
|
+
let pushPart = true;
|
|
403
423
|
if (handlers === null && (isLeafComponent || bubbles)) {
|
|
404
424
|
handlers = findHandlers(comp, eventIds, vid, eventName);
|
|
405
425
|
if (handlers === null) {
|
|
406
426
|
if (isLeafComponent && stopOnNoEvent && !bubbles) return false;
|
|
407
427
|
} else if (!isLeafComponent) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
pushStep = false;
|
|
428
|
+
parts.length = 0;
|
|
429
|
+
pushPart = false;
|
|
411
430
|
}
|
|
412
431
|
}
|
|
413
432
|
isLeafComponent = false;
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
if (step) {
|
|
418
|
-
step._originCid = cidNum;
|
|
419
|
-
pathSteps.push(step);
|
|
420
|
-
if (step instanceof DynStep) {
|
|
421
|
-
step.interiorCids.add(cidNum);
|
|
422
|
-
pendingDyns.push(step);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
433
|
+
if (pushPart) {
|
|
434
|
+
const part = resolvePathPart(comp, nodeRefs, vid);
|
|
435
|
+
if (part) parts.push(part);
|
|
425
436
|
}
|
|
426
|
-
for (let i = pendingDyns.length - 1; i >= 0; i--)
|
|
427
|
-
if (pendingDyns[i].producerCompId === cidNum) pendingDyns.splice(i, 1);
|
|
428
437
|
eventIds = [];
|
|
429
|
-
|
|
438
|
+
nodeRefs = [];
|
|
430
439
|
return true;
|
|
431
440
|
};
|
|
432
|
-
while (node && node !== rootNode
|
|
441
|
+
while (node && node !== rootNode) {
|
|
433
442
|
if (node?.dataset) {
|
|
434
|
-
const { eid, cid, vid } = node.dataset;
|
|
443
|
+
const { eid, cid, vid, rp } = node.dataset;
|
|
435
444
|
if (eid !== void 0) eventIds.push(eid);
|
|
436
445
|
const metas = metaChain(node.previousSibling);
|
|
437
446
|
let sawComp = false;
|
|
@@ -439,21 +448,46 @@ var Path = class _Path {
|
|
|
439
448
|
if (m.$ === "Comp") {
|
|
440
449
|
sawComp = true;
|
|
441
450
|
if (!crossComponent(m.cid, m.vid)) return NO_EVENT_INFO;
|
|
442
|
-
|
|
451
|
+
nodeRefs.push({ nid: m.nid, base: pathFromJson(m.base) });
|
|
443
452
|
} else {
|
|
444
|
-
|
|
453
|
+
nodeRefs.push({ nid: m.nid, si: m.si, sk: m.sk });
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
if (!sawComp && cid !== void 0) {
|
|
457
|
+
if (!crossComponent(+cid, vid)) return NO_EVENT_INFO;
|
|
458
|
+
if (rp !== void 0) {
|
|
459
|
+
const base = parseRenderPath(rp);
|
|
460
|
+
if (base !== null) nodeRefs.push({ base });
|
|
445
461
|
}
|
|
446
462
|
}
|
|
447
|
-
if (!sawComp && cid !== void 0 && !crossComponent(+cid, vid)) return NO_EVENT_INFO;
|
|
448
463
|
}
|
|
449
|
-
depth += 1;
|
|
450
464
|
node = node.parentNode;
|
|
451
465
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
466
|
+
parts.reverse();
|
|
467
|
+
let path2 = new _DispatchPath();
|
|
468
|
+
for (const part of parts)
|
|
469
|
+
path2 = part.base !== void 0 ? path2.pushFrame(part.base) : path2.pushItem(part.step);
|
|
470
|
+
return [path2, handlers];
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
function keyedPath(path2, key) {
|
|
474
|
+
const steps = path2.steps;
|
|
475
|
+
const last = steps[steps.length - 1];
|
|
476
|
+
if (last instanceof SeqStep || last instanceof FieldStep)
|
|
477
|
+
return new Path(steps.slice(0, -1).concat(new SeqStep(last.field, key)));
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
function renderPathText(path2) {
|
|
481
|
+
return JSON.stringify(pathToJson(path2));
|
|
482
|
+
}
|
|
483
|
+
function parseRenderPath(text) {
|
|
484
|
+
try {
|
|
485
|
+
return pathFromJson(JSON.parse(text));
|
|
486
|
+
} catch (err) {
|
|
487
|
+
console.warn("bad render path", err, text);
|
|
488
|
+
return null;
|
|
455
489
|
}
|
|
456
|
-
}
|
|
490
|
+
}
|
|
457
491
|
function metaChain(n) {
|
|
458
492
|
const out = [];
|
|
459
493
|
while (n?.nodeType === 8 && n.textContent[0] === "§") {
|
|
@@ -498,28 +532,28 @@ var StepCtx = class _StepCtx {
|
|
|
498
532
|
resolveNode() {
|
|
499
533
|
return this.comp.getNodeForId(+this.meta.nid, this.vid);
|
|
500
534
|
}
|
|
501
|
-
applyKey(pi) {
|
|
502
|
-
if (pi === null) return null;
|
|
503
|
-
const m = this.meta;
|
|
504
|
-
if (m.si !== void 0) return pi.withIndex(+m.si);
|
|
505
|
-
if (m.sk !== void 0) return pi.withKey(m.sk);
|
|
506
|
-
return pi;
|
|
507
|
-
}
|
|
508
535
|
};
|
|
509
|
-
function
|
|
510
|
-
for (let i = 0; i <
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
if (
|
|
536
|
+
function resolvePathPart(comp, nodeRefs, vid) {
|
|
537
|
+
for (let i = 0; i < nodeRefs.length; i++) {
|
|
538
|
+
const ref = nodeRefs[i];
|
|
539
|
+
if (ref.base != null) return { base: ref.base };
|
|
540
|
+
if (ref.nid === void 0 || ref.nid === null) continue;
|
|
541
|
+
const ctx = new StepCtx(comp, nodeRefs, i, vid);
|
|
542
|
+
const step = ctx.resolveNode()?.toPathStep(ctx) ?? null;
|
|
543
|
+
if (step !== null) return { step };
|
|
514
544
|
}
|
|
515
545
|
return null;
|
|
516
546
|
}
|
|
517
547
|
var NO_EVENT_INFO = [null, null];
|
|
518
548
|
var BUBBLING_EVENTS = /* @__PURE__ */ new Set(["drop"]);
|
|
549
|
+
var path = () => new PathBuilder();
|
|
519
550
|
var PathBuilder = class {
|
|
520
551
|
constructor() {
|
|
521
552
|
this.pathChanges = [];
|
|
522
553
|
}
|
|
554
|
+
toPath() {
|
|
555
|
+
return new Path(this.pathChanges);
|
|
556
|
+
}
|
|
523
557
|
add(pathChange) {
|
|
524
558
|
this.pathChanges.push(pathChange);
|
|
525
559
|
return this;
|
|
@@ -535,10 +569,8 @@ var PathBuilder = class {
|
|
|
535
569
|
}
|
|
536
570
|
};
|
|
537
571
|
|
|
538
|
-
// src/util/env.js
|
|
539
|
-
var isMac = (globalThis.navigator?.userAgent ?? "").toLowerCase().includes("mac");
|
|
540
|
-
|
|
541
572
|
// src/value.js
|
|
573
|
+
var isMac = (globalThis.navigator?.userAgent ?? "").toLowerCase().includes("mac");
|
|
542
574
|
var VALID_VAL_ID_RE = /^[a-zA-Z][a-zA-Z0-9_]*\??$/;
|
|
543
575
|
var isValidValId = (name) => VALID_VAL_ID_RE.test(name);
|
|
544
576
|
var VALID_FLOAT_RE = /^-?[0-9]+(\.[0-9]+)?$/;
|
|
@@ -720,22 +752,10 @@ function _parsePredicate(s, tokens, px) {
|
|
|
720
752
|
}
|
|
721
753
|
return new PredicateVal(pred, args);
|
|
722
754
|
}
|
|
723
|
-
|
|
724
|
-
if (val === null) return 0;
|
|
725
|
-
if (val instanceof ConstVal) return val.kind;
|
|
726
|
-
if (val instanceof StrTplVal) return val.kind;
|
|
727
|
-
if (val instanceof SeqAccessVal) return K_SEQ;
|
|
728
|
-
if (val instanceof FieldVal) return K_FIELD;
|
|
729
|
-
if (val instanceof MethodVal) return K_METHOD;
|
|
730
|
-
if (val instanceof BindVal) return K_BIND;
|
|
731
|
-
if (val instanceof DynVal) return K_DYN;
|
|
732
|
-
if (val instanceof NameVal) return K_NAME;
|
|
733
|
-
if (val instanceof EventMemberVal) return K_EVENT;
|
|
734
|
-
return 0;
|
|
735
|
-
}
|
|
755
|
+
var kindOf = (val) => val?.kind ?? 0;
|
|
736
756
|
var BaseVal = class {
|
|
737
|
-
|
|
738
|
-
|
|
757
|
+
// No kind: never accepted by a group (PredicateVal, which only parseBool builds).
|
|
758
|
+
kind = 0;
|
|
739
759
|
eval(_stack) {
|
|
740
760
|
}
|
|
741
761
|
toPathItem() {
|
|
@@ -755,9 +775,6 @@ var ConstVal = class extends BaseVal {
|
|
|
755
775
|
this.val = val;
|
|
756
776
|
this.kind = kind;
|
|
757
777
|
}
|
|
758
|
-
render(_stack, _rx) {
|
|
759
|
-
return this.val;
|
|
760
|
-
}
|
|
761
778
|
eval(_stack) {
|
|
762
779
|
return this.val;
|
|
763
780
|
}
|
|
@@ -783,9 +800,7 @@ var PredicateVal = class extends BaseVal {
|
|
|
783
800
|
return `${this.pred.name} ${this.args.map(String).join(" ")}`;
|
|
784
801
|
}
|
|
785
802
|
};
|
|
786
|
-
var
|
|
787
|
-
};
|
|
788
|
-
var StrTplVal = class _StrTplVal extends VarVal {
|
|
803
|
+
var StrTplVal = class _StrTplVal extends BaseVal {
|
|
789
804
|
constructor(vals) {
|
|
790
805
|
super();
|
|
791
806
|
this.vals = vals;
|
|
@@ -798,9 +813,6 @@ var StrTplVal = class _StrTplVal extends VarVal {
|
|
|
798
813
|
for (const v of this.vals) if (!(v instanceof ConstVal) || v.fromMacroVar) return false;
|
|
799
814
|
return true;
|
|
800
815
|
}
|
|
801
|
-
render(stack, _rx) {
|
|
802
|
-
return this.eval(stack);
|
|
803
|
-
}
|
|
804
816
|
eval(stack) {
|
|
805
817
|
const strs = new Array(this.vals.length);
|
|
806
818
|
for (let i = 0; i < this.vals.length; i++) strs[i] = this.vals[i]?.eval(stack, "");
|
|
@@ -837,7 +849,8 @@ var StrTplVal = class _StrTplVal extends VarVal {
|
|
|
837
849
|
return new _StrTplVal(lo === 0 && hi === vals.length ? vals : vals.slice(lo, hi));
|
|
838
850
|
}
|
|
839
851
|
};
|
|
840
|
-
var NameVal = class extends
|
|
852
|
+
var NameVal = class extends BaseVal {
|
|
853
|
+
kind = K_NAME;
|
|
841
854
|
constructor(name) {
|
|
842
855
|
super();
|
|
843
856
|
this.name = name;
|
|
@@ -887,6 +900,7 @@ var EVENT_CONVENIENCES = {
|
|
|
887
900
|
dragKey: nullSafe((info) => info.lookupBind("key"))
|
|
888
901
|
};
|
|
889
902
|
var EventMemberVal = class extends BaseVal {
|
|
903
|
+
kind = K_EVENT;
|
|
890
904
|
constructor(members) {
|
|
891
905
|
super();
|
|
892
906
|
this.members = members;
|
|
@@ -909,24 +923,21 @@ var EventMemberVal = class extends BaseVal {
|
|
|
909
923
|
return `e.${this.members.join(".")}`;
|
|
910
924
|
}
|
|
911
925
|
};
|
|
912
|
-
var
|
|
913
|
-
render(stack, _rx) {
|
|
914
|
-
return this.eval(stack);
|
|
915
|
-
}
|
|
916
|
-
};
|
|
917
|
-
var RenderNameVal = class extends RenderVal {
|
|
926
|
+
var SigilNameVal = class extends BaseVal {
|
|
918
927
|
constructor(name) {
|
|
919
928
|
super();
|
|
920
929
|
this.name = name;
|
|
921
930
|
}
|
|
931
|
+
toString() {
|
|
932
|
+
return this.sigil + this.name;
|
|
933
|
+
}
|
|
922
934
|
};
|
|
923
|
-
var BindVal = class extends
|
|
935
|
+
var BindVal = class extends SigilNameVal {
|
|
936
|
+
kind = K_BIND;
|
|
937
|
+
sigil = "@";
|
|
924
938
|
eval(stack) {
|
|
925
939
|
return stack.lookupBind(this.name);
|
|
926
940
|
}
|
|
927
|
-
toString() {
|
|
928
|
-
return `@${this.name}`;
|
|
929
|
-
}
|
|
930
941
|
};
|
|
931
942
|
var BindMemberVal = class extends BindVal {
|
|
932
943
|
constructor(name, member) {
|
|
@@ -941,37 +952,35 @@ var BindMemberVal = class extends BindVal {
|
|
|
941
952
|
return `@${this.name}.${this.member}`;
|
|
942
953
|
}
|
|
943
954
|
};
|
|
944
|
-
var DynVal = class extends
|
|
955
|
+
var DynVal = class extends SigilNameVal {
|
|
956
|
+
kind = K_DYN;
|
|
957
|
+
sigil = "*";
|
|
945
958
|
eval(stack) {
|
|
946
959
|
return stack.lookupDynamic(this.name);
|
|
947
960
|
}
|
|
948
|
-
toString() {
|
|
949
|
-
return `*${this.name}`;
|
|
950
|
-
}
|
|
951
961
|
};
|
|
952
|
-
var FieldVal = class extends
|
|
962
|
+
var FieldVal = class extends SigilNameVal {
|
|
963
|
+
kind = K_FIELD;
|
|
964
|
+
sigil = ".";
|
|
953
965
|
eval(stack) {
|
|
954
966
|
return stack.lookupFieldRaw(this.name);
|
|
955
967
|
}
|
|
956
968
|
toPathItem() {
|
|
957
969
|
return new FieldStep(this.name);
|
|
958
970
|
}
|
|
959
|
-
toString() {
|
|
960
|
-
return `.${this.name}`;
|
|
961
|
-
}
|
|
962
971
|
};
|
|
963
|
-
var MethodVal = class extends
|
|
972
|
+
var MethodVal = class extends SigilNameVal {
|
|
973
|
+
kind = K_METHOD;
|
|
974
|
+
sigil = "$";
|
|
964
975
|
eval(stack) {
|
|
965
976
|
return stack.lookupMethod(this.name);
|
|
966
977
|
}
|
|
967
978
|
evalAsHandler(stack) {
|
|
968
979
|
return stack.lookupFieldRaw(this.name);
|
|
969
980
|
}
|
|
970
|
-
toString() {
|
|
971
|
-
return `$${this.name}`;
|
|
972
|
-
}
|
|
973
981
|
};
|
|
974
|
-
var SeqAccessVal = class extends
|
|
982
|
+
var SeqAccessVal = class extends BaseVal {
|
|
983
|
+
kind = K_SEQ;
|
|
975
984
|
constructor(seqVal, keyVal) {
|
|
976
985
|
super();
|
|
977
986
|
this.seqVal = seqVal;
|
|
@@ -994,9 +1003,6 @@ var Attributes = class {
|
|
|
994
1003
|
constructor(items) {
|
|
995
1004
|
this.items = items;
|
|
996
1005
|
}
|
|
997
|
-
static parse(attributes, px, parseAll = false) {
|
|
998
|
-
return new AttrParser(px).parse(attributes, parseAll);
|
|
999
|
-
}
|
|
1000
1006
|
isConstant() {
|
|
1001
1007
|
return false;
|
|
1002
1008
|
}
|
|
@@ -1014,10 +1020,16 @@ function parseDirectiveValue(px, directiveName, source, parser) {
|
|
|
1014
1020
|
}
|
|
1015
1021
|
return val;
|
|
1016
1022
|
}
|
|
1023
|
+
var ITER_DIRECTIVES = {
|
|
1024
|
+
when: "whenVal",
|
|
1025
|
+
"loop-with": "loopWithVal",
|
|
1026
|
+
"enrich-with": "enrichWithVal"
|
|
1027
|
+
};
|
|
1028
|
+
var parseIterDirective = (px, name, s) => parseDirectiveValue(px, name, s, parseAlterHandler);
|
|
1017
1029
|
function parseIterationDirectives(attributes, px) {
|
|
1018
1030
|
const parseNamed = (name) => {
|
|
1019
1031
|
const attr = attributes.getNamedItem(`@${name}`);
|
|
1020
|
-
return attr ?
|
|
1032
|
+
return attr ? parseIterDirective(px, name, attr.value) : null;
|
|
1021
1033
|
};
|
|
1022
1034
|
return { whenVal: parseNamed("when"), loopWithVal: parseNamed("loop-with") };
|
|
1023
1035
|
}
|
|
@@ -1058,11 +1070,22 @@ var AttrParser = class {
|
|
|
1058
1070
|
this.px.onParseIssue("bad-value", info);
|
|
1059
1071
|
}
|
|
1060
1072
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1073
|
+
// `@then` / `@else` fill the branches of the `@if.<attr>` on the same element.
|
|
1074
|
+
parseBranch(slot, name, s) {
|
|
1075
|
+
if (this.ifAttr === null) return this._orphan(name, s, "if");
|
|
1076
|
+
this.ifAttr[slot] = parseText(s, this.px) ?? NOT_SET_VAL;
|
|
1077
|
+
}
|
|
1078
|
+
// `@when` / `@loop-with` / `@enrich-with` amend the `@each` on the same element. A
|
|
1079
|
+
// loop-less `@enrich-with` is a scope of its own; the other two need the loop.
|
|
1080
|
+
parseIter(name, s) {
|
|
1081
|
+
const val = parseIterDirective(this.px, name, s);
|
|
1082
|
+
if (this.eachAttr !== null) this.eachAttr[ITER_DIRECTIVES[name]] = val;
|
|
1083
|
+
else if (name === "enrich-with") this.pushWrapper("scope", s, val);
|
|
1084
|
+
else this._orphan(name, s, "each");
|
|
1063
1085
|
}
|
|
1064
|
-
|
|
1065
|
-
|
|
1086
|
+
// A directive that only means something next to another one, written alone.
|
|
1087
|
+
_orphan(name, value, needs) {
|
|
1088
|
+
this.px.onParseIssue("orphan-directive", { name, value, needs });
|
|
1066
1089
|
}
|
|
1067
1090
|
parseEvent(directiveName, value) {
|
|
1068
1091
|
const [eventName, ...modifiers] = directiveName.slice(3).split("+");
|
|
@@ -1076,77 +1099,51 @@ var AttrParser = class {
|
|
|
1076
1099
|
this.events.add(eventName, handler, modifiers);
|
|
1077
1100
|
}
|
|
1078
1101
|
}
|
|
1079
|
-
_parseDirectiveValue(directiveName, s, parserFn) {
|
|
1080
|
-
return parseDirectiveValue(this.px, directiveName, s, parserFn);
|
|
1081
|
-
}
|
|
1082
1102
|
parseDirective(s, directiveName) {
|
|
1083
1103
|
switch (directiveName) {
|
|
1084
1104
|
case "dangerouslysetinnerhtml":
|
|
1085
1105
|
this.attrs ??= [];
|
|
1086
|
-
this.attrs.push(new RawHtmlAttr(this.
|
|
1106
|
+
this.attrs.push(new RawHtmlAttr(parseDirectiveValue(this.px, directiveName, s, parseText)));
|
|
1087
1107
|
this.hasDynamic = true;
|
|
1088
1108
|
return;
|
|
1089
1109
|
case "push-view":
|
|
1090
|
-
this.pushWrapper("push-view", s, this.
|
|
1110
|
+
this.pushWrapper("push-view", s, parseDirectiveValue(this.px, directiveName, s, parseText));
|
|
1091
1111
|
return;
|
|
1092
1112
|
case "text":
|
|
1093
|
-
this.textChild = this.
|
|
1113
|
+
this.textChild = parseDirectiveValue(this.px, directiveName, s, parseText);
|
|
1094
1114
|
return;
|
|
1095
1115
|
case "show":
|
|
1096
|
-
this.pushWrapper("show", s, this.
|
|
1116
|
+
this.pushWrapper("show", s, parseDirectiveValue(this.px, directiveName, s, parseBool));
|
|
1097
1117
|
return;
|
|
1098
1118
|
case "hide":
|
|
1099
|
-
this.pushWrapper("hide", s, this.
|
|
1119
|
+
this.pushWrapper("hide", s, parseDirectiveValue(this.px, directiveName, s, parseBool));
|
|
1100
1120
|
return;
|
|
1101
1121
|
case "each": {
|
|
1102
|
-
const val = this.
|
|
1122
|
+
const val = parseDirectiveValue(this.px, directiveName, s, parseSequence);
|
|
1103
1123
|
this.eachAttr = this.pushWrapper("each", s, val);
|
|
1104
1124
|
return;
|
|
1105
1125
|
}
|
|
1106
|
-
case "enrich-with":
|
|
1107
|
-
if (this.eachAttr !== null)
|
|
1108
|
-
this.eachAttr.enrichWithVal = this._parseDirectiveValue(
|
|
1109
|
-
directiveName,
|
|
1110
|
-
s,
|
|
1111
|
-
parseAlterHandler
|
|
1112
|
-
);
|
|
1113
|
-
else
|
|
1114
|
-
this.pushWrapper(
|
|
1115
|
-
"scope",
|
|
1116
|
-
s,
|
|
1117
|
-
this._parseDirectiveValue(directiveName, s, parseAlterHandler)
|
|
1118
|
-
);
|
|
1119
|
-
return;
|
|
1120
1126
|
case "when":
|
|
1121
|
-
this._parseWhen(s);
|
|
1122
|
-
return;
|
|
1123
1127
|
case "loop-with":
|
|
1124
|
-
|
|
1128
|
+
case "enrich-with":
|
|
1129
|
+
this.parseIter(directiveName, s);
|
|
1125
1130
|
return;
|
|
1126
1131
|
case "then":
|
|
1127
|
-
this.
|
|
1132
|
+
this.parseBranch("thenVal", directiveName, s);
|
|
1128
1133
|
return;
|
|
1129
1134
|
case "else":
|
|
1130
|
-
this.
|
|
1135
|
+
this.parseBranch("elseVal", directiveName, s);
|
|
1131
1136
|
return;
|
|
1132
1137
|
}
|
|
1133
1138
|
if (directiveName.startsWith("on.")) this.parseEvent(directiveName, s);
|
|
1134
1139
|
else if (directiveName.startsWith("if.")) this.parseIf(directiveName, s);
|
|
1135
|
-
else if (directiveName.startsWith("then.")) this.
|
|
1136
|
-
else if (directiveName.startsWith("else.")) this.
|
|
1140
|
+
else if (directiveName.startsWith("then.")) this.parseBranch("thenVal", directiveName, s);
|
|
1141
|
+
else if (directiveName.startsWith("else.")) this.parseBranch("elseVal", directiveName, s);
|
|
1137
1142
|
else {
|
|
1138
1143
|
const info = { name: directiveName, value: s };
|
|
1139
1144
|
this.px.onParseIssue("unknown-directive", info);
|
|
1140
1145
|
}
|
|
1141
1146
|
}
|
|
1142
|
-
_parseWhen(s) {
|
|
1143
|
-
if (this.eachAttr !== null)
|
|
1144
|
-
this.eachAttr.whenVal = this._parseDirectiveValue("when", s, parseAlterHandler);
|
|
1145
|
-
}
|
|
1146
|
-
_parseLoopWith(s) {
|
|
1147
|
-
if (this.eachAttr !== null)
|
|
1148
|
-
this.eachAttr.loopWithVal = this._parseDirectiveValue("loop-with", s, parseAlterHandler);
|
|
1149
|
-
}
|
|
1150
1147
|
parse(attributes, parseAll = false) {
|
|
1151
1148
|
for (const { name, value } of attributes) {
|
|
1152
1149
|
const charCode = name.charCodeAt(0);
|
|
@@ -1499,19 +1496,21 @@ var VNode = class _VNode extends VBase {
|
|
|
1499
1496
|
const createOpts = attrs.is != null ? { is: attrs.is } : void 0;
|
|
1500
1497
|
const node = ns === null ? doc.createElement(tag, createOpts) : doc.createElementNS(ns, tag, createOpts);
|
|
1501
1498
|
const cOpts = childOpts(this, ns, opts);
|
|
1502
|
-
|
|
1503
|
-
const { value, checked, ...rest } = attrs;
|
|
1504
|
-
applyProperties(node, rest);
|
|
1505
|
-
appendChildNodes(node, this.childs, cOpts);
|
|
1506
|
-
if (value !== void 0) applyValueLast(node, value);
|
|
1507
|
-
if (checked !== void 0) setProp(node, "checked", checked, false);
|
|
1508
|
-
} else {
|
|
1509
|
-
applyProperties(node, attrs);
|
|
1510
|
-
appendChildNodes(node, this.childs, cOpts);
|
|
1511
|
-
}
|
|
1499
|
+
applyAttrs(node, attrs, () => appendChildNodes(node, this.childs, cOpts));
|
|
1512
1500
|
return node;
|
|
1513
1501
|
}
|
|
1514
1502
|
};
|
|
1503
|
+
function applyAttrs(node, attrs, placeChildren) {
|
|
1504
|
+
const hasValue = "value" in attrs;
|
|
1505
|
+
const hasChecked = "checked" in attrs;
|
|
1506
|
+
if (hasValue || hasChecked) {
|
|
1507
|
+
const { value: _v, checked: _c, ...rest } = attrs;
|
|
1508
|
+
applyProperties(node, rest);
|
|
1509
|
+
} else applyProperties(node, attrs);
|
|
1510
|
+
placeChildren();
|
|
1511
|
+
if (hasValue) applyValueLast(node, attrs.value);
|
|
1512
|
+
if (hasChecked) setProp(node, "checked", attrs.checked, isNamespaced(node));
|
|
1513
|
+
}
|
|
1515
1514
|
function diffProps(a, b) {
|
|
1516
1515
|
if (a === b) return null;
|
|
1517
1516
|
let diff = null;
|
|
@@ -1542,22 +1541,16 @@ function morphNode(domNode, source, target, opts) {
|
|
|
1542
1541
|
}
|
|
1543
1542
|
if (type === 1 && source.isSameKind(target)) {
|
|
1544
1543
|
const propsDiff = diffProps(source.attrs, target.attrs);
|
|
1545
|
-
const
|
|
1546
|
-
|
|
1547
|
-
if (propsDiff) {
|
|
1548
|
-
if (hasValue || hasChecked) {
|
|
1549
|
-
const { value: _v, checked: _c, ...rest } = propsDiff;
|
|
1550
|
-
applyProperties(domNode, rest);
|
|
1551
|
-
} else applyProperties(domNode, propsDiff);
|
|
1552
|
-
}
|
|
1553
|
-
if (!target.attrs.dangerouslySetInnerHTML) {
|
|
1544
|
+
const placeChildren = () => {
|
|
1545
|
+
if (target.attrs.dangerouslySetInnerHTML) return;
|
|
1554
1546
|
const ns = effectiveNs(target, opts);
|
|
1555
1547
|
morphChildren(domNode, source.childs, target.childs, childOpts(target, ns, opts));
|
|
1548
|
+
};
|
|
1549
|
+
if (propsDiff) applyAttrs(domNode, propsDiff, placeChildren);
|
|
1550
|
+
else placeChildren();
|
|
1551
|
+
if (!(propsDiff && "value" in propsDiff) && source.tag === "SELECT") {
|
|
1552
|
+
if (target.attrs.value !== void 0) applyValueLast(domNode, target.attrs.value);
|
|
1556
1553
|
}
|
|
1557
|
-
if (hasValue) applyValueLast(domNode, propsDiff.value);
|
|
1558
|
-
else if (source.tag === "SELECT" && target.attrs.value !== void 0)
|
|
1559
|
-
applyValueLast(domNode, target.attrs.value);
|
|
1560
|
-
if (hasChecked) setProp(domNode, "checked", propsDiff.checked, false);
|
|
1561
1554
|
return domNode;
|
|
1562
1555
|
}
|
|
1563
1556
|
if (type === 11) {
|
|
@@ -1676,20 +1669,26 @@ function h(tagName, properties, children, namespace) {
|
|
|
1676
1669
|
}
|
|
1677
1670
|
|
|
1678
1671
|
// src/anode.js
|
|
1679
|
-
function
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1672
|
+
function renderTarget(val, stack) {
|
|
1673
|
+
if (val instanceof DynVal) {
|
|
1674
|
+
const loc = stack.lookupDynamicLocated(val.name);
|
|
1675
|
+
if (loc?.path == null) return [loc?.value ?? null, stack.renderPath, null];
|
|
1676
|
+
return [loc.value, stack.renderPath.pushFrame(loc.path), loc.path];
|
|
1677
|
+
}
|
|
1678
|
+
const step = val.toPathItem?.() ?? null;
|
|
1679
|
+
if (step === null) return [val.eval(stack), stack.renderPath, null];
|
|
1680
|
+
const path2 = stack.renderPath.pushItem(step);
|
|
1681
|
+
return [val.eval(stack), path2, stack.pendingFrame ? path2.toTransactionPath() : null];
|
|
1686
1682
|
}
|
|
1687
1683
|
var BaseNode = class {
|
|
1688
1684
|
render(_stack, _rx) {
|
|
1689
1685
|
return null;
|
|
1690
1686
|
}
|
|
1691
|
-
|
|
1692
|
-
|
|
1687
|
+
// Stamp a `data-*` attribute on the element(s) this node renders. A no-op for
|
|
1688
|
+
// nodes that produce no element of their own (text, `<x render*>`): a view whose
|
|
1689
|
+
// root is one of those records its component boundary in the `§Comp§` meta
|
|
1690
|
+
// comment instead (see Renderer._rValComp).
|
|
1691
|
+
setDataAttr(_key, _val) {
|
|
1693
1692
|
}
|
|
1694
1693
|
isConstant() {
|
|
1695
1694
|
return false;
|
|
@@ -1728,21 +1727,12 @@ var TextNode = class extends BaseNode {
|
|
|
1728
1727
|
isConstant() {
|
|
1729
1728
|
return true;
|
|
1730
1729
|
}
|
|
1731
|
-
setDataAttr(_key, _val) {
|
|
1732
|
-
}
|
|
1733
1730
|
};
|
|
1734
1731
|
var CommentNode = class extends TextNode {
|
|
1735
1732
|
render(_stack, rx) {
|
|
1736
1733
|
return rx.renderComment(this.val);
|
|
1737
1734
|
}
|
|
1738
1735
|
};
|
|
1739
|
-
function optimizeChilds(childs) {
|
|
1740
|
-
for (let i = 0; i < childs.length; i++) {
|
|
1741
|
-
const child = childs[i];
|
|
1742
|
-
if (child.isConstant()) childs[i] = new RenderOnceNode(child);
|
|
1743
|
-
else child.optimize();
|
|
1744
|
-
}
|
|
1745
|
-
}
|
|
1746
1736
|
function optimizeNode(node) {
|
|
1747
1737
|
if (node.isConstant()) return new RenderOnceNode(node);
|
|
1748
1738
|
node.optimize();
|
|
@@ -1757,7 +1747,8 @@ var ChildsNode = class extends BaseNode {
|
|
|
1757
1747
|
return this.childs.every((v) => v.isConstant());
|
|
1758
1748
|
}
|
|
1759
1749
|
optimize() {
|
|
1760
|
-
|
|
1750
|
+
const { childs } = this;
|
|
1751
|
+
for (let i = 0; i < childs.length; i++) childs[i] = optimizeNode(childs[i]);
|
|
1761
1752
|
}
|
|
1762
1753
|
};
|
|
1763
1754
|
var DomNode = class extends ChildsNode {
|
|
@@ -1769,8 +1760,7 @@ var DomNode = class extends ChildsNode {
|
|
|
1769
1760
|
}
|
|
1770
1761
|
render(stack, rx) {
|
|
1771
1762
|
const childNodes = new Array(this.childs.length);
|
|
1772
|
-
for (let i = 0; i < childNodes.length; i++)
|
|
1773
|
-
childNodes[i] = this.childs[i]?.render?.(stack, rx) ?? null;
|
|
1763
|
+
for (let i = 0; i < childNodes.length; i++) childNodes[i] = this.childs[i].render(stack, rx);
|
|
1774
1764
|
return rx.renderTag(this.tagName, this.attrs.eval(stack), childNodes, this.namespace);
|
|
1775
1765
|
}
|
|
1776
1766
|
setDataAttr(key, val) {
|
|
@@ -1782,7 +1772,7 @@ var DomNode = class extends ChildsNode {
|
|
|
1782
1772
|
};
|
|
1783
1773
|
var FragmentNode = class extends ChildsNode {
|
|
1784
1774
|
render(stack, rx) {
|
|
1785
|
-
return rx.renderFragment(this.childs.map((c) => c
|
|
1775
|
+
return rx.renderFragment(this.childs.map((c) => c.render(stack, rx)));
|
|
1786
1776
|
}
|
|
1787
1777
|
setDataAttr(key, val) {
|
|
1788
1778
|
for (const child of this.childs) child.setDataAttr(key, val);
|
|
@@ -1796,8 +1786,10 @@ var ANode = class _ANode extends BaseNode {
|
|
|
1796
1786
|
this.nodeId = nodeId;
|
|
1797
1787
|
this.val = val;
|
|
1798
1788
|
}
|
|
1799
|
-
|
|
1800
|
-
|
|
1789
|
+
// The addressing step this node contributes to an event's dispatch path, given
|
|
1790
|
+
// the `StepCtx` of the meta that named it; null when it addresses nothing.
|
|
1791
|
+
toPathStep(_ctx) {
|
|
1792
|
+
return this.val?.toPathItem?.() ?? null;
|
|
1801
1793
|
}
|
|
1802
1794
|
static parse(html2, px) {
|
|
1803
1795
|
const nodes = px.parseHTML(html2);
|
|
@@ -1813,8 +1805,8 @@ var ANode = class _ANode extends BaseNode {
|
|
|
1813
1805
|
return maybeFragment(trimmed);
|
|
1814
1806
|
}
|
|
1815
1807
|
static fromDOM(node, px) {
|
|
1816
|
-
if (node
|
|
1817
|
-
|
|
1808
|
+
if (node.nodeType === 3) return new TextNode(node.textContent);
|
|
1809
|
+
if (node.nodeType === 8) return new CommentNode(node.textContent);
|
|
1818
1810
|
const { childNodes, attributes: attrs, tagName: tag } = node;
|
|
1819
1811
|
const childs = [];
|
|
1820
1812
|
for (let i = 0; i < childNodes.length; i++) {
|
|
@@ -1832,11 +1824,11 @@ var ANode = class _ANode extends BaseNode {
|
|
|
1832
1824
|
const slotName = attrs.getNamedItem("name")?.value ?? "_";
|
|
1833
1825
|
return px.frame.macroSlots[slotName] ?? maybeFragment(childs);
|
|
1834
1826
|
}
|
|
1835
|
-
const [nAttrs, wrappers] =
|
|
1827
|
+
const [nAttrs, wrappers] = new AttrParser(px).parse(attrs, true);
|
|
1836
1828
|
px.onAttributes(nAttrs, wrappers, null, true, tag);
|
|
1837
1829
|
return wrap(px.newMacroNode(macroName, nAttrs.toMacroVars(), childs), px, wrappers);
|
|
1838
1830
|
} else if (VALID_NODE_RE.test(tag)) {
|
|
1839
|
-
const [nAttrs, wrappers, textChild] =
|
|
1831
|
+
const [nAttrs, wrappers, textChild] = new AttrParser(px).parse(attrs);
|
|
1840
1832
|
px.onAttributes(nAttrs, wrappers, textChild, false, tag);
|
|
1841
1833
|
if (textChild) childs.unshift(new RenderTextNode(null, textChild));
|
|
1842
1834
|
const domChilds = tag !== "PRE" ? condenseChildsWhites(childs) : childs;
|
|
@@ -1998,39 +1990,32 @@ var RenderViewId = class extends ANode {
|
|
|
1998
1990
|
evalViewName(stack) {
|
|
1999
1991
|
return this.viewVal ? this.viewVal.eval(stack) : null;
|
|
2000
1992
|
}
|
|
2001
|
-
// A `<x render*>` produces no DOM element of its own to carry `data-cid`;
|
|
2002
|
-
// when it is a view's root the component boundary is recorded in the `Comp`
|
|
2003
|
-
// meta comment instead (see Renderer._rValComp), so this is a no-op.
|
|
2004
|
-
setDataAttr(_key, _val) {
|
|
2005
|
-
}
|
|
2006
1993
|
};
|
|
2007
|
-
function dynRenderStep(comp, name, key) {
|
|
2008
|
-
const p = resolveDynProducer(comp, name);
|
|
2009
|
-
if (!p) return null;
|
|
2010
|
-
return key === void 0 ? new DynStep(p.producerCompId, p.producerSteps) : new DynEachStep(p.producerCompId, p.producerSteps, key);
|
|
2011
|
-
}
|
|
2012
1994
|
var RenderNode = class extends RenderViewId {
|
|
2013
1995
|
render(stack, rx) {
|
|
2014
|
-
const
|
|
2015
|
-
|
|
1996
|
+
const [value, renderPath, base] = renderTarget(this.val, stack);
|
|
1997
|
+
const newStack = stack.enter(value, {}, true, renderPath, false);
|
|
1998
|
+
return rx.renderIt(newStack, this, this.evalViewName(stack), base);
|
|
2016
1999
|
}
|
|
2000
|
+
// A `*name` target contributes no step: the site recorded the absolute base it
|
|
2001
|
+
// resumed at, and event reconstruction turns that into a continuation frame.
|
|
2017
2002
|
toPathStep(ctx) {
|
|
2018
|
-
if (this.val instanceof DynVal) return
|
|
2003
|
+
if (this.val instanceof DynVal) return null;
|
|
2019
2004
|
return super.toPathStep(ctx);
|
|
2020
2005
|
}
|
|
2021
2006
|
};
|
|
2022
2007
|
var RenderItNode = class extends RenderViewId {
|
|
2023
2008
|
render(stack, rx) {
|
|
2024
|
-
const
|
|
2025
|
-
|
|
2009
|
+
const base = stack.pendingFrame ? stack.renderPath.toTransactionPath() : null;
|
|
2010
|
+
const newStack = stack.enter(stack.it, {}, true, stack.renderPath, false);
|
|
2011
|
+
return rx.renderIt(newStack, this, this.evalViewName(stack), base);
|
|
2026
2012
|
}
|
|
2027
2013
|
toPathStep(ctx) {
|
|
2028
2014
|
const next = ctx.next();
|
|
2029
2015
|
if (next === null) return null;
|
|
2030
2016
|
const nextNode = next.resolveNode();
|
|
2031
2017
|
if (nextNode instanceof EachNode && next.hasKey) {
|
|
2032
|
-
if (nextNode.val instanceof DynVal)
|
|
2033
|
-
return dynRenderStep(ctx.comp, nextNode.val.name, next.key);
|
|
2018
|
+
if (nextNode.val instanceof DynVal) return null;
|
|
2034
2019
|
return new EachRenderItStep(nextNode.val.name, next.key);
|
|
2035
2020
|
}
|
|
2036
2021
|
return null;
|
|
@@ -2052,9 +2037,6 @@ var RenderTextNode = class extends ANode {
|
|
|
2052
2037
|
render(stack, _rx) {
|
|
2053
2038
|
return this.val.eval(stack);
|
|
2054
2039
|
}
|
|
2055
|
-
// Renders to a text node, which can't carry `data-cid`.
|
|
2056
|
-
setDataAttr(_key, _val) {
|
|
2057
|
-
}
|
|
2058
2040
|
};
|
|
2059
2041
|
var RenderOnceNode = class extends BaseNode {
|
|
2060
2042
|
constructor(node) {
|
|
@@ -2129,35 +2111,45 @@ var ScopeNode = class extends WrapperNode {
|
|
|
2129
2111
|
var EachNode = class extends WrapperNode {
|
|
2130
2112
|
constructor(nodeId, val) {
|
|
2131
2113
|
super(nodeId, val);
|
|
2132
|
-
this.iterInfo = new IterInfo(
|
|
2114
|
+
this.iterInfo = new IterInfo();
|
|
2133
2115
|
}
|
|
2134
2116
|
render(stack, rx) {
|
|
2135
|
-
return rx.renderEachWhen(stack, this
|
|
2117
|
+
return rx.renderEachWhen(stack, this);
|
|
2118
|
+
}
|
|
2119
|
+
// The sequence and the three loop handlers, resolved against `stack`.
|
|
2120
|
+
evalIter(stack) {
|
|
2121
|
+
const { whenVal, loopWithVal, enrichWithVal } = this.iterInfo;
|
|
2122
|
+
return {
|
|
2123
|
+
seq: this.val.eval(stack) ?? [],
|
|
2124
|
+
filter: whenVal?.evalAsHandler(stack) ?? filterAlwaysTrue,
|
|
2125
|
+
loopWith: loopWithVal?.evalAsHandler(stack) ?? nullLoopWith,
|
|
2126
|
+
enricher: enrichWithVal?.evalAsHandler(stack) ?? null
|
|
2127
|
+
};
|
|
2128
|
+
}
|
|
2129
|
+
// Rebuild the per-item binds for `key` (see iteration.js bindsForKey).
|
|
2130
|
+
enrichBinds(stack, key) {
|
|
2131
|
+
const { seq, filter, loopWith, enricher } = this.evalIter(stack);
|
|
2132
|
+
const ctx = makeLoopCtx(stack, filter);
|
|
2133
|
+
return bindsForKey({ seq, it: stack.it, loopWith, enricher, ctx }, key);
|
|
2136
2134
|
}
|
|
2137
2135
|
toPathStep(ctx) {
|
|
2138
|
-
return ctx.hasKey ? new EachBindStep(this
|
|
2136
|
+
return ctx.hasKey ? new EachBindStep(this, ctx.key) : null;
|
|
2137
|
+
}
|
|
2138
|
+
// Where one item lives, for the render path. `@each` re-binds `it` to the item
|
|
2139
|
+
// whether or not the body is a component, so the position moves either way and
|
|
2140
|
+
// the step has to address it — `.rows` iterated at `key` IS `rows[key]`.
|
|
2141
|
+
// Null for a dynamic sequence: `*rows` carries its own absolute path and enters
|
|
2142
|
+
// a continuation frame instead (see Renderer.renderEachWhen).
|
|
2143
|
+
itemStep(key) {
|
|
2144
|
+
return this.val instanceof FieldVal ? new SeqStep(this.val.name, key) : null;
|
|
2139
2145
|
}
|
|
2140
2146
|
static register = true;
|
|
2141
2147
|
};
|
|
2142
2148
|
var IterInfo = class {
|
|
2143
|
-
constructor(
|
|
2144
|
-
this.
|
|
2145
|
-
this.
|
|
2146
|
-
this.
|
|
2147
|
-
this.enrichWithVal = enrichWithVal;
|
|
2148
|
-
}
|
|
2149
|
-
eval(stack) {
|
|
2150
|
-
const seq = this.val.eval(stack) ?? [];
|
|
2151
|
-
const filter = this.whenVal?.evalAsHandler(stack) ?? filterAlwaysTrue;
|
|
2152
|
-
const loopWith = this.loopWithVal?.evalAsHandler(stack) ?? nullLoopWith;
|
|
2153
|
-
const enricher = this.enrichWithVal?.evalAsHandler(stack) ?? null;
|
|
2154
|
-
return { seq, filter, loopWith, enricher };
|
|
2155
|
-
}
|
|
2156
|
-
// Rebuild the per-item binds for `key` (see iteration.js bindsForKey).
|
|
2157
|
-
enrichBinds(stack, key) {
|
|
2158
|
-
const { seq, filter, loopWith, enricher } = this.eval(stack);
|
|
2159
|
-
const ctx = makeLoopCtx(stack, filter);
|
|
2160
|
-
return bindsForKey({ seq, it: stack.it, loopWith, enricher, ctx }, key);
|
|
2149
|
+
constructor() {
|
|
2150
|
+
this.whenVal = null;
|
|
2151
|
+
this.loopWithVal = null;
|
|
2152
|
+
this.enrichWithVal = null;
|
|
2161
2153
|
}
|
|
2162
2154
|
};
|
|
2163
2155
|
function xOp(consumed = [], { wrappable = false, wrapper = null, ignoresChildren = false } = {}) {
|
|
@@ -2185,26 +2177,31 @@ var WRAPPER_NODES = {
|
|
|
2185
2177
|
scope: ScopeNode,
|
|
2186
2178
|
"push-view": PushViewNameNode
|
|
2187
2179
|
};
|
|
2188
|
-
var ParseContext = class
|
|
2189
|
-
constructor(document2
|
|
2190
|
-
this.
|
|
2191
|
-
this.
|
|
2192
|
-
this.
|
|
2193
|
-
this.
|
|
2194
|
-
this.
|
|
2195
|
-
this.
|
|
2196
|
-
this.Text = Text ?? globalThis.Text;
|
|
2197
|
-
this.Comment = Comment ?? globalThis.Comment;
|
|
2198
|
-
this.cacheConstNodes = true;
|
|
2180
|
+
var ParseContext = class {
|
|
2181
|
+
constructor(document2 = globalThis.document) {
|
|
2182
|
+
this.document = document2;
|
|
2183
|
+
this.nodes = [];
|
|
2184
|
+
this.events = [];
|
|
2185
|
+
this.macroNodes = [];
|
|
2186
|
+
this.parent = null;
|
|
2187
|
+
this.frame = {};
|
|
2199
2188
|
this.currentTag = null;
|
|
2200
2189
|
}
|
|
2201
2190
|
isInsideMacro(name) {
|
|
2202
2191
|
return this.frame.macroName === name || this.parent?.isInsideMacro(name);
|
|
2203
2192
|
}
|
|
2193
|
+
// A macro body parses in a child context that shares every accumulator with
|
|
2194
|
+
// its parent (nodes, events, macroNodes, and whatever a subclass collects) and
|
|
2195
|
+
// differs only in its frame. Cloned rather than constructed so a subclass
|
|
2196
|
+
// keeps collecting inside macro bodies without re-implementing this — the
|
|
2197
|
+
// lint and class-set contexts used to lose (or had to copy) exactly that.
|
|
2204
2198
|
enterMacro(macroName, macroVars, macroSlots) {
|
|
2205
|
-
const
|
|
2206
|
-
|
|
2207
|
-
|
|
2199
|
+
const child = Object.create(Object.getPrototypeOf(this));
|
|
2200
|
+
return Object.assign(child, this, {
|
|
2201
|
+
frame: { macroName, macroVars, macroSlots },
|
|
2202
|
+
parent: this,
|
|
2203
|
+
currentTag: null
|
|
2204
|
+
});
|
|
2208
2205
|
}
|
|
2209
2206
|
parseHTML(html2) {
|
|
2210
2207
|
const t = this.document.createElement("template");
|
|
@@ -2269,10 +2266,10 @@ var isBlockDomNode = (n) => {
|
|
|
2269
2266
|
return node instanceof DomNode && HTML_BLOCK_TAGS.has(node.tagName);
|
|
2270
2267
|
};
|
|
2271
2268
|
var isEmptyText = (c) => c instanceof TextNode && c.val === "";
|
|
2272
|
-
var isIgnorableXChild = (c) => c instanceof CommentNode ||
|
|
2269
|
+
var isIgnorableXChild = (c) => c instanceof CommentNode || c.isWhiteSpace();
|
|
2273
2270
|
var hasMeaningfulChilds = (childs) => childs.some((c) => !isIgnorableXChild(c));
|
|
2274
2271
|
function trimEdgeWhite(node) {
|
|
2275
|
-
if (!node.isWhiteSpace
|
|
2272
|
+
if (!node.isWhiteSpace()) return false;
|
|
2276
2273
|
node.condenseWhiteSpace();
|
|
2277
2274
|
return true;
|
|
2278
2275
|
}
|
|
@@ -2283,7 +2280,7 @@ function condenseChildsWhites(childs) {
|
|
|
2283
2280
|
if (last > 0 && trimEdgeWhite(childs[last])) emptied = true;
|
|
2284
2281
|
for (let i = 1; i < last; i++) {
|
|
2285
2282
|
const cur = childs[i];
|
|
2286
|
-
if (!(cur.isWhiteSpace
|
|
2283
|
+
if (!(cur.isWhiteSpace() && cur.hasNewLine())) continue;
|
|
2287
2284
|
const bothBlock = isBlockDomNode(childs[i - 1]) && isBlockDomNode(childs[i + 1]);
|
|
2288
2285
|
cur.condenseWhiteSpace(bothBlock ? "" : " ");
|
|
2289
2286
|
if (bothBlock) emptied = true;
|
|
@@ -2291,12 +2288,12 @@ function condenseChildsWhites(childs) {
|
|
|
2291
2288
|
return emptied ? childs.filter((c) => !isEmptyText(c)) : childs;
|
|
2292
2289
|
}
|
|
2293
2290
|
var View = class {
|
|
2294
|
-
constructor(name, rawView = "No View Defined", style = ""
|
|
2291
|
+
constructor(name, rawView = "No View Defined", style = "") {
|
|
2295
2292
|
this.name = name;
|
|
2296
|
-
this.anode = anode;
|
|
2297
|
-
this.style = style;
|
|
2298
|
-
this.ctx = ctx;
|
|
2299
2293
|
this.rawView = rawView;
|
|
2294
|
+
this.style = style;
|
|
2295
|
+
this.anode = null;
|
|
2296
|
+
this.ctx = null;
|
|
2300
2297
|
}
|
|
2301
2298
|
compile(ctx, scope, cid) {
|
|
2302
2299
|
this.ctx = ctx;
|
|
@@ -2304,7 +2301,7 @@ var View = class {
|
|
|
2304
2301
|
this.anode.setDataAttr("data-cid", cid);
|
|
2305
2302
|
this.anode.setDataAttr("data-vid", this.name);
|
|
2306
2303
|
this.ctx.compile(scope);
|
|
2307
|
-
|
|
2304
|
+
this.anode = optimizeNode(this.anode);
|
|
2308
2305
|
}
|
|
2309
2306
|
render(stack, rx) {
|
|
2310
2307
|
if (this.anode === null) {
|
|
@@ -2329,7 +2326,7 @@ var NodeEvents = class {
|
|
|
2329
2326
|
getHandlersFor(eventName) {
|
|
2330
2327
|
let r = null;
|
|
2331
2328
|
for (const handler of this.handlers)
|
|
2332
|
-
if (handler.
|
|
2329
|
+
if (handler.name === eventName) {
|
|
2333
2330
|
r ??= [];
|
|
2334
2331
|
r.push(handler);
|
|
2335
2332
|
}
|
|
@@ -2343,9 +2340,6 @@ var NodeEvent = class {
|
|
|
2343
2340
|
this.modifierWrapper = compileModifiers(name, modifiers);
|
|
2344
2341
|
this.modifiers = modifiers;
|
|
2345
2342
|
}
|
|
2346
|
-
handlesEventName(name) {
|
|
2347
|
-
return this.name === name;
|
|
2348
|
-
}
|
|
2349
2343
|
getHandlerAndArgs(stack, event) {
|
|
2350
2344
|
const r = this.handlerCall.getHandlerAndArgs(stack, event);
|
|
2351
2345
|
r[0] = this.modifierWrapper(r[0], event);
|
|
@@ -2397,69 +2391,287 @@ function compileModifiers(eventName, names) {
|
|
|
2397
2391
|
};
|
|
2398
2392
|
}
|
|
2399
2393
|
|
|
2400
|
-
// src/
|
|
2401
|
-
var
|
|
2402
|
-
var
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2394
|
+
// src/stack.js
|
|
2395
|
+
var STOP = /* @__PURE__ */ Symbol("STOP");
|
|
2396
|
+
var NEXT = /* @__PURE__ */ Symbol("NEXT");
|
|
2397
|
+
var DEFAULT_ROUTE = ["dyn", "lex"];
|
|
2398
|
+
var isTypeName = (s) => {
|
|
2399
|
+
const c = s.charCodeAt(0);
|
|
2400
|
+
return c >= 65 && c <= 90;
|
|
2401
|
+
};
|
|
2402
|
+
function lookup(chain, name, dv = null) {
|
|
2403
|
+
let n = chain;
|
|
2404
|
+
while (n !== null) {
|
|
2405
|
+
const r = n[0].lookup(name);
|
|
2406
|
+
if (r === STOP) return dv;
|
|
2407
|
+
if (r !== NEXT) return r;
|
|
2408
|
+
n = n[1];
|
|
2411
2409
|
}
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2410
|
+
return dv;
|
|
2411
|
+
}
|
|
2412
|
+
var BindFrame = class {
|
|
2413
|
+
constructor(it, binds, isFrame) {
|
|
2414
|
+
this.it = it;
|
|
2415
|
+
this.binds = binds;
|
|
2416
|
+
this.isFrame = isFrame;
|
|
2415
2417
|
}
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
+
lookup(name) {
|
|
2419
|
+
const v = this.binds[name];
|
|
2420
|
+
return v === void 0 ? this.isFrame ? STOP : NEXT : v;
|
|
2418
2421
|
}
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2422
|
+
};
|
|
2423
|
+
var DynFrame = class {
|
|
2424
|
+
constructor(binds, types) {
|
|
2425
|
+
this.binds = binds;
|
|
2426
|
+
this.types = types;
|
|
2424
2427
|
}
|
|
2425
|
-
|
|
2426
|
-
const
|
|
2427
|
-
|
|
2428
|
-
return styles.join("\n");
|
|
2428
|
+
lookup(name) {
|
|
2429
|
+
const v = (isTypeName(name) ? this.types : this.binds)[name];
|
|
2430
|
+
return v === void 0 ? NEXT : v;
|
|
2429
2431
|
}
|
|
2430
2432
|
};
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
this.macros = {};
|
|
2433
|
+
function computeViewsId(views) {
|
|
2434
|
+
let s = "";
|
|
2435
|
+
let n = views;
|
|
2436
|
+
while (n !== null) {
|
|
2437
|
+
s += n[0];
|
|
2438
|
+
n = n[1];
|
|
2438
2439
|
}
|
|
2439
|
-
|
|
2440
|
-
|
|
2440
|
+
return s === "main" ? "" : s;
|
|
2441
|
+
}
|
|
2442
|
+
var Stack = class _Stack {
|
|
2443
|
+
constructor(fields) {
|
|
2444
|
+
Object.assign(this, fields);
|
|
2441
2445
|
}
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2446
|
+
_with(patch) {
|
|
2447
|
+
return new _Stack({ ...this, ...patch });
|
|
2448
|
+
}
|
|
2449
|
+
// Evaluate every provide the entered component publishes and push them as one
|
|
2450
|
+
// dynBinds frame, keyed by NAME. A value is published together with the absolute
|
|
2451
|
+
// path it lives at: the same declaration is read as `*name` AND resumed at by
|
|
2452
|
+
// `<x render="*name">`, so a consumer needs both halves. Types go in the same
|
|
2453
|
+
// frame's other map. No-op with no provides.
|
|
2454
|
+
_pushProvides() {
|
|
2455
|
+
const comp = this.comps.getCompFor(this.it);
|
|
2456
|
+
if (comp == null) return this;
|
|
2457
|
+
const { provide, provideType } = comp;
|
|
2458
|
+
const binds = {};
|
|
2459
|
+
const types = {};
|
|
2460
|
+
let has = false;
|
|
2461
|
+
const base = this._publishBase();
|
|
2462
|
+
for (const k in provide) {
|
|
2463
|
+
const step = provide[k].toPathItem?.() ?? null;
|
|
2464
|
+
if (step === null) continue;
|
|
2465
|
+
const value = provide[k].eval(this);
|
|
2466
|
+
binds[k] = { value, path: base === null ? null : base.concat([step]) };
|
|
2467
|
+
has = true;
|
|
2449
2468
|
}
|
|
2450
|
-
for (const
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
if (comp !== void 0) this.byName[alias] = comp;
|
|
2454
|
-
else console.warn("alias", alias, "to inexistent component", aliases[alias]);
|
|
2469
|
+
for (const k in provideType) {
|
|
2470
|
+
types[k] = provideType[k];
|
|
2471
|
+
has = true;
|
|
2455
2472
|
}
|
|
2473
|
+
if (!has) return this;
|
|
2474
|
+
return this._with({ dynBinds: [new DynFrame(binds, types), this.dynBinds] });
|
|
2475
|
+
}
|
|
2476
|
+
// The absolute address of the component being entered, or null when this
|
|
2477
|
+
// position cannot be written down as one. Frame-only steps carry bindings and
|
|
2478
|
+
// address nothing, so they are compacted away first; what is left is checked
|
|
2479
|
+
// against the value actually being rendered, because a scope CAN move the
|
|
2480
|
+
// render position without contributing an addressing step (a plain `@each`
|
|
2481
|
+
// body re-binds `it` to the item while its rebuild step is an identity).
|
|
2482
|
+
//
|
|
2483
|
+
// A null base still publishes the VALUE — `*name` reads it fine — but there is
|
|
2484
|
+
// nowhere to resume, so `<x render="*name">` renders nothing rather than
|
|
2485
|
+
// editing whatever happens to live at the address we guessed.
|
|
2486
|
+
_publishBase() {
|
|
2487
|
+
const base = this.renderPath.toTransactionPath().compact();
|
|
2488
|
+
return base.lookup(this.root, NOT_FOUND) === this.it ? base : null;
|
|
2489
|
+
}
|
|
2490
|
+
static root(comps, it, ctx = null) {
|
|
2491
|
+
return new _Stack({
|
|
2492
|
+
comps,
|
|
2493
|
+
root: it,
|
|
2494
|
+
it,
|
|
2495
|
+
binds: [new BindFrame(it, {}, true), null],
|
|
2496
|
+
dynBinds: [new DynFrame({}, {}), null],
|
|
2497
|
+
views: ["main", null],
|
|
2498
|
+
viewsId: "",
|
|
2499
|
+
renderPath: new DispatchPath(),
|
|
2500
|
+
pendingFrame: false,
|
|
2501
|
+
ctx
|
|
2502
|
+
})._pushProvides();
|
|
2503
|
+
}
|
|
2504
|
+
// `renderPath` defaults to this stack's own: an ordinary scope does not move.
|
|
2505
|
+
// `pendingFrame` clears on a component frame (which emits the base) and is
|
|
2506
|
+
// inherited by transparent scopes, which have to carry it to the next boundary.
|
|
2507
|
+
enter(it, bindings = {}, isFrame = true, renderPath = this.renderPath, pendingFrame = null) {
|
|
2508
|
+
const stack = this._with({
|
|
2509
|
+
it,
|
|
2510
|
+
binds: [new BindFrame(it, bindings, isFrame), this.binds],
|
|
2511
|
+
renderPath,
|
|
2512
|
+
pendingFrame: pendingFrame ?? (isFrame ? false : this.pendingFrame)
|
|
2513
|
+
});
|
|
2514
|
+
return isFrame ? stack._pushProvides() : stack;
|
|
2456
2515
|
}
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2516
|
+
pushViewName(name) {
|
|
2517
|
+
const views = [name, this.views];
|
|
2518
|
+
return this._with({ views, viewsId: computeViewsId(views) });
|
|
2519
|
+
}
|
|
2520
|
+
// Published types are stable per scope and would only churn the render cache, so
|
|
2521
|
+
// the cache key covers values alone.
|
|
2522
|
+
_pushDynBindValuesToArray(arr, comp) {
|
|
2523
|
+
for (const k in comp.provide) arr.push(this.lookupDynamic(k));
|
|
2524
|
+
for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
|
|
2525
|
+
}
|
|
2526
|
+
// `*name`: the nearest binding above (including this component's own provides,
|
|
2527
|
+
// pushed on entering it), else a path registered in the component's lexical
|
|
2528
|
+
// scope, else this component's declared default, else null.
|
|
2529
|
+
//
|
|
2530
|
+
// One chain walk and no producer resolution: a lookup names what it WANTS, so the
|
|
2531
|
+
// frame it wants is keyed by that name. The default belongs to the CONSUMER's
|
|
2532
|
+
// declaration and is evaluated against the consumer's stack, which is why it is
|
|
2533
|
+
// consulted only after the whole chain has missed.
|
|
2534
|
+
lookupDynamicLocated(name) {
|
|
2535
|
+
if (isTypeName(name)) return null;
|
|
2536
|
+
const v = lookup(this.dynBinds, name);
|
|
2537
|
+
if (v != null) return v;
|
|
2538
|
+
const comp = this.comps.getCompFor(this.it);
|
|
2539
|
+
if (comp == null) return null;
|
|
2540
|
+
const path2 = comp.scope?.lookupPath?.(name) ?? null;
|
|
2541
|
+
if (path2 !== null) {
|
|
2542
|
+
const value2 = path2.lookup(this.root, NOT_FOUND);
|
|
2543
|
+
if (value2 !== NOT_FOUND) return { value: value2, path: path2 };
|
|
2544
|
+
}
|
|
2545
|
+
const dval = comp.lookup[name] ?? null;
|
|
2546
|
+
if (dval === null) return null;
|
|
2547
|
+
const step = dval.toPathItem?.() ?? null;
|
|
2548
|
+
const value = dval.eval(this);
|
|
2549
|
+
return step === null ? { value, path: null } : { value, path: this.renderPath.toTransactionPath().concat([step]) };
|
|
2550
|
+
}
|
|
2551
|
+
lookupDynamic(name) {
|
|
2552
|
+
if (isTypeName(name)) return lookup(this.dynBinds, name);
|
|
2553
|
+
return this.lookupDynamicLocated(name)?.value ?? null;
|
|
2554
|
+
}
|
|
2555
|
+
lookupBind(name) {
|
|
2556
|
+
return lookup(this.binds, name);
|
|
2557
|
+
}
|
|
2558
|
+
lookupFieldRaw(name) {
|
|
2559
|
+
return this.it[name] ?? null;
|
|
2560
|
+
}
|
|
2561
|
+
lookupMethod(name) {
|
|
2562
|
+
const fn = this.it[name];
|
|
2563
|
+
return fn instanceof Function ? fn.call(this.it) : null;
|
|
2564
|
+
}
|
|
2565
|
+
// The dispatched DOM event / drag info, read only by EventMemberVal's
|
|
2566
|
+
// `e.<member>` handler args. Null outside a live event transaction (`ctx` is
|
|
2567
|
+
// then a send/intent transaction, which carries no `e`).
|
|
2568
|
+
lookupEvent() {
|
|
2569
|
+
return this.ctx?.e ?? null;
|
|
2570
|
+
}
|
|
2571
|
+
lookupDragInfo() {
|
|
2572
|
+
return this.ctx?.dragInfo ?? null;
|
|
2573
|
+
}
|
|
2574
|
+
getHandlerFor(name, key) {
|
|
2575
|
+
return this.comps.getHandlerFor(this.it, name, key);
|
|
2576
|
+
}
|
|
2577
|
+
lookupBestView(views, defaultViewName) {
|
|
2578
|
+
let n = this.views;
|
|
2579
|
+
while (n !== null) {
|
|
2580
|
+
const view = views[n[0]];
|
|
2581
|
+
if (view !== void 0) return view;
|
|
2582
|
+
n = n[1];
|
|
2583
|
+
}
|
|
2584
|
+
return views[defaultViewName];
|
|
2585
|
+
}
|
|
2586
|
+
};
|
|
2587
|
+
var NOT_FOUND = /* @__PURE__ */ Symbol("NOT_FOUND");
|
|
2588
|
+
|
|
2589
|
+
// src/components.js
|
|
2590
|
+
var COMPONENT = /* @__PURE__ */ Symbol.for("tutuca.component");
|
|
2591
|
+
var Components = class {
|
|
2592
|
+
constructor() {
|
|
2593
|
+
this.byId = /* @__PURE__ */ new Map();
|
|
2594
|
+
}
|
|
2595
|
+
registerComponent(Comp) {
|
|
2596
|
+
this.byId.set(Comp.id, Comp);
|
|
2597
|
+
}
|
|
2598
|
+
getComponentForId(id) {
|
|
2599
|
+
return this.byId.get(id) ?? null;
|
|
2600
|
+
}
|
|
2601
|
+
getCompFor(v) {
|
|
2602
|
+
const Comp = v?.constructor;
|
|
2603
|
+
return Comp?.[COMPONENT] ? Comp : null;
|
|
2604
|
+
}
|
|
2605
|
+
getHandlerFor(v, name, key) {
|
|
2606
|
+
return this.getCompFor(v)?.[key][name] ?? null;
|
|
2607
|
+
}
|
|
2608
|
+
// The `lex` leg of an intent's route: the handlers registered on the scope chain of
|
|
2609
|
+
// the component that raised it, innermost first. A list, not a first match — see
|
|
2610
|
+
// ComponentStack.lookupIntentChain.
|
|
2611
|
+
getIntentChainFor(v, name) {
|
|
2612
|
+
return this.getCompFor(v)?.scope.lookupIntentChain(name) ?? [];
|
|
2613
|
+
}
|
|
2614
|
+
compileStyles() {
|
|
2615
|
+
const styles = [];
|
|
2616
|
+
for (const Comp of this.byId.values()) styles.push(Comp.compileStyle());
|
|
2617
|
+
return styles.join("\n");
|
|
2618
|
+
}
|
|
2619
|
+
};
|
|
2620
|
+
var ComponentStack = class _ComponentStack {
|
|
2621
|
+
constructor(comps = new Components(), parent = null) {
|
|
2622
|
+
this.comps = comps;
|
|
2623
|
+
this.parent = parent;
|
|
2624
|
+
this.byName = {};
|
|
2625
|
+
this.intentsByName = {};
|
|
2626
|
+
this.macros = {};
|
|
2627
|
+
this.paths = {};
|
|
2628
|
+
}
|
|
2629
|
+
enter() {
|
|
2630
|
+
return new _ComponentStack(this.comps, this);
|
|
2631
|
+
}
|
|
2632
|
+
registerComponents(comps, opts) {
|
|
2633
|
+
const { aliases = {}, paths } = opts ?? {};
|
|
2634
|
+
if (paths) this.registerPaths(paths);
|
|
2635
|
+
for (let i = 0; i < comps.length; i++) {
|
|
2636
|
+
const Comp = comps[i];
|
|
2637
|
+
Comp.scope = this.enter();
|
|
2638
|
+
this.comps.registerComponent(Comp);
|
|
2639
|
+
this.byName[Comp.name] = Comp;
|
|
2640
|
+
}
|
|
2641
|
+
for (const alias in aliases) {
|
|
2642
|
+
const comp = this.byName[aliases[alias]];
|
|
2643
|
+
console.assert(this.byName[alias] === void 0, "alias overrides component", alias);
|
|
2644
|
+
if (comp !== void 0) this.byName[alias] = comp;
|
|
2645
|
+
else console.warn("alias", alias, "to inexistent component", aliases[alias]);
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
// Register lowercase names as absolute paths from the app state root. A
|
|
2649
|
+
// descendant that declares one in its `lookup` reads and renders `*name` without
|
|
2650
|
+
// anything above it publishing one — which is what makes a session, a theme or a
|
|
2651
|
+
// host-owned value available in its natural registration scope, instead of forcing
|
|
2652
|
+
// an application root whose only job is to `provide` it. Register on a nested
|
|
2653
|
+
// scope to narrow a name; nearest registration wins.
|
|
2654
|
+
//
|
|
2655
|
+
// Uppercase names are ignored: a component TYPE is what `lookupComponent` already
|
|
2656
|
+
// answers, and a type has no path.
|
|
2657
|
+
registerPaths(paths) {
|
|
2658
|
+
for (const name in paths) {
|
|
2659
|
+
if (isTypeName(name)) {
|
|
2660
|
+
console.warn("registerPaths: a type name has no path", name);
|
|
2661
|
+
continue;
|
|
2662
|
+
}
|
|
2663
|
+
this.paths[name] = paths[name].toPath();
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
lookupPath(name) {
|
|
2667
|
+
return this.paths[name] ?? this.parent?.lookupPath(name) ?? null;
|
|
2668
|
+
}
|
|
2669
|
+
registerMacros(macros) {
|
|
2670
|
+
for (const key in macros) {
|
|
2671
|
+
const lower = key.toLowerCase();
|
|
2672
|
+
console.assert(this.macros[lower] === void 0, "macro key collision", lower);
|
|
2673
|
+
this.macros[lower] = macros[key];
|
|
2674
|
+
}
|
|
2463
2675
|
}
|
|
2464
2676
|
getCompFor(v) {
|
|
2465
2677
|
return this.comps.getCompFor(v);
|
|
@@ -2485,97 +2697,82 @@ var ComponentStack = class _ComponentStack {
|
|
|
2485
2697
|
lookupComponent(name) {
|
|
2486
2698
|
return this.byName[name] ?? this.parent?.lookupComponent(name) ?? null;
|
|
2487
2699
|
}
|
|
2488
|
-
//
|
|
2489
|
-
// names
|
|
2490
|
-
//
|
|
2491
|
-
//
|
|
2492
|
-
//
|
|
2493
|
-
|
|
2700
|
+
// Whether anything in this scope chain provides `name`. Existence only: a lookup
|
|
2701
|
+
// names what it WANTS and takes whoever is nearest above it at render time, so
|
|
2702
|
+
// there is no producer to identify — several components may publish one name and
|
|
2703
|
+
// the live render ancestry decides. Used by the linter to tell a lookup that can
|
|
2704
|
+
// be satisfied from one that never will be.
|
|
2705
|
+
hasProvider(name) {
|
|
2494
2706
|
for (const compName in this.byName) {
|
|
2495
|
-
|
|
2496
|
-
if (Comp.provide?.[name] !== void 0) return Comp;
|
|
2707
|
+
if (this.byName[compName].provide?.[name] !== void 0) return true;
|
|
2497
2708
|
}
|
|
2498
|
-
return this.parent?.
|
|
2709
|
+
return this.parent?.hasProvider(name) ?? false;
|
|
2499
2710
|
}
|
|
2500
2711
|
lookupMacro(name) {
|
|
2501
2712
|
return this.macros[name] ?? this.parent?.lookupMacro(name) ?? null;
|
|
2502
2713
|
}
|
|
2503
2714
|
};
|
|
2504
|
-
var ProvideInfo = class {
|
|
2505
|
-
constructor(val) {
|
|
2506
|
-
this.val = val;
|
|
2507
|
-
}
|
|
2508
|
-
};
|
|
2509
|
-
var LookupInfo = class {
|
|
2510
|
-
constructor(val) {
|
|
2511
|
-
this.val = val;
|
|
2512
|
-
}
|
|
2513
|
-
};
|
|
2514
2715
|
var isString = (v) => typeof v === "string";
|
|
2515
|
-
var isTypeName = (s) => {
|
|
2516
|
-
const c = s.charCodeAt(0);
|
|
2517
|
-
return c >= 65 && c <= 90;
|
|
2518
|
-
};
|
|
2519
2716
|
var _rawSpecKeys = "name view style commonStyle globalStyle receive intent alter views provide lookup fields methods statics";
|
|
2520
2717
|
var KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
|
|
2521
2718
|
var _compId = 0;
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2719
|
+
function initComponent(Class, o) {
|
|
2720
|
+
Class[COMPONENT] = Class;
|
|
2721
|
+
Class.id = _compId++;
|
|
2722
|
+
Class.views = { main: new View("main", o.view, o.style) };
|
|
2723
|
+
Class.commonStyle = o.commonStyle ?? "";
|
|
2724
|
+
Class.globalStyle = o.globalStyle ?? "";
|
|
2725
|
+
Class.receive = o.receive ?? {};
|
|
2726
|
+
Class.intent = o.intent ?? {};
|
|
2727
|
+
Class.alter = o.alter ?? {};
|
|
2728
|
+
for (const name in o.views ?? {}) {
|
|
2729
|
+
const v = o.views[name];
|
|
2730
|
+
const { view, style } = isString(v) ? { view: v } : v;
|
|
2731
|
+
Class.views[name] = new View(name, view, style);
|
|
2732
|
+
}
|
|
2733
|
+
Class._rawProvide = o.provide ?? {};
|
|
2734
|
+
Class._rawLookup = o.lookup ?? [];
|
|
2735
|
+
Class.provide = {};
|
|
2736
|
+
Class.provideType = {};
|
|
2737
|
+
Class.lookup = {};
|
|
2738
|
+
Class.scope = null;
|
|
2739
|
+
Class.spec = o;
|
|
2740
|
+
Class.extra = {};
|
|
2741
|
+
for (const key of Object.keys(o)) if (!KNOWN_SPEC_KEYS.has(key)) Class.extra[key] = o[key];
|
|
2742
|
+
return Object.assign(Class, COMPONENT_METHODS);
|
|
2743
|
+
}
|
|
2744
|
+
var COMPONENT_METHODS = {
|
|
2548
2745
|
compile(ParseContext2) {
|
|
2549
2746
|
for (const name in this.views)
|
|
2550
2747
|
this.views[name].compile(new ParseContext2(), this.scope, this.id);
|
|
2551
2748
|
const ctx = this.views.main.ctx;
|
|
2552
2749
|
for (const key in this._rawProvide) {
|
|
2553
2750
|
if (isTypeName(key)) {
|
|
2554
|
-
if (this._rawProvide[key] === "self") this.provideType[key] = this
|
|
2751
|
+
if (this._rawProvide[key] === "self") this.provideType[key] = this;
|
|
2555
2752
|
continue;
|
|
2556
2753
|
}
|
|
2557
2754
|
const val = parseProvide(this._rawProvide[key], ctx);
|
|
2558
|
-
if (val) this.provide[key] =
|
|
2755
|
+
if (val) this.provide[key] = val;
|
|
2559
2756
|
}
|
|
2560
2757
|
for (const entry of this._rawLookup) {
|
|
2561
2758
|
const name = isString(entry) ? entry : isString(entry?.name) ? entry.name : null;
|
|
2562
2759
|
if (name === null) continue;
|
|
2563
2760
|
const defStr = isString(entry?.default) ? entry.default : null;
|
|
2564
|
-
this.lookup[name] =
|
|
2761
|
+
this.lookup[name] = defStr === null ? null : parseField(defStr, ctx);
|
|
2565
2762
|
}
|
|
2566
2763
|
for (const key in this.lookup)
|
|
2567
2764
|
if (this.provide[key] !== void 0)
|
|
2568
2765
|
console.warn("name declared in both provide and lookup", this.name, key);
|
|
2569
|
-
}
|
|
2766
|
+
},
|
|
2570
2767
|
getView(name) {
|
|
2571
2768
|
return this.views[name] ?? this.views.main;
|
|
2572
|
-
}
|
|
2573
|
-
getEventForId(id,
|
|
2574
|
-
return this.getView(
|
|
2575
|
-
}
|
|
2576
|
-
getNodeForId(id,
|
|
2577
|
-
return this.getView(
|
|
2578
|
-
}
|
|
2769
|
+
},
|
|
2770
|
+
getEventForId(id, viewName) {
|
|
2771
|
+
return this.getView(viewName).ctx.getEventForId(id);
|
|
2772
|
+
},
|
|
2773
|
+
getNodeForId(id, viewName) {
|
|
2774
|
+
return this.getView(viewName).ctx.getNodeForId(id);
|
|
2775
|
+
},
|
|
2579
2776
|
compileStyle() {
|
|
2580
2777
|
const { id, commonStyle, globalStyle, views } = this;
|
|
2581
2778
|
const styles = commonStyle ? [`[data-cid="${id}"]{${commonStyle}}`] : [];
|
|
@@ -2588,170 +2785,38 @@ var Component = class {
|
|
|
2588
2785
|
}
|
|
2589
2786
|
};
|
|
2590
2787
|
|
|
2591
|
-
// src/
|
|
2592
|
-
var
|
|
2593
|
-
var
|
|
2594
|
-
var
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
if (leg === "dyn") {
|
|
2599
|
-
const v = dyn();
|
|
2600
|
-
if (v != null) return v;
|
|
2601
|
-
} else if (leg === "lex") {
|
|
2602
|
-
const v = lex();
|
|
2603
|
-
if (v != null) return v;
|
|
2604
|
-
} else {
|
|
2605
|
-
console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
|
|
2606
|
-
}
|
|
2607
|
-
}
|
|
2788
|
+
// src/oo.js
|
|
2789
|
+
var BAD_VALUE = /* @__PURE__ */ Symbol("BadValue");
|
|
2790
|
+
var COERCE_NONE = (_v) => null;
|
|
2791
|
+
var COERCE_MAP = (v) => {
|
|
2792
|
+
if (v instanceof Map) return new Map(v);
|
|
2793
|
+
if (Array.isArray(v) || isPlainObject(v))
|
|
2794
|
+
return new Map(Array.isArray(v) ? v : Object.entries(v));
|
|
2608
2795
|
return null;
|
|
2609
|
-
}
|
|
2610
|
-
function lookup(chain, name, dv = null) {
|
|
2611
|
-
let n = chain;
|
|
2612
|
-
while (n !== null) {
|
|
2613
|
-
const r = n[0].lookup(name);
|
|
2614
|
-
if (r === STOP) return dv;
|
|
2615
|
-
if (r !== NEXT) return r;
|
|
2616
|
-
n = n[1];
|
|
2617
|
-
}
|
|
2618
|
-
return dv;
|
|
2619
|
-
}
|
|
2620
|
-
var BindFrame = class {
|
|
2621
|
-
constructor(it, binds, isFrame) {
|
|
2622
|
-
this.it = it;
|
|
2623
|
-
this.binds = binds;
|
|
2624
|
-
this.isFrame = isFrame;
|
|
2625
|
-
}
|
|
2626
|
-
lookup(name) {
|
|
2627
|
-
const v = this.binds[name];
|
|
2628
|
-
return v === void 0 ? this.isFrame ? STOP : NEXT : v;
|
|
2629
|
-
}
|
|
2630
2796
|
};
|
|
2631
|
-
var
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
}
|
|
2797
|
+
var FIELD_TYPES = {
|
|
2798
|
+
any: [(_v) => true, COERCE_NONE, () => null],
|
|
2799
|
+
text: [(v) => typeof v === "string", (v) => v?.toString?.() ?? "", () => ""],
|
|
2800
|
+
int: [Number.isInteger, (v) => Number.isFinite(v) ? Math.trunc(v) : null, () => 0],
|
|
2801
|
+
float: [Number.isFinite, COERCE_NONE, () => 0],
|
|
2802
|
+
bool: [(v) => typeof v === "boolean", (v) => !!v, () => false],
|
|
2803
|
+
list: [Array.isArray, (v) => Array.isArray(v) ? [...v] : null, () => []],
|
|
2804
|
+
object: [isPlainObject, (v) => isPlainObject(v) ? { ...v } : null, () => ({})],
|
|
2805
|
+
map: [(v) => v instanceof Map, COERCE_MAP, () => /* @__PURE__ */ new Map()],
|
|
2806
|
+
set: [
|
|
2807
|
+
(v) => v instanceof Set,
|
|
2808
|
+
(v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null,
|
|
2809
|
+
() => /* @__PURE__ */ new Set()
|
|
2810
|
+
]
|
|
2639
2811
|
};
|
|
2640
|
-
function computeViewsId(views) {
|
|
2641
|
-
let s = "";
|
|
2642
|
-
let n = views;
|
|
2643
|
-
while (n !== null) {
|
|
2644
|
-
s += n[0];
|
|
2645
|
-
n = n[1];
|
|
2646
|
-
}
|
|
2647
|
-
return s === "main" ? "" : s;
|
|
2648
|
-
}
|
|
2649
|
-
var Stack = class _Stack {
|
|
2650
|
-
constructor(comps, it, binds, dynBinds, views, viewsId, ctx = null) {
|
|
2651
|
-
this.comps = comps;
|
|
2652
|
-
this.it = it;
|
|
2653
|
-
this.binds = binds;
|
|
2654
|
-
this.dynBinds = dynBinds;
|
|
2655
|
-
this.views = views;
|
|
2656
|
-
this.viewsId = viewsId;
|
|
2657
|
-
this.ctx = ctx;
|
|
2658
|
-
}
|
|
2659
|
-
// Evaluate every provide the entered component publishes and push them as one
|
|
2660
|
-
// dynBinds frame, keyed by NAME. Published types go in the same frame: a type name
|
|
2661
|
-
// starts A-Z and a value name does not, so the two namespaces cannot collide and
|
|
2662
|
-
// nearest-ancestor-wins falls out of frame order for both. No-op with no provides.
|
|
2663
|
-
_pushProvides() {
|
|
2664
|
-
const comp = this.comps.getCompFor(this.it);
|
|
2665
|
-
if (comp == null) return this;
|
|
2666
|
-
const { provide, provideType } = comp;
|
|
2667
|
-
const dynObj = {};
|
|
2668
|
-
let has = false;
|
|
2669
|
-
for (const k in provide) {
|
|
2670
|
-
dynObj[k] = provide[k].val.eval(this);
|
|
2671
|
-
has = true;
|
|
2672
|
-
}
|
|
2673
|
-
for (const k in provideType) {
|
|
2674
|
-
dynObj[k] = provideType[k];
|
|
2675
|
-
has = true;
|
|
2676
|
-
}
|
|
2677
|
-
if (!has) return this;
|
|
2678
|
-
const newDynBinds = [new ObjectFrame(dynObj), this.dynBinds];
|
|
2679
|
-
const { comps, it, binds, views, viewsId, ctx } = this;
|
|
2680
|
-
return new _Stack(comps, it, binds, newDynBinds, views, viewsId, ctx);
|
|
2681
|
-
}
|
|
2682
|
-
static root(comps, it, ctx) {
|
|
2683
|
-
const binds = [new BindFrame(it, {}, true), null];
|
|
2684
|
-
const dynBinds = [new ObjectFrame({}), null];
|
|
2685
|
-
const views = ["main", null];
|
|
2686
|
-
return new _Stack(comps, it, binds, dynBinds, views, "", ctx)._pushProvides();
|
|
2687
|
-
}
|
|
2688
|
-
enter(it, bindings = {}, isFrame = true) {
|
|
2689
|
-
const { comps, binds, dynBinds, views, viewsId, ctx } = this;
|
|
2690
|
-
const newBinds = [new BindFrame(it, bindings, isFrame), binds];
|
|
2691
|
-
const stack = new _Stack(comps, it, newBinds, dynBinds, views, viewsId, ctx);
|
|
2692
|
-
return isFrame ? stack._pushProvides() : stack;
|
|
2693
|
-
}
|
|
2694
|
-
pushViewName(name) {
|
|
2695
|
-
const { comps, it, binds, dynBinds, views, ctx } = this;
|
|
2696
|
-
const newViews = [name, views];
|
|
2697
|
-
return new _Stack(comps, it, binds, dynBinds, newViews, computeViewsId(newViews), ctx);
|
|
2698
|
-
}
|
|
2699
|
-
// Published types are stable per scope and would only churn the render cache, so
|
|
2700
|
-
// the cache key covers values alone.
|
|
2701
|
-
_pushDynBindValuesToArray(arr, comp) {
|
|
2702
|
-
for (const k in comp.provide) arr.push(this.lookupDynamic(k));
|
|
2703
|
-
for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
|
|
2704
|
-
}
|
|
2705
|
-
// `*name`: the nearest binding above (including this component's own provides,
|
|
2706
|
-
// pushed on entering it), else this component's declared default, else null.
|
|
2707
|
-
lookupDynamic(name) {
|
|
2708
|
-
const v = lookup(this.dynBinds, name);
|
|
2709
|
-
if (v != null) return v;
|
|
2710
|
-
const comp = this.comps.getCompFor(this.it);
|
|
2711
|
-
return comp?.lookup[name]?.val?.eval(this) ?? null;
|
|
2712
|
-
}
|
|
2713
|
-
lookupBind(name) {
|
|
2714
|
-
return lookup(this.binds, name);
|
|
2715
|
-
}
|
|
2716
|
-
lookupFieldRaw(name) {
|
|
2717
|
-
return this.it[name] ?? null;
|
|
2718
|
-
}
|
|
2719
|
-
lookupMethod(name) {
|
|
2720
|
-
const fn = this.it[name];
|
|
2721
|
-
return fn instanceof Function ? fn.call(this.it) : null;
|
|
2722
|
-
}
|
|
2723
|
-
// The dispatched DOM event / drag info, read only by EventMemberVal's
|
|
2724
|
-
// `e.<member>` handler args. Null outside a live event transaction.
|
|
2725
|
-
lookupEvent() {
|
|
2726
|
-
return this.ctx?.event ?? null;
|
|
2727
|
-
}
|
|
2728
|
-
lookupDragInfo() {
|
|
2729
|
-
return this.ctx?.dragInfo ?? null;
|
|
2730
|
-
}
|
|
2731
|
-
getHandlerFor(name, key) {
|
|
2732
|
-
return this.comps.getHandlerFor(this.it, name, key);
|
|
2733
|
-
}
|
|
2734
|
-
lookupBestView(views, defaultViewName) {
|
|
2735
|
-
let n = this.views;
|
|
2736
|
-
while (n !== null) {
|
|
2737
|
-
const view = views[n[0]];
|
|
2738
|
-
if (view !== void 0) return view;
|
|
2739
|
-
n = n[1];
|
|
2740
|
-
}
|
|
2741
|
-
return views[defaultViewName];
|
|
2742
|
-
}
|
|
2743
|
-
};
|
|
2744
|
-
|
|
2745
|
-
// src/oo.js
|
|
2746
|
-
var BAD_VALUE = /* @__PURE__ */ Symbol("BadValue");
|
|
2747
|
-
var nullCoercer = (v) => v;
|
|
2748
2812
|
var Field = class {
|
|
2749
|
-
|
|
2813
|
+
// `type` names an entry of FIELD_TYPES; `defaultValue` falls back to the type's own.
|
|
2814
|
+
constructor(type, name, defaultValue, [typeCheck, coercer, makeDefault] = FIELD_TYPES[type]) {
|
|
2750
2815
|
this.type = type;
|
|
2751
2816
|
this.name = name;
|
|
2752
2817
|
this.typeCheck = typeCheck;
|
|
2753
2818
|
this.coercer = coercer;
|
|
2754
|
-
this.defaultValue = defaultValue;
|
|
2819
|
+
this.defaultValue = defaultValue === void 0 ? makeDefault() : defaultValue;
|
|
2755
2820
|
}
|
|
2756
2821
|
isValid(v) {
|
|
2757
2822
|
return this.typeCheck(v);
|
|
@@ -2765,81 +2830,14 @@ var Field = class {
|
|
|
2765
2830
|
return this.coerceOr(v, this.defaultValue);
|
|
2766
2831
|
}
|
|
2767
2832
|
};
|
|
2768
|
-
var
|
|
2769
|
-
var CHECK_TYPE_INT = Number.isInteger;
|
|
2770
|
-
var CHECK_TYPE_FLOAT = Number.isFinite;
|
|
2771
|
-
var CHECK_TYPE_BOOL = (v) => typeof v === "boolean";
|
|
2772
|
-
var CHECK_TYPE_STRING = (v) => typeof v === "string";
|
|
2773
|
-
var CHECK_TYPE_LIST = Array.isArray;
|
|
2774
|
-
var CHECK_TYPE_OBJECT = isPlainObject;
|
|
2775
|
-
var CHECK_TYPE_MAP = (v) => v instanceof Map;
|
|
2776
|
-
var CHECK_TYPE_SET = (v) => v instanceof Set;
|
|
2777
|
-
var COERCE_NONE = (_v) => null;
|
|
2778
|
-
var COERCE_BOOL = (v) => !!v;
|
|
2779
|
-
var COERCE_STRING = (v) => v?.toString?.() ?? "";
|
|
2780
|
-
var COERCE_INT = (v) => Number.isFinite(v) ? Math.trunc(v) : null;
|
|
2781
|
-
var COERCE_LIST = (v) => Array.isArray(v) ? [...v] : null;
|
|
2782
|
-
var COERCE_OBJECT = (v) => isPlainObject(v) ? { ...v } : null;
|
|
2783
|
-
var COERCE_MAP = (v) => {
|
|
2784
|
-
if (v instanceof Map) return new Map(v);
|
|
2785
|
-
if (Array.isArray(v) || isPlainObject(v))
|
|
2786
|
-
return new Map(Array.isArray(v) ? v : Object.entries(v));
|
|
2787
|
-
return null;
|
|
2788
|
-
};
|
|
2789
|
-
var COERCE_SET = (v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null;
|
|
2790
|
-
var FieldBool = class extends Field {
|
|
2791
|
-
constructor(name, defaultValue = false) {
|
|
2792
|
-
super("bool", name, CHECK_TYPE_BOOL, COERCE_BOOL, defaultValue);
|
|
2793
|
-
}
|
|
2794
|
-
};
|
|
2795
|
-
var FieldAny = class extends Field {
|
|
2796
|
-
constructor(name, defaultValue = null) {
|
|
2797
|
-
super("any", name, CHECK_TYPE_ANY, nullCoercer, defaultValue);
|
|
2798
|
-
}
|
|
2799
|
-
};
|
|
2800
|
-
var FieldString = class extends Field {
|
|
2801
|
-
constructor(name, defaultValue = "") {
|
|
2802
|
-
super("text", name, CHECK_TYPE_STRING, COERCE_STRING, defaultValue);
|
|
2803
|
-
}
|
|
2804
|
-
};
|
|
2805
|
-
var FieldInt = class extends Field {
|
|
2806
|
-
constructor(name, defaultValue = 0) {
|
|
2807
|
-
super("int", name, CHECK_TYPE_INT, COERCE_INT, defaultValue);
|
|
2808
|
-
}
|
|
2809
|
-
};
|
|
2810
|
-
var FieldFloat = class extends Field {
|
|
2811
|
-
constructor(name, defaultValue = 0) {
|
|
2812
|
-
super("float", name, CHECK_TYPE_FLOAT, COERCE_NONE, defaultValue);
|
|
2813
|
-
}
|
|
2814
|
-
};
|
|
2815
|
-
var metaOf = (v) => v?.constructor?.[COMPONENT] ?? v?.constructor?.getMetaClass?.();
|
|
2833
|
+
var metaOf = (v) => v?.constructor?.getMetaClass?.();
|
|
2816
2834
|
var getTypeName = (v) => metaOf(v)?.name ?? null;
|
|
2817
2835
|
var FieldComp = class extends Field {
|
|
2818
2836
|
constructor(type, name, args) {
|
|
2819
|
-
super(type, name, (v) => getTypeName(v) === type,
|
|
2837
|
+
super(type, name, null, [(v) => getTypeName(v) === type, COERCE_NONE, () => null]);
|
|
2820
2838
|
this.args = args;
|
|
2821
2839
|
}
|
|
2822
2840
|
};
|
|
2823
|
-
var FieldList = class extends Field {
|
|
2824
|
-
constructor(name, defaultValue = []) {
|
|
2825
|
-
super("list", name, CHECK_TYPE_LIST, COERCE_LIST, defaultValue);
|
|
2826
|
-
}
|
|
2827
|
-
};
|
|
2828
|
-
var FieldObject = class extends Field {
|
|
2829
|
-
constructor(name, defaultValue = {}) {
|
|
2830
|
-
super("object", name, CHECK_TYPE_OBJECT, COERCE_OBJECT, defaultValue);
|
|
2831
|
-
}
|
|
2832
|
-
};
|
|
2833
|
-
var FieldMap = class extends Field {
|
|
2834
|
-
constructor(name, defaultValue = /* @__PURE__ */ new Map()) {
|
|
2835
|
-
super("map", name, CHECK_TYPE_MAP, COERCE_MAP, defaultValue);
|
|
2836
|
-
}
|
|
2837
|
-
};
|
|
2838
|
-
var FieldSet = class extends Field {
|
|
2839
|
-
constructor(name, defaultValue = /* @__PURE__ */ new Set()) {
|
|
2840
|
-
super("set", name, CHECK_TYPE_SET, COERCE_SET, defaultValue);
|
|
2841
|
-
}
|
|
2842
|
-
};
|
|
2843
2841
|
function mkCompField(field, scope, args) {
|
|
2844
2842
|
const Comp = scope?.lookupComponent(field.type) ?? null;
|
|
2845
2843
|
if (Comp === null)
|
|
@@ -2848,112 +2846,66 @@ function mkCompField(field, scope, args) {
|
|
|
2848
2846
|
);
|
|
2849
2847
|
return Comp?.make({ ...field.args, ...args }, { scope }) ?? null;
|
|
2850
2848
|
}
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
const
|
|
2861
|
-
const
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
}
|
|
2869
|
-
}
|
|
2870
|
-
}[name];
|
|
2871
|
-
Class[immer_exports.immerable] = true;
|
|
2872
|
-
Object.assign(Class.prototype, _methods);
|
|
2873
|
-
const metaClass = { fields, name, methods: _methods };
|
|
2874
|
-
Object.assign(
|
|
2875
|
-
Class,
|
|
2876
|
-
{
|
|
2877
|
-
getMetaClass: () => metaClass,
|
|
2878
|
-
make(inArgs = {}, opts = {}) {
|
|
2879
|
-
const args = {};
|
|
2880
|
-
const scope = opts.scope ?? this[COMPONENT]?.scope ?? this.scope;
|
|
2881
|
-
for (const key in inArgs) {
|
|
2882
|
-
const field = fields[key];
|
|
2883
|
-
if (compFields.has(key)) args[key] = mkCompField(field, scope, inArgs[key]);
|
|
2884
|
-
else if (field === void 0)
|
|
2885
|
-
console.warn("extra argument to constructor:", name, key, inArgs);
|
|
2886
|
-
else args[key] = field.coerceOrDefault(inArgs[key]);
|
|
2887
|
-
}
|
|
2888
|
-
for (const key of compFields)
|
|
2889
|
-
if (args[key] === void 0) args[key] = mkCompField(fields[key], scope, inArgs[key]);
|
|
2890
|
-
return (0, immer_exports.freeze)(new this(args), true);
|
|
2891
|
-
}
|
|
2892
|
-
},
|
|
2893
|
-
this._statics
|
|
2894
|
-
);
|
|
2895
|
-
return Class;
|
|
2896
|
-
}
|
|
2897
|
-
methods(proto) {
|
|
2898
|
-
for (const k in proto) this._methods[k] = proto[k];
|
|
2899
|
-
}
|
|
2900
|
-
statics(proto) {
|
|
2901
|
-
for (const k in proto) this._statics[k] = proto[k];
|
|
2902
|
-
}
|
|
2903
|
-
addField(name, dval, FieldCls) {
|
|
2904
|
-
const field = new FieldCls(name, dval);
|
|
2905
|
-
this.fields[name] = field;
|
|
2906
|
-
return field;
|
|
2907
|
-
}
|
|
2908
|
-
addCompField(name, type, args) {
|
|
2909
|
-
const field = new FieldComp(type, name, args);
|
|
2910
|
-
this.compFields.add(name);
|
|
2911
|
-
this.fields[name] = field;
|
|
2912
|
-
return field;
|
|
2913
|
-
}
|
|
2914
|
-
};
|
|
2915
|
-
var FIELD_CLASS = /* @__PURE__ */ Symbol.for("tutuca.fieldClass");
|
|
2916
|
-
var fieldsByTypeName = {
|
|
2917
|
-
text: FieldString,
|
|
2918
|
-
int: FieldInt,
|
|
2919
|
-
float: FieldFloat,
|
|
2920
|
-
bool: FieldBool,
|
|
2921
|
-
list: FieldList,
|
|
2922
|
-
object: FieldObject,
|
|
2923
|
-
map: FieldMap,
|
|
2924
|
-
set: FieldSet,
|
|
2925
|
-
any: FieldAny
|
|
2926
|
-
};
|
|
2927
|
-
function fieldFromDescriptor(name, value) {
|
|
2928
|
-
const FieldCls = fieldsByTypeName[value.type] ?? FieldAny;
|
|
2929
|
-
const probe = new FieldCls(name);
|
|
2930
|
-
return [FieldCls, probe.coerceOr(value.defaultValue, probe.defaultValue)];
|
|
2849
|
+
function fieldFromSpec(name, value) {
|
|
2850
|
+
const type = typeof value;
|
|
2851
|
+
if (type === "string") return new Field("text", name, value);
|
|
2852
|
+
if (type === "number") return new Field("float", name, value);
|
|
2853
|
+
if (type === "boolean") return new Field("bool", name, value);
|
|
2854
|
+
if (Array.isArray(value)) return new Field("list", name, [...value]);
|
|
2855
|
+
if (value instanceof Set) return new Field("set", name, new Set(value));
|
|
2856
|
+
if (value instanceof Map) return new Field("map", name, new Map(value));
|
|
2857
|
+
if (value?.type && Object.hasOwn(value, "defaultValue")) {
|
|
2858
|
+
const t = value.type in FIELD_TYPES ? value.type : "any";
|
|
2859
|
+
const probe = new Field(t, name);
|
|
2860
|
+
return new Field(t, name, probe.coerceOr(value.defaultValue, probe.defaultValue));
|
|
2861
|
+
}
|
|
2862
|
+
if (value?.component && value?.args !== void 0)
|
|
2863
|
+
return new FieldComp(value.component, name, value.args);
|
|
2864
|
+
if (isPlainObject(value)) return new Field("object", name, { ...value });
|
|
2865
|
+
return new Field("any", name, value);
|
|
2931
2866
|
}
|
|
2932
|
-
function classFromData(name, { fields = {}, methods, statics }) {
|
|
2933
|
-
const
|
|
2934
|
-
for (const
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
else if (value?.type && Object.hasOwn(value, "defaultValue")) {
|
|
2944
|
-
const [FieldCls, dval] = fieldFromDescriptor(field, value);
|
|
2945
|
-
b.addField(field, dval, FieldCls);
|
|
2946
|
-
} else if (value?.component && value?.args !== void 0)
|
|
2947
|
-
b.addCompField(field, value.component, value.args);
|
|
2948
|
-
else if (isPlainObject(value)) b.addField(field, { ...value }, FieldObject);
|
|
2949
|
-
else {
|
|
2950
|
-
const FieldCls = value?.[FIELD_CLASS] ?? FieldAny;
|
|
2951
|
-
b.addField(field, value, FieldCls);
|
|
2867
|
+
function classFromData(name, { fields: spec = {}, methods = {}, statics = {} }) {
|
|
2868
|
+
const fields = {};
|
|
2869
|
+
for (const key in spec) fields[key] = fieldFromSpec(key, spec[key]);
|
|
2870
|
+
const defaults = Object.fromEntries(
|
|
2871
|
+
Object.entries(fields).map(([key, field]) => [key, field.defaultValue])
|
|
2872
|
+
);
|
|
2873
|
+
const Class = {
|
|
2874
|
+
[name]: class {
|
|
2875
|
+
constructor(values = {}) {
|
|
2876
|
+
Object.assign(this, defaults, values);
|
|
2877
|
+
}
|
|
2952
2878
|
}
|
|
2953
|
-
}
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2879
|
+
}[name];
|
|
2880
|
+
Class[immer_exports.immerable] = true;
|
|
2881
|
+
Object.assign(Class.prototype, methods);
|
|
2882
|
+
Object.assign(
|
|
2883
|
+
Class,
|
|
2884
|
+
{
|
|
2885
|
+
fields,
|
|
2886
|
+
methods,
|
|
2887
|
+
// The documented way for instance code to reach its class's fields
|
|
2888
|
+
// (`this.constructor.getMetaClass().fields.x.defaultValue`).
|
|
2889
|
+
getMetaClass: () => Class,
|
|
2890
|
+
make(inArgs = {}, opts = {}) {
|
|
2891
|
+
const args = {};
|
|
2892
|
+
const scope = opts.scope ?? this.scope;
|
|
2893
|
+
for (const key in inArgs) {
|
|
2894
|
+
const field = fields[key];
|
|
2895
|
+
if (field === void 0)
|
|
2896
|
+
console.warn("extra argument to constructor:", name, key, inArgs);
|
|
2897
|
+
else if (field instanceof FieldComp) args[key] = mkCompField(field, scope, inArgs[key]);
|
|
2898
|
+
else args[key] = field.coerceOrDefault(inArgs[key]);
|
|
2899
|
+
}
|
|
2900
|
+
for (const key in fields)
|
|
2901
|
+
if (fields[key] instanceof FieldComp && args[key] === void 0)
|
|
2902
|
+
args[key] = mkCompField(fields[key], scope, void 0);
|
|
2903
|
+
return (0, immer_exports.freeze)(new this(args), true);
|
|
2904
|
+
}
|
|
2905
|
+
},
|
|
2906
|
+
statics
|
|
2907
|
+
);
|
|
2908
|
+
return Class;
|
|
2957
2909
|
}
|
|
2958
2910
|
function validateDraftFields(current, draft) {
|
|
2959
2911
|
const meta = metaOf(current);
|
|
@@ -2969,14 +2921,11 @@ function validateDraftFields(current, draft) {
|
|
|
2969
2921
|
}
|
|
2970
2922
|
}
|
|
2971
2923
|
}
|
|
2972
|
-
var META_KEYS = "name id fields methods views receive intent alter provide provideType lookup spec extra commonStyle globalStyle scope _rawProvide _rawLookup".split(
|
|
2973
|
-
" "
|
|
2974
|
-
);
|
|
2975
2924
|
var RESERVED_COMPONENT_STATICS = /* @__PURE__ */ new Set([
|
|
2976
|
-
...
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
...Object.
|
|
2925
|
+
..."name id fields methods views receive intent alter provide provideType lookup spec extra commonStyle globalStyle scope _rawProvide _rawLookup make getMetaClass".split(
|
|
2926
|
+
" "
|
|
2927
|
+
),
|
|
2928
|
+
...Object.keys(COMPONENT_METHODS)
|
|
2980
2929
|
]);
|
|
2981
2930
|
function assertNoReservedComponentStatics(statics = {}) {
|
|
2982
2931
|
for (const name in statics) {
|
|
@@ -2985,32 +2934,10 @@ function assertNoReservedComponentStatics(statics = {}) {
|
|
|
2985
2934
|
}
|
|
2986
2935
|
}
|
|
2987
2936
|
}
|
|
2988
|
-
|
|
2937
|
+
function component(opts) {
|
|
2989
2938
|
assertNoReservedComponentStatics(opts.statics);
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
const metaClass = Class.getMetaClass();
|
|
2993
|
-
comp.fields = metaClass.fields;
|
|
2994
|
-
comp.methods = metaClass.methods;
|
|
2995
|
-
Class.getMetaClass = () => comp;
|
|
2996
|
-
Class[COMPONENT] = comp;
|
|
2997
|
-
for (const key of META_KEYS)
|
|
2998
|
-
if (!Object.hasOwn(Class, key))
|
|
2999
|
-
Object.defineProperty(Class, key, {
|
|
3000
|
-
get() {
|
|
3001
|
-
return comp[key];
|
|
3002
|
-
},
|
|
3003
|
-
set(v) {
|
|
3004
|
-
comp[key] = v;
|
|
3005
|
-
},
|
|
3006
|
-
configurable: true
|
|
3007
|
-
});
|
|
3008
|
-
for (const key of Object.getOwnPropertyNames(Component.prototype))
|
|
3009
|
-
if (key !== "constructor" && !Object.hasOwn(Class, key))
|
|
3010
|
-
Class[key] = (...args) => comp[key](...args);
|
|
3011
|
-
return Class;
|
|
3012
|
-
};
|
|
3013
|
-
var component = (opts) => Component.fromSpec(opts);
|
|
2939
|
+
return initComponent(classFromData(opts.name ?? "UnkComp", opts), opts);
|
|
2940
|
+
}
|
|
3014
2941
|
|
|
3015
2942
|
// src/transactor.js
|
|
3016
2943
|
var State = class {
|
|
@@ -3051,14 +2978,7 @@ var Transactor = class {
|
|
|
3051
2978
|
// carry: kind, name, args, path, pathKeys, targetPath, handler, handlerName,
|
|
3052
2979
|
// matched, before, after, parent, timestamp. Purely observational.
|
|
3053
2980
|
observe(cb) {
|
|
3054
|
-
this._observers
|
|
3055
|
-
return () => {
|
|
3056
|
-
const i = this._observers.indexOf(cb);
|
|
3057
|
-
if (i !== -1) this._observers.splice(i, 1);
|
|
3058
|
-
};
|
|
3059
|
-
}
|
|
3060
|
-
_emit(record) {
|
|
3061
|
-
for (const cb of this._observers) cb(record);
|
|
2981
|
+
return subscribe(this._observers, cb);
|
|
3062
2982
|
}
|
|
3063
2983
|
// Record a refusal: a dispatch the runtime could not carry out. Kinds are
|
|
3064
2984
|
// open strings; today the runtime raises NO_HANDLER (a receive name with no
|
|
@@ -3077,20 +2997,16 @@ var Transactor = class {
|
|
|
3077
2997
|
// Subscribe to refusal records as they happen. Returns an unsubscribe fn,
|
|
3078
2998
|
// like observe().
|
|
3079
2999
|
observeRefusals(cb) {
|
|
3080
|
-
this._refusalObservers
|
|
3081
|
-
return () => {
|
|
3082
|
-
const i = this._refusalObservers.indexOf(cb);
|
|
3083
|
-
if (i !== -1) this._refusalObservers.splice(i, 1);
|
|
3084
|
-
};
|
|
3000
|
+
return subscribe(this._refusalObservers, cb);
|
|
3085
3001
|
}
|
|
3086
3002
|
// Build and dispatch one observer record. Pins field-resolved keys (e.g. a
|
|
3087
3003
|
// `.a[.selId]` render target reconstructed from a DOM event) against the root the
|
|
3088
3004
|
// handler read, so pathKeys carries concrete keys instead of dropping the dynamic
|
|
3089
3005
|
// step. No-op when nobody is observing.
|
|
3090
|
-
_emitRecord(root, { kind, name, args, path, targetPath, handler, handlerName, matched, before, after, parent }) {
|
|
3006
|
+
_emitRecord(root, { kind, name, args, path: path2, targetPath, handler, handlerName, matched, before, after, parent }) {
|
|
3091
3007
|
if (this._observers.length === 0) return;
|
|
3092
|
-
const pinned =
|
|
3093
|
-
|
|
3008
|
+
const pinned = path2.pinKeys(root);
|
|
3009
|
+
const record = {
|
|
3094
3010
|
kind,
|
|
3095
3011
|
name,
|
|
3096
3012
|
args: args ?? null,
|
|
@@ -3104,14 +3020,14 @@ var Transactor = class {
|
|
|
3104
3020
|
after,
|
|
3105
3021
|
parent,
|
|
3106
3022
|
timestamp: Date.now()
|
|
3107
|
-
}
|
|
3023
|
+
};
|
|
3024
|
+
for (const cb of this._observers) cb(record);
|
|
3108
3025
|
}
|
|
3109
3026
|
// The observer record for a settled transaction. The resolved handler
|
|
3110
3027
|
// (`_resolvedHandler`/`_matched`) and per-leaf before/after (`_before`/`_after`)
|
|
3111
3028
|
// were captured while the handler ran (see callHandler / Transaction.run).
|
|
3112
3029
|
_emitTransaction(transaction, root) {
|
|
3113
3030
|
if (this._observers.length === 0) return;
|
|
3114
|
-
if (transaction._resolvedHandler === void 0) return;
|
|
3115
3031
|
this._emitRecord(root, {
|
|
3116
3032
|
kind: transaction.observeKind,
|
|
3117
3033
|
name: transaction.observeName,
|
|
@@ -3125,33 +3041,29 @@ var Transactor = class {
|
|
|
3125
3041
|
parent: transaction.parentTransaction
|
|
3126
3042
|
});
|
|
3127
3043
|
}
|
|
3128
|
-
// Make `child` a tracked unit of `parent`'s subtree: the parent's completion stays open
|
|
3129
|
-
// until the child's *whole* subtree settles. Tracking happens at dispatch time — during
|
|
3130
|
-
// the parent's handler or afterTransaction — while the parent's self-unit is still held,
|
|
3131
|
-
// so the parent counter can't reach zero before the child is registered. Returns `child`.
|
|
3132
|
-
_link(child, parent) {
|
|
3133
|
-
if (parent) {
|
|
3134
|
-
const release = parent.completion.track();
|
|
3135
|
-
child.completion.whenSubtreeSettled().then(release);
|
|
3136
|
-
}
|
|
3137
|
-
return child;
|
|
3138
|
-
}
|
|
3139
3044
|
// `origin` is where the message came FROM, pinned now for the same reason an
|
|
3140
3045
|
// intent's answerPath is: a reply must reach the sender that asked even if a key
|
|
3141
3046
|
// moved while the message was in flight. Null when nobody is waiting — a host
|
|
3142
3047
|
// sendAtRoot or a view's own `@on.*` — and ctx.sendReply refuses on that.
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3048
|
+
//
|
|
3049
|
+
// The parent's subtree stays open until this message's whole subtree settles. The
|
|
3050
|
+
// unit is taken now, at dispatch — during the parent's handler or afterTransaction,
|
|
3051
|
+
// while the parent's self-unit is still held — so its counter can't reach zero first.
|
|
3052
|
+
pushSend(path2, name, args = [], parent = null, origin = null, txnPath = null) {
|
|
3053
|
+
const t = new SendEvent(path2, this, name, args, parent);
|
|
3054
|
+
t.txnPath = txnPath;
|
|
3055
|
+
t.origin = origin;
|
|
3056
|
+
t.originPinned = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
|
|
3146
3057
|
this.pushTransaction(t);
|
|
3147
|
-
|
|
3058
|
+
if (parent) t.completion.carry(parent.completion.track());
|
|
3059
|
+
return t;
|
|
3148
3060
|
}
|
|
3149
3061
|
// Raise an intent: a job the sender did not address, walked along a route until
|
|
3150
3062
|
// something answers. `opts.route` is a list of legs (see DEFAULT_ROUTE) and
|
|
3151
3063
|
// `opts.livePath` opts out of pinning the answer's destination keys.
|
|
3152
|
-
pushIntent(
|
|
3064
|
+
pushIntent(path2, name, args = [], opts = {}, parent = null) {
|
|
3153
3065
|
const release = parent ? parent.completion.track() : null;
|
|
3154
|
-
const walk = new IntentWalk(this,
|
|
3066
|
+
const walk = new IntentWalk(this, path2, name, args, opts, parent, release);
|
|
3155
3067
|
walk.advance();
|
|
3156
3068
|
return walk;
|
|
3157
3069
|
}
|
|
@@ -3168,7 +3080,7 @@ var Transactor = class {
|
|
|
3168
3080
|
return this.transactions.length > 0;
|
|
3169
3081
|
}
|
|
3170
3082
|
transactNext() {
|
|
3171
|
-
|
|
3083
|
+
this.transact(this.transactions.shift());
|
|
3172
3084
|
}
|
|
3173
3085
|
transact(transaction) {
|
|
3174
3086
|
try {
|
|
@@ -3181,20 +3093,31 @@ var Transactor = class {
|
|
|
3181
3093
|
} else console.warn("undefined new state", { curState, transaction });
|
|
3182
3094
|
} finally {
|
|
3183
3095
|
transaction.ensureWalkAdvanced?.();
|
|
3184
|
-
transaction._completion?.
|
|
3185
|
-
transaction._completion?.releaseSelf();
|
|
3096
|
+
transaction._completion?.finish();
|
|
3186
3097
|
}
|
|
3187
3098
|
}
|
|
3188
|
-
transactInputNow(
|
|
3189
|
-
this.transact(new InputEvent(
|
|
3099
|
+
transactInputNow(path2, event, eventHandler, dragInfo) {
|
|
3100
|
+
this.transact(new InputEvent(path2, event, eventHandler, this, dragInfo));
|
|
3190
3101
|
}
|
|
3191
3102
|
};
|
|
3192
3103
|
function nullHandler() {
|
|
3193
3104
|
return this;
|
|
3194
3105
|
}
|
|
3106
|
+
function subscribe(list, cb) {
|
|
3107
|
+
list.push(cb);
|
|
3108
|
+
return () => {
|
|
3109
|
+
const i = list.indexOf(cb);
|
|
3110
|
+
if (i !== -1) list.splice(i, 1);
|
|
3111
|
+
};
|
|
3112
|
+
}
|
|
3113
|
+
function warnUnknownLeg(what, leg) {
|
|
3114
|
+
console.warn(`unknown ${what} route leg`, leg, '- expected "dyn" or "lex"');
|
|
3115
|
+
return null;
|
|
3116
|
+
}
|
|
3195
3117
|
var Transaction = class {
|
|
3196
|
-
constructor(
|
|
3197
|
-
this.path =
|
|
3118
|
+
constructor(path2, transactor, parentTransaction = null) {
|
|
3119
|
+
this.path = path2;
|
|
3120
|
+
this.txnPath = null;
|
|
3198
3121
|
this.transactor = transactor;
|
|
3199
3122
|
this.parentTransaction = parentTransaction;
|
|
3200
3123
|
this._completion = null;
|
|
@@ -3214,39 +3137,41 @@ var Transaction = class {
|
|
|
3214
3137
|
whenSubtreeSettled() {
|
|
3215
3138
|
return this.completion.whenSubtreeSettled();
|
|
3216
3139
|
}
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
//
|
|
3220
|
-
// rather than throwing, so a stray call from the wrong place is a message and not a
|
|
3221
|
-
// crash. `walk` is undefined here, which is how ctx.reply/ctx.fail tell the two apart.
|
|
3222
|
-
stop() {
|
|
3223
|
-
warnNotIntent("stop");
|
|
3224
|
-
}
|
|
3225
|
-
forward(_opts) {
|
|
3226
|
-
console.warn('ctx.forward() needs a "receive" or "intent" handler - ignored');
|
|
3227
|
-
}
|
|
3228
|
-
// The kind reported to observers (see Transactor.observe); null on the base.
|
|
3229
|
-
get observeKind() {
|
|
3230
|
-
return null;
|
|
3231
|
-
}
|
|
3232
|
-
// The name reported to observers; null on the base. Overridden by NameArgs (the
|
|
3233
|
-
// dispatched message name) and InputEvent (the DOM event type). Kept separate from
|
|
3234
|
-
// `name`/`ctx.name` so it can't change handler-visible behavior.
|
|
3235
|
-
get observeName() {
|
|
3236
|
-
return null;
|
|
3237
|
-
}
|
|
3140
|
+
// Every transaction kind defines `observeKind` / `observeName` (what observers see;
|
|
3141
|
+
// kept apart from `name`/`ctx.name` so they can't change handler-visible behavior),
|
|
3142
|
+
// `getHandlerAndArgs(root, instance, comps)`, and the three `_forward*` hooks below.
|
|
3238
3143
|
callHandler(root, instance, draft, comps) {
|
|
3239
3144
|
const [handler, args] = this.getHandlerAndArgs(root, instance, comps);
|
|
3240
3145
|
this._resolvedHandler = handler;
|
|
3241
3146
|
return handler.apply(instance, [draft, ...args]);
|
|
3242
3147
|
}
|
|
3243
|
-
|
|
3244
|
-
|
|
3148
|
+
// `forward` is where a name can LEAVE the component: the message being handled is
|
|
3149
|
+
// raised again as an INTENT under the same name (`_forwardName()`) with the same
|
|
3150
|
+
// arguments unless `opts.args` says otherwise, from the position `_forwardPath()`
|
|
3151
|
+
// names. Deferred to afterTransaction so the state the handler produced is in place
|
|
3152
|
+
// before the walk starts. (`IntentEvent` overrides both: in an intent body, forward
|
|
3153
|
+
// amends the walk that is already running.)
|
|
3154
|
+
forward(opts) {
|
|
3155
|
+
this._forward = opts ?? {};
|
|
3245
3156
|
}
|
|
3246
|
-
|
|
3247
|
-
|
|
3157
|
+
afterTransaction() {
|
|
3158
|
+
const f = this._forward;
|
|
3159
|
+
if (f === void 0) return;
|
|
3160
|
+
this._forward = void 0;
|
|
3161
|
+
const name = this._forwardName();
|
|
3162
|
+
if (name === void 0) {
|
|
3163
|
+
this.transactor.refuse("FORWARD_NO_NAME", {});
|
|
3164
|
+
return;
|
|
3165
|
+
}
|
|
3166
|
+
const { args = this._forwardArgs(), ...rest } = f;
|
|
3167
|
+
this.transactor.pushIntent(this._forwardPath(), name, args, rest, this);
|
|
3168
|
+
}
|
|
3169
|
+
// The path used to apply the mutation: the ACTIVE frame of the dispatch path, so
|
|
3170
|
+
// an event inside a `<x render="*name">` subtree updates the value where it
|
|
3171
|
+
// really lives (the dispatch `this.path` keeps the visual callers, for bubbling).
|
|
3172
|
+
// `txnPath`, when pinned by the sender, wins.
|
|
3248
3173
|
getTransactionPath() {
|
|
3249
|
-
return this.path.toTransactionPath()
|
|
3174
|
+
return this.txnPath ?? this.path.toTransactionPath();
|
|
3250
3175
|
}
|
|
3251
3176
|
run(curRoot, comps) {
|
|
3252
3177
|
const txnPath = this.getTransactionPath();
|
|
@@ -3263,19 +3188,27 @@ var Transaction = class {
|
|
|
3263
3188
|
}
|
|
3264
3189
|
};
|
|
3265
3190
|
var InputEvent = class extends Transaction {
|
|
3266
|
-
constructor(
|
|
3267
|
-
super(
|
|
3191
|
+
constructor(path2, e, handler, transactor, dragInfo) {
|
|
3192
|
+
super(path2, transactor);
|
|
3268
3193
|
this.e = e;
|
|
3269
3194
|
this.handler = handler;
|
|
3270
3195
|
this.dragInfo = dragInfo;
|
|
3271
3196
|
this._dispatchPath = null;
|
|
3272
3197
|
}
|
|
3273
|
-
// Frame steps removed,
|
|
3274
|
-
//
|
|
3198
|
+
// Frame-only steps removed, one step per crossed component kept — inside every
|
|
3199
|
+
// continuation frame independently, so bubbling it visits every component and
|
|
3200
|
+
// then returns to the caller that wrote the `*name`.
|
|
3275
3201
|
get dispatchPath() {
|
|
3276
3202
|
this._dispatchPath ??= this.path.compact();
|
|
3277
3203
|
return this._dispatchPath;
|
|
3278
3204
|
}
|
|
3205
|
+
// Only a DOM event's path carries frame-only bind steps (they replay the handler's
|
|
3206
|
+
// arguments, but address no state); the compacted path is where the mutation lands,
|
|
3207
|
+
// so a handler inside @each still updates the component that owns the view. Every
|
|
3208
|
+
// other transaction is dispatched at an already-compacted position.
|
|
3209
|
+
getTransactionPath() {
|
|
3210
|
+
return this.dispatchPath.toTransactionPath();
|
|
3211
|
+
}
|
|
3279
3212
|
// A DOM event is an ADDRESSED message like any other — it reaches the component that
|
|
3280
3213
|
// owns the view and stops. It keeps its own class only because it resolves its handler
|
|
3281
3214
|
// from the compiled view and runs synchronously, not because it is a different channel.
|
|
@@ -3285,47 +3218,36 @@ var InputEvent = class extends Transaction {
|
|
|
3285
3218
|
get observeName() {
|
|
3286
3219
|
return this.e?.type ?? null;
|
|
3287
3220
|
}
|
|
3288
|
-
// A view's name is a message
|
|
3289
|
-
//
|
|
3290
|
-
//
|
|
3291
|
-
//
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
}
|
|
3295
|
-
afterTransaction() {
|
|
3296
|
-
const f = this._forward;
|
|
3297
|
-
if (f === void 0) return;
|
|
3298
|
-
this._forward = void 0;
|
|
3299
|
-
const { args = this._handlerArgs ?? [], ...rest } = f;
|
|
3221
|
+
// A view's name is a message, and forwarding it means a view that says
|
|
3222
|
+
// `@on.click="saveDraft .text"` never has to change when an ancestor takes the job
|
|
3223
|
+
// over. (A `$method` handler has a name too, so forwarding one raises an intent under
|
|
3224
|
+
// it.) `handler` is a NodeEvent, whose own `name` is the DOM event type; the MESSAGE
|
|
3225
|
+
// name is the one the view wrote, on the handler call it wraps.
|
|
3226
|
+
_forwardName() {
|
|
3300
3227
|
const hv = this.handler?.handlerCall?.handlerVal ?? this.handler?.handlerVal;
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3228
|
+
return hv?.name;
|
|
3229
|
+
}
|
|
3230
|
+
_forwardArgs() {
|
|
3231
|
+
return this._handlerArgs ?? [];
|
|
3232
|
+
}
|
|
3233
|
+
_forwardPath() {
|
|
3234
|
+
return this.dispatchPath;
|
|
3307
3235
|
}
|
|
3308
3236
|
getHandlerAndArgs(root, _instance, comps) {
|
|
3309
|
-
const stack = this.path.
|
|
3237
|
+
const stack = this.path.buildStack(Stack.root(comps, root, this));
|
|
3310
3238
|
const [handler, args] = this.handler.getHandlerAndArgs(stack, this);
|
|
3311
3239
|
this._handlerArgs = [...args];
|
|
3312
|
-
const
|
|
3313
|
-
args.push(new EventContext(
|
|
3240
|
+
const path2 = this.dispatchPath;
|
|
3241
|
+
args.push(new EventContext(path2, this.transactor, this));
|
|
3314
3242
|
return [handler, args];
|
|
3315
3243
|
}
|
|
3316
|
-
// The dispatched DOM event, read only through `e.<member>` handler args
|
|
3317
|
-
// (src/value.js EventMemberVal via Stack.lookupEvent). There is no bare
|
|
3318
|
-
// implicit-name vocabulary anymore: every arg carries a sigil.
|
|
3319
|
-
get event() {
|
|
3320
|
-
return this.e;
|
|
3321
|
-
}
|
|
3322
3244
|
};
|
|
3323
3245
|
var NameArgsTransaction = class extends Transaction {
|
|
3324
|
-
constructor(
|
|
3325
|
-
super(
|
|
3246
|
+
constructor(path2, transactor, name, args, parentTransaction) {
|
|
3247
|
+
super(path2, transactor, parentTransaction);
|
|
3326
3248
|
this.name = name;
|
|
3327
3249
|
this.args = args;
|
|
3328
|
-
this.targetPath =
|
|
3250
|
+
this.targetPath = path2;
|
|
3329
3251
|
}
|
|
3330
3252
|
handlerProp = null;
|
|
3331
3253
|
// NameArgs verbs map their handler bucket straight to the observed kind:
|
|
@@ -3336,6 +3258,17 @@ var NameArgsTransaction = class extends Transaction {
|
|
|
3336
3258
|
get observeName() {
|
|
3337
3259
|
return this.name;
|
|
3338
3260
|
}
|
|
3261
|
+
// `forward` in a RECEIVE body starts a walk: the message that arrived becomes an
|
|
3262
|
+
// intent, keeping its name and payload.
|
|
3263
|
+
_forwardName() {
|
|
3264
|
+
return this.name;
|
|
3265
|
+
}
|
|
3266
|
+
_forwardArgs() {
|
|
3267
|
+
return this.args;
|
|
3268
|
+
}
|
|
3269
|
+
_forwardPath() {
|
|
3270
|
+
return this.path;
|
|
3271
|
+
}
|
|
3339
3272
|
getHandlerForName(comp) {
|
|
3340
3273
|
const handlers = comp?.[this.handlerProp];
|
|
3341
3274
|
const exact = handlers?.[this.name];
|
|
@@ -3366,24 +3299,11 @@ var SendEvent = class extends NameArgsTransaction {
|
|
|
3366
3299
|
get observeKind() {
|
|
3367
3300
|
return this._isAnswer ? "answer" : "receive";
|
|
3368
3301
|
}
|
|
3369
|
-
// `forward` in a RECEIVE body starts a walk: the message that arrived becomes an
|
|
3370
|
-
// intent, keeping its name and payload. This is what lets a view's name leave the
|
|
3371
|
-
// component without the view changing.
|
|
3372
|
-
forward(opts) {
|
|
3373
|
-
this._forward = opts ?? {};
|
|
3374
|
-
}
|
|
3375
|
-
afterTransaction() {
|
|
3376
|
-
const f = this._forward;
|
|
3377
|
-
if (f === void 0) return;
|
|
3378
|
-
this._forward = void 0;
|
|
3379
|
-
const { args = this.args, ...rest } = f;
|
|
3380
|
-
this.transactor.pushIntent(this.path, this.name, args, rest, this);
|
|
3381
|
-
}
|
|
3382
3302
|
};
|
|
3383
3303
|
var IntentEvent = class extends NameArgsTransaction {
|
|
3384
3304
|
handlerProp = "intent";
|
|
3385
|
-
constructor(
|
|
3386
|
-
super(
|
|
3305
|
+
constructor(path2, transactor, walk) {
|
|
3306
|
+
super(path2, transactor, walk.name, walk.args, walk.parent);
|
|
3387
3307
|
this.walk = walk;
|
|
3388
3308
|
this.targetPath = walk.origin;
|
|
3389
3309
|
}
|
|
@@ -3392,9 +3312,6 @@ var IntentEvent = class extends NameArgsTransaction {
|
|
|
3392
3312
|
forward(opts) {
|
|
3393
3313
|
this.walk.amend(opts, this.path);
|
|
3394
3314
|
}
|
|
3395
|
-
stop() {
|
|
3396
|
-
this.walk.finish(null, null);
|
|
3397
|
-
}
|
|
3398
3315
|
afterTransaction() {
|
|
3399
3316
|
this.ensureWalkAdvanced();
|
|
3400
3317
|
}
|
|
@@ -3410,17 +3327,17 @@ var PASS = /* @__PURE__ */ Symbol.for("tutuca.intent.pass");
|
|
|
3410
3327
|
var REFUSAL_RING_CAP = 200;
|
|
3411
3328
|
var INTENT_DEPTH = 64;
|
|
3412
3329
|
var IntentWalk = class {
|
|
3413
|
-
constructor(transactor,
|
|
3330
|
+
constructor(transactor, path2, name, args, opts, parent, release) {
|
|
3414
3331
|
this.transactor = transactor;
|
|
3415
3332
|
this.name = name;
|
|
3416
3333
|
this.args = args;
|
|
3417
3334
|
this.route = opts?.route ?? DEFAULT_ROUTE;
|
|
3418
3335
|
this.parent = parent;
|
|
3419
3336
|
this.release = release;
|
|
3420
|
-
this.origin =
|
|
3421
|
-
this.answerPath = opts?.livePath ? null :
|
|
3337
|
+
this.origin = path2;
|
|
3338
|
+
this.answerPath = opts?.livePath ? null : path2.toTransactionPath().pinKeys(transactor.state.val);
|
|
3422
3339
|
this.legIndex = 0;
|
|
3423
|
-
this.dynAt =
|
|
3340
|
+
this.dynAt = path2;
|
|
3424
3341
|
this.hops = 0;
|
|
3425
3342
|
this.ended = false;
|
|
3426
3343
|
}
|
|
@@ -3432,7 +3349,7 @@ var IntentWalk = class {
|
|
|
3432
3349
|
while (this.legIndex < this.route.length) {
|
|
3433
3350
|
const leg = this.route[this.legIndex];
|
|
3434
3351
|
if (leg === "dyn") {
|
|
3435
|
-
if (this.dynAt.
|
|
3352
|
+
if (!this.dynAt.canPop()) {
|
|
3436
3353
|
this.legIndex++;
|
|
3437
3354
|
continue;
|
|
3438
3355
|
}
|
|
@@ -3444,7 +3361,7 @@ var IntentWalk = class {
|
|
|
3444
3361
|
this.legIndex++;
|
|
3445
3362
|
return this._tryLex();
|
|
3446
3363
|
}
|
|
3447
|
-
|
|
3364
|
+
warnUnknownLeg("intent", leg);
|
|
3448
3365
|
this.legIndex++;
|
|
3449
3366
|
}
|
|
3450
3367
|
this.exhaust("noHandler");
|
|
@@ -3529,11 +3446,11 @@ var IntentWalk = class {
|
|
|
3529
3446
|
if (this.ended) return;
|
|
3530
3447
|
this.ended = true;
|
|
3531
3448
|
if (name === null) return this.release?.();
|
|
3532
|
-
const
|
|
3533
|
-
|
|
3449
|
+
const t = new SendEvent(this.origin, this.transactor, name, args, this.parent);
|
|
3450
|
+
t.txnPath = this.answerPath;
|
|
3534
3451
|
t._isAnswer = true;
|
|
3535
3452
|
this.transactor.pushTransaction(t);
|
|
3536
|
-
if (this.release) t.completion.
|
|
3453
|
+
if (this.release) t.completion.carry(this.release);
|
|
3537
3454
|
}
|
|
3538
3455
|
};
|
|
3539
3456
|
var Completion = class {
|
|
@@ -3541,12 +3458,12 @@ var Completion = class {
|
|
|
3541
3458
|
this.val = void 0;
|
|
3542
3459
|
this.selfSettled = false;
|
|
3543
3460
|
this.subtreeSettled = false;
|
|
3544
|
-
this.pending =
|
|
3461
|
+
this.pending = 0;
|
|
3545
3462
|
this._selfResolve = null;
|
|
3546
3463
|
this._selfPromise = null;
|
|
3547
3464
|
this._subtreeResolve = null;
|
|
3548
3465
|
this._subtreePromise = null;
|
|
3549
|
-
this.
|
|
3466
|
+
this._releaseSelf = this.track();
|
|
3550
3467
|
}
|
|
3551
3468
|
whenSettled() {
|
|
3552
3469
|
if (this.selfSettled) return Promise.resolve(this.val);
|
|
@@ -3569,38 +3486,38 @@ var Completion = class {
|
|
|
3569
3486
|
this.val = val;
|
|
3570
3487
|
this._selfResolve?.(val);
|
|
3571
3488
|
}
|
|
3572
|
-
//
|
|
3573
|
-
|
|
3574
|
-
|
|
3489
|
+
// The transaction is done processing: self is settled (with the value the handler
|
|
3490
|
+
// produced, or none on the undefined-state / throw paths) and the self-unit goes.
|
|
3491
|
+
finish() {
|
|
3492
|
+
this.markSelfSettled(this.val);
|
|
3493
|
+
this._releaseSelf();
|
|
3575
3494
|
}
|
|
3576
|
-
// Register an outstanding unit; returns
|
|
3495
|
+
// Register an outstanding unit; returns its one-shot release. The subtree settles
|
|
3496
|
+
// when the last unit is released.
|
|
3577
3497
|
track() {
|
|
3578
3498
|
this.pending++;
|
|
3579
3499
|
let done = false;
|
|
3580
3500
|
return () => {
|
|
3581
3501
|
if (done) return;
|
|
3582
3502
|
done = true;
|
|
3583
|
-
this.
|
|
3503
|
+
if (--this.pending === 0) {
|
|
3504
|
+
this.subtreeSettled = true;
|
|
3505
|
+
this._subtreeResolve?.(this.val);
|
|
3506
|
+
}
|
|
3584
3507
|
};
|
|
3585
3508
|
}
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
this.
|
|
3590
|
-
}
|
|
3591
|
-
_release() {
|
|
3592
|
-
if (--this.pending === 0) {
|
|
3593
|
-
this.subtreeSettled = true;
|
|
3594
|
-
this._subtreeResolve?.(this.val);
|
|
3595
|
-
}
|
|
3509
|
+
// Hand a unit held elsewhere to this subtree: `release` runs once this transaction
|
|
3510
|
+
// and everything it spawned have settled.
|
|
3511
|
+
carry(release) {
|
|
3512
|
+
this.whenSubtreeSettled().then(release);
|
|
3596
3513
|
}
|
|
3597
3514
|
};
|
|
3598
3515
|
var Dispatcher = class {
|
|
3599
|
-
constructor(
|
|
3600
|
-
this.path =
|
|
3516
|
+
constructor(path2, transactor, parentTransaction) {
|
|
3517
|
+
this.path = path2;
|
|
3601
3518
|
this.transactor = transactor;
|
|
3602
3519
|
this.parent = parentTransaction;
|
|
3603
|
-
this.root =
|
|
3520
|
+
this.root = transactor.state.val;
|
|
3604
3521
|
}
|
|
3605
3522
|
// Walk the component instances on this ctx's path, leaf→root, calling
|
|
3606
3523
|
// callback(Component, instance). Return false from the callback to stop early.
|
|
@@ -3625,28 +3542,37 @@ var Dispatcher = class {
|
|
|
3625
3542
|
// replayed; provides are pushed on every component frame either way, so a provide
|
|
3626
3543
|
// that reads a loop binding is the one case this cannot reproduce faithfully.
|
|
3627
3544
|
_stack() {
|
|
3628
|
-
this._stackMemo ??= this.path.
|
|
3545
|
+
this._stackMemo ??= this.path.buildStack(
|
|
3546
|
+
Stack.root(this.transactor.comps, this.root, this.parent)
|
|
3547
|
+
);
|
|
3629
3548
|
return this._stackMemo;
|
|
3630
3549
|
}
|
|
3631
3550
|
// Resolve a name the way the renderer would. `opts.route` takes the same legs in
|
|
3632
3551
|
// the same spelling as `ctx.intent` — `["dyn"]` the render ancestry, `["lex"]` the
|
|
3633
3552
|
// registration scope, default both.
|
|
3553
|
+
// Legs are tried in route order, like the intent walker's; an unknown leg warns and
|
|
3554
|
+
// is skipped, and an empty route resolves to null.
|
|
3634
3555
|
lookup(name, opts) {
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
()
|
|
3638
|
-
|
|
3639
|
-
|
|
3556
|
+
for (const leg of opts?.route ?? DEFAULT_ROUTE) {
|
|
3557
|
+
const v = leg === "dyn" ? this._stack()?.lookupDynamic(name) ?? null : leg === "lex" ? this._lookupLex(name) : warnUnknownLeg("lookup", leg);
|
|
3558
|
+
if (v != null) return v;
|
|
3559
|
+
}
|
|
3560
|
+
return null;
|
|
3640
3561
|
}
|
|
3641
3562
|
// The `lex` leg without a Stack: the scope of the component whose handler is
|
|
3642
|
-
// running, which is the leaf of this ctx's path.
|
|
3563
|
+
// running, which is the leaf of this ctx's path. A type name resolves to the
|
|
3564
|
+
// component registered under it; a value name to a path registered under it
|
|
3565
|
+
// (see ComponentStack.registerPaths), read against the current root.
|
|
3643
3566
|
_lookupLex(name) {
|
|
3644
3567
|
let Comp = null;
|
|
3645
3568
|
this.walkPath((c) => {
|
|
3646
3569
|
Comp = c;
|
|
3647
3570
|
return false;
|
|
3648
3571
|
});
|
|
3649
|
-
|
|
3572
|
+
const scope = Comp?.scope;
|
|
3573
|
+
if (scope == null) return null;
|
|
3574
|
+
if (isTypeName(name)) return scope.lookupComponent(name) ?? null;
|
|
3575
|
+
return scope.lookupPath(name)?.lookup(this.root) ?? null;
|
|
3650
3576
|
}
|
|
3651
3577
|
// A component lookup is a value lookup constrained to a component. The `lex` leg
|
|
3652
3578
|
// can only ever answer with one; the `dyn` leg reads a binding an ancestor
|
|
@@ -3668,8 +3594,8 @@ var Dispatcher = class {
|
|
|
3668
3594
|
send(name, args) {
|
|
3669
3595
|
return this.sendAtPath(this.path, name, args);
|
|
3670
3596
|
}
|
|
3671
|
-
sendAtPath(
|
|
3672
|
-
return this.transactor.pushSend(
|
|
3597
|
+
sendAtPath(path2, name, args) {
|
|
3598
|
+
return this.transactor.pushSend(path2, name, args, this.parent, this.path);
|
|
3673
3599
|
}
|
|
3674
3600
|
// An intent is ROUTED: it names a job and walks until something answers. The verb
|
|
3675
3601
|
// does not decide which scope answers — `opts.route` does, written here at the call
|
|
@@ -3677,8 +3603,8 @@ var Dispatcher = class {
|
|
|
3677
3603
|
intent(name, args, opts) {
|
|
3678
3604
|
return this.intentAtPath(this.path, name, args, opts);
|
|
3679
3605
|
}
|
|
3680
|
-
intentAtPath(
|
|
3681
|
-
return this.transactor.pushIntent(
|
|
3606
|
+
intentAtPath(path2, name, args, opts) {
|
|
3607
|
+
return this.transactor.pushIntent(path2, name, args, opts, this.parent);
|
|
3682
3608
|
}
|
|
3683
3609
|
};
|
|
3684
3610
|
var EventContext = class extends Dispatcher {
|
|
@@ -3707,11 +3633,19 @@ var EventContext = class extends Dispatcher {
|
|
|
3707
3633
|
this.transactor.refuse("NO_SENDER", { name });
|
|
3708
3634
|
return null;
|
|
3709
3635
|
}
|
|
3710
|
-
return this.
|
|
3636
|
+
return this.transactor.pushSend(
|
|
3637
|
+
origin,
|
|
3638
|
+
name,
|
|
3639
|
+
args,
|
|
3640
|
+
this.parent,
|
|
3641
|
+
this.path,
|
|
3642
|
+
this.parent.originPinned
|
|
3643
|
+
);
|
|
3711
3644
|
}
|
|
3712
|
-
// End the walk answering nothing — "served, and no answer".
|
|
3645
|
+
// End the walk answering nothing — "served, and no answer". Like reply/fail,
|
|
3646
|
+
// legal only in an `intent` handler.
|
|
3713
3647
|
stop() {
|
|
3714
|
-
return this.parent.stop();
|
|
3648
|
+
return this.parent.walk === void 0 ? warnNotIntent("stop") : this.parent.walk.finish(null, null);
|
|
3715
3649
|
}
|
|
3716
3650
|
// From an `intent` body: hand the intent to the next hop, optionally amending it.
|
|
3717
3651
|
// From a `receive` body: turn the message that arrived into an intent. One word from
|
|
@@ -3739,7 +3673,7 @@ var PathChanges = class extends PathBuilder {
|
|
|
3739
3673
|
}
|
|
3740
3674
|
};
|
|
3741
3675
|
function rootDispatcher(transactor) {
|
|
3742
|
-
return new Dispatcher(new
|
|
3676
|
+
return new Dispatcher(new DispatchPath(), transactor, null);
|
|
3743
3677
|
}
|
|
3744
3678
|
|
|
3745
3679
|
// src/app.js
|
|
@@ -3752,7 +3686,6 @@ var App = class {
|
|
|
3752
3686
|
this.transactor = new Transactor(comps, null);
|
|
3753
3687
|
this.ParseContext = ParseContext2;
|
|
3754
3688
|
this.renderer = renderer;
|
|
3755
|
-
this.maxEventNodeDepth = Infinity;
|
|
3756
3689
|
this._transactNextBatchId = this._evictCacheId = null;
|
|
3757
3690
|
this._eventNames = new Set(_evs);
|
|
3758
3691
|
this.dragInfo = this.curDragOver = null;
|
|
@@ -3779,18 +3712,17 @@ var App = class {
|
|
|
3779
3712
|
_dispatchEvent(e) {
|
|
3780
3713
|
const { type } = e;
|
|
3781
3714
|
const isDrag = type === "dragover" || type === "dragstart" || type === "dragend" || type === "drop";
|
|
3782
|
-
const { rootNode
|
|
3783
|
-
const [
|
|
3715
|
+
const { rootNode, comps, transactor } = this;
|
|
3716
|
+
const [path2, handlers] = DispatchPath.fromNodeAndEventName(
|
|
3784
3717
|
e.target,
|
|
3785
3718
|
type,
|
|
3786
|
-
|
|
3787
|
-
maxDepth,
|
|
3719
|
+
rootNode,
|
|
3788
3720
|
comps,
|
|
3789
3721
|
!isDrag
|
|
3790
3722
|
);
|
|
3791
|
-
if (isDrag) this._handleDragEvent(e, type,
|
|
3792
|
-
if (
|
|
3793
|
-
for (const handler of handlers) transactor.transactInputNow(
|
|
3723
|
+
if (isDrag) this._handleDragEvent(e, type, path2);
|
|
3724
|
+
if (path2 !== null && handlers !== null)
|
|
3725
|
+
for (const handler of handlers) transactor.transactInputNow(path2, e, handler, this.dragInfo);
|
|
3794
3726
|
}
|
|
3795
3727
|
_handleTouchEvent(e) {
|
|
3796
3728
|
const { type } = e;
|
|
@@ -3833,7 +3765,7 @@ var App = class {
|
|
|
3833
3765
|
this._touch = null;
|
|
3834
3766
|
}
|
|
3835
3767
|
}
|
|
3836
|
-
_handleDragEvent(e, type,
|
|
3768
|
+
_handleDragEvent(e, type, path2) {
|
|
3837
3769
|
if (type === "dragover") {
|
|
3838
3770
|
const dropTarget = getClosestDropTarget(e.target, this.rootNode, Infinity);
|
|
3839
3771
|
if (dropTarget !== null) {
|
|
@@ -3845,10 +3777,10 @@ var App = class {
|
|
|
3845
3777
|
} else if (type === "dragstart") {
|
|
3846
3778
|
e.target.dataset.dragging = 1;
|
|
3847
3779
|
const rootValue = this.state.val;
|
|
3848
|
-
const txnPath =
|
|
3780
|
+
const txnPath = path2.compact().toTransactionPath();
|
|
3849
3781
|
const value = txnPath.lookup(rootValue);
|
|
3850
3782
|
const dragType = e.target.dataset.dragtype ?? "?";
|
|
3851
|
-
const stack =
|
|
3783
|
+
const stack = path2.buildStack(this.makeStack(rootValue));
|
|
3852
3784
|
this.dragInfo = new DragInfo(stack, value, dragType, e.target);
|
|
3853
3785
|
} else if (type === "drop") {
|
|
3854
3786
|
e.preventDefault();
|
|
@@ -3893,10 +3825,9 @@ var App = class {
|
|
|
3893
3825
|
}
|
|
3894
3826
|
compile() {
|
|
3895
3827
|
for (const Comp of this.comps.byId.values()) {
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
for (const name of meta.views[key].ctx.genEventNames()) this._eventNames.add(name);
|
|
3828
|
+
Comp.compile(this.ParseContext);
|
|
3829
|
+
for (const key in Comp.views)
|
|
3830
|
+
for (const name of Comp.views[key].ctx.genEventNames()) this._eventNames.add(name);
|
|
3900
3831
|
}
|
|
3901
3832
|
this._compiled = true;
|
|
3902
3833
|
}
|
|
@@ -3919,11 +3850,13 @@ var App = class {
|
|
|
3919
3850
|
}
|
|
3920
3851
|
stop() {
|
|
3921
3852
|
this.stopCacheEvictionInterval();
|
|
3853
|
+
clearTimeout(this._transactNextBatchId);
|
|
3854
|
+
this._transactNextBatchId = null;
|
|
3922
3855
|
for (const name of this._eventNames)
|
|
3923
3856
|
this.rootNode.removeEventListener(name, this, listenerOpts(name));
|
|
3924
3857
|
}
|
|
3925
3858
|
sendAtRoot(name, args) {
|
|
3926
|
-
this.transactor.pushSend(new
|
|
3859
|
+
this.transactor.pushSend(new DispatchPath(), name, args);
|
|
3927
3860
|
}
|
|
3928
3861
|
registerComponents(comps, opts) {
|
|
3929
3862
|
const scope = this.compStack.enter();
|
|
@@ -4005,28 +3938,21 @@ var NullDomCache = class {
|
|
|
4005
3938
|
set(_keys, _cacheKey, _v) {
|
|
4006
3939
|
}
|
|
4007
3940
|
evict() {
|
|
4008
|
-
return { hit: 0, miss: 0, badKey: 0 };
|
|
4009
3941
|
}
|
|
4010
3942
|
};
|
|
4011
3943
|
var WeakMapDomCache = class {
|
|
4012
3944
|
constructor() {
|
|
4013
|
-
this.hit = this.miss = this.badKey = 0;
|
|
4014
3945
|
this.keysByLen = /* @__PURE__ */ new Map();
|
|
4015
3946
|
}
|
|
4016
|
-
_returnValue(r) {
|
|
4017
|
-
if (r === void 0) this.miss += 1;
|
|
4018
|
-
else this.hit += 1;
|
|
4019
|
-
return r;
|
|
4020
|
-
}
|
|
4021
3947
|
get(keys, cacheKey) {
|
|
4022
3948
|
const len = keys.length;
|
|
4023
3949
|
let cur = this.keysByLen.get(len);
|
|
4024
|
-
if (!cur) return
|
|
3950
|
+
if (!cur) return void 0;
|
|
4025
3951
|
for (let i = 0; i < len - 1; i++) {
|
|
4026
3952
|
cur = cur.get(keys[i]);
|
|
4027
|
-
if (!cur) return
|
|
3953
|
+
if (!cur) return void 0;
|
|
4028
3954
|
}
|
|
4029
|
-
return
|
|
3955
|
+
return cur.get(keys[len - 1])?.[cacheKey];
|
|
4030
3956
|
}
|
|
4031
3957
|
set(keys, cacheKey, v) {
|
|
4032
3958
|
const len = keys.length;
|
|
@@ -4039,10 +3965,7 @@ var WeakMapDomCache = class {
|
|
|
4039
3965
|
const key = keys[i];
|
|
4040
3966
|
let next = cur.get(key);
|
|
4041
3967
|
if (!next) {
|
|
4042
|
-
if (!isWeakKey(key))
|
|
4043
|
-
this.badKey += 1;
|
|
4044
|
-
return;
|
|
4045
|
-
}
|
|
3968
|
+
if (!isWeakKey(key)) return;
|
|
4046
3969
|
next = /* @__PURE__ */ new WeakMap();
|
|
4047
3970
|
cur.set(key, next);
|
|
4048
3971
|
}
|
|
@@ -4052,23 +3975,38 @@ var WeakMapDomCache = class {
|
|
|
4052
3975
|
const leaf = cur.get(lastKey);
|
|
4053
3976
|
if (leaf) leaf[cacheKey] = v;
|
|
4054
3977
|
else if (isWeakKey(lastKey)) cur.set(lastKey, { [cacheKey]: v });
|
|
4055
|
-
else this.badKey += 1;
|
|
4056
3978
|
}
|
|
4057
3979
|
evict() {
|
|
4058
|
-
const { hit, miss, badKey } = this;
|
|
4059
|
-
this.hit = this.miss = this.badKey = 0;
|
|
4060
3980
|
this.keysByLen = /* @__PURE__ */ new Map();
|
|
4061
|
-
return { hit, miss, badKey };
|
|
4062
3981
|
}
|
|
4063
3982
|
};
|
|
4064
3983
|
|
|
4065
3984
|
// src/renderer.js
|
|
4066
|
-
|
|
3985
|
+
function stampRenderBase(vdom, text) {
|
|
3986
|
+
if (vdom instanceof VNode)
|
|
3987
|
+
return new VNode(
|
|
3988
|
+
vdom.tag,
|
|
3989
|
+
{ ...vdom.attrs, "data-rp": text },
|
|
3990
|
+
vdom.childs,
|
|
3991
|
+
vdom.key,
|
|
3992
|
+
vdom.namespace
|
|
3993
|
+
);
|
|
3994
|
+
if (vdom instanceof VFragment)
|
|
3995
|
+
return new VFragment(vdom.childs.map((c) => stampRenderBase(c, text)));
|
|
3996
|
+
return vdom;
|
|
3997
|
+
}
|
|
4067
3998
|
var Renderer = class {
|
|
4068
3999
|
constructor(comps) {
|
|
4069
4000
|
this.comps = comps;
|
|
4070
4001
|
this.cache = new WeakMapDomCache();
|
|
4071
|
-
|
|
4002
|
+
}
|
|
4003
|
+
// Parse nodes build their VDOM through the renderer, never by importing vdom.js
|
|
4004
|
+
// themselves: a component may have been compiled by ANOTHER copy of the library
|
|
4005
|
+
// (a docs example imports the dist bundle while the host app runs from src), and
|
|
4006
|
+
// `render()` recognizes VNodes by `instanceof` against ITS copy's classes. Going
|
|
4007
|
+
// through `rx` guarantees the rendering copy makes them.
|
|
4008
|
+
renderTag(tag, attrs, childs, namespace) {
|
|
4009
|
+
return h(tag, attrs, childs, namespace);
|
|
4072
4010
|
}
|
|
4073
4011
|
renderFragment(childs) {
|
|
4074
4012
|
return new VFragment(childs);
|
|
@@ -4079,38 +4017,24 @@ var Renderer = class {
|
|
|
4079
4017
|
setNullCache() {
|
|
4080
4018
|
this.cache = new NullDomCache();
|
|
4081
4019
|
}
|
|
4082
|
-
// Library utilities for consumers embedding tutuca: render a value to a detached
|
|
4083
|
-
// DOM node / an HTML string. Not called by the framework itself.
|
|
4084
|
-
renderToDOM(stack, val) {
|
|
4085
|
-
const rootNode = document.createElement("div");
|
|
4086
|
-
const rOpts = { document };
|
|
4087
|
-
render(h("DIV", null, [this.renderRoot(stack, val)]), rootNode, rOpts);
|
|
4088
|
-
return rootNode.childNodes[0];
|
|
4089
|
-
}
|
|
4090
|
-
renderToString(stack, val, cleanAttrs = true) {
|
|
4091
|
-
const dom = this.renderToDOM(stack, val);
|
|
4092
|
-
if (cleanAttrs) {
|
|
4093
|
-
const nodes = dom.querySelectorAll("[data-nid],[data-cid],[data-eid]");
|
|
4094
|
-
for (const { dataset } of nodes) for (const name of DATASET_ATTRS) delete dataset[name];
|
|
4095
|
-
}
|
|
4096
|
-
return dom.innerHTML;
|
|
4097
|
-
}
|
|
4098
4020
|
renderRoot(stack, val, viewName = null) {
|
|
4099
4021
|
const comp = this.comps.getCompFor(val);
|
|
4100
4022
|
if (comp === null) return null;
|
|
4101
4023
|
return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
|
|
4102
4024
|
}
|
|
4103
|
-
|
|
4025
|
+
// Render `stack.it` at a `<x render*>` site (`node`). `base` is the absolute
|
|
4026
|
+
// path the site resumed at, when it rendered a `*name` (see _rValComp).
|
|
4027
|
+
renderIt(stack, node, viewName, base = null) {
|
|
4104
4028
|
const comp = this.comps.getCompFor(stack.it);
|
|
4105
|
-
return comp ? this._rValComp(stack, stack.it, comp, node,
|
|
4029
|
+
return comp ? this._rValComp(stack, stack.it, comp, node, "", viewName, base) : null;
|
|
4106
4030
|
}
|
|
4107
4031
|
// `node` is the parse node of the render site (`<x render>` / `render-it` /
|
|
4108
4032
|
// `render-each`, or the view's root anode for the app root). It keys the
|
|
4109
4033
|
// cache as a globally-unique object: node ids alone are unique only within a
|
|
4110
4034
|
// single view, so the same value rendered by two components (e.g. through a
|
|
4111
4035
|
// shared dynamic-var sequence) would otherwise collide in the cache.
|
|
4112
|
-
_rValComp(stack, val, comp, node, key, viewName) {
|
|
4113
|
-
const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}`;
|
|
4036
|
+
_rValComp(stack, val, comp, node, key, viewName, base = null) {
|
|
4037
|
+
const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}${stack.renderPath.addressKey}`;
|
|
4114
4038
|
const cachePath = [node, val];
|
|
4115
4039
|
stack._pushDynBindValuesToArray(cachePath, comp);
|
|
4116
4040
|
const cachedNode = this.cache.get(cachePath, cacheKey);
|
|
@@ -4118,30 +4042,43 @@ var Renderer = class {
|
|
|
4118
4042
|
const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
|
|
4119
4043
|
const body = this.renderView(view, stack);
|
|
4120
4044
|
if (body == null) return null;
|
|
4045
|
+
const baseJson = base === null ? null : pathToJson(base);
|
|
4121
4046
|
const meta = this._renderMetadata({
|
|
4122
4047
|
$: "Comp",
|
|
4123
4048
|
nid: node?.nodeId ?? null,
|
|
4124
4049
|
cid: comp.id,
|
|
4125
|
-
vid: view.name
|
|
4050
|
+
vid: view.name,
|
|
4051
|
+
...baseJson === null ? null : { base: baseJson }
|
|
4126
4052
|
});
|
|
4127
|
-
const dom = new VFragment([
|
|
4053
|
+
const dom = new VFragment([
|
|
4054
|
+
meta,
|
|
4055
|
+
baseJson === null ? body : stampRenderBase(body, JSON.stringify(baseJson))
|
|
4056
|
+
]);
|
|
4128
4057
|
this.cache.set(cachePath, cacheKey, dom);
|
|
4129
4058
|
return dom;
|
|
4130
4059
|
}
|
|
4131
4060
|
pushEachEntry(r, nid, attrName, key, dom) {
|
|
4132
4061
|
r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
|
|
4133
4062
|
}
|
|
4134
|
-
renderEachWhen(stack,
|
|
4135
|
-
const {
|
|
4063
|
+
renderEachWhen(stack, each) {
|
|
4064
|
+
const { val: seqVal, node: view, nodeId: nid } = each;
|
|
4065
|
+
const { seq, filter, loopWith, enricher } = each.evalIter(stack);
|
|
4066
|
+
const seqPath = seqVal instanceof DynVal ? stack.lookupDynamicLocated(seqVal.name)?.path ?? null : null;
|
|
4136
4067
|
const r = [];
|
|
4137
4068
|
const it = stack.it;
|
|
4138
4069
|
const renderOne = (key, value, attrName, binds) => {
|
|
4070
|
+
const itemBase = seqPath === null ? null : keyedPath(seqPath, key);
|
|
4071
|
+
const itemStep = itemBase === null ? each.itemStep(key) : null;
|
|
4072
|
+
const itemPath = itemBase !== null ? stack.renderPath.pushFrame(itemBase) : itemStep !== null ? stack.renderPath.pushItem(itemStep) : stack.renderPath;
|
|
4139
4073
|
const cachePath = enricher ? [view, it, value] : [view, value];
|
|
4140
|
-
const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
|
|
4074
|
+
const cacheKey = `${stack.viewsId ?? ""}${nid}${key}${itemPath.addressKey}`;
|
|
4141
4075
|
const cachedNode = this.cache.get(cachePath, cacheKey);
|
|
4142
4076
|
if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
|
|
4143
4077
|
else {
|
|
4144
|
-
const dom = this.renderView(
|
|
4078
|
+
const dom = this.renderView(
|
|
4079
|
+
view,
|
|
4080
|
+
stack.enter(value, binds, false, itemPath, itemBase !== null)
|
|
4081
|
+
);
|
|
4145
4082
|
if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
|
|
4146
4083
|
this.cache.set(cachePath, cacheKey, dom);
|
|
4147
4084
|
}
|
|
@@ -4205,17 +4142,18 @@ function dispatchPhase(dispatcher, targetPath, phase, self) {
|
|
|
4205
4142
|
var css = String.raw;
|
|
4206
4143
|
var html = String.raw;
|
|
4207
4144
|
var macro = (defaults, rawView) => new Macro(defaults, rawView);
|
|
4145
|
+
var devOnly = (name, result) => {
|
|
4146
|
+
console.warn(`${name} is a no-op in the core tutuca build; import tutuca/dev for the real one`);
|
|
4147
|
+
return result;
|
|
4148
|
+
};
|
|
4208
4149
|
function check(_app) {
|
|
4209
|
-
return { error: 0, warn: 0, hint: 0
|
|
4150
|
+
return devOnly("check", { error: 0, warn: 0, hint: 0 });
|
|
4210
4151
|
}
|
|
4211
4152
|
async function test(_opts) {
|
|
4212
|
-
return null;
|
|
4153
|
+
return devOnly("test", null);
|
|
4213
4154
|
}
|
|
4214
4155
|
function collectIterBindings() {
|
|
4215
|
-
|
|
4216
|
-
"collectIterBindings is a no-op in the core tutuca build; use the tutuca-dev build for a functional implementation"
|
|
4217
|
-
);
|
|
4218
|
-
return [];
|
|
4156
|
+
return devOnly("collectIterBindings", []);
|
|
4219
4157
|
}
|
|
4220
4158
|
function tutuca(nodeOrSelector) {
|
|
4221
4159
|
const rootNode = typeof nodeOrSelector === "string" ? document.querySelector(nodeOrSelector) : nodeOrSelector;
|
|
@@ -4225,7 +4163,6 @@ function tutuca(nodeOrSelector) {
|
|
|
4225
4163
|
}
|
|
4226
4164
|
export {
|
|
4227
4165
|
COMPONENT,
|
|
4228
|
-
FIELD_CLASS,
|
|
4229
4166
|
PASS,
|
|
4230
4167
|
ParseContext,
|
|
4231
4168
|
SEQ_INFO,
|
|
@@ -4237,6 +4174,7 @@ export {
|
|
|
4237
4174
|
html,
|
|
4238
4175
|
injectCss,
|
|
4239
4176
|
macro,
|
|
4177
|
+
path,
|
|
4240
4178
|
phaseOps,
|
|
4241
4179
|
resolveArgs,
|
|
4242
4180
|
rootDispatcher,
|