@yahoo/uds-create-config 4.6.0 → 4.8.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
package/dist/spec/index.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
23
|
-
* through their owner Fiber reaches a
|
|
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
|
-
*
|
|
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
|
-
*
|
|
54
|
-
*
|
|
55
|
-
|
|
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 };
|