@yahoo/uds-create-config 4.6.0 → 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 +2 -2
- package/dist/framework/Config.js +59 -7
- 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/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 +14 -11
- package/dist/index.js +6 -5
- 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
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/framework/preview-store.ts
|
|
2
|
+
/** The `*` entry the `preview` collection stores — see `preview({ defaultProps })`. */
|
|
3
|
+
const UNCONDITIONAL_PREVIEW_KEY = "*";
|
|
4
|
+
/** Every `preview` entry a body stores, keyed as stored. Empty for a body that stores none. */
|
|
5
|
+
function storedPreview(body) {
|
|
6
|
+
const preview = body?.preview;
|
|
7
|
+
return preview && typeof preview === "object" && !Array.isArray(preview) ? preview : {};
|
|
8
|
+
}
|
|
9
|
+
/** The props the `*` entry previews with, `undefined` when the body stores no such entry. */
|
|
10
|
+
function unconditionalPreviewProps(body) {
|
|
11
|
+
return storedPreview(body)["*"]?.props;
|
|
12
|
+
}
|
|
13
|
+
/** Whether an entry holds under a condition rather than for every cell. */
|
|
14
|
+
function isConditionalPreview(entry) {
|
|
15
|
+
return entry.when !== null && Object.keys(entry.when).length > 0;
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { UNCONDITIONAL_PREVIEW_KEY, isConditionalPreview, storedPreview, unconditionalPreviewProps };
|
|
@@ -2,6 +2,7 @@ import { Ref } from "./utils/refs.js";
|
|
|
2
2
|
import { ItemOf } from "./defineEntity.js";
|
|
3
3
|
import { Config } from "./Config.js";
|
|
4
4
|
import { StylePropLeaf, StyleProperty } from "../entities/system/StyleProperty.js";
|
|
5
|
+
import { StoredPreviewCondition, UNCONDITIONAL_PREVIEW_KEY, storedPreview, unconditionalPreviewProps } from "./preview-store.js";
|
|
5
6
|
|
|
6
7
|
//#region src/framework/projections.d.ts
|
|
7
8
|
/**
|
|
@@ -410,10 +411,10 @@ declare function propValueDomain(config: Config, decl: PropDeclaration): {
|
|
|
410
411
|
* `component/validate` op's truth is config-defined and the pipeline only renders it. */
|
|
411
412
|
declare function validateComponentProps(config: Config, componentPath: string, props: Record<string, unknown>): string[];
|
|
412
413
|
/**
|
|
413
|
-
* The props one preview tile starts from: the component's `defaultProps` with its
|
|
414
|
+
* The props one preview tile starts from: the component's `defaultProps` with its `*` preview entry
|
|
414
415
|
* over the top.
|
|
415
416
|
*
|
|
416
|
-
* Preview wins, and that direction is the whole reason this exists once.
|
|
417
|
+
* Preview wins, and that direction is the whole reason this exists once. The `*` entry is the state
|
|
417
418
|
* a person chose to show a component in — Button's `variant`, an Icon prop's example glyph — so a
|
|
418
419
|
* surface showing that tile has to agree with it, while `defaultProps` remains the baseline for every
|
|
419
420
|
* prop the tile doesn't pin. Get the order backwards and the panel reports a value the canvas isn't
|
|
@@ -421,7 +422,7 @@ declare function validateComponentProps(config: Config, componentPath: string, p
|
|
|
421
422
|
*
|
|
422
423
|
* THE PRECEDENCE RULE, stated once: only a RENDER surface — a preview tile, the catalogue card, slot
|
|
423
424
|
* text seeding — reads this merge. An EDIT surface reads and writes the single field it owns
|
|
424
|
-
* (`defaultProps` OR
|
|
425
|
+
* (`defaultProps` OR the `*` entry, never the merge), and anything that SEEDS committed data — a
|
|
425
426
|
* prop binding, a forward's re-seed, a stored instance prop, a default-value form — reads raw
|
|
426
427
|
* `defaultProps`. A preview value that reaches any of those stops being staging and becomes a real
|
|
427
428
|
* default the moment it is saved, which is exactly the leak UDS-3514 closed. Before adding a caller,
|
|
@@ -451,16 +452,33 @@ cell?: Record<string, unknown>): Record<string, unknown>;
|
|
|
451
452
|
* without restating defaults that would shadow an edit.
|
|
452
453
|
*/
|
|
453
454
|
declare function previewConditionalProps(config: Config, path: string, cell: Record<string, unknown>): Record<string, unknown>;
|
|
454
|
-
/** One preview
|
|
455
|
-
interface
|
|
456
|
-
readonly
|
|
457
|
-
readonly
|
|
455
|
+
/** One conditional `preview` entry with the key it is stored under, which is how a write addresses it. */
|
|
456
|
+
interface IndexedPreviewCondition {
|
|
457
|
+
readonly key: string;
|
|
458
|
+
readonly condition: StoredPreviewCondition;
|
|
458
459
|
}
|
|
460
|
+
/**
|
|
461
|
+
* The conditional `preview` entries that hold for a cell, in stored order: every prop a condition names
|
|
462
|
+
* holds the value it asks for in the defaults under the cell's own values. The same test
|
|
463
|
+
* `ruleApplies` makes for a style rule, kept here because `layer-styles` imports this module.
|
|
464
|
+
*/
|
|
465
|
+
declare function holdingPreviewConditions({
|
|
466
|
+
config,
|
|
467
|
+
path,
|
|
468
|
+
cell
|
|
469
|
+
}: {
|
|
470
|
+
config: Config;
|
|
471
|
+
path: string;
|
|
472
|
+
cell: Readonly<Record<string, unknown>>;
|
|
473
|
+
}): IndexedPreviewCondition[];
|
|
459
474
|
/**
|
|
460
475
|
* A component's conditional preview props, in authored order. Empty for a component that declares
|
|
461
476
|
* none, or a path that names no component.
|
|
462
477
|
*/
|
|
463
478
|
declare function previewConditions(config: Config, path: string): readonly StoredPreviewCondition[];
|
|
479
|
+
/** The conditional entries under the keys they are stored at, the `*` entry left out. Empty for a
|
|
480
|
+
* component that declares none. */
|
|
481
|
+
declare function keyedPreviewConditions(config: Config, path: string): Readonly<Record<string, StoredPreviewCondition>>;
|
|
464
482
|
/** A preview grid, derived. Rows sweep a prop's declared values; columns are the states the
|
|
465
483
|
* Component actually styles for. */
|
|
466
484
|
interface PreviewMatrix {
|
|
@@ -497,5 +515,7 @@ declare function previewMatrix(config: Config, path: string): PreviewMatrix;
|
|
|
497
515
|
* stored as the string `'true'` and never equals the `true` an author writes.
|
|
498
516
|
*/
|
|
499
517
|
declare function propValueFromAxis(config: Config, path: string, prop: string, value: string): unknown;
|
|
518
|
+
/** The `value` a string, number or boolean prop declares as its default, per prop. */
|
|
519
|
+
declare function declaredPropDefaults(props: Record<string, unknown> | undefined): Record<string, unknown>;
|
|
500
520
|
//#endregion
|
|
501
|
-
export { PreviewMatrix, PropDeclaration,
|
|
521
|
+
export { IndexedPreviewCondition, PreviewMatrix, PropDeclaration, StylePropEntry, StylePropertyItem, authoredBag, authoredValue, className, cssPropValue, cssVar, cssVarRef, declaredPropDefaults, declaredPropRouting, holdingPreviewConditions, keyedPreviewConditions, memberFromLeaf, memberLeaves, negatedCssValue, opacityPercentage, previewConditionalProps, previewConditions, previewDefaults, previewMatrix, propOwner, propValueDomain, propValueFromAxis, renderStyleValue, resolveComponentProps, resolveFieldValue, resolveTokenValue, resolveTokenValueUnder, routedBag, sourceVarPrefix, styleAliasesOf, styleDeclarations, stylePropEntries, stylePropLeafForToken, stylePropNegates, stylePropTokenGroup, stylePropTokenPath, stylePropTokenValues, stylePropValueFromLeaf, stylePropValueLeaves, stylePropValues, stylePropertiesWriting, stylePropertyAccepts, stylePropertyFor, stylePropertyPathFor, validateComponentProps };
|
|
@@ -7,6 +7,7 @@ import { modifierCategory } from "../entities/system/Modifier.js";
|
|
|
7
7
|
import { classPrefixOf, varPrefixOf } from "../entities/system/Settings.js";
|
|
8
8
|
import { StyleProperty, leavesOfStylePropValue } from "../entities/system/StyleProperty.js";
|
|
9
9
|
import { createSliceMemo } from "./memo.js";
|
|
10
|
+
import { isConditionalPreview, storedPreview, unconditionalPreviewProps } from "./preview-store.js";
|
|
10
11
|
//#region src/framework/projections.ts
|
|
11
12
|
/**
|
|
12
13
|
* Expanded value domains, cached per config instance.
|
|
@@ -903,10 +904,10 @@ function validateComponentProps(config, componentPath, props) {
|
|
|
903
904
|
return errors;
|
|
904
905
|
}
|
|
905
906
|
/**
|
|
906
|
-
* The props one preview tile starts from: the component's `defaultProps` with its
|
|
907
|
+
* The props one preview tile starts from: the component's `defaultProps` with its `*` preview entry
|
|
907
908
|
* over the top.
|
|
908
909
|
*
|
|
909
|
-
* Preview wins, and that direction is the whole reason this exists once.
|
|
910
|
+
* Preview wins, and that direction is the whole reason this exists once. The `*` entry is the state
|
|
910
911
|
* a person chose to show a component in — Button's `variant`, an Icon prop's example glyph — so a
|
|
911
912
|
* surface showing that tile has to agree with it, while `defaultProps` remains the baseline for every
|
|
912
913
|
* prop the tile doesn't pin. Get the order backwards and the panel reports a value the canvas isn't
|
|
@@ -914,7 +915,7 @@ function validateComponentProps(config, componentPath, props) {
|
|
|
914
915
|
*
|
|
915
916
|
* THE PRECEDENCE RULE, stated once: only a RENDER surface — a preview tile, the catalogue card, slot
|
|
916
917
|
* text seeding — reads this merge. An EDIT surface reads and writes the single field it owns
|
|
917
|
-
* (`defaultProps` OR
|
|
918
|
+
* (`defaultProps` OR the `*` entry, never the merge), and anything that SEEDS committed data — a
|
|
918
919
|
* prop binding, a forward's re-seed, a stored instance prop, a default-value form — reads raw
|
|
919
920
|
* `defaultProps`. A preview value that reaches any of those stops being staging and becomes a real
|
|
920
921
|
* default the moment it is saved, which is exactly the leak UDS-3514 closed. Before adding a caller,
|
|
@@ -935,8 +936,10 @@ function validateComponentProps(config, componentPath, props) {
|
|
|
935
936
|
function previewDefaults(config, path, cell) {
|
|
936
937
|
const component = config.resolve("component", path);
|
|
937
938
|
if (!component) return {};
|
|
938
|
-
const {
|
|
939
|
+
const { props, defaultProps } = component.body;
|
|
940
|
+
const previewProps = unconditionalPreviewProps(component.body);
|
|
939
941
|
const base = {
|
|
942
|
+
...declaredPropDefaults(props),
|
|
940
943
|
...defaultProps,
|
|
941
944
|
...previewProps
|
|
942
945
|
};
|
|
@@ -953,28 +956,48 @@ function previewDefaults(config, path, cell) {
|
|
|
953
956
|
* without restating defaults that would shadow an edit.
|
|
954
957
|
*/
|
|
955
958
|
function previewConditionalProps(config, path, cell) {
|
|
959
|
+
const conditional = {};
|
|
960
|
+
for (const { condition } of holdingPreviewConditions({
|
|
961
|
+
config,
|
|
962
|
+
path,
|
|
963
|
+
cell
|
|
964
|
+
})) Object.assign(conditional, condition.props);
|
|
965
|
+
return conditional;
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* The conditional `preview` entries that hold for a cell, in stored order: every prop a condition names
|
|
969
|
+
* holds the value it asks for in the defaults under the cell's own values. The same test
|
|
970
|
+
* `ruleApplies` makes for a style rule, kept here because `layer-styles` imports this module.
|
|
971
|
+
*/
|
|
972
|
+
function holdingPreviewConditions({ config, path, cell }) {
|
|
956
973
|
const component = config.resolve("component", path);
|
|
957
|
-
if (!component) return
|
|
958
|
-
const { defaultProps
|
|
974
|
+
if (!component) return [];
|
|
975
|
+
const { defaultProps } = component.body;
|
|
959
976
|
const state = {
|
|
960
977
|
...defaultProps,
|
|
961
|
-
...
|
|
978
|
+
...unconditionalPreviewProps(component.body),
|
|
962
979
|
...cell
|
|
963
980
|
};
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
981
|
+
return Object.entries(keyedPreviewConditions(config, path)).flatMap(([key, condition]) => {
|
|
982
|
+
return Object.entries(condition.when ?? {}).every(([prop, value]) => String(state[prop]) === String(refLeaf(value))) ? [{
|
|
983
|
+
key,
|
|
984
|
+
condition
|
|
985
|
+
}] : [];
|
|
986
|
+
});
|
|
968
987
|
}
|
|
969
988
|
/**
|
|
970
989
|
* A component's conditional preview props, in authored order. Empty for a component that declares
|
|
971
990
|
* none, or a path that names no component.
|
|
972
991
|
*/
|
|
973
992
|
function previewConditions(config, path) {
|
|
993
|
+
return Object.values(keyedPreviewConditions(config, path));
|
|
994
|
+
}
|
|
995
|
+
/** The conditional entries under the keys they are stored at, the `*` entry left out. Empty for a
|
|
996
|
+
* component that declares none. */
|
|
997
|
+
function keyedPreviewConditions(config, path) {
|
|
974
998
|
const component = config.resolve("component", path);
|
|
975
|
-
if (!component) return
|
|
976
|
-
|
|
977
|
-
return Array.isArray(previewWhen) ? previewWhen : [];
|
|
999
|
+
if (!component) return {};
|
|
1000
|
+
return Object.fromEntries(Object.entries(storedPreview(component.body)).filter(([, entry]) => isConditionalPreview(entry)));
|
|
978
1001
|
}
|
|
979
1002
|
/**
|
|
980
1003
|
* The preview grid a component implies.
|
|
@@ -1014,7 +1037,7 @@ function previewMatrix(config, path) {
|
|
|
1014
1037
|
if (modifierCategory(config, key.slice(1)) === "state") modifiers.add(key);
|
|
1015
1038
|
}
|
|
1016
1039
|
}
|
|
1017
|
-
for (const { when } of previewConditions(config, path)) for (const [prop, stored] of Object.entries(when)) nameAxisPoint(prop, stored);
|
|
1040
|
+
for (const { when } of previewConditions(config, path)) for (const [prop, stored] of Object.entries(when ?? {})) nameAxisPoint(prop, stored);
|
|
1018
1041
|
for (const [name, decl] of Object.entries(declared)) {
|
|
1019
1042
|
if (decl?.type === "composite" && isRef(decl.value)) {
|
|
1020
1043
|
const values = declaredFirst([...memberLeaves(config, decl.value)], props[name] ?? []);
|
|
@@ -1052,5 +1075,16 @@ function propValueFromAxis(config, path, prop, value) {
|
|
|
1052
1075
|
}
|
|
1053
1076
|
return value;
|
|
1054
1077
|
}
|
|
1078
|
+
/** The `value` a string, number or boolean prop declares as its default, per prop. */
|
|
1079
|
+
function declaredPropDefaults(props) {
|
|
1080
|
+
const out = {};
|
|
1081
|
+
for (const [name, decl] of Object.entries(props ?? {})) {
|
|
1082
|
+
if (!decl || typeof decl !== "object") continue;
|
|
1083
|
+
const { type, value } = decl;
|
|
1084
|
+
if (type !== "string" && type !== "number" && type !== "boolean") continue;
|
|
1085
|
+
if (value !== void 0) out[name] = value;
|
|
1086
|
+
}
|
|
1087
|
+
return out;
|
|
1088
|
+
}
|
|
1055
1089
|
//#endregion
|
|
1056
|
-
export { authoredBag, authoredValue, className, cssPropValue, cssVar, cssVarRef, declaredPropRouting, memberFromLeaf, memberLeaves, negatedCssValue, opacityPercentage, previewConditionalProps, previewConditions, previewDefaults, previewMatrix, propOwner, propValueDomain, propValueFromAxis, renderStyleValue, resolveComponentProps, resolveFieldValue, resolveTokenValue, resolveTokenValueUnder, routedBag, sourceVarPrefix, styleAliasesOf, styleDeclarations, stylePropEntries, stylePropLeafForToken, stylePropNegates, stylePropTokenGroup, stylePropTokenPath, stylePropTokenValues, stylePropValueFromLeaf, stylePropValueLeaves, stylePropValues, stylePropertiesWriting, stylePropertyAccepts, stylePropertyFor, stylePropertyPathFor, validateComponentProps };
|
|
1090
|
+
export { authoredBag, authoredValue, className, cssPropValue, cssVar, cssVarRef, declaredPropDefaults, declaredPropRouting, holdingPreviewConditions, keyedPreviewConditions, memberFromLeaf, memberLeaves, negatedCssValue, opacityPercentage, previewConditionalProps, previewConditions, previewDefaults, previewMatrix, propOwner, propValueDomain, propValueFromAxis, renderStyleValue, resolveComponentProps, resolveFieldValue, resolveTokenValue, resolveTokenValueUnder, routedBag, sourceVarPrefix, styleAliasesOf, styleDeclarations, stylePropEntries, stylePropLeafForToken, stylePropNegates, stylePropTokenGroup, stylePropTokenPath, stylePropTokenValues, stylePropValueFromLeaf, stylePropValueLeaves, stylePropValues, stylePropertiesWriting, stylePropertyAccepts, stylePropertyFor, stylePropertyPathFor, validateComponentProps };
|
|
@@ -3,9 +3,14 @@ import { Config } from "./Config.js";
|
|
|
3
3
|
//#region src/framework/prop-surface.d.ts
|
|
4
4
|
/** What a prop IS — the config's own prop types, and nothing invented. */
|
|
5
5
|
type SurfacePropKind = 'variant' | 'styleProperty' | 'composite' | 'motion' | 'slot' | 'string' | 'number' | 'boolean';
|
|
6
|
+
/** When a prop applies, as leaves: each key a sibling prop, each value the leaves it must hold one
|
|
7
|
+
* of. What a control compares the values in force against — see {@link propApplies}. */
|
|
8
|
+
type SurfacePropCondition = Readonly<Record<string, readonly unknown[]>>;
|
|
6
9
|
interface SurfacePropBase {
|
|
7
10
|
readonly name: string;
|
|
8
11
|
readonly required: boolean;
|
|
12
|
+
/** Absent when the prop always applies. */
|
|
13
|
+
readonly when?: SurfacePropCondition;
|
|
9
14
|
}
|
|
10
15
|
interface SurfaceVariantProp extends SurfacePropBase {
|
|
11
16
|
readonly kind: 'variant';
|
|
@@ -62,7 +67,16 @@ interface PropDecl extends Record<string, unknown> {
|
|
|
62
67
|
into?: string;
|
|
63
68
|
text?: boolean;
|
|
64
69
|
accepts?: unknown;
|
|
70
|
+
when?: unknown;
|
|
65
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Whether a prop applies under the values in force — every prop its condition names holds one of the
|
|
74
|
+
* leaves listed for it. A prop with no condition always applies; an unset prop satisfies nothing.
|
|
75
|
+
* Compared as strings, since a control hands back what it displays.
|
|
76
|
+
*/
|
|
77
|
+
declare function propApplies(prop: {
|
|
78
|
+
readonly when?: SurfacePropCondition;
|
|
79
|
+
}, values: Readonly<Record<string, unknown>>): boolean;
|
|
66
80
|
/**
|
|
67
81
|
* The prop a slot routes INTO, resolved through the layer it targets: the layer's element is a
|
|
68
82
|
* component, and `into` names one of that component's props. `undefined` when the slot targets
|
|
@@ -110,6 +124,12 @@ declare function surfaceProps(config: Config, componentPath: string, /** Forward
|
|
|
110
124
|
seen?: ReadonlySet<string>): SurfaceProp[];
|
|
111
125
|
/** One prop by name, or `undefined` when the component doesn't expose it. */
|
|
112
126
|
declare function surfaceProp(config: Config, componentPath: string, name: string): SurfaceProp | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* The registry entity a prop writes through, as a path: its composite, its style property, or the
|
|
129
|
+
* ref a variant's values come from. Resolved through a route, so a prop forwarded into a layer's
|
|
130
|
+
* composite names that composite. `undefined` for a prop with none.
|
|
131
|
+
*/
|
|
132
|
+
declare function propRegistryRef(config: Config, componentPath: string, name: string): string | undefined;
|
|
113
133
|
/**
|
|
114
134
|
* Each non-root layer a component forwards to, and the sorted prop names each one receives — the
|
|
115
135
|
* emitter's view, which writes a literal key set per layer. Root is absent on purpose: it takes
|
|
@@ -129,4 +149,4 @@ interface ForwardClaims {
|
|
|
129
149
|
}
|
|
130
150
|
declare function forwardClaims(config: Config, componentPath: string): ForwardClaims;
|
|
131
151
|
//#endregion
|
|
132
|
-
export { ForwardClaims, SurfaceCompositeProp, SurfaceProp, SurfacePropKind, SurfaceScalarProp, SurfaceSlotProp, SurfaceStylePropertyProp, SurfaceVariantProp, forwardClaims, forwardedLayers, intoName, routedProp, routedPropIn, routesContent, surfaceProp, surfaceProps };
|
|
152
|
+
export { ForwardClaims, SurfaceCompositeProp, SurfaceProp, SurfacePropCondition, SurfacePropKind, SurfaceScalarProp, SurfaceSlotProp, SurfaceStylePropertyProp, SurfaceVariantProp, forwardClaims, forwardedLayers, intoName, propApplies, propRegistryRef, routedProp, routedPropIn, routesContent, surfaceProp, surfaceProps };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isRef, kindOf, memberOf, pathOf } from "./utils/refs.js";
|
|
1
|
+
import { isRef, kindOf, memberOf, pathOf, refLeaf } from "./utils/refs.js";
|
|
2
2
|
import { forwardsOf, rootLayerOf } from "../entities/system/element.js";
|
|
3
3
|
import { propValueDomain, resolveComponentProps } from "./projections.js";
|
|
4
4
|
import { Component } from "../entities/system/Component.js";
|
|
@@ -19,6 +19,34 @@ import { Component } from "../entities/system/Component.js";
|
|
|
19
19
|
* its domain (an icon prop drawing its names from the icon library rather than copying them in),
|
|
20
20
|
* which is why value lists are never read straight off `value`.
|
|
21
21
|
*/
|
|
22
|
+
/** A stored condition as leaves, one list per prop it names; `undefined` for none. */
|
|
23
|
+
function conditionLeaves(when) {
|
|
24
|
+
if (when === null || typeof when !== "object" || Array.isArray(when)) return;
|
|
25
|
+
const entries = Object.entries(when).filter(([, value]) => value !== void 0 && value !== null).map(([name, value]) => [name, (Array.isArray(value) ? value : [value]).map(refLeaf)]);
|
|
26
|
+
return entries.length > 0 ? Object.fromEntries(entries) : void 0;
|
|
27
|
+
}
|
|
28
|
+
/** The projected prop under ITS OWN condition. A forward resolved through carries the far prop's
|
|
29
|
+
* condition, which speaks of that component's siblings and means nothing here. */
|
|
30
|
+
function conditioned(prop, when) {
|
|
31
|
+
const { when: _far, ...own } = prop;
|
|
32
|
+
return when ? {
|
|
33
|
+
...own,
|
|
34
|
+
when
|
|
35
|
+
} : own;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Whether a prop applies under the values in force — every prop its condition names holds one of the
|
|
39
|
+
* leaves listed for it. A prop with no condition always applies; an unset prop satisfies nothing.
|
|
40
|
+
* Compared as strings, since a control hands back what it displays.
|
|
41
|
+
*/
|
|
42
|
+
function propApplies(prop, values) {
|
|
43
|
+
if (!prop.when) return true;
|
|
44
|
+
return Object.entries(prop.when).every(([name, leaves]) => {
|
|
45
|
+
const value = values[name];
|
|
46
|
+
if (value === void 0 || value === null) return false;
|
|
47
|
+
return leaves.some((leaf) => String(leaf) === String(value));
|
|
48
|
+
});
|
|
49
|
+
}
|
|
22
50
|
/** The tail of a ref, when the value is one. */
|
|
23
51
|
function refPath(value) {
|
|
24
52
|
return isRef(value) ? pathOf(value.__ref) : void 0;
|
|
@@ -82,25 +110,62 @@ function routedProp(config, componentPath, decl) {
|
|
|
82
110
|
return routedPropIn(config, body?.layers, decl);
|
|
83
111
|
}
|
|
84
112
|
function routesContent(config, layers, decl) {
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
87
|
-
|
|
113
|
+
const end = routeEnd(config, layers, decl);
|
|
114
|
+
if (!end) return false;
|
|
115
|
+
if (end.decl) return end.decl.type === "slot";
|
|
116
|
+
return refPath(end.route.target) !== void 0 && intoName(end.route.into) === "children";
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Whether a route is typed in place once every forward between is followed: it lands on a text slot,
|
|
120
|
+
* or the last forward it follows says `text` itself. A message's `badge` routes into `Badge.children`,
|
|
121
|
+
* which forwards onto Badge's text layer, so the route is text although neither end of the first hop
|
|
122
|
+
* says so. The declaration the route starts from is the caller's to read.
|
|
123
|
+
*/
|
|
124
|
+
function routesIntoText(config, layers, decl) {
|
|
125
|
+
const end = routeEnd(config, layers, decl);
|
|
126
|
+
if (!end) return false;
|
|
127
|
+
if (end.decl?.type === "slot" && end.decl.text === true) return true;
|
|
128
|
+
return end.route.text === true;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Where a route ends once every forward between is followed. A message's `badge` routes into
|
|
132
|
+
* `Badge.children`, which is itself a forward onto Badge's text layer; the route ends where that one
|
|
133
|
+
* does. `decl` is the declaration it lands on, absent for a layer that renders a tag or a prop nobody
|
|
134
|
+
* declares, and `route` is the last forward followed, whose `into` names what it asked that layer
|
|
135
|
+
* for. `undefined` for a route that comes back around to itself.
|
|
136
|
+
*/
|
|
137
|
+
function routeEnd(config, layers, decl, seen = /* @__PURE__ */ new Set()) {
|
|
138
|
+
const landed = routedPropIn(config, layers, decl);
|
|
139
|
+
if (landed?.type !== "forward") return {
|
|
140
|
+
decl: landed,
|
|
141
|
+
route: decl
|
|
142
|
+
};
|
|
143
|
+
const layerKey = refPath(decl.target);
|
|
144
|
+
const target = layerKey ? refPath(layers?.[layerKey]) : void 0;
|
|
145
|
+
if (!target) return {
|
|
146
|
+
decl: landed,
|
|
147
|
+
route: decl
|
|
148
|
+
};
|
|
149
|
+
const key = `${target}#${intoName(decl.into)}`;
|
|
150
|
+
if (seen.has(key)) return void 0;
|
|
151
|
+
const farLayers = (Component.get(config, target)?.toJSON())?.layers;
|
|
152
|
+
return routeEnd(config, farLayers, landed, new Set([...seen, key]));
|
|
88
153
|
}
|
|
89
154
|
function slotProp(config, componentPath, name, decl) {
|
|
90
155
|
const refs = acceptsRefs(decl.accepts);
|
|
91
156
|
const icons = refs.find((ref) => kindOf(ref) === "icon");
|
|
92
|
-
const
|
|
157
|
+
const layers = (Component.get(config, componentPath)?.toJSON())?.layers;
|
|
158
|
+
const routed = routeEnd(config, layers, decl)?.decl;
|
|
93
159
|
const routedKind = routed?.type;
|
|
94
160
|
const targetLayer = refPath(decl.target);
|
|
95
|
-
const routesIntoText = routed?.type === "slot" && routed.text === true;
|
|
96
161
|
return {
|
|
97
162
|
name,
|
|
98
163
|
kind: "slot",
|
|
99
164
|
required: decl.required === true,
|
|
100
|
-
text: decl.text === true || routesIntoText,
|
|
165
|
+
text: decl.text === true || routesIntoText(config, layers, decl),
|
|
101
166
|
...targetLayer ? {
|
|
102
167
|
targetLayer,
|
|
103
|
-
into: decl.into
|
|
168
|
+
into: intoName(decl.into)
|
|
104
169
|
} : {},
|
|
105
170
|
accepts: refs.filter((ref) => kindOf(ref) === "component").map((ref) => pathOf(ref)),
|
|
106
171
|
...icons ? { acceptsIconLibrary: pathOf(icons) } : {},
|
|
@@ -121,61 +186,62 @@ function surfaceProps(config, componentPath, seen = /* @__PURE__ */ new Set()) {
|
|
|
121
186
|
const props = [];
|
|
122
187
|
for (const [name, decl] of Object.entries(declared)) {
|
|
123
188
|
const required = decl.required === true;
|
|
189
|
+
const when = conditionLeaves(decl.when);
|
|
124
190
|
if (decl.type === "forward") {
|
|
125
191
|
if (routesContent(config, layers, decl)) {
|
|
126
|
-
props.push(slotProp(config, componentPath, name, decl));
|
|
192
|
+
props.push(conditioned(slotProp(config, componentPath, name, decl), when));
|
|
127
193
|
continue;
|
|
128
194
|
}
|
|
129
195
|
const routed = boundProp(config, layers, decl, seen);
|
|
130
|
-
if (routed) props.push({
|
|
196
|
+
if (routed) props.push(conditioned({
|
|
131
197
|
...routed,
|
|
132
198
|
name,
|
|
133
199
|
required
|
|
134
|
-
});
|
|
200
|
+
}, when));
|
|
135
201
|
continue;
|
|
136
202
|
}
|
|
137
203
|
if (decl.type === "slot") {
|
|
138
|
-
props.push(slotProp(config, componentPath, name, decl));
|
|
204
|
+
props.push(conditioned(slotProp(config, componentPath, name, decl), when));
|
|
139
205
|
continue;
|
|
140
206
|
}
|
|
141
207
|
if (decl.type === "variant") {
|
|
142
208
|
const iconLibrary = variantIconLibrary(config, decl.value);
|
|
143
|
-
props.push({
|
|
209
|
+
props.push(conditioned({
|
|
144
210
|
name,
|
|
145
211
|
kind: "variant",
|
|
146
212
|
required,
|
|
147
213
|
values: propValueDomain(config, decl)?.leaves ?? [],
|
|
148
214
|
...iconLibrary !== void 0 ? { iconLibrary } : {}
|
|
149
|
-
});
|
|
215
|
+
}, when));
|
|
150
216
|
continue;
|
|
151
217
|
}
|
|
152
218
|
if (decl.type === "styleProperty") {
|
|
153
219
|
const styleProperty = refPath(decl.value);
|
|
154
220
|
if (!styleProperty) continue;
|
|
155
|
-
props.push({
|
|
221
|
+
props.push(conditioned({
|
|
156
222
|
name,
|
|
157
223
|
kind: "styleProperty",
|
|
158
224
|
required,
|
|
159
225
|
styleProperty
|
|
160
|
-
});
|
|
226
|
+
}, when));
|
|
161
227
|
continue;
|
|
162
228
|
}
|
|
163
229
|
if (decl.type === "composite") {
|
|
164
230
|
const compositeName = refPath(decl.value);
|
|
165
231
|
if (!compositeName) continue;
|
|
166
|
-
props.push({
|
|
232
|
+
props.push(conditioned({
|
|
167
233
|
name,
|
|
168
234
|
kind: "composite",
|
|
169
235
|
required,
|
|
170
236
|
compositeName
|
|
171
|
-
});
|
|
237
|
+
}, when));
|
|
172
238
|
continue;
|
|
173
239
|
}
|
|
174
|
-
if (decl.type === "string" || decl.type === "number" || decl.type === "boolean" || decl.type === "motion") props.push({
|
|
240
|
+
if (decl.type === "string" || decl.type === "number" || decl.type === "boolean" || decl.type === "motion") props.push(conditioned({
|
|
175
241
|
name,
|
|
176
242
|
kind: decl.type,
|
|
177
243
|
required
|
|
178
|
-
});
|
|
244
|
+
}, when));
|
|
179
245
|
}
|
|
180
246
|
return props;
|
|
181
247
|
}
|
|
@@ -198,6 +264,18 @@ function surfaceProp(config, componentPath, name) {
|
|
|
198
264
|
return surfaceProps(config, componentPath).find((prop) => prop.name === name);
|
|
199
265
|
}
|
|
200
266
|
/**
|
|
267
|
+
* The registry entity a prop writes through, as a path: its composite, its style property, or the
|
|
268
|
+
* ref a variant's values come from. Resolved through a route, so a prop forwarded into a layer's
|
|
269
|
+
* composite names that composite. `undefined` for a prop with none.
|
|
270
|
+
*/
|
|
271
|
+
function propRegistryRef(config, componentPath, name) {
|
|
272
|
+
const surfaced = surfaceProp(config, componentPath, name);
|
|
273
|
+
if (surfaced?.kind === "composite") return surfaced.compositeName;
|
|
274
|
+
if (surfaced?.kind === "styleProperty") return surfaced.styleProperty;
|
|
275
|
+
const decl = resolveComponentProps(config, componentPath)[name];
|
|
276
|
+
return decl?.type === "variant" ? refPath(decl.value) : void 0;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
201
279
|
* The one precedence walk behind {@link forwardedLayers} and {@link forwardClaims}: every name a
|
|
202
280
|
* `forwards` entry exposes, attributed to the layer that delivers it.
|
|
203
281
|
*
|
|
@@ -275,4 +353,4 @@ function forwardClaims(config, componentPath) {
|
|
|
275
353
|
};
|
|
276
354
|
}
|
|
277
355
|
//#endregion
|
|
278
|
-
export { forwardClaims, forwardedLayers, intoName, routedProp, routedPropIn, routesContent, surfaceProp, surfaceProps };
|
|
356
|
+
export { forwardClaims, forwardedLayers, intoName, propApplies, propRegistryRef, routedProp, routedPropIn, routesContent, routesIntoText, surfaceProp, surfaceProps };
|
|
@@ -204,6 +204,54 @@ type VisibilityOperator = Exclude<keyof StateCondition, '$state' | 'not'>;
|
|
|
204
204
|
declare function readStatePointer(state: Record<string, unknown>, pointer: string): unknown;
|
|
205
205
|
/** The prop a `$state` pointer reads — its first segment, for a pointer into an object prop. */
|
|
206
206
|
declare function statePointerProp(pointer: string): string;
|
|
207
|
+
/**
|
|
208
|
+
* The marker on an element a render placed inline, naming its anatomy node. A layer is addressed by
|
|
209
|
+
* `data-uds-layer`; this is for the node between layers that has no bag of its own. Stamped by the
|
|
210
|
+
* emitted module and by both projections, so a rendered element and a projected one read alike.
|
|
211
|
+
*/
|
|
212
|
+
declare const ANATOMY_MARKER = "data-uds-anatomy";
|
|
213
|
+
/**
|
|
214
|
+
* The value prop a component paints as a layer's text: the anatomy node rendering `layer` binds its
|
|
215
|
+
* `children` to `{ $state: '/<prop>' }`, and that prop is declared a string or number. A forward
|
|
216
|
+
* surfaces its layer on the prop itself; this answers for a render that reads a value prop and
|
|
217
|
+
* writes it into a layer by hand, the way a `title` string reaches a heading.
|
|
218
|
+
*/
|
|
219
|
+
declare function layerTextProp({
|
|
220
|
+
config,
|
|
221
|
+
path,
|
|
222
|
+
layer
|
|
223
|
+
}: {
|
|
224
|
+
config: Config;
|
|
225
|
+
path: string;
|
|
226
|
+
layer: string;
|
|
227
|
+
}): string | undefined;
|
|
228
|
+
/**
|
|
229
|
+
* The value prop a component hands an element it placed inline: the anatomy node `element` binds
|
|
230
|
+
* `into` to `{ $state: '/<prop>' }`, and that prop is a string or number. `into` is `children` for
|
|
231
|
+
* text the element shows itself, or the far component's prop the render passed it to
|
|
232
|
+
* (`<CommentHeader title={title} />` binds `title`).
|
|
233
|
+
*/
|
|
234
|
+
declare function anatomyTextProp({
|
|
235
|
+
config,
|
|
236
|
+
path,
|
|
237
|
+
element,
|
|
238
|
+
into
|
|
239
|
+
}: {
|
|
240
|
+
config: Config;
|
|
241
|
+
path: string;
|
|
242
|
+
element: string;
|
|
243
|
+
into: string;
|
|
244
|
+
}): string | undefined;
|
|
245
|
+
/** The component an inline anatomy element renders, when it is one (`component:<path>`). */
|
|
246
|
+
declare function anatomyElementComponent({
|
|
247
|
+
config,
|
|
248
|
+
path,
|
|
249
|
+
element
|
|
250
|
+
}: {
|
|
251
|
+
config: Config;
|
|
252
|
+
path: string;
|
|
253
|
+
element: string;
|
|
254
|
+
}): string | undefined;
|
|
207
255
|
/** A value in json-render's `visible` grammar, resolved against a prop bag. `undefined` when this
|
|
208
256
|
* reader can't settle it, which leaves the condition on the element for the renderer to judge. */
|
|
209
257
|
declare function resolveVisibility(condition: unknown, state: Record<string, unknown>): boolean | undefined;
|
|
@@ -301,10 +349,62 @@ interface SlotTarget {
|
|
|
301
349
|
* the layer meant to hold it renders empty. Every one of Studio's own components shaped that way
|
|
302
350
|
* (`Badge`, `StudioButton`, `Toggle`, …) painted as an empty shell in the component editor.
|
|
303
351
|
*
|
|
304
|
-
* Exported so the answer has ONE owner. An untargeted slot
|
|
305
|
-
*
|
|
352
|
+
* Exported so the answer has ONE owner. An untargeted slot is hosted by the layer the anatomy
|
|
353
|
+
* places its content-position node in — a row's `leading` slot sits in its `leading` Box — and by the
|
|
354
|
+
* root when the anatomy records no position for it, which is where the expansion renders it.
|
|
306
355
|
*/
|
|
307
356
|
declare function slotTargetsOf(config: Config, path: string): SlotTarget[];
|
|
357
|
+
/**
|
|
358
|
+
* Layer → the forwards that hand a VALUE to one of its props.
|
|
359
|
+
*
|
|
360
|
+
* `startIcon: { type: 'forward', target: 'startIcon/name' }` and `swatch: { type: 'forward', target:
|
|
361
|
+
* 'swatch/swatchColor' }` are this shape: the instance's value lands on the layer's element as the
|
|
362
|
+
* prop the forward names, the way the emitted module spreads it into that layer's bundle. A forward
|
|
363
|
+
* whose target prop is a slot is content instead, and {@link slotTargetsOf} answers for it.
|
|
364
|
+
*/
|
|
365
|
+
declare function valueForwardsOf(config: Config, path: string): SlotTarget[];
|
|
366
|
+
/**
|
|
367
|
+
* The instance props whose values or content reach `layer`: content routed into it, values
|
|
368
|
+
* forwarded onto one of its props, and the props the layer exposes through `forwards`. A file
|
|
369
|
+
* editing a placed instance can change exactly these about the layer; the layer's own styles are
|
|
370
|
+
* the component's.
|
|
371
|
+
*/
|
|
372
|
+
/**
|
|
373
|
+
* The instance prop whose content or value lands on `into` of `layer`, or `undefined` when the
|
|
374
|
+
* layer owns that prop itself. A change to the layer's prop in a cell is then a change to the
|
|
375
|
+
* instance prop's preview value, not a style.
|
|
376
|
+
*/
|
|
377
|
+
declare function propInto({
|
|
378
|
+
config,
|
|
379
|
+
path,
|
|
380
|
+
layer,
|
|
381
|
+
into
|
|
382
|
+
}: {
|
|
383
|
+
config: Config;
|
|
384
|
+
path: string;
|
|
385
|
+
layer: string;
|
|
386
|
+
into: string;
|
|
387
|
+
}): string | undefined;
|
|
388
|
+
/** The route a prop's content or value takes into a layer, or `undefined` for a prop that reaches
|
|
389
|
+
* no layer. */
|
|
390
|
+
declare function routeOf({
|
|
391
|
+
config,
|
|
392
|
+
path,
|
|
393
|
+
prop
|
|
394
|
+
}: {
|
|
395
|
+
config: Config;
|
|
396
|
+
path: string;
|
|
397
|
+
prop: string;
|
|
398
|
+
}): SlotTarget | undefined;
|
|
399
|
+
declare function propsIntoLayer({
|
|
400
|
+
config,
|
|
401
|
+
path,
|
|
402
|
+
layer
|
|
403
|
+
}: {
|
|
404
|
+
config: Config;
|
|
405
|
+
path: string;
|
|
406
|
+
layer: string;
|
|
407
|
+
}): string[];
|
|
308
408
|
/**
|
|
309
409
|
* Expand every component element the renderer can't name into the tree it is made of.
|
|
310
410
|
*
|
|
@@ -323,4 +423,4 @@ declare function expandSpec(config: Config, spec: RenderSpec, options?: ExpandOp
|
|
|
323
423
|
*/
|
|
324
424
|
declare function expandComponent(config: Config, path: string, props?: Record<string, unknown>, options?: ExpandOptions): ExpansionResult;
|
|
325
425
|
//#endregion
|
|
326
|
-
export { ComponentSpec, ComponentSpecs, ExpandOptions, ExpansionResult, RenderElement, RenderSpec, SlotTarget, VisibilityOperator, VisibilityTerm, boundElementType, componentSpec, deriveSpec, entryKeyOf, expandComponent, expandSpec, instanceSpec, liveStateProps, previewSpec, readStatePointer, resolveVisibility, slotTargetsOf, specWithResolvedVisibility, statePointerProp, visibilityStateProps, visibilityTerms, withoutTruthyTerm, wrappedElementKey };
|
|
426
|
+
export { ANATOMY_MARKER, ComponentSpec, ComponentSpecs, ExpandOptions, ExpansionResult, RenderElement, RenderSpec, SlotTarget, VisibilityOperator, VisibilityTerm, anatomyElementComponent, anatomyTextProp, boundElementType, componentSpec, deriveSpec, entryKeyOf, expandComponent, expandSpec, instanceSpec, layerTextProp, liveStateProps, previewSpec, propInto, propsIntoLayer, readStatePointer, resolveVisibility, routeOf, slotTargetsOf, specWithResolvedVisibility, statePointerProp, valueForwardsOf, visibilityStateProps, visibilityTerms, withoutTruthyTerm, wrappedElementKey };
|