arrmatura 6.3.2 → 6.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -15
- package/dist/index.cjs +6 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.js +5 -1
- package/dist/index.js.map +4 -4
- package/docs/components.md +89 -0
- package/docs/{hello.md → getstarted.md} +21 -4
- package/docs/manual.md +7 -310
- package/docs/templates.md +222 -0
- package/index.ts +4 -4
- package/package.json +8 -6
- package/src/core/Arrmatron.ts +47 -41
- package/src/core/Component.ts +19 -21
- package/src/core/ManifestNode.ts +22 -19
- package/src/core/resolveExpression.ts +7 -3
- package/src/registry/{iterations.ts → Iterative.ts} +11 -16
- package/src/registry/composition.ts +7 -4
- package/src/registry/conditionals.ts +8 -5
- package/src/registry/connector.ts +7 -6
- package/src/registry/index.ts +2 -5
- package/src/registry/root.ts +1 -1
- package/src/registry/routing.ts +6 -1
- package/src/utils/FingerprintMashine.ts +18 -20
- package/src/utils/compileExpression.ts +11 -90
- package/src/utils/narrowData.ts +15 -0
- package/types.ts +35 -27
- package/docs/glossary.md +0 -19
package/src/core/Arrmatron.ts
CHANGED
|
@@ -16,7 +16,7 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
16
16
|
readonly scope: Arrmatron;
|
|
17
17
|
|
|
18
18
|
$children?: Map<Uid, Arrmatron>;
|
|
19
|
-
isDone = false;
|
|
19
|
+
#isDone = false;
|
|
20
20
|
#isInited = false;
|
|
21
21
|
#fprints: Hash = {};
|
|
22
22
|
#initialState: Hash = {};
|
|
@@ -26,7 +26,12 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
26
26
|
#refs?: Hash<IArrmatron>;
|
|
27
27
|
#propFnMap?: Map<string, Proc>;
|
|
28
28
|
|
|
29
|
-
constructor(
|
|
29
|
+
constructor(
|
|
30
|
+
readonly platform: IPlatform,
|
|
31
|
+
protected readonly manifest: T,
|
|
32
|
+
private _parent?: Arrmatron,
|
|
33
|
+
scope?: Arrmatron
|
|
34
|
+
) {
|
|
30
35
|
this.scope = scope ?? this;
|
|
31
36
|
this.root = scope?.root ?? this;
|
|
32
37
|
this.#initialState = this.manifest.resolveInitialProps(this);
|
|
@@ -34,7 +39,7 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
34
39
|
|
|
35
40
|
if (this.refId) {
|
|
36
41
|
// this.log('addReference', this.refId)
|
|
37
|
-
this.scope.addReference(
|
|
42
|
+
this.refId.split(",").forEach((refId) => this.scope.addReference(refId, this));
|
|
38
43
|
}
|
|
39
44
|
}
|
|
40
45
|
|
|
@@ -59,7 +64,7 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
59
64
|
|
|
60
65
|
// updates component state
|
|
61
66
|
up(delta?: Delta | Promise<any> | null | void | unknown, force = false): void {
|
|
62
|
-
if (!delta || this
|
|
67
|
+
if (!delta || this.#isDone) {
|
|
63
68
|
return;
|
|
64
69
|
}
|
|
65
70
|
|
|
@@ -75,14 +80,12 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
75
80
|
const isSpreading = !k || k.startsWith("...");
|
|
76
81
|
const racer = this.raceCondition(`set:${k}`);
|
|
77
82
|
void v.then((val) => racer(() => this.up(isSpreading ? val : { [k]: val })));
|
|
78
|
-
|
|
79
83
|
} else if (k && typeof v !== "undefined") {
|
|
80
84
|
if (k[0] === "$") {
|
|
81
85
|
if (this.$component[k] !== v) {
|
|
82
86
|
changes.set(k.slice(1), v);
|
|
83
87
|
}
|
|
84
88
|
} else {
|
|
85
|
-
|
|
86
89
|
const fprint = objectFingerprint(v);
|
|
87
90
|
if (!(k in this.#fprints) || fprint !== this.#fprints[k]) {
|
|
88
91
|
this.#fprints[k] = fprint;
|
|
@@ -116,7 +119,7 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
116
119
|
* use '@' prefix to access platform resources.
|
|
117
120
|
* Functional values are bounded and cached.
|
|
118
121
|
*
|
|
119
|
-
* @param {string} propId - The ID of the property.
|
|
122
|
+
* @param {string} propId - The ID of the property.
|
|
120
123
|
* @return {unknown} The value associated with the property ID.
|
|
121
124
|
*/
|
|
122
125
|
get(propId: string): unknown {
|
|
@@ -132,8 +135,8 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
132
135
|
if (instant && typeof instant === "function") {
|
|
133
136
|
const bound = instant.bind(impl);
|
|
134
137
|
fn = () => bound;
|
|
135
|
-
} else if (impl.
|
|
136
|
-
fn = () => (impl.
|
|
138
|
+
} else if (impl.__getStateProperty) {
|
|
139
|
+
fn = () => (impl.__getStateProperty as (p: string) => unknown)(propId);
|
|
137
140
|
} else {
|
|
138
141
|
const [pk, ...path] = propId.split(".");
|
|
139
142
|
if (pk === "R") {
|
|
@@ -141,7 +144,8 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
141
144
|
fn = () => val;
|
|
142
145
|
} else {
|
|
143
146
|
const gettr = impl[`get${capitalize(pk)}`];
|
|
144
|
-
const fn0 =
|
|
147
|
+
const fn0 =
|
|
148
|
+
typeof gettr === "function" ? () => gettr.call(impl) ?? null : () => impl[pk] ?? null;
|
|
145
149
|
|
|
146
150
|
fn = !path.length ? fn0 : () => path.reduce((r, p) => r?.[p] ?? null, fn0());
|
|
147
151
|
}
|
|
@@ -175,7 +179,7 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
175
179
|
|
|
176
180
|
// emits action event to this component
|
|
177
181
|
emit(key: string, data: Delta): void {
|
|
178
|
-
if (this
|
|
182
|
+
if (this.#isDone) return;
|
|
179
183
|
|
|
180
184
|
try {
|
|
181
185
|
if (key.endsWith(")")) {
|
|
@@ -194,9 +198,9 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
194
198
|
|
|
195
199
|
const result = method.call(impl, data);
|
|
196
200
|
|
|
197
|
-
// this.log(`-> ${refId}.${target}(data)`, result, data, impl);
|
|
198
|
-
|
|
199
201
|
ref.up(result);
|
|
202
|
+
|
|
203
|
+
// this.log(`-> ${refId}.${target}(data)`, data, impl);
|
|
200
204
|
} else {
|
|
201
205
|
let [refId, target] = key.split(".");
|
|
202
206
|
if (!target) {
|
|
@@ -213,7 +217,6 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
213
217
|
|
|
214
218
|
// this.log(`-> ${refId}.${target} = `, data);
|
|
215
219
|
}
|
|
216
|
-
|
|
217
220
|
} catch (ex) {
|
|
218
221
|
this.logError(`emit ${key}:`, ex);
|
|
219
222
|
}
|
|
@@ -223,12 +226,10 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
223
226
|
|
|
224
227
|
// Done hook.
|
|
225
228
|
done() {
|
|
226
|
-
if (this
|
|
229
|
+
if (this.#isDone) {
|
|
227
230
|
return;
|
|
228
231
|
}
|
|
229
|
-
this
|
|
230
|
-
|
|
231
|
-
this.$component.done?.(this);
|
|
232
|
+
this.#isDone = true;
|
|
232
233
|
|
|
233
234
|
this.children?.forEach((c) => c.done());
|
|
234
235
|
|
|
@@ -254,7 +255,7 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
254
255
|
|
|
255
256
|
this.up(this.#initialState, true);
|
|
256
257
|
|
|
257
|
-
this.up(this.$component.
|
|
258
|
+
this.up(this.$component.__init?.(this));
|
|
258
259
|
} else {
|
|
259
260
|
void this.up(this.manifest.resolveProps(this), true);
|
|
260
261
|
}
|
|
@@ -263,7 +264,7 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
263
264
|
// --- Content.
|
|
264
265
|
|
|
265
266
|
// map of children contexts
|
|
266
|
-
get children(): Map<Uid,
|
|
267
|
+
get children(): Map<Uid, Arrmatron> | undefined {
|
|
267
268
|
return this.$children;
|
|
268
269
|
}
|
|
269
270
|
|
|
@@ -292,7 +293,8 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
292
293
|
const children = new Map<Uid, Arrmatron>();
|
|
293
294
|
|
|
294
295
|
nodes?.forEach((node: IManifestNode, uid: Uid) => {
|
|
295
|
-
const e =
|
|
296
|
+
const e =
|
|
297
|
+
this.$children?.get(uid) ?? node.createArrmatron(this.platform, this, this.recontentScope);
|
|
296
298
|
children.set(e.uid, e);
|
|
297
299
|
});
|
|
298
300
|
|
|
@@ -342,33 +344,37 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
342
344
|
}
|
|
343
345
|
|
|
344
346
|
if (!sourcePropName) {
|
|
345
|
-
this.logError(`Connect: No
|
|
347
|
+
this.logError(`Connect: No Source Property Name: ${refId}`);
|
|
346
348
|
continue;
|
|
347
349
|
}
|
|
348
350
|
|
|
349
351
|
const applicator = targetPropName ? (rr: any) => ({ [targetPropName]: rr }) : identity;
|
|
350
352
|
|
|
351
|
-
this.defer(
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
353
|
+
this.defer(
|
|
354
|
+
(() => {
|
|
355
|
+
let popre;
|
|
356
|
+
return ref.subscribe(async (source: Arrmatron) => {
|
|
357
|
+
try {
|
|
358
|
+
const val = await source.get(sourcePropName);
|
|
359
|
+
const fingerprint = objectFingerprint(val);
|
|
360
|
+
|
|
361
|
+
if (popre !== undefined && popre === fingerprint) return;
|
|
362
|
+
|
|
363
|
+
popre = fingerprint;
|
|
364
|
+
const r = applicator(pipes(this, val));
|
|
365
|
+
this.up(r as Delta);
|
|
366
|
+
} catch (ex) {
|
|
367
|
+
source.logError("Notify ", ex);
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
})()
|
|
368
371
|
);
|
|
369
372
|
|
|
370
373
|
// hot subscription
|
|
371
|
-
Object.assign(
|
|
374
|
+
Object.assign(
|
|
375
|
+
this.#initialState,
|
|
376
|
+
applicator(pipes(ref, sourcePropName ? ref.get(sourcePropName) : ref.$component))
|
|
377
|
+
);
|
|
372
378
|
}
|
|
373
379
|
}
|
|
374
380
|
}
|
|
@@ -409,7 +415,7 @@ export class Arrmatron<T extends IManifestNode = IManifestNode> implements IArrm
|
|
|
409
415
|
|
|
410
416
|
toast = (t: LogEntry) => {
|
|
411
417
|
this.platform.toast(t, this);
|
|
412
|
-
}
|
|
418
|
+
};
|
|
413
419
|
|
|
414
420
|
toString(): string {
|
|
415
421
|
return stringify(this);
|
package/src/core/Component.ts
CHANGED
|
@@ -1,43 +1,42 @@
|
|
|
1
1
|
import type { Delta, Hash, LogEntry } from "ultimus/types";
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { IComponentContext, IArrmatron, IComponent } from "../../types";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Base Ancestor for custom components.
|
|
7
7
|
*/
|
|
8
8
|
export abstract class Component implements IComponent {
|
|
9
9
|
[key: string]: unknown;
|
|
10
|
+
readonly $ctx: IComponentContext;
|
|
10
11
|
|
|
11
|
-
constructor(
|
|
12
|
-
|
|
12
|
+
constructor(initials: Hash, ctx: IComponentContext) {
|
|
13
|
+
this.$ctx = ctx;
|
|
14
|
+
this.__created(initials);
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
17
|
+
// Lifecycle method called within constructor.
|
|
18
|
+
__created(_: Hash) {}
|
|
18
19
|
|
|
19
|
-
get
|
|
20
|
-
return this.$ctx.
|
|
20
|
+
get refId() {
|
|
21
|
+
return this.$ctx.refId;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
//no-op
|
|
24
|
+
get platform() {
|
|
25
|
+
return this.$ctx.platform;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
// hook on
|
|
28
|
+
// hook on initialization
|
|
29
29
|
// returned value will be used to update state
|
|
30
|
-
|
|
30
|
+
__init(_: IArrmatron): Delta | null | undefined | unknown {
|
|
31
31
|
return undefined;
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
// get from ctx state
|
|
33
|
+
// get from$ctx state
|
|
35
34
|
get(key: string) {
|
|
36
35
|
return this.$ctx.get(key);
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
// update
|
|
40
|
-
up(d
|
|
38
|
+
// update$ctx state
|
|
39
|
+
up(d) {
|
|
41
40
|
return this.$ctx.up(d);
|
|
42
41
|
}
|
|
43
42
|
|
|
@@ -46,7 +45,7 @@ export abstract class Component implements IComponent {
|
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
// emit action event to another component by key
|
|
49
|
-
emit(key: string, data: Delta) {
|
|
48
|
+
emit(key: string, data: Delta = {}) {
|
|
50
49
|
return this.$ctx.emit(key, data);
|
|
51
50
|
}
|
|
52
51
|
|
|
@@ -55,14 +54,13 @@ export abstract class Component implements IComponent {
|
|
|
55
54
|
this.$ctx.defer(fn);
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
// register callback to be called on done
|
|
59
57
|
defineCalculatedProperty(key, fn: (...args: any[]) => any, deps?: string[]) {
|
|
60
58
|
let depKey = "";
|
|
61
59
|
let depValue = undefined;
|
|
62
60
|
Object.defineProperty(this, key, {
|
|
63
61
|
get() {
|
|
64
62
|
const args: any[] = deps?.length ? deps.map((k) => this.$ctx.get(k)) : [];
|
|
65
|
-
const newKey = args.join(
|
|
63
|
+
const newKey = args.join(":");
|
|
66
64
|
if (depKey !== newKey) {
|
|
67
65
|
depKey = newKey;
|
|
68
66
|
depValue = fn.apply(this, args);
|
|
@@ -85,6 +83,6 @@ export abstract class Component implements IComponent {
|
|
|
85
83
|
}
|
|
86
84
|
|
|
87
85
|
toString() {
|
|
88
|
-
return this.$ctx.toString()
|
|
86
|
+
return `${this.$ctx.toString()}${this.name ? `(${this.name})` : ""}`;
|
|
89
87
|
}
|
|
90
88
|
}
|
package/src/core/ManifestNode.ts
CHANGED
|
@@ -7,14 +7,13 @@ import { compilePipeExpression } from "../utils/compileExpression";
|
|
|
7
7
|
import { Arrmatron } from "./Arrmatron";
|
|
8
8
|
import { resolveExpression } from "./resolveExpression";
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
type PropertyResolver = (c: IArrmatron, acc: Delta) => Delta;
|
|
12
11
|
|
|
13
12
|
type PropertyGetter = (c: IArrmatron) => unknown;
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* ManifestNode is a abstract factory for Arrmatron.
|
|
17
|
-
*
|
|
16
|
+
*
|
|
18
17
|
* Each specific kind of Arrmatron has its own descending implementation of ManifestNode.
|
|
19
18
|
*/
|
|
20
19
|
export abstract class ManifestNode implements IManifestNode {
|
|
@@ -30,7 +29,12 @@ export abstract class ManifestNode implements IManifestNode {
|
|
|
30
29
|
this.uid = `${nextId("N")}:${xid}`;
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
abstract get EntitronConstructor(): new (
|
|
32
|
+
abstract get EntitronConstructor(): new (
|
|
33
|
+
platform: IPlatform,
|
|
34
|
+
node: typeof this,
|
|
35
|
+
parent?: Arrmatron,
|
|
36
|
+
scope?: Arrmatron
|
|
37
|
+
) => Arrmatron;
|
|
34
38
|
|
|
35
39
|
/**
|
|
36
40
|
* Create a context object.
|
|
@@ -87,7 +91,7 @@ export abstract class ManifestNode implements IManifestNode {
|
|
|
87
91
|
|
|
88
92
|
private addEmitter(expr: ExpressionText, k: string) {
|
|
89
93
|
const { key, pipec } = compilePipeExpression(expr);
|
|
90
|
-
const rkey = key + (expr.endsWith(")") ? "()" :
|
|
94
|
+
const rkey = key + (expr.endsWith(")") ? "()" : "");
|
|
91
95
|
this.initialState[k] = (c) => (data: unknown) => c.scope?.emit(rkey, pipec(c, data) as Delta);
|
|
92
96
|
}
|
|
93
97
|
|
|
@@ -110,9 +114,9 @@ export abstract class ManifestNode implements IManifestNode {
|
|
|
110
114
|
this.addDataPropertyResolver(resolveExpression(v), k.slice(5));
|
|
111
115
|
} else if (k === "Ref") {
|
|
112
116
|
this.refId = String(v);
|
|
113
|
-
} else if (k ===
|
|
117
|
+
} else if (k === "Props") {
|
|
114
118
|
const sv = String(v);
|
|
115
|
-
if (sv
|
|
119
|
+
if (sv.startsWith("<-")) {
|
|
116
120
|
this.addConnector(sv.slice(2), "");
|
|
117
121
|
} else {
|
|
118
122
|
this.addPropertiesResolver(resolveExpression(sv));
|
|
@@ -121,22 +125,21 @@ export abstract class ManifestNode implements IManifestNode {
|
|
|
121
125
|
if (typeof v !== "string") {
|
|
122
126
|
this.initialState[k] = () => v;
|
|
123
127
|
} else {
|
|
124
|
-
if (v
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
this.addEmitter(v.slice(6), k);
|
|
130
|
-
} else {
|
|
131
|
-
if (!v.includes("{")) {
|
|
132
|
-
this.initialState[k] = () => v;
|
|
133
|
-
} else {
|
|
134
|
-
if (v[0] === "R" && v[1] === "." && v.match(/^@@[a-z0-9\-_.]+$/i) != null) {
|
|
135
|
-
this.initialState[k] = (c) => c.platform.getResource(v.slice(2));
|
|
128
|
+
if (!v.includes("{")) {
|
|
129
|
+
if (v.startsWith("<-")) {
|
|
130
|
+
if (v.endsWith(".This")) {
|
|
131
|
+
const refId = v.slice(2, -5).trim();
|
|
132
|
+
this.initialState[k] = (c) => c.getByRef(refId)?.$component;
|
|
136
133
|
} else {
|
|
137
|
-
this.
|
|
134
|
+
this.addConnector(v.slice(2), k);
|
|
138
135
|
}
|
|
136
|
+
} else if (v.startsWith("->")) {
|
|
137
|
+
this.addEmitter(v.slice(2).trim() || "*", k);
|
|
138
|
+
} else {
|
|
139
|
+
this.initialState[k] = () => v;
|
|
139
140
|
}
|
|
141
|
+
} else {
|
|
142
|
+
this.addPropertyResolver(resolveExpression(v), k);
|
|
140
143
|
}
|
|
141
144
|
}
|
|
142
145
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { createStringInterpolator } from "ultimus/src/parsing/createStringInterpolator";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
compileConstant,
|
|
5
|
+
compileJsExpression,
|
|
6
|
+
compilePlaceholder,
|
|
7
|
+
} from "../utils/compileExpression";
|
|
4
8
|
// \{\{([\w\.]+)\}\}
|
|
5
9
|
|
|
6
10
|
const CACHE = new Map();
|
|
@@ -13,7 +17,7 @@ function compileExpression(v: unknown) {
|
|
|
13
17
|
if (v.includes("{")) {
|
|
14
18
|
const vCutOne = v.slice(1, -1);
|
|
15
19
|
if (v[0] === "{" && v[v.length - 1] === "}" && !vCutOne.includes("{")) {
|
|
16
|
-
return compilePlaceholder(vCutOne)
|
|
20
|
+
return compilePlaceholder(vCutOne);
|
|
17
21
|
}
|
|
18
22
|
return createStringInterpolator(v.replace(/\s+/g, " "), compilePlaceholder);
|
|
19
23
|
}
|
|
@@ -22,4 +26,4 @@ function compileExpression(v: unknown) {
|
|
|
22
26
|
|
|
23
27
|
export const resolveExpression = (x: unknown) => {
|
|
24
28
|
return CACHE.has(x) ? CACHE.get(x) : CACHE.set(x, compileExpression(x)).get(x);
|
|
25
|
-
}
|
|
29
|
+
};
|
|
@@ -1,22 +1,10 @@
|
|
|
1
1
|
import type { EContent, IManifestNode, IPlatform } from "arrmatura/types";
|
|
2
|
-
import { mapEntries } from "ultimus";
|
|
3
2
|
import type { Datum, Delta, Hash, Uid, XmlNode } from "ultimus/types";
|
|
4
3
|
|
|
5
4
|
import { Arrmatron } from "../core/Arrmatron";
|
|
6
5
|
import { ManifestNode } from "../core/ManifestNode";
|
|
7
6
|
import { compilePlaceholder } from "../utils/compileExpression";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const narrowData = (data: any) => {
|
|
11
|
-
if (!data) {
|
|
12
|
-
return [];
|
|
13
|
-
}
|
|
14
|
-
if (Array.isArray(data)) return data;
|
|
15
|
-
if (typeof data[Symbol.iterator] === 'function') return [...data];
|
|
16
|
-
if (typeof data === "string") return data.split(',').map(id => ({ id, name: id }));
|
|
17
|
-
if (typeof data === "object") return mapEntries(data, (key, value) => ({ ...value, id: key }));
|
|
18
|
-
return [{ id: String(data), name: String(data) }];
|
|
19
|
-
}
|
|
7
|
+
import { narrowData } from "../utils/narrowData";
|
|
20
8
|
|
|
21
9
|
class Iterative extends Arrmatron<CForNode> {
|
|
22
10
|
pkHash: Hash<Datum> = {};
|
|
@@ -32,7 +20,6 @@ class Iterative extends Arrmatron<CForNode> {
|
|
|
32
20
|
this.pkHash = {};
|
|
33
21
|
if (each?.length) {
|
|
34
22
|
if (!each.forEach) {
|
|
35
|
-
|
|
36
23
|
throw new Error(`[each] Items has no forEach() ${each.toString()}`);
|
|
37
24
|
}
|
|
38
25
|
each.forEach((_datum: Datum, index: number) => {
|
|
@@ -83,7 +70,12 @@ export class CForNode extends ManifestNode {
|
|
|
83
70
|
|
|
84
71
|
getItemCtxNode(platform: IPlatform) {
|
|
85
72
|
return (
|
|
86
|
-
this.#itemNode ??
|
|
73
|
+
this.#itemNode ??
|
|
74
|
+
(this.#itemNode = new CItemNode(
|
|
75
|
+
this.#xmlNode.id,
|
|
76
|
+
this.#itemName,
|
|
77
|
+
platform.getCompiledNodes([this.#xmlNode])
|
|
78
|
+
))
|
|
87
79
|
);
|
|
88
80
|
}
|
|
89
81
|
|
|
@@ -107,7 +99,10 @@ class IterativeItem extends Arrmatron<CItemNode> {
|
|
|
107
99
|
|
|
108
100
|
get(propId: string) {
|
|
109
101
|
const iName: string = this.manifest.itemName;
|
|
110
|
-
const value =
|
|
102
|
+
const value =
|
|
103
|
+
propId.startsWith(`${iName}.`) || propId === iName
|
|
104
|
+
? super.get(propId)
|
|
105
|
+
: this.scope.get(propId);
|
|
111
106
|
// this.log(propId, value);
|
|
112
107
|
return value;
|
|
113
108
|
}
|
|
@@ -48,10 +48,13 @@ export class CCompositeNode extends ManifestNode {
|
|
|
48
48
|
return groups;
|
|
49
49
|
}, {});
|
|
50
50
|
|
|
51
|
-
this.slotContent = Object.entries(slots ?? {}).reduce<Hash<EContent>>(
|
|
52
|
-
groups[gId]
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
this.slotContent = Object.entries(slots ?? {}).reduce<Hash<EContent>>(
|
|
52
|
+
(groups, [gId, nodes]) => {
|
|
53
|
+
groups[gId] = platform.getCompiledNodes(nodes);
|
|
54
|
+
return groups;
|
|
55
|
+
},
|
|
56
|
+
{}
|
|
57
|
+
);
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
return this.slotContent[slotId || "default"] ?? undefined;
|
|
@@ -49,10 +49,10 @@ export class CIfNode extends ManifestNode {
|
|
|
49
49
|
$then = ifThen
|
|
50
50
|
? ifThen.nodes
|
|
51
51
|
: [
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
Object.assign(this.#xml, {
|
|
53
|
+
nodes: nodes.filter((e) => e !== ifElse),
|
|
54
|
+
}),
|
|
55
|
+
];
|
|
56
56
|
} else if (ifThen) {
|
|
57
57
|
$then = ifThen.nodes;
|
|
58
58
|
}
|
|
@@ -68,7 +68,10 @@ export class CIfNode extends ManifestNode {
|
|
|
68
68
|
} else if (expr.slice(0, 5) === "slot(") {
|
|
69
69
|
const slotId = expr.slice(5, -1);
|
|
70
70
|
// DO NOT place inside iterations.
|
|
71
|
-
this.addPropertyResolver(
|
|
71
|
+
this.addPropertyResolver(
|
|
72
|
+
(c: IArrmatron) => !!(c.scope as Composite).slotContent?.(slotId)?.size,
|
|
73
|
+
"condition"
|
|
74
|
+
);
|
|
72
75
|
} else {
|
|
73
76
|
const resolver = compilePlaceholder(expr);
|
|
74
77
|
this.addPropertyResolver((c) => !!resolver(c), "condition");
|
|
@@ -7,8 +7,6 @@ import { objectFingerprint } from "../utils/FingerprintMashine";
|
|
|
7
7
|
class Connector extends Arrmatron<ConnectorNode> {
|
|
8
8
|
prevkey: any;
|
|
9
9
|
|
|
10
|
-
counter = 0;
|
|
11
|
-
|
|
12
10
|
/**
|
|
13
11
|
* Triggers an with changes after a specified timeout.
|
|
14
12
|
*
|
|
@@ -16,11 +14,15 @@ class Connector extends Arrmatron<ConnectorNode> {
|
|
|
16
14
|
*/
|
|
17
15
|
touch() {
|
|
18
16
|
super.touch();
|
|
19
|
-
|
|
17
|
+
|
|
20
18
|
const { trigger, data, change } = this.$component;
|
|
21
|
-
const
|
|
19
|
+
const hasTrigger = "trigger" in this.$component;
|
|
20
|
+
|
|
21
|
+
if (hasTrigger && trigger == null) return;
|
|
22
|
+
|
|
23
|
+
const key = hasTrigger ? trigger : data;
|
|
22
24
|
const fprint = objectFingerprint(key);
|
|
23
|
-
const changed = !(
|
|
25
|
+
const changed = !("prevkey" in this) || this.prevkey != fprint;
|
|
24
26
|
|
|
25
27
|
if (!changed) return;
|
|
26
28
|
|
|
@@ -43,5 +45,4 @@ export class ConnectorNode extends ManifestNode {
|
|
|
43
45
|
get EntitronConstructor() {
|
|
44
46
|
return Connector;
|
|
45
47
|
}
|
|
46
|
-
|
|
47
48
|
}
|
package/src/registry/index.ts
CHANGED
|
@@ -2,20 +2,17 @@ import { Uid, XmlNode } from "ultimus";
|
|
|
2
2
|
|
|
3
3
|
import { IManifestNode } from "../../types";
|
|
4
4
|
|
|
5
|
+
import { CForNode } from "./Iterative";
|
|
5
6
|
import { CCompositeNode } from "./composition";
|
|
6
7
|
import { CIfNode } from "./conditionals";
|
|
7
8
|
import { ConnectorNode } from "./connector";
|
|
8
9
|
import { CElementNode } from "./elementary";
|
|
9
10
|
import { CFragmentNode } from "./fragment";
|
|
10
|
-
import { CForNode } from "./iterations";
|
|
11
11
|
import { CDynamicTagNode } from "./routing";
|
|
12
12
|
import { CSelectorNode } from "./selection";
|
|
13
13
|
import { CSlotNode } from "./slot";
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
15
|
const cloneNode = (x: XmlNode, eliminateAttr: string): XmlNode => {
|
|
18
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
19
16
|
const { [eliminateAttr]: _, ...attrs } = x.attrs ?? {};
|
|
20
17
|
return {
|
|
21
18
|
text: x.text,
|
|
@@ -27,7 +24,7 @@ const cloneNode = (x: XmlNode, eliminateAttr: string): XmlNode => {
|
|
|
27
24
|
};
|
|
28
25
|
|
|
29
26
|
const __compileManifestNode = (x: XmlNode): IManifestNode => {
|
|
30
|
-
const { tag =
|
|
27
|
+
const { tag = "div", attrs } = x;
|
|
31
28
|
if (attrs?.["Each"]) {
|
|
32
29
|
return new CForNode(cloneNode(x, "Each"), String(attrs["Each"]).trim().split(" "));
|
|
33
30
|
}
|
package/src/registry/root.ts
CHANGED
package/src/registry/routing.ts
CHANGED
|
@@ -12,7 +12,12 @@ class DynamicTag extends Arrmatron<CDynamicTagNode> {
|
|
|
12
12
|
const tag = this.$component.tag;
|
|
13
13
|
const uid = `${this.uid}:${String(tag ?? "")}`;
|
|
14
14
|
return new Map([
|
|
15
|
-
[
|
|
15
|
+
[
|
|
16
|
+
uid,
|
|
17
|
+
Object.assign(this.platform.getCompiledNodes({ ...this.manifest.xml, tag } as XmlNode), {
|
|
18
|
+
uid,
|
|
19
|
+
}),
|
|
20
|
+
],
|
|
16
21
|
]);
|
|
17
22
|
}
|
|
18
23
|
}
|