@yahoo/uds-create-config 4.5.2 → 4.7.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/configs/CanvasConfig.d.ts +900 -80
- package/dist/configs/react-native-system.d.ts +603 -55
- package/dist/configs/system.d.ts +603 -55
- package/dist/configs/system.js +7 -1
- package/dist/entities/system/Component.d.ts +480 -39
- package/dist/entities/system/Component.js +264 -93
- package/dist/entities/system/File.d.ts +1 -1
- package/dist/entities/system/LinkedSystem.d.ts +2 -2
- package/dist/entities/system/Operation.d.ts +1 -1
- package/dist/entities/system/StyleProperty.d.ts +8 -0
- package/dist/entities/system/StyleProperty.js +12 -1
- package/dist/entities/system/Tool.d.ts +1 -1
- package/dist/entities/system/component-module.js +1 -1
- package/dist/entities/system/defineComponent.d.ts +45 -7
- package/dist/entities/system/defineComponent.js +52 -25
- package/dist/framework/Config.d.ts +8 -2
- package/dist/framework/Config.js +97 -23
- package/dist/framework/class-names.d.ts +4 -2
- package/dist/framework/class-names.js +56 -17
- package/dist/framework/defineDerivedEntity.d.ts +1 -1
- package/dist/framework/defineEntity.d.ts +2 -2
- package/dist/framework/defineSubEntity.d.ts +3 -2
- package/dist/framework/preview-store.d.ts +19 -0
- package/dist/framework/preview-store.js +18 -0
- package/dist/framework/projections.d.ts +28 -8
- package/dist/framework/projections.js +50 -16
- package/dist/framework/prop-surface.d.ts +21 -1
- package/dist/framework/prop-surface.js +99 -21
- package/dist/framework/render-spec.d.ts +103 -3
- package/dist/framework/render-spec.js +285 -37
- package/dist/framework/schema-version.d.ts +1 -1
- package/dist/framework/schema-version.js +5 -5
- package/dist/framework/source-integrity.d.ts +14 -1
- package/dist/framework/utils/refs.js +10 -1
- package/dist/framework/validate-spec.d.ts +2 -0
- package/dist/framework/validate-spec.js +13 -0
- package/dist/index.d.ts +15 -12
- package/dist/index.js +7 -6
- package/dist/migrations/2.0.0/v1-artifact.d.ts +305 -29
- package/dist/migrations/20260919051027_prop_conditions_and_anatomy_positions_at_rest.d.ts +22 -0
- package/dist/migrations/20260919051027_prop_conditions_and_anatomy_positions_at_rest.js +10 -0
- package/dist/migrations/20260920004019_preview_conditions_keyed.d.ts +27 -0
- package/dist/migrations/20260920004019_preview_conditions_keyed.js +148 -0
- package/dist/migrations/20260920161309_prefixed_token_group.d.ts +12 -0
- package/dist/migrations/20260920161309_prefixed_token_group.js +10 -0
- package/dist/renderer/wrapRegistry.js +8 -2
- package/dist/renderer/wrappers/component-refs.d.ts +14 -0
- package/dist/renderer/wrappers/component-refs.js +79 -0
- package/dist/renderer/wrappers/hex-normalize.d.ts +5 -7
- package/dist/renderer/wrappers/hex-normalize.js +11 -10
- package/dist/spec/cell.d.ts +171 -0
- package/dist/spec/cell.js +361 -0
- package/dist/spec/detach.d.ts +125 -0
- package/dist/spec/detach.js +511 -0
- package/dist/spec/empty-node-slots.d.ts +11 -23
- package/dist/spec/empty-node-slots.js +12 -39
- package/dist/spec/index.d.ts +5 -3
- package/dist/spec/index.js +3 -3
- package/dist/spec/instance.d.ts +77 -0
- package/dist/spec/instance.js +80 -0
- package/dist/spec/preview-content.d.ts +95 -0
- package/dist/spec/preview-content.js +149 -0
- package/dist/spec/preview-elements.d.ts +54 -15
- package/dist/spec/preview-elements.js +109 -29
- package/dist/spec/prop-conditions.d.ts +28 -0
- package/dist/spec/prop-conditions.js +39 -0
- package/dist/spec/slot.d.ts +206 -0
- package/dist/spec/slot.js +542 -0
- package/dist/spec/visibility.d.ts +82 -0
- package/dist/spec/visibility.js +109 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { reject, rejectData } from "../../framework/rejection.js";
|
|
2
|
-
import { isRef, kindOf, memberOf, pathOf, ref, refLeaf, refSchema, splitRef } from "../../framework/utils/refs.js";
|
|
2
|
+
import { canonicalCondition, isRef, kindOf, memberOf, pathOf, ref, refLeaf, refSchema, splitRef } from "../../framework/utils/refs.js";
|
|
3
3
|
import { isPlainObject } from "../../framework/utils/field-path.js";
|
|
4
4
|
import { pathInput } from "../../framework/schemas.js";
|
|
5
5
|
import { defineEntity } from "../../framework/defineEntity.js";
|
|
@@ -9,9 +9,13 @@ import { childrenPolicy } from "./element.js";
|
|
|
9
9
|
import { listedIn } from "../../framework/utils/enumerated.js";
|
|
10
10
|
import { valueSchemaOf } from "../../framework/value-domain.js";
|
|
11
11
|
import { Style, addBagKeyIssues, anatomyPropValue, bagNameEdges, bagRewriteName, bindBagTokens, collectBag, isPlainRecord, openBag, styleValue } from "./style-bag.js";
|
|
12
|
+
import { storedPreview } from "../../framework/preview-store.js";
|
|
12
13
|
import { propValueDomain, resolveComponentProps, stylePropValueLeaves, validateComponentProps } from "../../framework/projections.js";
|
|
13
14
|
import { intoName, routedPropIn, routesContent } from "../../framework/prop-surface.js";
|
|
14
15
|
import { slotAnatomyPropOf, slotTargetsOf, withoutTruthyTerm } from "../../framework/render-spec.js";
|
|
16
|
+
import { listSlots, namedSlots, slotHostedBy, slotOf } from "../../spec/slot.js";
|
|
17
|
+
import { cellAt } from "../../spec/cell.js";
|
|
18
|
+
import { instanceAt } from "../../spec/instance.js";
|
|
15
19
|
import { Package } from "./Package.js";
|
|
16
20
|
import { File } from "./File.js";
|
|
17
21
|
import { z } from "zod";
|
|
@@ -303,8 +307,8 @@ const VariantValue = defineSubEntity({
|
|
|
303
307
|
* which takes its styling with it. */
|
|
304
308
|
deprecated: z.boolean().optional()
|
|
305
309
|
}),
|
|
306
|
-
namedValuesIn: ["defaultProps", "
|
|
307
|
-
orphans: ["styles"]
|
|
310
|
+
namedValuesIn: ["defaultProps", "preview/*/props"],
|
|
311
|
+
orphans: ["styles", "preview"]
|
|
308
312
|
});
|
|
309
313
|
/**
|
|
310
314
|
* A variant's domain: the values it owns, or a ref that delegates to whatever owns them.
|
|
@@ -347,60 +351,87 @@ function routedTarget(decl) {
|
|
|
347
351
|
into: decl.target.slice(separator + 1)
|
|
348
352
|
};
|
|
349
353
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
354
|
+
/** A prop that applies only under a condition on its siblings — the record a style rule's `when`
|
|
355
|
+
* holds. Open here, where no owner is known; `propSchemaFor` narrows it to the owner's props. */
|
|
356
|
+
const conditionLeaf = z.union([
|
|
357
|
+
z.string(),
|
|
358
|
+
z.number(),
|
|
359
|
+
z.boolean(),
|
|
360
|
+
refSchema
|
|
361
|
+
]);
|
|
362
|
+
const propCondition = z.record(z.string(), z.union([conditionLeaf, z.array(conditionLeaf)])).optional();
|
|
363
|
+
/**
|
|
364
|
+
* The declaration shape over a given condition schema: the generic one with `when` open, and one
|
|
365
|
+
* per owner with `when` narrowed to that component's props. A factory rather than a per-owner
|
|
366
|
+
* transform over the generic shape, because a field-path write (`sub-update` at `value.warning`)
|
|
367
|
+
* derives its schema from the arms' object shapes, which a transform would hide.
|
|
368
|
+
*/
|
|
369
|
+
function propShapeWith(when) {
|
|
370
|
+
return z.discriminatedUnion("type", [
|
|
371
|
+
z.strictObject({
|
|
372
|
+
type: z.literal("slot"),
|
|
373
|
+
text: z.boolean().optional(),
|
|
374
|
+
accepts: z.union([refSchema, z.array(refSchema)]).optional(),
|
|
375
|
+
required: z.boolean().optional(),
|
|
376
|
+
when
|
|
377
|
+
}),
|
|
378
|
+
z.strictObject({
|
|
379
|
+
type: z.literal("variant"),
|
|
380
|
+
value: variantDomain,
|
|
381
|
+
required: z.boolean().optional(),
|
|
382
|
+
when
|
|
383
|
+
}),
|
|
384
|
+
z.strictObject({
|
|
385
|
+
type: z.literal("forward"),
|
|
386
|
+
target: z.union([layerPath, refSchema]),
|
|
387
|
+
into: z.union([z.string(), refSchema]).optional(),
|
|
388
|
+
accepts: z.union([refSchema, z.array(refSchema)]).optional(),
|
|
389
|
+
text: z.boolean().optional(),
|
|
390
|
+
required: z.boolean().optional(),
|
|
391
|
+
when
|
|
392
|
+
}).transform(routedTarget),
|
|
393
|
+
z.strictObject({
|
|
394
|
+
type: z.literal("styleProperty"),
|
|
395
|
+
value: refSchema,
|
|
396
|
+
when
|
|
397
|
+
}),
|
|
398
|
+
z.strictObject({
|
|
399
|
+
type: z.literal("composite"),
|
|
400
|
+
value: refSchema,
|
|
401
|
+
when
|
|
402
|
+
}),
|
|
403
|
+
z.strictObject({
|
|
404
|
+
type: z.literal("motion"),
|
|
405
|
+
value: refSchema.default({ __ref: "motion:" }),
|
|
406
|
+
when
|
|
407
|
+
}),
|
|
408
|
+
z.strictObject({
|
|
409
|
+
type: z.literal("string"),
|
|
410
|
+
value: z.string().optional(),
|
|
411
|
+
required: z.boolean().optional(),
|
|
412
|
+
when
|
|
413
|
+
}),
|
|
414
|
+
z.strictObject({
|
|
415
|
+
type: z.literal("number"),
|
|
416
|
+
value: z.number().optional(),
|
|
417
|
+
required: z.boolean().optional(),
|
|
418
|
+
when
|
|
419
|
+
}),
|
|
420
|
+
z.strictObject({
|
|
421
|
+
type: z.literal("boolean"),
|
|
422
|
+
value: z.boolean().optional(),
|
|
423
|
+
required: z.boolean().optional(),
|
|
424
|
+
when
|
|
425
|
+
})
|
|
426
|
+
], { error: (issue) => {
|
|
427
|
+
const type = issue.input?.type;
|
|
428
|
+
if (typeof type !== "string") return void 0;
|
|
429
|
+
const types = PROP_TYPES.join(", ");
|
|
430
|
+
if (/^style-?property$/i.test(type)) return `\`${type}\` is not a prop type; a style property is exposed as \`{ "type": "styleProperty", "value": { "__ref": "style-property:<name>" } }\`. The types are ${types}.`;
|
|
431
|
+
return `\`${type}\` is not a prop type. The types are ${types}; a style property is \`{ "type": "styleProperty", "value": { "__ref": "style-property:<name>" } }\`, content is \`{ "type": "slot", "text": true }\`.`;
|
|
432
|
+
} });
|
|
433
|
+
}
|
|
434
|
+
const propShape = propShapeWith(propCondition);
|
|
404
435
|
/** The prop types, for the refusal that names them. */
|
|
405
436
|
const PROP_TYPES = [
|
|
406
437
|
"slot",
|
|
@@ -413,7 +444,7 @@ const PROP_TYPES = [
|
|
|
413
444
|
"number",
|
|
414
445
|
"boolean"
|
|
415
446
|
];
|
|
416
|
-
const PROP_DESCRIPTION = "What the prop is. A style property the component exposes, `{ \"type\": \"styleProperty\", \"value\": { \"__ref\": \"style-property:<name>\" } }`, which is what a primitive's props mostly are and what makes a style rule's bag key valid on it; a closed set of names a style rule keys on, `{ \"type\": \"variant\", \"value\": [\"sm\", \"lg\"] }`; content, `{ \"type\": \"slot\", \"text\": true }`; or a plain `string`, `number` or `boolean`. `null` hides a prop an inherited layer would expose.";
|
|
447
|
+
const PROP_DESCRIPTION = "What the prop is. A style property the component exposes, `{ \"type\": \"styleProperty\", \"value\": { \"__ref\": \"style-property:<name>\" } }`, which is what a primitive's props mostly are and what makes a style rule's bag key valid on it; a closed set of names a style rule keys on, `{ \"type\": \"variant\", \"value\": [\"sm\", \"lg\"] }`; content, `{ \"type\": \"slot\", \"text\": true }`; or a plain `string`, `number` or `boolean`. `null` hides a prop an inherited layer would expose. A prop that applies only under a condition on a sibling carries `when`, the record a style rule uses: `{ \"type\": \"string\", \"when\": { \"type\": \"color\" } }` shows it for one value, `{ \"when\": { \"type\": [\"default\", \"header\"] } }` for several.";
|
|
417
448
|
/** Nullable because `null` is the omit tombstone: a component that `inherits` its element's props
|
|
418
449
|
* drops one by declaring it `null`, rather than needing a second "don't expose" mechanism. */
|
|
419
450
|
const Prop = defineSubEntity({
|
|
@@ -439,14 +470,15 @@ const Prop = defineSubEntity({
|
|
|
439
470
|
});
|
|
440
471
|
},
|
|
441
472
|
namespaces: "value",
|
|
473
|
+
namesMembersOf: { when: "props" },
|
|
442
474
|
subEntities: { value: VariantValue },
|
|
443
|
-
keyedByNameIn: ["defaultProps", "
|
|
444
|
-
orphans: ["styles"]
|
|
475
|
+
keyedByNameIn: ["defaultProps", "preview/*/props"],
|
|
476
|
+
orphans: ["styles", "preview"]
|
|
445
477
|
});
|
|
446
478
|
/** {@link Prop}'s per-owner schema. Reached only through the memo in its `fields`. */
|
|
447
479
|
function propSchemaFor(config, owner) {
|
|
448
480
|
const layers = owner.layers;
|
|
449
|
-
return
|
|
481
|
+
return propShapeWith(ownerCondition(config, owner)).nullable().check((ctx) => {
|
|
450
482
|
const decl = ctx.value;
|
|
451
483
|
if (decl === null || !isPlainObject(decl) || decl.type !== "forward" || decl.accepts === void 0 || routesContent(config, layers, decl)) return;
|
|
452
484
|
const layer = isRef(decl.target) ? pathOf(decl.target) : String(decl.target ?? "?");
|
|
@@ -460,13 +492,30 @@ function propSchemaFor(config, owner) {
|
|
|
460
492
|
});
|
|
461
493
|
});
|
|
462
494
|
}
|
|
495
|
+
/**
|
|
496
|
+
* A condition narrowed to one owner's props: each key a sibling, each value one of that sibling's
|
|
497
|
+
* values or a list of them. Values are stored as refs the way a style rule's condition is
|
|
498
|
+
* (`conditionValue`), so a value rename cascades into the condition and a delete finds it.
|
|
499
|
+
*/
|
|
500
|
+
function ownerCondition(config, owner) {
|
|
501
|
+
const shape = {};
|
|
502
|
+
for (const [name, decl] of Object.entries(owner.props ?? {})) {
|
|
503
|
+
if (decl === null || decl === void 0) continue;
|
|
504
|
+
const value = conditionValue(name, decl, config);
|
|
505
|
+
shape[name] = z.union([value, z.array(value).min(1)]).optional();
|
|
506
|
+
}
|
|
507
|
+
return z.strictObject(shape, { error: (issue) => {
|
|
508
|
+
const { code, keys } = issue;
|
|
509
|
+
if (code !== "unrecognized_keys" || !keys) return void 0;
|
|
510
|
+
return `\`when\` names ${keys.map((key) => `\`${key}\``).join(", ")}, which is not a prop of this component. A condition names a sibling prop and one of its values, \`{ "when": { "type": "color" } }\`, or a list of them.`;
|
|
511
|
+
} }).optional();
|
|
512
|
+
}
|
|
463
513
|
const propSchemas = /* @__PURE__ */ new WeakMap();
|
|
464
514
|
/** A rule's identity is its condition, as a canonical query string (`*`, `size=xs`,
|
|
465
515
|
* `size=xs&variant=tertiary`) — sorted, so the same condition always yields the same key however
|
|
466
516
|
* the editor collected it. */
|
|
467
517
|
function canonicalWhen(when) {
|
|
468
|
-
|
|
469
|
-
return entries.length === 0 ? "*" : entries.map(([k, v]) => `${k}=${String(refLeaf(v))}`).join("&");
|
|
518
|
+
return canonicalCondition(when);
|
|
470
519
|
}
|
|
471
520
|
/**
|
|
472
521
|
* What a rule may set one prop to: the prop's own value domain, plus — for a prop that writes CSS —
|
|
@@ -752,11 +801,14 @@ function composedCreateExample(config, path) {
|
|
|
752
801
|
when: null,
|
|
753
802
|
layers: styleLayers
|
|
754
803
|
} } } : {},
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
804
|
+
preview: { ["*"]: {
|
|
805
|
+
when: null,
|
|
806
|
+
props: {
|
|
807
|
+
icon: true,
|
|
808
|
+
label: "Example",
|
|
809
|
+
children: "Body text"
|
|
810
|
+
}
|
|
811
|
+
} }
|
|
760
812
|
}
|
|
761
813
|
};
|
|
762
814
|
}
|
|
@@ -859,6 +911,56 @@ function perOwner({ cache, config, owner, build }) {
|
|
|
859
911
|
return schema;
|
|
860
912
|
}
|
|
861
913
|
const ruleSchemas = /* @__PURE__ */ new WeakMap();
|
|
914
|
+
const PREVIEW_DESCRIPTION = "What the catalogue tile and the matrix render with, keyed by condition the way `styles` is. The `*` entry, `{ \"when\": null, \"props\": { ... } }`, is what every cell starts from; an entry with a condition, `{ \"when\": { \"type\": \"image\" }, \"props\": { \"src\": \"…\" } }`, adds its props to the cells whose values match, in the grammar a style rule uses. A slot prop previews with a stored element, `{ \"element\": { \"__ref\": \"component:<Name>\" } }`. A condition on a prop no rule styles makes that prop a preview axis with the value named. A component whose root takes children renders empty until an entry or `defaultProps` carries some.";
|
|
915
|
+
const previewShapeError = "a preview entry is `{ \"when\": null | { \"<prop>\": \"<value>\" }, \"props\": { \"<prop>\": <value> } }`: `when` names this component's own props at one of their values (`null` for the entry every cell reads) and `props` the props the matching cells preview with.";
|
|
916
|
+
const previewBase = z.object({
|
|
917
|
+
when: z.record(z.string(), z.unknown()).nullable().default(null),
|
|
918
|
+
props: z.record(z.string(), z.unknown())
|
|
919
|
+
});
|
|
920
|
+
const previewSchemas = /* @__PURE__ */ new WeakMap();
|
|
921
|
+
/** Whether a preview entry previews nothing: its `props` are empty. */
|
|
922
|
+
function previewShowsNothing(entry) {
|
|
923
|
+
const props = entry?.props;
|
|
924
|
+
return !props || Object.keys(props).length === 0;
|
|
925
|
+
}
|
|
926
|
+
/**
|
|
927
|
+
* The props a cell previews with, keyed by the condition they hold under the way a style rule is:
|
|
928
|
+
* `*` for every cell, a condition on the component's own props for some. `when` names those props,
|
|
929
|
+
* so a prop rename moves it and a value rename moves the ref it stores; `props` is keyed by prop
|
|
930
|
+
* too, which `Prop.keyedByNameIn` and `VariantValue.namedValuesIn` reach through `preview/*\/props`.
|
|
931
|
+
* Deleting a prop or a value drops the entries conditioned on it.
|
|
932
|
+
*/
|
|
933
|
+
const Preview = defineSubEntity({
|
|
934
|
+
name: "preview",
|
|
935
|
+
label: "Preview",
|
|
936
|
+
labelPlural: "Previews",
|
|
937
|
+
key: (entry) => canonicalWhen(entry?.when),
|
|
938
|
+
vacant: previewShowsNothing,
|
|
939
|
+
namesMembersOf: { when: "props" },
|
|
940
|
+
base: previewBase,
|
|
941
|
+
fields: ({ config, parent }) => {
|
|
942
|
+
const body = parent;
|
|
943
|
+
if (!config || !body) return previewBase;
|
|
944
|
+
return perOwner({
|
|
945
|
+
cache: previewSchemas,
|
|
946
|
+
config,
|
|
947
|
+
owner: body,
|
|
948
|
+
build: () => {
|
|
949
|
+
const when = {};
|
|
950
|
+
for (const [name, decl] of Object.entries(body.props ?? {})) when[name] = conditionValue(name, decl, config).optional();
|
|
951
|
+
return z.strictObject({
|
|
952
|
+
when: z.strictObject(when, { error: previewShapeError }).nullable().default(null),
|
|
953
|
+
props: z.record(z.string(), z.unknown())
|
|
954
|
+
}, { error: previewShapeError });
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
/** Why a slot refuses an element, for a write the config turns away. */
|
|
960
|
+
function slotRefusal({ slot, element }) {
|
|
961
|
+
const named = isRef(element) ? element.__ref : String(element);
|
|
962
|
+
return `\`${slot.from}\` takes ${slot.accepts.join(", ")}; \`${named}\` is outside it.`;
|
|
963
|
+
}
|
|
862
964
|
/** What one node renders. The order is the resolution order the build reads a tag by: one of this
|
|
863
965
|
* Component's layers, then something the config names, then a package export — plus `$state` for an
|
|
864
966
|
* element the instance chooses (`GLYPHS[name]`), whose legal values are the prop's own domain
|
|
@@ -906,7 +1008,7 @@ const stateCondition = z.strictObject({
|
|
|
906
1008
|
...test,
|
|
907
1009
|
$state: test.$state.startsWith("/") ? test.$state : `/${test.$state}`
|
|
908
1010
|
}));
|
|
909
|
-
const orCondition = z.strictObject({ $or: z.array(stateCondition) });
|
|
1011
|
+
const orCondition = z.strictObject({ $or: z.array(z.union([stateCondition, z.array(stateCondition)])) });
|
|
910
1012
|
/**
|
|
911
1013
|
* The flat condition an editor collects — `{ variant: 'solid' }` — compiled to the grammar above.
|
|
912
1014
|
*
|
|
@@ -948,8 +1050,9 @@ const visibility = z.union([
|
|
|
948
1050
|
stateCondition,
|
|
949
1051
|
orCondition,
|
|
950
1052
|
z.array(z.union([stateCondition, orCondition])),
|
|
1053
|
+
z.literal(false),
|
|
951
1054
|
flatCondition
|
|
952
|
-
], { error: "a condition is `{ \"$state\": \"<prop>\" }` for a prop that is set, `{ \"$state\": \"<prop>\", \"eq\": \"<value>\" }` for one value of it (`neq`, `gt`, `gte`, `lt`, `lte` and `not: true` also work), `{ \"$or\": [ … ] }` for any of several,
|
|
1055
|
+
], { error: "a condition is `{ \"$state\": \"<prop>\" }` for a prop that is set, `{ \"$state\": \"<prop>\", \"eq\": \"<value>\" }` for one value of it (`neq`, `gt`, `gte`, `lt`, `lte` and `not: true` also work), `{ \"$or\": [ … ] }` for any of several, a list of conditions that must all hold, or `false` for a position only an interaction shows" });
|
|
953
1056
|
/** What an event on a node does to the state model: json-render's own `setState`, its path and the
|
|
954
1057
|
* value to write, which may itself be a choice on the state (`{ $cond, $then, $else }`). */
|
|
955
1058
|
const stateAction = z.strictObject({
|
|
@@ -1005,7 +1108,7 @@ function nodeComponent(body, element) {
|
|
|
1005
1108
|
* Owned by the op rather than by each fill surface, so one gesture stays one patch per node and
|
|
1006
1109
|
* every caller agrees the fill will actually paint.
|
|
1007
1110
|
*/
|
|
1008
|
-
function slotFillHostWrites({ config, path, parent, key, slotProp, node, layers }) {
|
|
1111
|
+
function slotFillHostWrites({ config, path, parent, key, slotProp, node, layers, element }) {
|
|
1009
1112
|
const host = nodeComponent({ layers }, node?.element ?? layers?.[parent]);
|
|
1010
1113
|
if (host && config.resolve("component", host)) {
|
|
1011
1114
|
const declared = resolveComponentProps(config, host);
|
|
@@ -1020,6 +1123,16 @@ function slotFillHostWrites({ config, path, parent, key, slotProp, node, layers
|
|
|
1020
1123
|
const parentLayer = isRef(parentElement) && kindOf(parentElement.__ref) === "layer" ? pathOf(parentElement.__ref) : parent;
|
|
1021
1124
|
const route = slotTargetsOf(config, path).find((t) => t.layer === parentLayer && t.into === slotProp);
|
|
1022
1125
|
if (!route) return { slots };
|
|
1126
|
+
const filled = nodeTypeOf(element, layers);
|
|
1127
|
+
const slot = filled !== void 0 ? slotOf({
|
|
1128
|
+
config,
|
|
1129
|
+
path,
|
|
1130
|
+
slot: route.from
|
|
1131
|
+
}) : void 0;
|
|
1132
|
+
if (slot && filled !== void 0 && !slot.takes(filled)) rejectData(slotRefusal({
|
|
1133
|
+
slot,
|
|
1134
|
+
element: filled
|
|
1135
|
+
}), ["slots", slotProp]);
|
|
1023
1136
|
return {
|
|
1024
1137
|
slots,
|
|
1025
1138
|
visible: withoutTruthyTerm({
|
|
@@ -1028,6 +1141,14 @@ function slotFillHostWrites({ config, path, parent, key, slotProp, node, layers
|
|
|
1028
1141
|
})
|
|
1029
1142
|
};
|
|
1030
1143
|
}
|
|
1144
|
+
/** What a node renders, as a type `Slot.takes` reads: a layer ref through the layer's element. */
|
|
1145
|
+
function nodeTypeOf(element, layers) {
|
|
1146
|
+
if (isRef(element) && kindOf(element.__ref) === "layer") {
|
|
1147
|
+
const layer = layers?.[pathOf(element.__ref)];
|
|
1148
|
+
return typeof layer === "string" || isRef(layer) ? layer : void 0;
|
|
1149
|
+
}
|
|
1150
|
+
if (typeof element === "string" || isRef(element)) return element;
|
|
1151
|
+
}
|
|
1031
1152
|
/**
|
|
1032
1153
|
* The `slots` entries of `node` naming any of `doomed` — each answered with what survives, so a
|
|
1033
1154
|
* caller writes the filtered list back at the entry's own address (or `null` when nothing does)
|
|
@@ -1135,13 +1256,16 @@ const AnatomyNode = defineSubEntity({
|
|
|
1135
1256
|
path: ["text"],
|
|
1136
1257
|
message: `\`text\` is a fixed string, and "${node.text}" is one of this component's props. To show the prop's value here, declare it \`{ "type": "forward", "target": "${isRef(element) && kindOf(element.__ref) === "layer" ? pathOf(element.__ref) : "<layer>"}/children" }\` and give this node no \`text\`.`
|
|
1137
1258
|
});
|
|
1259
|
+
let placed = element;
|
|
1260
|
+
if (isRef(placed) && kindOf(placed.__ref) === "layer") placed = body.layers?.[pathOf(placed.__ref)];
|
|
1261
|
+
const instanceProps = isRef(placed) && kindOf(placed.__ref) === "component" ? resolveComponentProps(config, pathOf(placed.__ref)) : void 0;
|
|
1138
1262
|
for (const [prop, value] of Object.entries(node.props ?? {})) {
|
|
1139
1263
|
if (isRef(value) && [
|
|
1140
1264
|
"icon",
|
|
1141
1265
|
"component",
|
|
1142
1266
|
"package",
|
|
1143
1267
|
"layer"
|
|
1144
|
-
].includes(kindOf(value.__ref))) {
|
|
1268
|
+
].includes(kindOf(value.__ref)) && !(instanceProps && prop in instanceProps)) {
|
|
1145
1269
|
ctx.issues.push({
|
|
1146
1270
|
code: "custom",
|
|
1147
1271
|
input: value,
|
|
@@ -1256,18 +1380,16 @@ const componentFields = z.strictObject({
|
|
|
1256
1380
|
*/
|
|
1257
1381
|
forwards: Forward.optional(),
|
|
1258
1382
|
styles: StyleRule.optional(),
|
|
1383
|
+
/** The props the render reads that the component neither declares nor forwards — its render
|
|
1384
|
+
* extras (`.render<{ swatchColor?: string }>()`). Read off the render at build like `anatomy`,
|
|
1385
|
+
* never authored as data. The module consumes them; a component painted out of its anatomy has
|
|
1386
|
+
* no module, so the expansion keeps them off the root rather than wearing them as attributes. */
|
|
1387
|
+
renderProps: z.array(z.string()).optional().describe("Props the render reads beyond those declared, read off the source at build. Not authored: declare a prop in `props` instead."),
|
|
1259
1388
|
anatomy: AnatomyNode.optional().describe("The tree of what this component renders, keyed by node. Each node names what it renders (a layer, for anything a style rule reaches) and lists its `children`; the node keyed `root` is the top. A one-layer component may omit it."),
|
|
1260
1389
|
defaultProps: Style.optional(),
|
|
1261
|
-
/**
|
|
1262
|
-
*
|
|
1263
|
-
|
|
1264
|
-
previewProps: z.record(z.string(), z.unknown()).optional().describe("The props the catalogue tile and the variant matrix render with. A component whose root takes children renders empty until this or `defaultProps` carries some."),
|
|
1265
|
-
/** Preview props that hold under a condition on the cell — see `preview({ when })`. Like
|
|
1266
|
-
* `previewProps`, read off `.preview()` at build and never authored as data. */
|
|
1267
|
-
previewWhen: z.array(z.object({
|
|
1268
|
-
when: z.record(z.string(), z.unknown()),
|
|
1269
|
-
props: z.record(z.string(), z.unknown())
|
|
1270
|
-
})).optional().describe("Preview props that hold only when the cell matches `when`, in the grammar a style rule uses: `{ \"when\": { \"type\": \"image\" }, \"props\": { \"src\": \"…\" } }`. A condition on a prop no rule styles makes that prop a preview axis with the value named."),
|
|
1390
|
+
/** What the tile and the matrix render with — see `preview({ defaultProps, when })`. Read off
|
|
1391
|
+
* `.preview()` at build, and written per cell from the matrix. */
|
|
1392
|
+
preview: Preview.optional().describe(PREVIEW_DESCRIPTION),
|
|
1271
1393
|
/**
|
|
1272
1394
|
* The source that implements this component — a ref, because the file is an entity the seal owns.
|
|
1273
1395
|
*
|
|
@@ -1289,17 +1411,43 @@ const componentFields = z.strictObject({
|
|
|
1289
1411
|
export: z.string().optional()
|
|
1290
1412
|
}).check((ctx) => {
|
|
1291
1413
|
const body = ctx.value;
|
|
1414
|
+
if (isPlainObject(body.props)) for (const [name, decl] of Object.entries(body.props)) {
|
|
1415
|
+
const when = decl?.when;
|
|
1416
|
+
if (!isPlainObject(when) || !(name in when)) continue;
|
|
1417
|
+
ctx.issues.push({
|
|
1418
|
+
code: "custom",
|
|
1419
|
+
input: decl,
|
|
1420
|
+
path: [
|
|
1421
|
+
"props",
|
|
1422
|
+
name,
|
|
1423
|
+
"when",
|
|
1424
|
+
name
|
|
1425
|
+
],
|
|
1426
|
+
message: `\`${name}\` conditions on itself. \`when\` names a sibling prop and one of its values.`
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1292
1429
|
if (body.forwards || body.file || isRef(body.props)) return;
|
|
1293
1430
|
const declared = body.props ?? {};
|
|
1294
|
-
|
|
1295
|
-
|
|
1431
|
+
if (Object.hasOwn(declared, "children") && declared.children !== null) return;
|
|
1432
|
+
const content = [{
|
|
1433
|
+
values: body.defaultProps,
|
|
1434
|
+
path: ["defaultProps", "children"]
|
|
1435
|
+
}, ...Object.entries(storedPreview(body)).map(([key, entry]) => ({
|
|
1436
|
+
values: entry?.props,
|
|
1437
|
+
path: [
|
|
1438
|
+
"preview",
|
|
1439
|
+
key,
|
|
1440
|
+
"props",
|
|
1441
|
+
"children"
|
|
1442
|
+
]
|
|
1443
|
+
}))];
|
|
1444
|
+
for (const { values, path } of content) {
|
|
1296
1445
|
if (!isPlainObject(values) || !("children" in values)) continue;
|
|
1297
|
-
if (Object.hasOwn(declared, "children") && declared.children !== null) continue;
|
|
1298
1446
|
ctx.issues.push({
|
|
1299
1447
|
code: "custom",
|
|
1300
1448
|
input: values,
|
|
1301
|
-
path
|
|
1302
|
-
message: `\`${
|
|
1449
|
+
path,
|
|
1450
|
+
message: `\`${path.join(".")}\` has nowhere to land — this component declares no \`children\` prop, so the content is dropped and the component renders empty. Declare \`"children": { "type": "slot", "text": true }\` in \`props\` — or, to land it in a nested layer, \`"children": { "type": "forward", "target": "<layer>/children" }\`.`
|
|
1303
1451
|
});
|
|
1304
1452
|
}
|
|
1305
1453
|
});
|
|
@@ -1358,6 +1506,7 @@ const ComponentEntity = defineEntity({
|
|
|
1358
1506
|
props: Prop,
|
|
1359
1507
|
forwards: Forward,
|
|
1360
1508
|
styles: StyleRule,
|
|
1509
|
+
preview: Preview,
|
|
1361
1510
|
anatomy: AnatomyNode,
|
|
1362
1511
|
defaultProps: Style
|
|
1363
1512
|
},
|
|
@@ -1385,7 +1534,7 @@ slots: ({ config, path, member }) => {
|
|
|
1385
1534
|
schemas: {
|
|
1386
1535
|
create: {
|
|
1387
1536
|
data: componentFields,
|
|
1388
|
-
description: "Create a component at a new qualified `path`. For a component with nothing in it yet, one to design on the canvas before it has props or variants, use `create-blank` with only a `path` instead of authoring a minimal body here. When its complete body is known, include all layers, anatomy, props, defaults, and styles in this one operation. `data.layers` maps each stable layer name to the element it renders and must include `root` — a tag for a primitive (`{ \"layers\": { \"root\": \"div\" } }`), and for anything built on top, a ref to a primitive (`{ \"root\": { \"__ref\": \"component:Box\" }, \"label\": { \"__ref\": \"component:Text\" } }`), which is what lets the layer take that primitive's style props. A primitive exposes style props as `{ \"type\": \"styleProperty\", \"value\": { \"__ref\": \"style-property:bg\" } }` and declares `children: { \"type\": \"slot\", \"text\": true }` when it holds content — without the slot, nothing nested inside it renders. A composed component exposes a layer's props at its own top level through `forwards`, keyed by layer: `{ \"forwards\": { \"root\": \"*\" } }` passes every prop the root's element takes (so `<Button width=\"full\">` reaches the root's `width`), and a list of member refs (`[{ \"__ref\": \"component:Box#props/width\" }]`, each naming the component that declares the prop) passes only those. A prop a layer already exposes is forwarded, never redeclared in `props`; a `props` entry of `{ \"type\": \"forward\" }` is the reverse route, one of this component's props into a layer's. `anatomy` is the tree the build would read off a render: each node renders a layer (`{ \"__ref\": \"layer:<name>\" }`), an intrinsic tag, or `Slot` (`{ \"element\": \"Slot\", \"props\": { \"name\": \"children\" } }`, where a slot prop's content lands — but a declared slot fills `root` by default, so a primitive still declares its `children` slot prop yet needs no `Slot` node or `children` entry for it, and a `Slot` node is only for landing that content in a nested layer); `children` lists sibling node keys from the `root` node, never a prop name; `text` is a fixed string; `visible` is a condition on a prop (`{ \"$state\": \"<prop>\" }`, or with `eq`); content shown in a nested layer is a route, `children: { \"type\": \"forward\", \"target\": \"label/children\" }`. `$state` appears only in `visible` and in an `element` a prop chooses, never as a prop value. A glyph is a node of its own, `{ \"element\": { \"__ref\": \"icon:<library>/<Name>\" } }`, listed in the `children` of the layer node that holds it, never a prop value. Text a prop supplies to a text layer is never a node either: declare the prop `{ \"type\": \"forward\", \"target\": \"<layer>/children\" }` and the layer shows it; a `Slot` node goes only under a layer that takes nodes. A style rule is `\"styles\": { \"*\": { \"when\": null, \"layers\": { \"root\": { \"bg\": \"surface\" } } } }`, keyed by a name, with each layer's bag under `layers`. A composed component carries its own styles: give it an unconditional (`\"*\"`) rule that lays out and surfaces it through the system's style properties — spacing, alignment and gap on the container, a background and color where it has one — rather than leaning on the bare primitives, which paint flat. Style a layer only with the style props the primitive it renders exposes, set to the values those props accept: `component/get` on the primitive lists both, so read it and choose from that rather than reaching for a CSS name or value the system has no style property for. Set `
|
|
1537
|
+
description: "Create a component at a new qualified `path`. For a component with nothing in it yet, one to design on the canvas before it has props or variants, use `create-blank` with only a `path` instead of authoring a minimal body here. When its complete body is known, include all layers, anatomy, props, defaults, and styles in this one operation. `data.layers` maps each stable layer name to the element it renders and must include `root` — a tag for a primitive (`{ \"layers\": { \"root\": \"div\" } }`), and for anything built on top, a ref to a primitive (`{ \"root\": { \"__ref\": \"component:Box\" }, \"label\": { \"__ref\": \"component:Text\" } }`), which is what lets the layer take that primitive's style props. A primitive exposes style props as `{ \"type\": \"styleProperty\", \"value\": { \"__ref\": \"style-property:bg\" } }` and declares `children: { \"type\": \"slot\", \"text\": true }` when it holds content — without the slot, nothing nested inside it renders. A composed component exposes a layer's props at its own top level through `forwards`, keyed by layer: `{ \"forwards\": { \"root\": \"*\" } }` passes every prop the root's element takes (so `<Button width=\"full\">` reaches the root's `width`), and a list of member refs (`[{ \"__ref\": \"component:Box#props/width\" }]`, each naming the component that declares the prop) passes only those. A prop a layer already exposes is forwarded, never redeclared in `props`; a `props` entry of `{ \"type\": \"forward\" }` is the reverse route, one of this component's props into a layer's. `anatomy` is the tree the build would read off a render: each node renders a layer (`{ \"__ref\": \"layer:<name>\" }`), an intrinsic tag, or `Slot` (`{ \"element\": \"Slot\", \"props\": { \"name\": \"children\" } }`, where a slot prop's content lands — but a declared slot fills `root` by default, so a primitive still declares its `children` slot prop yet needs no `Slot` node or `children` entry for it, and a `Slot` node is only for landing that content in a nested layer); `children` lists sibling node keys from the `root` node, never a prop name; `text` is a fixed string; `visible` is a condition on a prop (`{ \"$state\": \"<prop>\" }`, or with `eq`); content shown in a nested layer is a route, `children: { \"type\": \"forward\", \"target\": \"label/children\" }`. `$state` appears only in `visible` and in an `element` a prop chooses, never as a prop value. A glyph is a node of its own, `{ \"element\": { \"__ref\": \"icon:<library>/<Name>\" } }`, listed in the `children` of the layer node that holds it, never a prop value. Text a prop supplies to a text layer is never a node either: declare the prop `{ \"type\": \"forward\", \"target\": \"<layer>/children\" }` and the layer shows it; a `Slot` node goes only under a layer that takes nodes. A style rule is `\"styles\": { \"*\": { \"when\": null, \"layers\": { \"root\": { \"bg\": \"surface\" } } } }`, keyed by a name, with each layer's bag under `layers`. A composed component carries its own styles: give it an unconditional (`\"*\"`) rule that lays out and surfaces it through the system's style properties — spacing, alignment and gap on the container, a background and color where it has one — rather than leaning on the bare primitives, which paint flat. Style a layer only with the style props the primitive it renders exposes, set to the values those props accept: `component/get` on the primitive lists both, so read it and choose from that rather than reaching for a CSS name or value the system has no style property for. Set `preview` (`{ \"*\": { \"when\": null, \"props\": { \"children\": \"Example\" } } }`) or `defaultProps` so the component previews with content; a root that takes children renders empty otherwise. A style rule's layer bag is keyed by style-prop name (`fontWeight`, `color`), never by CSS property name, and each value is one of that prop's values.",
|
|
1389
1538
|
example: ({ config }) => {
|
|
1390
1539
|
const path = freeExamplePath(config);
|
|
1391
1540
|
const composed = composedCreateExample(config, path);
|
|
@@ -1421,7 +1570,10 @@ slots: ({ config, path, member }) => {
|
|
|
1421
1570
|
children: ["icon", "content"]
|
|
1422
1571
|
}
|
|
1423
1572
|
},
|
|
1424
|
-
|
|
1573
|
+
preview: { ["*"]: {
|
|
1574
|
+
when: null,
|
|
1575
|
+
props: { children: "Example" }
|
|
1576
|
+
} }
|
|
1425
1577
|
}
|
|
1426
1578
|
};
|
|
1427
1579
|
}
|
|
@@ -1625,7 +1777,8 @@ slots: ({ config, path, member }) => {
|
|
|
1625
1777
|
key,
|
|
1626
1778
|
slotProp,
|
|
1627
1779
|
node: body?.anatomy?.[parent],
|
|
1628
|
-
layers: body?.layers
|
|
1780
|
+
layers: body?.layers,
|
|
1781
|
+
element
|
|
1629
1782
|
}) : void 0;
|
|
1630
1783
|
return next.apply({
|
|
1631
1784
|
kind: "component",
|
|
@@ -1922,9 +2075,27 @@ function componentRef(path) {
|
|
|
1922
2075
|
});
|
|
1923
2076
|
return value;
|
|
1924
2077
|
}
|
|
1925
|
-
/**
|
|
1926
|
-
*
|
|
1927
|
-
|
|
2078
|
+
/**
|
|
2079
|
+
* Component keeps the generic entity surface, plus the questions only a component answers: its
|
|
2080
|
+
* slots (`Slot`, in `spec/slot`), what one of its matrix cells shows (`Cell`, in `spec/cell`) and
|
|
2081
|
+
* what a design file put in a placed instance's slots (`Instance`, in `spec/instance`).
|
|
2082
|
+
* Only its ref authoring result knows about the source-only `.render()` step.
|
|
2083
|
+
*/
|
|
2084
|
+
const Component = Object.assign(ComponentEntity, {
|
|
2085
|
+
ref: componentRef,
|
|
2086
|
+
/** Every slot content can be placed in, with what each takes. */
|
|
2087
|
+
slots: listSlots,
|
|
2088
|
+
/** One slot by the prop it arrives as. */
|
|
2089
|
+
slot: slotOf,
|
|
2090
|
+
/** The slot whose content lands in a layer. */
|
|
2091
|
+
slotHostedBy,
|
|
2092
|
+
/** The named node slots alone, without a positioned `children`. */
|
|
2093
|
+
namedSlots,
|
|
2094
|
+
/** The component under one coordinate of its matrix. */
|
|
2095
|
+
cell: cellAt,
|
|
2096
|
+
/** The component a design file's spec element places, with what the file put in its slots. */
|
|
2097
|
+
instance: instanceAt
|
|
2098
|
+
});
|
|
1928
2099
|
/** A valid example prop bag — the first variant prop's first value, else empty. Lives here because
|
|
1929
2100
|
* it reads a component's stored props; the generic example filler only knows address fields.
|
|
1930
2101
|
* Resolved through the domain (not `ownValues`): a variant that delegates its values to a group
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Ref } from "../../framework/utils/refs.js";
|
|
2
2
|
import { SubEntityClass } from "../../framework/defineSubEntity.js";
|
|
3
|
-
import { EntityClass } from "../../framework/defineEntity.js";
|
|
4
3
|
import { ComputedInput } from "../../framework/registry.js";
|
|
4
|
+
import { EntityClass } from "../../framework/defineEntity.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
|
|
7
7
|
//#region src/entities/system/File.d.ts
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { CopyPlan } from "./copy-plan.js";
|
|
2
|
-
import { RegisteredEntities } from "../../framework/registered.js";
|
|
3
2
|
import { SourceResolver, SourceState } from "../../framework/sources.js";
|
|
3
|
+
import { ComputedInput } from "../../framework/registry.js";
|
|
4
|
+
import { RegisteredEntities } from "../../framework/registered.js";
|
|
4
5
|
import { EntityClass, ItemOf } from "../../framework/defineEntity.js";
|
|
5
6
|
import { PartialBodyOf } from "../../framework/defineConfig.js";
|
|
6
7
|
import { BreakPlan, LinkPlan, SourceRowState, UnlinkPlan } from "./link-plan.js";
|
|
7
8
|
import { Config } from "../../framework/Config.js";
|
|
8
|
-
import { ComputedInput } from "../../framework/registry.js";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
|
|
11
11
|
//#region src/entities/system/LinkedSystem.d.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { ComputedInput } from "../../framework/registry.js";
|
|
1
2
|
import { DerivedEntityClass } from "../../framework/defineDerivedEntity.js";
|
|
2
3
|
import { Config } from "../../framework/Config.js";
|
|
3
|
-
import { ComputedInput } from "../../framework/registry.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
6
|
//#region src/entities/system/Operation.d.ts
|
|
@@ -25,6 +25,14 @@ declare const STYLE_PROP_VALUE_FORMS: {
|
|
|
25
25
|
}?: StylePropValueCtx) => z.ZodObject<{
|
|
26
26
|
__ref: z.ZodString;
|
|
27
27
|
}, z.core.$strip>;
|
|
28
|
+
readonly tokenGroupPrefixed: ({
|
|
29
|
+
config
|
|
30
|
+
}?: StylePropValueCtx) => z.ZodObject<{
|
|
31
|
+
value: z.ZodObject<{
|
|
32
|
+
__ref: z.ZodString;
|
|
33
|
+
}, z.core.$strip>;
|
|
34
|
+
prefix: z.ZodString;
|
|
35
|
+
}, z.core.$strict>;
|
|
28
36
|
readonly flag: (_ctx?: StylePropValueCtx) => z.ZodObject<{
|
|
29
37
|
alias: z.ZodBoolean;
|
|
30
38
|
value: z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
@@ -71,7 +71,7 @@ const namesItem = (config, wanted) => (ctx) => {
|
|
|
71
71
|
* of one string too, so `aliasToken` comes before `aliasDeclarations`; a boolean alias is a flag
|
|
72
72
|
* whatever its value holds, so `flag` comes before the named aliases. `token` and `tokenGroup` are
|
|
73
73
|
* one stored shape; with a config each refuses what the other admits, and without one a ref reads
|
|
74
|
-
* as `token`.
|
|
74
|
+
* as `token`. A prefixed group is a group ref beside the prefix its leaves carry.
|
|
75
75
|
*/
|
|
76
76
|
/** A declaration bag's key. A ref is a record of one string too, so the ref marker is not a key here,
|
|
77
77
|
* or a ref no ref form admits would read as a bag. */
|
|
@@ -93,6 +93,13 @@ const STYLE_PROP_VALUE_FORMS = {
|
|
|
93
93
|
title: "Token group",
|
|
94
94
|
description: "A ref to a token group, which offers every token in it and follows the group as it changes."
|
|
95
95
|
}).check(namesItem(config, false)),
|
|
96
|
+
tokenGroupPrefixed: ({ config } = {}) => z.strictObject({
|
|
97
|
+
value: refSchema.check(namesItem(config, false)),
|
|
98
|
+
prefix: z.string().min(1)
|
|
99
|
+
}).meta({
|
|
100
|
+
title: "Prefixed token group",
|
|
101
|
+
description: "A ref to a token group whose every token is offered under `<prefix>/<name>`, so a group bound beside others reads as its own: `{ value: <ref to canvas>, prefix: \"canvas\" }` offers `canvas/slot`."
|
|
102
|
+
}),
|
|
96
103
|
flag: (_ctx = {}) => z.strictObject({
|
|
97
104
|
alias: z.boolean(),
|
|
98
105
|
value: z.union([
|
|
@@ -159,6 +166,10 @@ const STYLE_PROP_VALUE_LEAVES = {
|
|
|
159
166
|
leaf,
|
|
160
167
|
value: path
|
|
161
168
|
})),
|
|
169
|
+
tokenGroupPrefixed: (entry, members) => members(entry.value).map(({ leaf, path }) => ({
|
|
170
|
+
leaf: `${entry.prefix}/${leaf}`,
|
|
171
|
+
value: path
|
|
172
|
+
})),
|
|
162
173
|
flag: (entry, members) => entry.alias ? flagValue(entry.value, members).map((rest) => ({
|
|
163
174
|
leaf: "",
|
|
164
175
|
...rest
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { ComputedInput } from "../../framework/registry.js";
|
|
1
2
|
import { DerivedEntityClass } from "../../framework/defineDerivedEntity.js";
|
|
2
3
|
import { Config } from "../../framework/Config.js";
|
|
3
|
-
import { ComputedInput } from "../../framework/registry.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
6
|
//#region src/entities/system/Tool.d.ts
|