@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.
Files changed (72) hide show
  1. package/dist/configs/CanvasConfig.d.ts +900 -80
  2. package/dist/configs/react-native-system.d.ts +603 -55
  3. package/dist/configs/system.d.ts +603 -55
  4. package/dist/configs/system.js +7 -1
  5. package/dist/entities/system/Component.d.ts +480 -39
  6. package/dist/entities/system/Component.js +264 -93
  7. package/dist/entities/system/File.d.ts +1 -1
  8. package/dist/entities/system/LinkedSystem.d.ts +2 -2
  9. package/dist/entities/system/Operation.d.ts +1 -1
  10. package/dist/entities/system/StyleProperty.d.ts +8 -0
  11. package/dist/entities/system/StyleProperty.js +12 -1
  12. package/dist/entities/system/Tool.d.ts +1 -1
  13. package/dist/entities/system/component-module.js +1 -1
  14. package/dist/entities/system/defineComponent.d.ts +45 -7
  15. package/dist/entities/system/defineComponent.js +52 -25
  16. package/dist/framework/Config.d.ts +8 -2
  17. package/dist/framework/Config.js +97 -23
  18. package/dist/framework/class-names.d.ts +4 -2
  19. package/dist/framework/class-names.js +56 -17
  20. package/dist/framework/defineDerivedEntity.d.ts +1 -1
  21. package/dist/framework/defineEntity.d.ts +2 -2
  22. package/dist/framework/defineSubEntity.d.ts +3 -2
  23. package/dist/framework/preview-store.d.ts +19 -0
  24. package/dist/framework/preview-store.js +18 -0
  25. package/dist/framework/projections.d.ts +28 -8
  26. package/dist/framework/projections.js +50 -16
  27. package/dist/framework/prop-surface.d.ts +21 -1
  28. package/dist/framework/prop-surface.js +99 -21
  29. package/dist/framework/render-spec.d.ts +103 -3
  30. package/dist/framework/render-spec.js +285 -37
  31. package/dist/framework/schema-version.d.ts +1 -1
  32. package/dist/framework/schema-version.js +5 -5
  33. package/dist/framework/source-integrity.d.ts +14 -1
  34. package/dist/framework/utils/refs.js +10 -1
  35. package/dist/framework/validate-spec.d.ts +2 -0
  36. package/dist/framework/validate-spec.js +13 -0
  37. package/dist/index.d.ts +15 -12
  38. package/dist/index.js +7 -6
  39. package/dist/migrations/2.0.0/v1-artifact.d.ts +305 -29
  40. package/dist/migrations/20260919051027_prop_conditions_and_anatomy_positions_at_rest.d.ts +22 -0
  41. package/dist/migrations/20260919051027_prop_conditions_and_anatomy_positions_at_rest.js +10 -0
  42. package/dist/migrations/20260920004019_preview_conditions_keyed.d.ts +27 -0
  43. package/dist/migrations/20260920004019_preview_conditions_keyed.js +148 -0
  44. package/dist/migrations/20260920161309_prefixed_token_group.d.ts +12 -0
  45. package/dist/migrations/20260920161309_prefixed_token_group.js +10 -0
  46. package/dist/renderer/wrapRegistry.js +8 -2
  47. package/dist/renderer/wrappers/component-refs.d.ts +14 -0
  48. package/dist/renderer/wrappers/component-refs.js +79 -0
  49. package/dist/renderer/wrappers/hex-normalize.d.ts +5 -7
  50. package/dist/renderer/wrappers/hex-normalize.js +11 -10
  51. package/dist/spec/cell.d.ts +171 -0
  52. package/dist/spec/cell.js +361 -0
  53. package/dist/spec/detach.d.ts +125 -0
  54. package/dist/spec/detach.js +511 -0
  55. package/dist/spec/empty-node-slots.d.ts +11 -23
  56. package/dist/spec/empty-node-slots.js +12 -39
  57. package/dist/spec/index.d.ts +5 -3
  58. package/dist/spec/index.js +3 -3
  59. package/dist/spec/instance.d.ts +77 -0
  60. package/dist/spec/instance.js +80 -0
  61. package/dist/spec/preview-content.d.ts +95 -0
  62. package/dist/spec/preview-content.js +149 -0
  63. package/dist/spec/preview-elements.d.ts +54 -15
  64. package/dist/spec/preview-elements.js +109 -29
  65. package/dist/spec/prop-conditions.d.ts +28 -0
  66. package/dist/spec/prop-conditions.js +39 -0
  67. package/dist/spec/slot.d.ts +206 -0
  68. package/dist/spec/slot.js +542 -0
  69. package/dist/spec/visibility.d.ts +82 -0
  70. package/dist/spec/visibility.js +109 -0
  71. package/dist/tsconfig.tsbuildinfo +1 -1
  72. package/package.json +18 -2
@@ -1,8 +1,8 @@
1
- import { graftPreviewElements, hasPreviewElementDeep, isPreviewElement, previewElementNodes, previewElementType } from "./preview-elements.js";
1
+ import { graftPreviewElements, graftSlotContent, hasPreviewElementDeep, isComponentRef, isPreviewElement, isPreviewRef, previewElementNodes, previewElementType } from "./preview-elements.js";
2
2
  import { assetJsxForm, jsxIdentifier, parseAssetType } from "./asset-jsx.js";
3
- import { buildAcceptsChildNodes, buildClassifyChildNodes, namedSlotTargets, stripInjectedSlots, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots } from "./empty-node-slots.js";
4
3
  import { applyForcedModifiers, collectForcedModifierProps } from "./apply-forced-modifiers.js";
5
4
  import { collapseWrappedTextLabels } from "./collapse-text-labels.js";
5
+ import { buildAcceptsChildNodes, buildClassifyChildNodes, stripInjectedSlots, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots } from "./empty-node-slots.js";
6
6
  import { UNRESOLVED, hasUnresolvedProps, jsxToSpec, stripUnresolvedProps } from "./jsxToSpec.js";
7
7
  import { documentRenamesFromPatches, rewriteSpecRefs } from "./rewrite-refs.js";
8
8
  import { specToJsx } from "./specToJsx.js";
@@ -25,4 +25,4 @@ import { specToJsx } from "./specToJsx.js";
25
25
  * element, without changing the rendered layer hierarchy. */
26
26
  const SYNTHETIC_TEXT_SLOT_PROP = "data-uds-synthetic-text-slot";
27
27
  //#endregion
28
- export { SYNTHETIC_TEXT_SLOT_PROP, UNRESOLVED, applyForcedModifiers, assetJsxForm, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, documentRenamesFromPatches, graftPreviewElements, hasPreviewElementDeep, hasUnresolvedProps, isPreviewElement, jsxIdentifier, jsxToSpec, namedSlotTargets, parseAssetType, previewElementNodes, previewElementType, rewriteSpecRefs, specToJsx, stripInjectedSlots, stripUnresolvedProps, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
28
+ export { SYNTHETIC_TEXT_SLOT_PROP, UNRESOLVED, applyForcedModifiers, assetJsxForm, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, documentRenamesFromPatches, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, hasUnresolvedProps, isComponentRef, isPreviewElement, isPreviewRef, jsxIdentifier, jsxToSpec, parseAssetType, previewElementNodes, previewElementType, rewriteSpecRefs, specToJsx, stripInjectedSlots, stripUnresolvedProps, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
@@ -0,0 +1,77 @@
1
+ import { Config } from "../framework/Config.js";
2
+ import { Slot } from "./slot.js";
3
+
4
+ //#region src/spec/instance.d.ts
5
+ /** A spec element as a design file stores it: what this module reads of one. */
6
+ interface InstanceSpecElement {
7
+ readonly type?: string;
8
+ readonly children?: readonly string[];
9
+ readonly slots?: Readonly<Record<string, readonly string[]>>;
10
+ }
11
+ /** The elements of a spec a design file holds, keyed as the spec keys them. */
12
+ interface InstanceSpec {
13
+ readonly elements: Readonly<Record<string, InstanceSpecElement | undefined>>;
14
+ }
15
+ /** One slot of a placed instance: the slot as the component declares it, and what this instance holds in it. */
16
+ interface InstanceSlot {
17
+ readonly slot: Slot;
18
+ /** The keys of the elements placed in the slot, in order; empty when nothing is. */
19
+ readonly content: readonly string[];
20
+ readonly filled: boolean;
21
+ }
22
+ /**
23
+ * A component placed as one element of a design file's spec: which component it is, and what the
24
+ * file put in each of its slots. The component's slots come from the config (`Component.slots`),
25
+ * so a slot's hosting layer, what it takes and whether it is `children` are the component's answer;
26
+ * the content is this element's `children` for the content slot and its `slots` entry for a named
27
+ * one. `Component.instance` is the door.
28
+ */
29
+ declare class Instance {
30
+ private readonly config;
31
+ private readonly element;
32
+ /** The element's key in the spec. */
33
+ readonly key: string;
34
+ /** The component the element places, as a path. */
35
+ readonly component: string;
36
+ constructor(config: Config, key: string, component: string, element: InstanceSpecElement);
37
+ /** Every slot the component places content in, with what this instance holds there. */
38
+ get slots(): readonly InstanceSlot[];
39
+ /** The slot a prop names, or `undefined` when the prop is no slot. */
40
+ slot(prop: string): InstanceSlot | undefined;
41
+ /** The slot whose content lands in `layer`, or `undefined` when the layer hosts none. */
42
+ slotHostedBy(layer: string): InstanceSlot | undefined;
43
+ /** The slot holding the element `childKey`, or `undefined` when it holds none of this instance's. */
44
+ slotHolding(childKey: string): InstanceSlot | undefined;
45
+ }
46
+ /**
47
+ * The instance an element of a design file's spec places, or `undefined` when the element names no
48
+ * component of this config.
49
+ */
50
+ declare function instanceAt({
51
+ config,
52
+ spec,
53
+ elementKey
54
+ }: {
55
+ config: Config;
56
+ spec: InstanceSpec | null | undefined;
57
+ elementKey: string;
58
+ }): Instance | undefined;
59
+ /**
60
+ * The instance and slot holding the element `childKey`, or `undefined` when no instance in the spec
61
+ * holds it in a slot. A container's children are its children, not a slot's content: a component
62
+ * that takes children on its root lists no `children` slot, so its children resolve to nothing here.
63
+ */
64
+ declare function instanceHolding({
65
+ config,
66
+ spec,
67
+ childKey
68
+ }: {
69
+ config: Config;
70
+ spec: InstanceSpec | null | undefined;
71
+ childKey: string;
72
+ }): {
73
+ instance: Instance;
74
+ slot: InstanceSlot;
75
+ } | undefined;
76
+ //#endregion
77
+ export { Instance, InstanceSlot, InstanceSpec, instanceAt, instanceHolding };
@@ -0,0 +1,80 @@
1
+ import { listSlots } from "./slot.js";
2
+ import { Component } from "../entities/system/Component.js";
3
+ //#region src/spec/instance.ts
4
+ /**
5
+ * A component placed as one element of a design file's spec: which component it is, and what the
6
+ * file put in each of its slots. The component's slots come from the config (`Component.slots`),
7
+ * so a slot's hosting layer, what it takes and whether it is `children` are the component's answer;
8
+ * the content is this element's `children` for the content slot and its `slots` entry for a named
9
+ * one. `Component.instance` is the door.
10
+ */
11
+ var Instance = class {
12
+ /** The element's key in the spec. */
13
+ key;
14
+ /** The component the element places, as a path. */
15
+ component;
16
+ constructor(config, key, component, element) {
17
+ this.config = config;
18
+ this.element = element;
19
+ this.key = key;
20
+ this.component = component;
21
+ }
22
+ /** Every slot the component places content in, with what this instance holds there. */
23
+ get slots() {
24
+ return listSlots({
25
+ config: this.config,
26
+ path: this.component
27
+ }).map((slot) => {
28
+ const content = slot.isChildren ? this.element.children ?? [] : this.element.slots?.[slot.from] ?? [];
29
+ return {
30
+ slot,
31
+ content,
32
+ filled: content.length > 0
33
+ };
34
+ });
35
+ }
36
+ /** The slot a prop names, or `undefined` when the prop is no slot. */
37
+ slot(prop) {
38
+ return this.slots.find((entry) => entry.slot.from === prop);
39
+ }
40
+ /** The slot whose content lands in `layer`, or `undefined` when the layer hosts none. */
41
+ slotHostedBy(layer) {
42
+ return this.slots.find((entry) => entry.slot.layer === layer);
43
+ }
44
+ /** The slot holding the element `childKey`, or `undefined` when it holds none of this instance's. */
45
+ slotHolding(childKey) {
46
+ return this.slots.find((entry) => entry.content.includes(childKey));
47
+ }
48
+ };
49
+ /**
50
+ * The instance an element of a design file's spec places, or `undefined` when the element names no
51
+ * component of this config.
52
+ */
53
+ function instanceAt({ config, spec, elementKey }) {
54
+ const element = spec?.elements[elementKey];
55
+ const component = element?.type ? Component.get(config, element.type) : void 0;
56
+ if (!element || !component) return void 0;
57
+ return new Instance(config, elementKey, component.path, element);
58
+ }
59
+ /**
60
+ * The instance and slot holding the element `childKey`, or `undefined` when no instance in the spec
61
+ * holds it in a slot. A container's children are its children, not a slot's content: a component
62
+ * that takes children on its root lists no `children` slot, so its children resolve to nothing here.
63
+ */
64
+ function instanceHolding({ config, spec, childKey }) {
65
+ if (!spec) return void 0;
66
+ for (const key of Object.keys(spec.elements)) {
67
+ const instance = instanceAt({
68
+ config,
69
+ spec,
70
+ elementKey: key
71
+ });
72
+ const slot = instance?.slotHolding(childKey);
73
+ if (instance && slot) return {
74
+ instance,
75
+ slot
76
+ };
77
+ }
78
+ }
79
+ //#endregion
80
+ export { instanceAt, instanceHolding };
@@ -0,0 +1,95 @@
1
+ import { Config } from "../framework/Config.js";
2
+ import { RenderElement } from "../framework/render-spec.js";
3
+ import { IndexedPreviewCondition, holdingPreviewConditions } from "../framework/projections.js";
4
+ import { PreviewElement } from "./preview-elements.js";
5
+
6
+ //#region src/spec/preview-content.d.ts
7
+ /** Where a slot prop's value for a cell is stored: the last holding conditional `preview` entry,
8
+ * else the `*` entry, else `defaultProps`, the order `previewDefaults` merges them in. */
9
+ type PreviewValueSource = {
10
+ readonly kind: 'preview'; /** The entry's key, which a `sub-update` on `preview` addresses; `*` for the unconditional one. */
11
+ readonly key: string;
12
+ readonly prop: string;
13
+ } | {
14
+ readonly kind: 'defaultProps';
15
+ readonly prop: string;
16
+ };
17
+ /**
18
+ * The stored field the value a cell shows for `prop` comes from, or `undefined` when the component
19
+ * stores none. A default the prop's declaration carries is not stored preview content.
20
+ */
21
+ declare function previewValueSource({
22
+ config,
23
+ path,
24
+ cell,
25
+ prop
26
+ }: {
27
+ config: Config;
28
+ path: string;
29
+ cell: Readonly<Record<string, unknown>>;
30
+ prop: string;
31
+ }): PreviewValueSource | undefined;
32
+ /** The `preview` entry whose condition is exactly this coordinate, or `undefined` when the
33
+ * component stores none there. */
34
+ declare function previewConditionAt({
35
+ config,
36
+ path,
37
+ cell
38
+ }: {
39
+ config: Config;
40
+ path: string;
41
+ cell: Readonly<Record<string, unknown>>;
42
+ }): IndexedPreviewCondition | undefined;
43
+ /** Whether a cell's coordinate is the component's defaults, so its preview is every tile's. A forced
44
+ * modifier is a state the defaults never show, so a cell with one is not the defaults. */
45
+ declare function cellAtDefaults({
46
+ config,
47
+ path,
48
+ cell,
49
+ modifiers
50
+ }: {
51
+ config: Config;
52
+ path: string;
53
+ cell: Readonly<Record<string, unknown>>;
54
+ modifiers?: readonly string[];
55
+ }): boolean;
56
+ /** Where a grafted preview element sits inside the stored preview content. */
57
+ interface PreviewContentAddress {
58
+ /** The slot prop whose preview content the element is part of. */
59
+ readonly prop: string;
60
+ /** Which element of the prop's value, when the value is a list. */
61
+ readonly index: number;
62
+ /** Steps from that element down: a number is a child, a string an element-valued prop. */
63
+ readonly path: readonly (string | number)[];
64
+ }
65
+ /**
66
+ * The address a grafted element's key spells, or `null` for a key that is not preview content.
67
+ * A trailing bare `_` is a collision suffix and names no step.
68
+ *
69
+ * OPEN: a prop name holding `_` reads as two steps.
70
+ */
71
+ declare function parsePreviewContentKey(anatomyKey: string): PreviewContentAddress | null;
72
+ /** A rendered spec's elements, narrowed to what a preview element is read back from. */
73
+ type SpecElementsForPreview = Readonly<Record<string, Partial<Pick<RenderElement, 'type' | 'props' | 'children' | 'slots'>> | undefined>>;
74
+ /**
75
+ * A spec subtree read back as the preview element that would produce it: the inverse of
76
+ * `previewElementNodes`. A string `children` prop is the element's text, `children` keys are its
77
+ * children, a slot filled by one element is that element handed as a prop, and a slot filled by
78
+ * several is the list of them.
79
+ */
80
+ declare function previewElementFromSpec({
81
+ elements,
82
+ key,
83
+ config
84
+ }: {
85
+ elements: SpecElementsForPreview;
86
+ key: string;
87
+ /** Given, a type spelled as a marker (`component:Badge`) is stored as the ref it names, so the
88
+ * element joins the dependency graph; a marker the config resolves nothing for stays a type. */
89
+ config?: Config;
90
+ }): PreviewElement | null;
91
+ /** A slot prop's stored content as the list it is: a bare element is one element, a list is itself,
92
+ * and anything else holds no elements. */
93
+ declare function previewContentList(value: unknown): PreviewElement[];
94
+ //#endregion
95
+ export { type IndexedPreviewCondition, PreviewContentAddress, PreviewValueSource, SpecElementsForPreview, cellAtDefaults, holdingPreviewConditions, parsePreviewContentKey, previewConditionAt, previewContentList, previewElementFromSpec, previewValueSource };
@@ -0,0 +1,149 @@
1
+ import { ref, refLeaf, splitRef } from "../framework/utils/refs.js";
2
+ import { unconditionalPreviewProps } from "../framework/preview-store.js";
3
+ import { holdingPreviewConditions, keyedPreviewConditions } from "../framework/projections.js";
4
+ import { isPreviewElement } from "./preview-elements.js";
5
+ //#region src/spec/preview-content.ts
6
+ /** The `defaultProps` a component stores and the props of its `*` preview entry, empty for a path
7
+ * naming none. */
8
+ function storedDefaults(config, path) {
9
+ const body = config.resolve("component", path)?.body;
10
+ return {
11
+ defaultProps: body?.defaultProps,
12
+ previewProps: unconditionalPreviewProps(body)
13
+ };
14
+ }
15
+ /**
16
+ * The stored field the value a cell shows for `prop` comes from, or `undefined` when the component
17
+ * stores none. A default the prop's declaration carries is not stored preview content.
18
+ */
19
+ function previewValueSource({ config, path, cell, prop }) {
20
+ const holding = holdingPreviewConditions({
21
+ config,
22
+ path,
23
+ cell
24
+ });
25
+ for (let i = holding.length - 1; i >= 0; i--) {
26
+ const { key, condition } = holding[i];
27
+ if (prop in condition.props) return {
28
+ kind: "preview",
29
+ key,
30
+ prop
31
+ };
32
+ }
33
+ const { defaultProps, previewProps } = storedDefaults(config, path);
34
+ if (previewProps?.[prop] !== void 0) return {
35
+ kind: "preview",
36
+ key: "*",
37
+ prop
38
+ };
39
+ if (defaultProps?.[prop] !== void 0) return {
40
+ kind: "defaultProps",
41
+ prop
42
+ };
43
+ }
44
+ /** Whether two condition values name the same value, a ref or its leaf. */
45
+ function sameLeaf(a, b) {
46
+ return String(refLeaf(a)) === String(refLeaf(b));
47
+ }
48
+ /** The `preview` entry whose condition is exactly this coordinate, or `undefined` when the
49
+ * component stores none there. */
50
+ function previewConditionAt({ config, path, cell }) {
51
+ const props = Object.keys(cell);
52
+ const found = Object.entries(keyedPreviewConditions(config, path)).find(([, { when }]) => when !== null && Object.keys(when).length === props.length && props.every((prop) => prop in when && sameLeaf(when[prop], cell[prop])));
53
+ return found ? {
54
+ key: found[0],
55
+ condition: found[1]
56
+ } : void 0;
57
+ }
58
+ const NO_MODIFIERS = [];
59
+ /** Whether a cell's coordinate is the component's defaults, so its preview is every tile's. A forced
60
+ * modifier is a state the defaults never show, so a cell with one is not the defaults. */
61
+ function cellAtDefaults({ config, path, cell, modifiers = NO_MODIFIERS }) {
62
+ const { defaultProps = {} } = storedDefaults(config, path);
63
+ return modifiers.length === 0 && Object.entries(cell).every(([prop, value]) => sameLeaf(defaultProps[prop], value));
64
+ }
65
+ /**
66
+ * The address a grafted element's key spells, or `null` for a key that is not preview content.
67
+ * A trailing bare `_` is a collision suffix and names no step.
68
+ *
69
+ * OPEN: a prop name holding `_` reads as two steps.
70
+ */
71
+ function parsePreviewContentKey(anatomyKey) {
72
+ const parts = anatomyKey.split("__");
73
+ if (parts.length !== 3) return null;
74
+ const [owner, prop, rest] = parts;
75
+ if (!owner || !prop || !rest) return null;
76
+ const [head, ...steps] = rest.split("_");
77
+ if (!/^\d+$/.test(head)) return null;
78
+ return {
79
+ prop,
80
+ index: Number(head),
81
+ path: steps.filter((step) => step.length > 0).map((step) => /^\d+$/.test(step) ? Number(step) : step)
82
+ };
83
+ }
84
+ /** Props a projection stamps on an element to say what it is, never part of its content. */
85
+ const IDENTITY_PROPS = new Set([
86
+ "data-uds-layer",
87
+ "data-uds-anatomy",
88
+ "data-uds-component",
89
+ "data-uds-slot-anatomy",
90
+ "data-uds-slot-prop",
91
+ "data-element-key",
92
+ "data-element-parent",
93
+ "data-element-type"
94
+ ]);
95
+ /**
96
+ * A spec subtree read back as the preview element that would produce it: the inverse of
97
+ * `previewElementNodes`. A string `children` prop is the element's text, `children` keys are its
98
+ * children, a slot filled by one element is that element handed as a prop, and a slot filled by
99
+ * several is the list of them.
100
+ */
101
+ function previewElementFromSpec({ elements, key, config }) {
102
+ const el = elements[key];
103
+ if (!el?.type) return null;
104
+ const props = {};
105
+ let text;
106
+ for (const [name, value] of Object.entries(el.props ?? {})) {
107
+ if (IDENTITY_PROPS.has(name) || name.startsWith("data-uds-")) continue;
108
+ if (name === "children" && typeof value === "string") {
109
+ text = value;
110
+ continue;
111
+ }
112
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") props[name] = value;
113
+ }
114
+ for (const [into, keys] of Object.entries(el.slots ?? {})) {
115
+ const held = keys.map((fill) => previewElementFromSpec({
116
+ elements,
117
+ key: fill,
118
+ config
119
+ })).filter((fill) => fill !== null);
120
+ if (held.length === 1) props[into] = held[0];
121
+ else if (held.length > 1) props[into] = held;
122
+ }
123
+ const children = (el.children ?? []).map((child) => previewElementFromSpec({
124
+ elements,
125
+ key: child,
126
+ config
127
+ })).filter((child) => child !== null);
128
+ return {
129
+ element: config ? elementRefOf(config, el.type) : el.type,
130
+ ...Object.keys(props).length > 0 ? { props } : {},
131
+ ...children.length > 0 ? { children } : {},
132
+ ...text !== void 0 ? { text } : {}
133
+ };
134
+ }
135
+ /** A type spelled as a marker the config resolves, as the ref it names; anything else as it was. */
136
+ function elementRefOf(config, type) {
137
+ const { kind, qualifiedPath } = splitRef(type);
138
+ if (!kind || kind === type) return type;
139
+ return config.entityOf(kind) ? ref(kind, qualifiedPath) : type;
140
+ }
141
+ /** A slot prop's stored content as the list it is: a bare element is one element, a list is itself,
142
+ * and anything else holds no elements. */
143
+ function previewContentList(value) {
144
+ if (isPreviewElement(value)) return [value];
145
+ if (Array.isArray(value)) return value.filter(isPreviewElement);
146
+ return [];
147
+ }
148
+ //#endregion
149
+ export { cellAtDefaults, holdingPreviewConditions, parsePreviewContentKey, previewConditionAt, previewContentList, previewElementFromSpec, previewValueSource };
@@ -1,26 +1,41 @@
1
1
  //#region src/spec/preview-elements.d.ts
2
- /**
3
- * A preview element is the JSON-safe form of JSX stored in a component's preview props.
4
- *
5
- * It is authoring data, not a React element and not yet a json-render node. The helpers in this file
6
- * own the conversion into the UDS spec dialect so every render host receives the same shape.
7
- */
8
2
  interface PreviewElement {
9
3
  readonly element: string | {
10
4
  readonly __ref: string;
11
5
  };
12
- readonly props?: Record<string, string | number | boolean>;
6
+ /** Literals, an element handed as a prop (`<IconButton icon={<PlusIcon />} />`) or a list of them,
7
+ * which fill the slot the prop names the way the root's own element-valued props do, or a
8
+ * component handed as a prop (`icon={PlusIcon}`), which stays a prop as a ref. */
9
+ readonly props?: Record<string, string | number | boolean | PreviewElement | PreviewElement[] | PreviewRef>;
13
10
  readonly children?: PreviewElement[];
14
11
  readonly text?: string;
15
12
  }
16
- type PreviewValue = string | number | boolean | PreviewElement | PreviewValue[] | {
13
+ /**
14
+ * A component handed as a prop value rather than rendered: `icon={DotsThreeIcon}` names the glyph
15
+ * and lets the receiving component draw it. It is the `element` half of a {@link PreviewElement} on
16
+ * its own, spelled as the same ref the registry keys the component under (`icon:phosphor/StarIcon`,
17
+ * `component:studio/IconButton`, `package:@ariakit/react#Menu`), and a reviver hands the component
18
+ * back, not a node.
19
+ */
20
+ interface PreviewRef {
21
+ readonly __ref: string;
22
+ }
23
+ type PreviewValue = string | number | boolean | PreviewElement | PreviewRef | PreviewValue[] | {
17
24
  readonly [key: string]: PreviewValue;
18
25
  };
19
26
  /** Whether a stored value names an element to render. */
20
27
  declare function isPreviewElement(value: unknown): value is PreviewElement;
28
+ /** Whether a stored value is a bare `{ __ref }` and nothing else, whatever kind it names. */
29
+ declare function isPreviewRef(value: unknown): value is PreviewRef;
30
+ /**
31
+ * Whether a stored value names a component to hand over. A `token:` or `value:` ref is data a prop
32
+ * carries and has the same bare shape, so the kind is what tells them apart.
33
+ */
34
+ declare function isComponentRef(value: unknown): value is PreviewRef;
21
35
  /**
22
- * Whether anything under a value is a stored preview element. Stops at live React elements: walking
23
- * through their owner Fiber reaches a circular structure.
36
+ * Whether anything under a value is a stored preview element or component ref, so a reviver knows
37
+ * the bag needs work. Stops at live React elements: walking through their owner Fiber reaches a
38
+ * circular structure.
24
39
  */
25
40
  declare function hasPreviewElementDeep(value: unknown): boolean;
26
41
  /** The json-render `type` a stored element names. */
@@ -31,6 +46,8 @@ interface PreviewSpecNode {
31
46
  readonly props?: Record<string, unknown>;
32
47
  /** Child element keys, never literal content. */
33
48
  readonly children?: string[];
49
+ /** Element keys filling a slot prop, under that prop's name — json-render traverses these. */
50
+ readonly slots?: Record<string, string[]>;
34
51
  }
35
52
  /** Flatten one stored preview element into json-render's keyed element map. */
36
53
  declare function previewElementNodes(root: PreviewElement, rootKey: string, taken?: ReadonlySet<string>): {
@@ -48,11 +65,33 @@ type GraftableElement = {
48
65
  slots?: Record<string, string[]>;
49
66
  };
50
67
  /**
51
- * Lower direct element-valued props into json-render's native slot form.
68
+ * Graft a slot's content under its host: the elements join the spec's keyed element map as
69
+ * `<host>__<prop>__<index>` subtrees, and the host's `slots[into]` names their roots — json-render
70
+ * traverses `slots` itself, so the keys join neither `props` nor `children`. What an earlier graft
71
+ * of the same slot put there goes first, so the same keys are minted again for the content this
72
+ * pass holds and nothing stale is left for the next key to collide with. The spec comes back
73
+ * unchanged, by reference, when the grafted content is already what is there.
52
74
  *
53
- * The element subtrees join the spec's keyed element map, and the root's `slots` record names their
54
- * keys under the prop that carried them — json-render traverses `slots` itself, so the keys join
55
- * neither `props` nor `children`. Plain values—including text in `props.children`—remain props.
75
+ * Every host that grafts slot content goes through here: the tile projection, the matrix cell and
76
+ * the cell panel, so their keys agree and the layers rail and the drop hit-test read one scheme.
77
+ */
78
+ declare function graftSlotContent<S extends GraftableSpec>({
79
+ spec,
80
+ hostKey,
81
+ prop,
82
+ into,
83
+ elements: content
84
+ }: {
85
+ spec: S; /** The element whose `slots` names the fills. */
86
+ hostKey: string; /** The slot prop the content arrives as, which the grafted keys carry. */
87
+ prop: string; /** The host element's prop the fills land under. */
88
+ into: string;
89
+ elements: readonly PreviewElement[];
90
+ }): S;
91
+ /**
92
+ * Lower the root's direct element-valued props into json-render's native slot form, one
93
+ * {@link graftSlotContent} per prop, under the prop's own name. Plain values — including text in
94
+ * `props.children` — remain props.
56
95
  *
57
96
  * Elements nested inside arbitrary structured data are deliberately left for runtime marker
58
97
  * revival. A `slots` entry identifies a component prop, not a path inside an opaque application
@@ -60,4 +99,4 @@ type GraftableElement = {
60
99
  */
61
100
  declare function graftPreviewElements<S extends GraftableSpec>(spec: S, values: Record<string, unknown>): S;
62
101
  //#endregion
63
- export { GraftableElement, GraftableSpec, PreviewElement, PreviewSpecNode, PreviewValue, graftPreviewElements, hasPreviewElementDeep, isPreviewElement, previewElementNodes, previewElementType };
102
+ export { GraftableElement, GraftableSpec, PreviewElement, PreviewRef, PreviewSpecNode, PreviewValue, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, isComponentRef, isPreviewElement, isPreviewRef, previewElementNodes, previewElementType };