@yahoo/uds-create-config 2.45.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -0
- package/dist/ai-lanes.d.ts +23 -0
- package/dist/ai-lanes.js +34 -0
- package/dist/authoring/define-component.d.ts +85 -0
- package/dist/configs/CanvasConfig.d.ts +3237 -0
- package/dist/configs/CanvasConfig.js +332 -0
- package/dist/configs/build-options.d.ts +10 -0
- package/dist/configs/build-options.js +68 -0
- package/dist/configs/platform.d.ts +15 -0
- package/dist/configs/platform.js +17 -0
- package/dist/configs/react-native-system.d.ts +3026 -0
- package/dist/configs/react-native-system.js +64 -0
- package/dist/configs/system.d.ts +2771 -0
- package/dist/configs/system.js +74 -0
- package/dist/css/properties.generated.d.ts +14 -0
- package/dist/css/properties.generated.js +6260 -0
- package/dist/css/value-type.d.ts +48 -0
- package/dist/css/value-type.js +131 -0
- package/dist/css/values.d.ts +33 -0
- package/dist/css/values.js +116 -0
- package/dist/entities/native/NativeModifier.d.ts +25 -0
- package/dist/entities/native/NativeModifier.js +41 -0
- package/dist/entities/native/NativeSettings.d.ts +44 -0
- package/dist/entities/native/NativeSettings.js +39 -0
- package/dist/entities/native/NativeStyleProperty.d.ts +355 -0
- package/dist/entities/native/NativeStyleProperty.js +28 -0
- package/dist/entities/native/NativeToken.d.ts +31 -0
- package/dist/entities/native/NativeToken.js +49 -0
- package/dist/entities/system/CanvasRole.d.ts +34 -0
- package/dist/entities/system/CanvasRole.js +45 -0
- package/dist/entities/system/Component.d.ts +1287 -0
- package/dist/entities/system/Component.js +1840 -0
- package/dist/entities/system/Composite.d.ts +12 -0
- package/dist/entities/system/Composite.js +26 -0
- package/dist/entities/system/Device.d.ts +11 -0
- package/dist/entities/system/Device.js +18 -0
- package/dist/entities/system/File.d.ts +72 -0
- package/dist/entities/system/File.js +136 -0
- package/dist/entities/system/Font.d.ts +48 -0
- package/dist/entities/system/Font.js +56 -0
- package/dist/entities/system/GlobalStyle.d.ts +26 -0
- package/dist/entities/system/GlobalStyle.js +54 -0
- package/dist/entities/system/Guidance.d.ts +85 -0
- package/dist/entities/system/Guidance.js +59 -0
- package/dist/entities/system/GuidanceStyle.d.ts +14 -0
- package/dist/entities/system/GuidanceStyle.js +8 -0
- package/dist/entities/system/Icon.d.ts +46 -0
- package/dist/entities/system/Icon.js +46 -0
- package/dist/entities/system/LinkedSystem.d.ts +504 -0
- package/dist/entities/system/LinkedSystem.js +814 -0
- package/dist/entities/system/Modifier.d.ts +92 -0
- package/dist/entities/system/Modifier.js +120 -0
- package/dist/entities/system/Motion.d.ts +28 -0
- package/dist/entities/system/Motion.js +48 -0
- package/dist/entities/system/Operation.d.ts +100 -0
- package/dist/entities/system/Operation.js +236 -0
- package/dist/entities/system/Package.d.ts +42 -0
- package/dist/entities/system/Package.js +75 -0
- package/dist/entities/system/Settings.d.ts +94 -0
- package/dist/entities/system/Settings.js +166 -0
- package/dist/entities/system/StyleProperty.d.ts +135 -0
- package/dist/entities/system/StyleProperty.js +438 -0
- package/dist/entities/system/Token.d.ts +67 -0
- package/dist/entities/system/Token.js +129 -0
- package/dist/entities/system/Tool.d.ts +134 -0
- package/dist/entities/system/Tool.js +124 -0
- package/dist/entities/system/color.d.ts +64 -0
- package/dist/entities/system/color.js +123 -0
- package/dist/entities/system/component-module.d.ts +63 -0
- package/dist/entities/system/component-module.js +258 -0
- package/dist/entities/system/copy-plan.d.ts +98 -0
- package/dist/entities/system/copy-plan.js +451 -0
- package/dist/entities/system/defineComponent.d.ts +428 -0
- package/dist/entities/system/defineComponent.js +337 -0
- package/dist/entities/system/element.d.ts +101 -0
- package/dist/entities/system/element.js +171 -0
- package/dist/entities/system/icon-library.d.ts +64 -0
- package/dist/entities/system/icon-library.js +112 -0
- package/dist/entities/system/icon-metadata.d.ts +72 -0
- package/dist/entities/system/icon-metadata.js +111 -0
- package/dist/entities/system/link-declaration.d.ts +27 -0
- package/dist/entities/system/link-declaration.js +64 -0
- package/dist/entities/system/link-plan.d.ts +160 -0
- package/dist/entities/system/link-plan.js +770 -0
- package/dist/entities/system/runtime-modules.d.ts +25 -0
- package/dist/entities/system/runtime-modules.js +38 -0
- package/dist/entities/system/style-bag.d.ts +20 -0
- package/dist/entities/system/style-bag.js +246 -0
- package/dist/entities/system/token-index.d.ts +89 -0
- package/dist/entities/system/token-index.js +112 -0
- package/dist/framework/Config.d.ts +2472 -0
- package/dist/framework/Config.js +5023 -0
- package/dist/framework/Entity.d.ts +127 -0
- package/dist/framework/Entity.js +125 -0
- package/dist/framework/authoring.d.ts +24 -0
- package/dist/framework/authoring.js +7 -0
- package/dist/framework/change-hooks.d.ts +119 -0
- package/dist/framework/change-hooks.js +131 -0
- package/dist/framework/changes.d.ts +88 -0
- package/dist/framework/changes.js +246 -0
- package/dist/framework/class-names.d.ts +260 -0
- package/dist/framework/class-names.js +630 -0
- package/dist/framework/coalesce.d.ts +25 -0
- package/dist/framework/coalesce.js +60 -0
- package/dist/framework/config-op.d.ts +76 -0
- package/dist/framework/config-op.js +43 -0
- package/dist/framework/config-operation-kind.d.ts +14 -0
- package/dist/framework/config-operation-kind.js +14 -0
- package/dist/framework/config-ops.d.ts +34 -0
- package/dist/framework/config-ops.js +187 -0
- package/dist/framework/defineConfig.d.ts +196 -0
- package/dist/framework/defineConfig.js +100 -0
- package/dist/framework/defineDerivedEntity.d.ts +74 -0
- package/dist/framework/defineDerivedEntity.js +83 -0
- package/dist/framework/defineEntity.d.ts +318 -0
- package/dist/framework/defineEntity.js +183 -0
- package/dist/framework/defineSubEntity.d.ts +148 -0
- package/dist/framework/defineSubEntity.js +121 -0
- package/dist/framework/derived-mutations.d.ts +20 -0
- package/dist/framework/derived-mutations.js +38 -0
- package/dist/framework/entity-search.d.ts +29 -0
- package/dist/framework/entity-search.js +117 -0
- package/dist/framework/inline-sources.d.ts +49 -0
- package/dist/framework/inline-sources.js +388 -0
- package/dist/framework/layer-styles.d.ts +25 -0
- package/dist/framework/layer-styles.js +49 -0
- package/dist/framework/memo.d.ts +48 -0
- package/dist/framework/memo.js +84 -0
- package/dist/framework/overlay.d.ts +49 -0
- package/dist/framework/overlay.js +17 -0
- package/dist/framework/plan.d.ts +49 -0
- package/dist/framework/plan.js +73 -0
- package/dist/framework/projections.d.ts +480 -0
- package/dist/framework/projections.js +974 -0
- package/dist/framework/prop-surface.d.ts +132 -0
- package/dist/framework/prop-surface.js +278 -0
- package/dist/framework/ref-graph.d.ts +71 -0
- package/dist/framework/ref-graph.js +158 -0
- package/dist/framework/ref-integrity.d.ts +55 -0
- package/dist/framework/ref-integrity.js +171 -0
- package/dist/framework/registered.d.ts +95 -0
- package/dist/framework/registry-dir.d.ts +42 -0
- package/dist/framework/registry-dir.js +48 -0
- package/dist/framework/registry.d.ts +300 -0
- package/dist/framework/registry.js +104 -0
- package/dist/framework/rejection.d.ts +58 -0
- package/dist/framework/rejection.js +111 -0
- package/dist/framework/rename-inference.d.ts +26 -0
- package/dist/framework/rename-inference.js +50 -0
- package/dist/framework/render-spec.d.ts +296 -0
- package/dist/framework/render-spec.js +809 -0
- package/dist/framework/schema-version.d.ts +140 -0
- package/dist/framework/schema-version.js +214 -0
- package/dist/framework/schemas.d.ts +109 -0
- package/dist/framework/schemas.js +615 -0
- package/dist/framework/session.d.ts +196 -0
- package/dist/framework/session.js +367 -0
- package/dist/framework/signature.d.ts +15 -0
- package/dist/framework/signature.js +83 -0
- package/dist/framework/snapshot.d.ts +115 -0
- package/dist/framework/snapshot.js +277 -0
- package/dist/framework/source-integrity.d.ts +226 -0
- package/dist/framework/source-integrity.js +478 -0
- package/dist/framework/sources.d.ts +211 -0
- package/dist/framework/sources.js +89 -0
- package/dist/framework/utils/compiled-schema.d.ts +23 -0
- package/dist/framework/utils/compiled-schema.js +46 -0
- package/dist/framework/utils/content-key.d.ts +15 -0
- package/dist/framework/utils/content-key.js +33 -0
- package/dist/framework/utils/enumerated.d.ts +13 -0
- package/dist/framework/utils/enumerated.js +19 -0
- package/dist/framework/utils/field-path.d.ts +95 -0
- package/dist/framework/utils/field-path.js +136 -0
- package/dist/framework/utils/field-schema.d.ts +55 -0
- package/dist/framework/utils/field-schema.js +183 -0
- package/dist/framework/utils/group.d.ts +37 -0
- package/dist/framework/utils/group.js +13 -0
- package/dist/framework/utils/package-path.d.ts +37 -0
- package/dist/framework/utils/package-path.js +61 -0
- package/dist/framework/utils/refs.d.ts +194 -0
- package/dist/framework/utils/refs.js +403 -0
- package/dist/framework/validate-spec.d.ts +27 -0
- package/dist/framework/validate-spec.js +45 -0
- package/dist/framework/value-domain.d.ts +26 -0
- package/dist/framework/value-domain.js +202 -0
- package/dist/framework/views-facade.d.ts +15 -0
- package/dist/framework/views-facade.js +19 -0
- package/dist/index.d.ts +90 -42
- package/dist/index.js +86 -37
- package/dist/migrations/2.0.0/raw-css.d.ts +11 -0
- package/dist/migrations/2.0.0/raw-css.js +72 -0
- package/dist/migrations/2.0.0/v1-artifact.d.ts +1559 -0
- package/dist/migrations/2.0.0/v1-artifact.js +1018 -0
- package/dist/migrations/20260908171448_settings_as_kind.d.ts +22 -0
- package/dist/migrations/20260908171448_settings_as_kind.js +89 -0
- package/dist/migrations/20260908194245_visible_state_pointer.d.ts +19 -0
- package/dist/migrations/20260908194245_visible_state_pointer.js +115 -0
- package/dist/migrations/20260909183001_icon_knockout_variants.d.ts +18 -0
- package/dist/migrations/20260909183001_icon_knockout_variants.js +8 -0
- package/dist/migrations/20260910212453_linked_system_link_ops.d.ts +13 -0
- package/dist/migrations/20260910212453_linked_system_link_ops.js +8 -0
- package/dist/migrations/index.d.ts +2 -0
- package/dist/migrations/index.js +2 -0
- package/dist/react-native/style-keys.generated.d.ts +10 -0
- package/dist/react-native/style-keys.generated.js +167 -0
- package/dist/renderer/RendererErrorBoundary.d.ts +32 -5
- package/dist/renderer/RendererErrorBoundary.js +59 -10
- package/dist/renderer/UdsRenderer.d.ts +11 -10
- package/dist/renderer/UdsRenderer.js +1 -1
- package/dist/renderer/assetEntries.d.ts +37 -0
- package/dist/renderer/assetEntries.js +39 -0
- package/dist/renderer/assetRenderable.d.ts +14 -6
- package/dist/renderer/assetRenderable.js +8 -2
- package/dist/renderer/componentEntry.d.ts +58 -0
- package/dist/renderer/componentEntry.js +71 -0
- package/dist/renderer/icon-library.d.ts +20 -0
- package/dist/renderer/index.d.ts +7 -7
- package/dist/renderer/index.js +5 -5
- package/dist/renderer/primitives/SlotRenderer.d.ts +1 -1
- package/dist/renderer/primitives/SlotRenderer.js +1 -1
- package/dist/renderer/runtimeComponent.d.ts +33 -0
- package/dist/renderer/runtimeComponent.js +113 -0
- package/dist/renderer/spec-content.d.ts +43 -0
- package/dist/renderer/spec-content.js +95 -0
- package/dist/renderer/wrapRegistry.d.ts +22 -37
- package/dist/renderer/wrapRegistry.js +10 -20
- package/dist/renderer/wrappers/event-bridge.js +6 -5
- package/dist/renderer/wrappers/hex-normalize.js +6 -5
- package/dist/renderer/wrappers/html-aliases.d.ts +7 -2
- package/dist/renderer/wrappers/html-aliases.js +36 -19
- package/dist/renderer/wrappers/inline-styles.js +7 -6
- package/dist/spec/asset-jsx.d.ts +18 -5
- package/dist/spec/asset-jsx.js +27 -16
- package/dist/spec/collapse-text-labels.d.ts +5 -15
- package/dist/spec/collapse-text-labels.js +26 -19
- package/dist/spec/empty-node-slots.d.ts +56 -34
- package/dist/spec/empty-node-slots.js +87 -47
- package/dist/spec/index.d.ts +21 -10
- package/dist/spec/index.js +21 -10
- package/dist/spec/jsxToSpec.d.ts +14 -9
- package/dist/spec/jsxToSpec.js +20 -3
- package/dist/spec/preview-elements.d.ts +63 -0
- package/dist/spec/preview-elements.js +89 -0
- package/dist/spec/rewrite-refs.d.ts +58 -0
- package/dist/spec/rewrite-refs.js +250 -0
- package/dist/spec/specToJsx.js +18 -29
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +39 -52
- package/dist/AssetGroup.d.ts +0 -77
- package/dist/AssetGroup.js +0 -125
- package/dist/Component.d.ts +0 -333
- package/dist/Component.js +0 -1061
- package/dist/ComponentGroup.d.ts +0 -22
- package/dist/ComponentGroup.js +0 -51
- package/dist/CompositeStyle.d.ts +0 -30
- package/dist/CompositeStyle.js +0 -59
- package/dist/Config.d.ts +0 -528
- package/dist/Config.js +0 -1712
- package/dist/Mode.d.ts +0 -41
- package/dist/Mode.js +0 -81
- package/dist/Modifier.d.ts +0 -51
- package/dist/Modifier.js +0 -97
- package/dist/MotionDef.d.ts +0 -81
- package/dist/MotionDef.js +0 -212
- package/dist/Props.d.ts +0 -332
- package/dist/Props.js +0 -35
- package/dist/Provider.d.ts +0 -20
- package/dist/Provider.js +0 -14
- package/dist/StyleProp.d.ts +0 -145
- package/dist/StyleProp.js +0 -239
- package/dist/Token.d.ts +0 -76
- package/dist/Token.js +0 -133
- package/dist/TokenGroup.d.ts +0 -33
- package/dist/TokenGroup.js +0 -69
- package/dist/asset-kind.d.ts +0 -55
- package/dist/asset-kind.js +0 -29
- package/dist/asset-value.d.ts +0 -23
- package/dist/asset-value.js +0 -40
- package/dist/brands.d.ts +0 -30
- package/dist/brands.js +0 -20
- package/dist/captureCallerPath.d.ts +0 -48
- package/dist/captureCallerPath.js +0 -95
- package/dist/colorExpressions.d.ts +0 -131
- package/dist/colorExpressions.js +0 -148
- package/dist/component-referrers.d.ts +0 -30
- package/dist/component-referrers.js +0 -40
- package/dist/config-diagnostics.d.ts +0 -21
- package/dist/config-diagnostics.js +0 -12
- package/dist/defineAssetGroup.d.ts +0 -200
- package/dist/defineAssetGroup.js +0 -323
- package/dist/defineProvider.d.ts +0 -29
- package/dist/defineProvider.js +0 -60
- package/dist/element-marker.d.ts +0 -63
- package/dist/element-marker.js +0 -113
- package/dist/entity-utils.d.ts +0 -56
- package/dist/entity-utils.js +0 -105
- package/dist/extension.d.ts +0 -55
- package/dist/extension.js +0 -214
- package/dist/factories.d.ts +0 -863
- package/dist/factories.js +0 -475
- package/dist/font-face.d.ts +0 -25
- package/dist/font-face.js +0 -72
- package/dist/foreign-component-name.d.ts +0 -21
- package/dist/foreign-component-name.js +0 -42
- package/dist/interpolate.d.ts +0 -20
- package/dist/interpolate.js +0 -10
- package/dist/jsx/__fixtures__/cross-component-preview.d.ts +0 -3
- package/dist/jsx/__fixtures__/cross-component-preview.js +0 -15
- package/dist/jsx/jsx-dev-runtime.d.ts +0 -15
- package/dist/jsx/jsx-dev-runtime.js +0 -11
- package/dist/jsx/jsx-runtime.d.ts +0 -48
- package/dist/jsx/jsx-runtime.js +0 -305
- package/dist/linked-system-pins.d.ts +0 -30
- package/dist/linked-system-pins.js +0 -45
- package/dist/linkedNodePrefixes.d.ts +0 -73
- package/dist/linkedNodePrefixes.js +0 -99
- package/dist/markers.d.ts +0 -235
- package/dist/markers.js +0 -70
- package/dist/motion-constants.d.ts +0 -37
- package/dist/motion-constants.js +0 -61
- package/dist/native/NativeConfig.d.ts +0 -56
- package/dist/native/NativeConfig.js +0 -162
- package/dist/native/assets.d.ts +0 -20
- package/dist/native/assets.js +0 -17
- package/dist/native/components.d.ts +0 -139
- package/dist/native/components.js +0 -67
- package/dist/native/index.d.ts +0 -18
- package/dist/native/index.js +0 -17
- package/dist/native/modes.d.ts +0 -10
- package/dist/native/modes.js +0 -11
- package/dist/native/types.d.ts +0 -95
- package/dist/native/types.js +0 -0
- package/dist/native/validation.d.ts +0 -8
- package/dist/native/validation.js +0 -885
- package/dist/native/values.d.ts +0 -7
- package/dist/native/values.js +0 -15
- package/dist/native.d.ts +0 -12
- package/dist/native.js +0 -10
- package/dist/refs.d.ts +0 -204
- package/dist/refs.js +0 -129
- package/dist/registry-aliases.d.ts +0 -33
- package/dist/registry-aliases.js +0 -35
- package/dist/registry-dir.d.ts +0 -22
- package/dist/registry-dir.js +0 -10
- package/dist/renderer/makeRegistry.d.ts +0 -34
- package/dist/renderer/makeRegistry.js +0 -52
- package/dist/renderer/makeUdsRenderer.d.ts +0 -13
- package/dist/renderer/makeUdsRenderer.js +0 -51
- package/dist/renderer/wrappers/component-slots.d.ts +0 -41
- package/dist/renderer/wrappers/component-slots.js +0 -66
- package/dist/renderer/wrappers/slot-resolution.d.ts +0 -25
- package/dist/renderer/wrappers/slot-resolution.js +0 -68
- package/dist/renderer/wrappers/void-elements.d.ts +0 -37
- package/dist/renderer/wrappers/void-elements.js +0 -40
- package/dist/spec/layer-props.d.ts +0 -52
- package/dist/spec/layer-props.js +0 -149
- package/dist/spec/preview-controls.d.ts +0 -44
- package/dist/spec/preview-controls.js +0 -139
- package/dist/spec/slot-refs.d.ts +0 -39
- package/dist/spec/slot-refs.js +0 -56
- package/dist/token-override-rows.d.ts +0 -68
- package/dist/token-override-rows.js +0 -233
- package/dist/tokenValueType.d.ts +0 -34
- package/dist/tokenValueType.js +0 -138
- package/dist/types/css-properties.d.ts +0 -233
- package/dist/types/css-properties.js +0 -14
- package/dist/types/css-property-keywords.d.ts +0 -156
- package/dist/types/css-property-keywords.js +0 -616
- package/dist/types/css-values.d.ts +0 -63
- package/dist/types/css-values.js +0 -16
- package/dist/types.d.ts +0 -1138
- package/dist/types.js +0 -12
- package/dist/units.d.ts +0 -14
- package/dist/units.js +0 -16
- package/dist/utils/index.d.ts +0 -4
- package/dist/utils/index.js +0 -4
|
@@ -0,0 +1,809 @@
|
|
|
1
|
+
import { kindOf, memberOf, pathOf, ref, splitRef } from "./utils/refs.js";
|
|
2
|
+
import { authoredBag, memberFromLeaf, resolveComponentProps } from "./projections.js";
|
|
3
|
+
import { intoName, routesContent } from "./prop-surface.js";
|
|
4
|
+
import { graftPreviewElements } from "../spec/preview-elements.js";
|
|
5
|
+
import { componentLayerClass, componentPropClasses, componentRuleClasses, layerCompositeProps, splitStyleProps, styleRuleMotionClasses } from "./class-names.js";
|
|
6
|
+
//#region src/framework/render-spec.ts
|
|
7
|
+
const refOf = (value) => typeof value?.__ref === "string" ? value.__ref : void 0;
|
|
8
|
+
const layerOf = (element) => {
|
|
9
|
+
const target = refOf(element);
|
|
10
|
+
if (target === void 0) return void 0;
|
|
11
|
+
const { kind, qualifiedPath } = splitRef(target);
|
|
12
|
+
return kind === "layer" ? qualifiedPath : void 0;
|
|
13
|
+
};
|
|
14
|
+
/** The prop a node's element is CHOSEN BY — `{ $state: '/name' }`. */
|
|
15
|
+
const boundPropOf = (element) => {
|
|
16
|
+
const marker = element?.$state;
|
|
17
|
+
return typeof marker === "string" ? marker.replace(/^\//, "") : void 0;
|
|
18
|
+
};
|
|
19
|
+
/** The slot PROP a node STANDS FOR — a `Slot`-typed node whose `name` prop says which prop's
|
|
20
|
+
* instance content splices at its position among the siblings. json-render's own vocabulary (the
|
|
21
|
+
* `Slot` registry primitive and its `name` prop), so the stored node and the rendered element are
|
|
22
|
+
* one grammar. */
|
|
23
|
+
const slotAnatomyPropOf = (node) => {
|
|
24
|
+
if (node?.element !== "Slot") return void 0;
|
|
25
|
+
const name = node.props?.name;
|
|
26
|
+
return typeof name === "string" ? name : void 0;
|
|
27
|
+
};
|
|
28
|
+
const bodyOf = (config, path) => config.resolve("component", path)?.toJSON();
|
|
29
|
+
/** The node a tree is rooted at — the one named `root`, else the first declared. The same rule
|
|
30
|
+
* `rootLayerOf` uses for layers, which is why neither is stored. */
|
|
31
|
+
function rootKeyOf(nodes) {
|
|
32
|
+
return "root" in nodes ? "root" : Object.keys(nodes)[0] ?? "root";
|
|
33
|
+
}
|
|
34
|
+
/** Every key a node holds — child edges plus slot fills, which nest without an edge. */
|
|
35
|
+
function heldKeys(node) {
|
|
36
|
+
return [...node?.children ?? [], ...Object.values(node?.slots ?? {}).flat()];
|
|
37
|
+
}
|
|
38
|
+
/** How many nodes a key reaches, itself included — the size of the tree hanging off it. Visited-set
|
|
39
|
+
* guarded, so a cycle counts each node once instead of running forever. */
|
|
40
|
+
function reachOf(nodes, from) {
|
|
41
|
+
const seen = new Set([from]);
|
|
42
|
+
const queue = [from];
|
|
43
|
+
while (queue.length > 0) {
|
|
44
|
+
const key = queue.shift();
|
|
45
|
+
for (const child of heldKeys(nodes[key])) {
|
|
46
|
+
if (seen.has(child)) continue;
|
|
47
|
+
seen.add(child);
|
|
48
|
+
queue.push(child);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return seen.size;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The node a tree ENTERS at — the one nothing else claims as a child.
|
|
55
|
+
*
|
|
56
|
+
* A name can't answer this. `root` names the root LAYER, and a render is free to put a wrapper above
|
|
57
|
+
* that layer — a menu provider, a positioner, a portal — so the layer is somewhere in the middle of
|
|
58
|
+
* its own tree. Entering by name amputated every ancestor: the tree rendered without the provider
|
|
59
|
+
* that its own descendants read from, and those descendants threw on the first paint.
|
|
60
|
+
*
|
|
61
|
+
* Structure states it instead, and in a whole tree exactly one node is unclaimed. Two or more is a
|
|
62
|
+
* FOREST — a reader that couldn't finish, or a condition that left siblings with no parent — and then
|
|
63
|
+
* the component is whichever of them holds most of it: the strays get dropped either way, and
|
|
64
|
+
* dropping the real tree to render a stray leaf is the worse of the two. Nothing unclaimed at all
|
|
65
|
+
* (a cycle) falls back to the name, which is the best a malformed tree deserves.
|
|
66
|
+
*/
|
|
67
|
+
function entryKeyOf(nodes) {
|
|
68
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
69
|
+
for (const node of Object.values(nodes)) for (const child of heldKeys(node)) claimed.add(child);
|
|
70
|
+
const unclaimed = Object.keys(nodes).filter((key) => !claimed.has(key));
|
|
71
|
+
if (unclaimed.length <= 1) return unclaimed[0] ?? rootKeyOf(nodes);
|
|
72
|
+
let best = unclaimed[0];
|
|
73
|
+
let bestReach = reachOf(nodes, best);
|
|
74
|
+
for (const key of unclaimed.slice(1)) {
|
|
75
|
+
const reach = reachOf(nodes, key);
|
|
76
|
+
if (reach > bestReach || reach === bestReach && key === "root") {
|
|
77
|
+
best = key;
|
|
78
|
+
bestReach = reach;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return best;
|
|
82
|
+
}
|
|
83
|
+
/** What a node's `element` renders, as a spec `type`. A layer ref resolves through the component's
|
|
84
|
+
* own layers to whatever that layer is; a bound element has no json-render form (its `type` is a
|
|
85
|
+
* plain string), so it is reported rather than guessed at. */
|
|
86
|
+
function typeOf(layers, element) {
|
|
87
|
+
const layer = layerOf(element);
|
|
88
|
+
const target = layer === void 0 ? element : layers[layer];
|
|
89
|
+
if (typeof target === "string") return target;
|
|
90
|
+
return refOf(target);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A node's stored props as an author wrote them. The anatomy keeps a style prop's token as a ref
|
|
94
|
+
* (`bg` as `token:spectrum/carbon/5`) so a rename cascades; a spec sets the leaf the prop's own
|
|
95
|
+
* domain names (`carbon/5`), and a component handed the ref object paints nothing. `authoredBag` is
|
|
96
|
+
* the one inverse of that binding, so the projection reads through it rather than trimming a path.
|
|
97
|
+
*/
|
|
98
|
+
function authoredProps(config, props) {
|
|
99
|
+
return props ? authoredBag(config, props) : {};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* A component's anatomy, projected to the json-render spec a canvas reads.
|
|
103
|
+
*
|
|
104
|
+
* The artifact's SOURCE changes and its shape does not: `element` becomes `type` and a ref
|
|
105
|
+
* serializes to its string form, while `children` / `visible` / `text` pass through, since those
|
|
106
|
+
* were borrowed from json-render's own vocabulary rather than invented.
|
|
107
|
+
*
|
|
108
|
+
* A component with no stored anatomy but ONE layer is still answered outright — it is one element,
|
|
109
|
+
* and that was always stated rather than run.
|
|
110
|
+
*/
|
|
111
|
+
function deriveSpec(config, path) {
|
|
112
|
+
const body = bodyOf(config, path) ?? {};
|
|
113
|
+
const layers = body.layers ?? {};
|
|
114
|
+
const anatomy = body.anatomy;
|
|
115
|
+
if (!anatomy || Object.keys(anatomy).length === 0) {
|
|
116
|
+
if (Object.keys(layers).length !== 1) return void 0;
|
|
117
|
+
return instanceSpec(path, {
|
|
118
|
+
"data-uds-component": path,
|
|
119
|
+
"data-uds-layer": rootKeyOf(layers)
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
const elements = {};
|
|
123
|
+
for (const [key, node] of Object.entries(anatomy)) {
|
|
124
|
+
const slotAnatomyProp = slotAnatomyPropOf(node);
|
|
125
|
+
if (slotAnatomyProp !== void 0) {
|
|
126
|
+
elements[key] = {
|
|
127
|
+
type: "Slot",
|
|
128
|
+
props: {
|
|
129
|
+
...authoredProps(config, node.props),
|
|
130
|
+
"data-uds-slot-anatomy": slotAnatomyProp
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const type = typeOf(layers, node.element);
|
|
136
|
+
if (!type) return void 0;
|
|
137
|
+
const layer = layerOf(node.element);
|
|
138
|
+
const props = {
|
|
139
|
+
...authoredProps(config, node.props),
|
|
140
|
+
...node.text !== void 0 ? { children: node.text } : {},
|
|
141
|
+
...layer !== void 0 ? {
|
|
142
|
+
"data-uds-layer": layer,
|
|
143
|
+
"data-uds-component": path
|
|
144
|
+
} : {}
|
|
145
|
+
};
|
|
146
|
+
elements[key] = {
|
|
147
|
+
type,
|
|
148
|
+
...Object.keys(props).length ? { props } : {},
|
|
149
|
+
...node.children?.length ? { children: node.children } : {},
|
|
150
|
+
...node.slots && Object.keys(node.slots).length ? { slots: node.slots } : {},
|
|
151
|
+
...node.visible !== void 0 ? { visible: node.visible } : {}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
root: entryKeyOf(elements),
|
|
156
|
+
elements
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* ONE COMPONENT, as a spec — the instance form, and the only place the `component:` prefix is minted.
|
|
161
|
+
*
|
|
162
|
+
* A spec type is `kind:identity`, always: the emitted registry keys every entry that way and holds
|
|
163
|
+
* no bare names, so `Box` resolves to nothing while `component:Box` resolves to the module. That
|
|
164
|
+
* makes the prefix load-bearing rather than decorative, and something load-bearing spelled by
|
|
165
|
+
* string-concatenation at each site is a spelling waiting to drift — three places in this file did
|
|
166
|
+
* it, and a consumer that wanted the same shape had no choice but to add a fourth.
|
|
167
|
+
*
|
|
168
|
+
* So consumers ask for the SHAPE instead of assembling it, and the type itself comes from `ref` —
|
|
169
|
+
* the same maker `Component.ref` is bound to — rather than a template. A caller outside this
|
|
170
|
+
* package has no business knowing the prefix at all.
|
|
171
|
+
*/
|
|
172
|
+
function instanceSpec(path, props = {}) {
|
|
173
|
+
return {
|
|
174
|
+
root: "root",
|
|
175
|
+
elements: { root: {
|
|
176
|
+
type: ref("component", path).__ref,
|
|
177
|
+
props
|
|
178
|
+
} }
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* The spec for a component, ALWAYS — the leaf form when its composition doesn't project.
|
|
183
|
+
*
|
|
184
|
+
* `deriveSpec` declines rather than guess, and that is right: a tree missing a node is worse than an
|
|
185
|
+
* admission that it can't be built. But a caller still has to paint something, and the honest thing to
|
|
186
|
+
* paint is the component ITSELF — one element, `component:<path>`, which the renderer resolves to the
|
|
187
|
+
* real module with whatever its render closes over intact. That is strictly better than the previous
|
|
188
|
+
* fallback (a spec captured by an earlier build), which showed a structure the config no longer
|
|
189
|
+
* describes and let an editor author against it.
|
|
190
|
+
*
|
|
191
|
+
* So the two answers are separated: what to RENDER (always available) and WHICH of the two it is. A
|
|
192
|
+
* surface reads `kind` to decide whether to offer composition tools, and `reason` to explain why not.
|
|
193
|
+
*/
|
|
194
|
+
function componentSpec(config, path) {
|
|
195
|
+
const derived = deriveSpec(config, path);
|
|
196
|
+
if (derived) return {
|
|
197
|
+
spec: derived,
|
|
198
|
+
kind: "composition"
|
|
199
|
+
};
|
|
200
|
+
const body = bodyOf(config, path) ?? {};
|
|
201
|
+
const layers = Object.keys(body.layers ?? {}).length;
|
|
202
|
+
const reason = !body.anatomy || Object.keys(body.anatomy).length === 0 ? `"${path}" has ${layers} layers but no anatomy saying how they assemble, so its composition can't be shown or edited here — it renders as itself.` : `"${path}" has an element whose type only an instance settles (a bound element), so its composition can't be shown or edited here — it renders as itself.`;
|
|
203
|
+
return {
|
|
204
|
+
spec: instanceSpec(path),
|
|
205
|
+
kind: "leaf",
|
|
206
|
+
reason
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* A component as it is SHOWN — itself, in the example state its author wrote.
|
|
211
|
+
*
|
|
212
|
+
* The sibling above answers "what is this made of", and that is the question a layer panel and a
|
|
213
|
+
* variant matrix ask. A catalogue card, a palette card and a docs thumbnail ask the other one, and
|
|
214
|
+
* answering them with a composition is what left those tiles wrong: {@link deriveSpec} reads anatomy
|
|
215
|
+
* off the render's JSX STATICALLY, without the preview's props, so a prop-driven branch arrives as an
|
|
216
|
+
* unresolved `$state` condition and prop-driven text doesn't arrive at all. The tile painted the
|
|
217
|
+
* structure with its example content missing.
|
|
218
|
+
*
|
|
219
|
+
* One element, always, whatever the component is made of — because WHO DRAWS IT isn't this
|
|
220
|
+
* projection's call. A built component is in the renderer's registry, so the element resolves to its
|
|
221
|
+
* real module and is handed the example props directly. One created in Studio isn't, so
|
|
222
|
+
* {@link expandSpec} replaces it with the tree the config describes, resolving those same props'
|
|
223
|
+
* conditions and bound elements on the way. Both paths exist already; emitting the component and
|
|
224
|
+
* letting the renderer choose is what reaches them.
|
|
225
|
+
*
|
|
226
|
+
* Only the preview's props. A component's `defaultProps` are already folded into its layer's
|
|
227
|
+
* zero-specificity rule at build time, so seeding them here would re-apply as an instance's style
|
|
228
|
+
* props what the stylesheet has said once already.
|
|
229
|
+
*
|
|
230
|
+
* One stored preview value is not a prop yet: JSX such as `startIcon: <Icon/>` crosses config.json as
|
|
231
|
+
* a `PreviewElement` data marker. It is lowered here into the native `slots` + keyed-element form a
|
|
232
|
+
* spec authors directly. That makes this projection render-ready for every host instead of relying on
|
|
233
|
+
* a Studio-only registry wrapper to revive the marker later.
|
|
234
|
+
*/
|
|
235
|
+
function previewSpec(config, path) {
|
|
236
|
+
const body = bodyOf(config, path);
|
|
237
|
+
if (!body) return void 0;
|
|
238
|
+
const values = body.previewProps ?? {};
|
|
239
|
+
return graftPreviewElements(instanceSpec(path, values), values);
|
|
240
|
+
}
|
|
241
|
+
/** A key no emitted element is using yet. Anatomy keys are namespaced under the instance they belong
|
|
242
|
+
* to, so two instances of one component can't collide; the suffix loop is the backstop for a
|
|
243
|
+
* design file that happens to have named an element the same thing. */
|
|
244
|
+
function freeKey(sink, hint) {
|
|
245
|
+
if (!(hint in sink.elements)) return hint;
|
|
246
|
+
let index = 2;
|
|
247
|
+
while (`${hint}${index}` in sink.elements) index += 1;
|
|
248
|
+
return `${hint}${index}`;
|
|
249
|
+
}
|
|
250
|
+
/** Whether this expansion should replace `type` with what the config says it's made of. */
|
|
251
|
+
function expandable(sink, type) {
|
|
252
|
+
if (kindOf(type) !== "component") return false;
|
|
253
|
+
if (sink.rendered?.has(type)) return false;
|
|
254
|
+
const path = pathOf(type);
|
|
255
|
+
if (sink.expanding.includes(path)) return false;
|
|
256
|
+
return sink.config.resolve("component", path) !== void 0;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Resolve every element's `visible` CONDITION against a prop bag, leaving a plain boolean.
|
|
260
|
+
*
|
|
261
|
+
* json-render evaluates the condition itself at render time, so this changes nothing about what
|
|
262
|
+
* paints. What it changes is what the spec can be ASKED: an unresolved `{ $state: '/startIcon' }`
|
|
263
|
+
* says only "it depends", where `false` says "this draws nothing", and a reader that has to decide
|
|
264
|
+
* whether a container is empty needs the second.
|
|
265
|
+
*
|
|
266
|
+
* That reader is the UDS renderer. A container whose children all draw nothing must fall back to its
|
|
267
|
+
* `props.children` label — and with the condition unresolved it could not tell, so every `Button`
|
|
268
|
+
* cell in the matrix rendered blank: the root lists `startIcon`/`endIcon`, and an unset icon is a
|
|
269
|
+
* node whose condition is false. `expandSpec` already resolves visibility for an INSTANCE; this is
|
|
270
|
+
* the same answer for a spec rendered as the component's own anatomy, where the caller supplies the
|
|
271
|
+
* state instead of an instance.
|
|
272
|
+
*
|
|
273
|
+
* A condition that does not resolve is left exactly as it was, so nothing is claimed about it.
|
|
274
|
+
*/
|
|
275
|
+
function specWithResolvedVisibility(spec, state) {
|
|
276
|
+
let changed = false;
|
|
277
|
+
const elements = {};
|
|
278
|
+
for (const [key, element] of Object.entries(spec.elements)) {
|
|
279
|
+
const resolved = element.visible === void 0 ? void 0 : resolveVisibility(element.visible, state);
|
|
280
|
+
if (resolved === void 0 || resolved === element.visible) {
|
|
281
|
+
elements[key] = element;
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
changed = true;
|
|
285
|
+
elements[key] = {
|
|
286
|
+
...element,
|
|
287
|
+
visible: resolved
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
return changed ? {
|
|
291
|
+
...spec,
|
|
292
|
+
elements
|
|
293
|
+
} : spec;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* How each operator judges a state value against its operand. One table for every reader of the
|
|
297
|
+
* grammar: `resolveVisibility` applies it, `visibilityTerms` names it, and the entity's schema is
|
|
298
|
+
* checked against its keys. Insertion order is precedence for a malformed test that carries more
|
|
299
|
+
* than one operator, and the readers share it through {@link OPERATORS}.
|
|
300
|
+
*/
|
|
301
|
+
const COMPARATORS = {
|
|
302
|
+
eq: (value, operand) => value === operand,
|
|
303
|
+
neq: (value, operand) => value !== operand,
|
|
304
|
+
gt: (value, operand) => Number(value) > Number(operand),
|
|
305
|
+
gte: (value, operand) => Number(value) >= Number(operand),
|
|
306
|
+
lt: (value, operand) => Number(value) < Number(operand),
|
|
307
|
+
lte: (value, operand) => Number(value) <= Number(operand)
|
|
308
|
+
};
|
|
309
|
+
const OPERATORS = Object.keys(COMPARATORS);
|
|
310
|
+
/** The operator a `$state` test carries, or undefined for a bare truthiness test. */
|
|
311
|
+
function operatorOf(test) {
|
|
312
|
+
return OPERATORS.find((name) => name in test);
|
|
313
|
+
}
|
|
314
|
+
/** A value in json-render's `visible` grammar, resolved against a prop bag. `undefined` when this
|
|
315
|
+
* reader can't settle it, which leaves the condition on the element for the renderer to judge. */
|
|
316
|
+
function resolveVisibility(condition, state) {
|
|
317
|
+
if (condition === void 0) return true;
|
|
318
|
+
if (typeof condition === "boolean") return condition;
|
|
319
|
+
if (Array.isArray(condition)) {
|
|
320
|
+
let known = true;
|
|
321
|
+
for (const entry of condition) {
|
|
322
|
+
const result = resolveVisibility(entry, state);
|
|
323
|
+
if (result === false) return false;
|
|
324
|
+
if (result === void 0) known = false;
|
|
325
|
+
}
|
|
326
|
+
return known ? true : void 0;
|
|
327
|
+
}
|
|
328
|
+
if (typeof condition !== "object" || condition === null) return void 0;
|
|
329
|
+
const test = condition;
|
|
330
|
+
if (Array.isArray(test.$or)) {
|
|
331
|
+
let known = true;
|
|
332
|
+
for (const entry of test.$or) {
|
|
333
|
+
const result = resolveVisibility(entry, state);
|
|
334
|
+
if (result === true) return true;
|
|
335
|
+
if (result === void 0) known = false;
|
|
336
|
+
}
|
|
337
|
+
return known ? false : void 0;
|
|
338
|
+
}
|
|
339
|
+
if (Array.isArray(test.$and)) return resolveVisibility(test.$and, state);
|
|
340
|
+
if (typeof test.$state !== "string") return void 0;
|
|
341
|
+
const value = state[test.$state.replace(/^\//, "")];
|
|
342
|
+
const operator = operatorOf(test);
|
|
343
|
+
const result = operator ? COMPARATORS[operator](value, test[operator]) : Boolean(value);
|
|
344
|
+
return test.not === true ? !result : result;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Which PROPS a `visible` condition reads, in the order they appear.
|
|
348
|
+
*
|
|
349
|
+
* `resolveVisibility` answers "does this draw here"; this answers "what decides that" — the question
|
|
350
|
+
* an editor asks. A surface offering to bind visibility to a property needs to know whether one is
|
|
351
|
+
* already driving it, and a surface refusing an edit needs to name what to change instead of the
|
|
352
|
+
* layer. Both were guessing: the layers panel told an author to "switch to a matching cell" for a
|
|
353
|
+
* node gated on a slot being filled, where no cell in the matrix can ever match.
|
|
354
|
+
*
|
|
355
|
+
* Duplicates are collapsed, so a condition testing one prop twice reads as the one prop it is about.
|
|
356
|
+
*/
|
|
357
|
+
function visibilityStateProps(condition) {
|
|
358
|
+
const names = [];
|
|
359
|
+
collectStateProps(condition, names);
|
|
360
|
+
return [...new Set(names)];
|
|
361
|
+
}
|
|
362
|
+
function collectStateProps(condition, sink) {
|
|
363
|
+
if (Array.isArray(condition)) {
|
|
364
|
+
for (const entry of condition) collectStateProps(entry, sink);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
if (typeof condition !== "object" || condition === null) return;
|
|
368
|
+
const test = condition;
|
|
369
|
+
if (Array.isArray(test.$or)) {
|
|
370
|
+
for (const entry of test.$or) collectStateProps(entry, sink);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (Array.isArray(test.$and)) {
|
|
374
|
+
for (const entry of test.$and) collectStateProps(entry, sink);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
if (typeof test.$state === "string") sink.push(test.$state.replace(/^\//, ""));
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* The tests a `visible` condition makes, in order, flattened to one list.
|
|
381
|
+
*
|
|
382
|
+
* `visibilityStateProps` names the props; this keeps what is asked of each one, so a surface can say
|
|
383
|
+
* "shows when `tone` is `danger`" rather than "follows `tone`". Grouping survives as `junction` and
|
|
384
|
+
* `group`: the stored grammar is an and-list whose entries are single tests or one `$or` each, so
|
|
385
|
+
* the two together are enough to read the condition back. A boolean or a shape this reader does not
|
|
386
|
+
* know yields no terms.
|
|
387
|
+
*/
|
|
388
|
+
function visibilityTerms(condition) {
|
|
389
|
+
const sink = [];
|
|
390
|
+
collectTerms({
|
|
391
|
+
condition,
|
|
392
|
+
junction: "and",
|
|
393
|
+
group: 0,
|
|
394
|
+
sink
|
|
395
|
+
});
|
|
396
|
+
return sink;
|
|
397
|
+
}
|
|
398
|
+
/** The index for a term that opens a new group: one past the last term collected. */
|
|
399
|
+
function nextGroup(sink) {
|
|
400
|
+
const last = sink[sink.length - 1];
|
|
401
|
+
return last ? last.group + 1 : 0;
|
|
402
|
+
}
|
|
403
|
+
function collectTerms({ condition, junction, group, sink }) {
|
|
404
|
+
if (Array.isArray(condition)) {
|
|
405
|
+
for (const entry of condition) collectTerms({
|
|
406
|
+
condition: entry,
|
|
407
|
+
junction: "and",
|
|
408
|
+
group: nextGroup(sink),
|
|
409
|
+
sink
|
|
410
|
+
});
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
if (typeof condition !== "object" || condition === null) return;
|
|
414
|
+
const test = condition;
|
|
415
|
+
if (Array.isArray(test.$or)) {
|
|
416
|
+
const shared = nextGroup(sink);
|
|
417
|
+
for (const entry of test.$or) collectTerms({
|
|
418
|
+
condition: entry,
|
|
419
|
+
junction: "or",
|
|
420
|
+
group: shared,
|
|
421
|
+
sink
|
|
422
|
+
});
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
if (Array.isArray(test.$and)) {
|
|
426
|
+
collectTerms({
|
|
427
|
+
condition: test.$and,
|
|
428
|
+
junction: "and",
|
|
429
|
+
group,
|
|
430
|
+
sink
|
|
431
|
+
});
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
if (typeof test.$state !== "string") return;
|
|
435
|
+
const operator = operatorOf(test);
|
|
436
|
+
sink.push({
|
|
437
|
+
prop: test.$state.replace(/^\//, ""),
|
|
438
|
+
operator: operator ?? "truthy",
|
|
439
|
+
...operator ? { value: test[operator] } : {},
|
|
440
|
+
negated: test.not === true,
|
|
441
|
+
junction,
|
|
442
|
+
group
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* A `visible` condition with every bare truthiness test of `prop` taken out.
|
|
447
|
+
*
|
|
448
|
+
* Three answers: `undefined` for a condition that makes no such test, so a caller leaves the field
|
|
449
|
+
* alone; `null` when nothing survives the removal, which is the value that clears it; otherwise the
|
|
450
|
+
* condition that remains, unwrapped when one term is left.
|
|
451
|
+
*
|
|
452
|
+
* Only the bare form goes — `{ $state: '/prop' }` with no comparison and no `not`, which is the test
|
|
453
|
+
* a value being present answers. A `$or` group holding one goes whole, since a true member settles
|
|
454
|
+
* the group; its other members go with it, and moving the fill out later does not bring them back,
|
|
455
|
+
* which is also true of a bare gate. `$state` is matched without its JSON-pointer slash, so both
|
|
456
|
+
* spellings are the same test.
|
|
457
|
+
*/
|
|
458
|
+
function withoutTruthyTerm({ condition, prop }) {
|
|
459
|
+
if (spentBy({
|
|
460
|
+
condition,
|
|
461
|
+
prop
|
|
462
|
+
})) return null;
|
|
463
|
+
if (!Array.isArray(condition)) return void 0;
|
|
464
|
+
const kept = condition.filter((entry) => !spentBy({
|
|
465
|
+
condition: entry,
|
|
466
|
+
prop
|
|
467
|
+
}));
|
|
468
|
+
if (kept.length === condition.length) return void 0;
|
|
469
|
+
if (kept.length === 0) return null;
|
|
470
|
+
return kept.length === 1 ? kept[0] : kept;
|
|
471
|
+
}
|
|
472
|
+
/** Whether a single term of an and-list is one `prop` being present makes true: the bare test, an
|
|
473
|
+
* `eq: true` test (content is never the boolean `true`, so presence is the only reading that can
|
|
474
|
+
* ever hold), or an `$or` group with one of those among its members. */
|
|
475
|
+
function spentBy({ condition, prop }) {
|
|
476
|
+
if (typeof condition !== "object" || condition === null) return false;
|
|
477
|
+
const test = condition;
|
|
478
|
+
if (Array.isArray(test.$or)) return test.$or.some((entry) => spentBy({
|
|
479
|
+
condition: entry,
|
|
480
|
+
prop
|
|
481
|
+
}));
|
|
482
|
+
if (typeof test.$state !== "string") return false;
|
|
483
|
+
return test.$state.replace(/^\//, "") === prop && test.not !== true && (operatorOf(test) === void 0 || operatorOf(test) === "eq" && test.eq === true);
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* The type a BOUND element resolves to, given the value its prop was handed.
|
|
487
|
+
*
|
|
488
|
+
* This is the one thing json-render has no vocabulary for — `UIElement.type` is a plain string — so
|
|
489
|
+
* a stored spec can't hold it and `deriveSpec` declines. An expansion is a different position: the
|
|
490
|
+
* instance is right here, so the prop has a value, and the prop's own declaration says what domain
|
|
491
|
+
* that value is drawn from. `Icon` is the case that matters: one bound node, resolved per instance,
|
|
492
|
+
* rather than 51 conditioned siblings copying a library's index into every component that draws a
|
|
493
|
+
* glyph.
|
|
494
|
+
*
|
|
495
|
+
* Two ways the domain is known, in order: the prop DELEGATES it (`value` is a ref to the library,
|
|
496
|
+
* which is the shape that keeps 1,530 names in one place), or the prop enumerates its values and the
|
|
497
|
+
* member is found by asking which group declares it. The second exists because a config migrated
|
|
498
|
+
* from v1 spells an icon prop as an enumeration, and a glyph is no less resolvable for it.
|
|
499
|
+
*/
|
|
500
|
+
function boundElementType(config, componentPath, prop, value) {
|
|
501
|
+
if (typeof value !== "string" || !value) return void 0;
|
|
502
|
+
if (value.includes("/")) return void 0;
|
|
503
|
+
const decl = resolveComponentProps(config, componentPath)[prop];
|
|
504
|
+
const target = refOf(decl?.value);
|
|
505
|
+
if (target) {
|
|
506
|
+
if (memberOf(target) !== void 0) return void 0;
|
|
507
|
+
const member = memberFromLeaf(config, target, value);
|
|
508
|
+
return member ? `${kindOf(target)}:${member}` : void 0;
|
|
509
|
+
}
|
|
510
|
+
const matches = config.list("icon").filter((member) => (member.path.split("/").pop() ?? "") === value);
|
|
511
|
+
return matches.length === 1 ? `icon:${matches[0]?.path}` : void 0;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* The props of an instance that the component CONSUMES rather than passes on — the same split the
|
|
515
|
+
* emitted module's destructuring makes (`downlevel.ts`, `styleRuleBindings`).
|
|
516
|
+
*
|
|
517
|
+
* A variant selects a style rule and a slot routes content, so neither reaches the DOM. Neither does
|
|
518
|
+
* a DECLARED prop a style rule tests: the config says it picks a class and says nothing else about
|
|
519
|
+
* it, which makes it part of the component's API rather than an attribute. `PanelIconButton`'s
|
|
520
|
+
* `active` is the shape of it — a boolean the rules branch on, spread onto the button by both this
|
|
521
|
+
* projection and the emitted module until each learned to consume it.
|
|
522
|
+
*
|
|
523
|
+
* An UNDECLARED prop a rule tests stays: the component never claimed it, so a call site passing it
|
|
524
|
+
* (`disabled` on a Pressable) means it for the element.
|
|
525
|
+
*/
|
|
526
|
+
function consumedProps(config, path, body) {
|
|
527
|
+
const consumed = new Set(["className", "layerProps"]);
|
|
528
|
+
const declared = body.props ?? {};
|
|
529
|
+
for (const [name, decl] of Object.entries(declared)) if (decl?.type === "variant" || decl?.type === "slot" || decl?.type === "forward" || decl?.type === "composite") consumed.add(name);
|
|
530
|
+
for (const rule of componentRuleClasses(config, path)) for (const prop of Object.keys(rule.when)) if (declared[prop]) consumed.add(prop);
|
|
531
|
+
return consumed;
|
|
532
|
+
}
|
|
533
|
+
/** The prop JSX children arrive on — json-render's content channel, and the same name
|
|
534
|
+
* `childrenPolicy` reads to answer whether a component accepts any. Not a system-defined name: a
|
|
535
|
+
* design system names its components and tokens, and this one names the channel. */
|
|
536
|
+
const CONTENT_PROP = "children";
|
|
537
|
+
function slotTargets(config, body) {
|
|
538
|
+
const byLayer = /* @__PURE__ */ new Map();
|
|
539
|
+
const untargeted = [];
|
|
540
|
+
for (const [from, decl] of Object.entries(body.props ?? {})) {
|
|
541
|
+
if (decl?.type === "slot") {
|
|
542
|
+
untargeted.push({
|
|
543
|
+
...decl,
|
|
544
|
+
from,
|
|
545
|
+
untargeted: true
|
|
546
|
+
});
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
if (decl?.type !== "forward") continue;
|
|
550
|
+
if (!routesContent(config, body.layers, decl)) continue;
|
|
551
|
+
const target = refOf(decl.target);
|
|
552
|
+
if (target) byLayer.set(pathOf(target), {
|
|
553
|
+
...decl,
|
|
554
|
+
from
|
|
555
|
+
});
|
|
556
|
+
else untargeted.push({
|
|
557
|
+
...decl,
|
|
558
|
+
from,
|
|
559
|
+
untargeted: true
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
const root = rootKeyOf(body.layers ?? {});
|
|
563
|
+
const content = untargeted.find((route) => route.from === CONTENT_PROP) ?? untargeted[0];
|
|
564
|
+
if (root && content && !byLayer.has(root)) byLayer.set(root, content);
|
|
565
|
+
return {
|
|
566
|
+
byLayer,
|
|
567
|
+
untargeted
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Where a component's slot props route their content, as the config states it.
|
|
572
|
+
*
|
|
573
|
+
* {@link expandInstance} has always known this; nothing outside this file could ask. A surface that
|
|
574
|
+
* needs the same answer had to guess from the spec's shape instead — "the one element carrying a
|
|
575
|
+
* string" — and a spec projected from the ANATOMY carries no strings at all, so the guess fell
|
|
576
|
+
* through to the root. For a component whose content slot NAMES its layer that is the wrong element
|
|
577
|
+
* AND an occupied one: the label lands beside the element children it was supposed to go inside, and
|
|
578
|
+
* the layer meant to hold it renders empty. Every one of Studio's own components shaped that way
|
|
579
|
+
* (`Badge`, `StudioButton`, `Toggle`, …) painted as an empty shell in the component editor.
|
|
580
|
+
*
|
|
581
|
+
* Exported so the answer has ONE owner. An untargeted slot still resolves to the root here — that is
|
|
582
|
+
* `slotTargets`' rule, not a caller's assumption, and it is the same rule the expansion renders by.
|
|
583
|
+
*/
|
|
584
|
+
function slotTargetsOf(config, path) {
|
|
585
|
+
const body = bodyOf(config, path);
|
|
586
|
+
if (!body) return [];
|
|
587
|
+
const { byLayer, untargeted } = slotTargets(config, body);
|
|
588
|
+
const root = rootKeyOf(body.layers ?? {});
|
|
589
|
+
return [...[...byLayer].filter(([, decl]) => !decl.untargeted).map(([layer, decl]) => ({
|
|
590
|
+
layer,
|
|
591
|
+
from: decl.from,
|
|
592
|
+
into: intoName(decl.into)
|
|
593
|
+
})), ...root ? untargeted.map((decl) => ({
|
|
594
|
+
layer: root,
|
|
595
|
+
from: decl.from,
|
|
596
|
+
into: intoName(decl.into)
|
|
597
|
+
})) : []];
|
|
598
|
+
}
|
|
599
|
+
/** The className a layer wears for one instance: its own class, every style rule whose condition
|
|
600
|
+
* holds, and — on the root only — what the call site passed. */
|
|
601
|
+
function classesFor(config, path, layer, state, extra) {
|
|
602
|
+
const parts = [componentLayerClass(config, path, layer)];
|
|
603
|
+
parts.push(...componentPropClasses(config, path, state)[layer] ?? []);
|
|
604
|
+
parts.push(...styleRuleMotionClasses(config, path, state)[layer] ?? []);
|
|
605
|
+
for (const rule of componentRuleClasses(config, path)) {
|
|
606
|
+
if (rule.layer !== layer) continue;
|
|
607
|
+
if (Object.entries(rule.when).every(([prop, value]) => String(state[prop]) === String(value))) parts.push(rule.className);
|
|
608
|
+
}
|
|
609
|
+
parts.push(...extra);
|
|
610
|
+
return parts.filter(Boolean).join(" ");
|
|
611
|
+
}
|
|
612
|
+
/** Emit one element, expanding it when the config describes what it's made of. Returns the key it
|
|
613
|
+
* was emitted under. */
|
|
614
|
+
function place(sink, type, props, children, hint, slots) {
|
|
615
|
+
if (expandable(sink, type)) return expandInstance(sink, pathOf(type), props, children, hint, slots);
|
|
616
|
+
let tag = type;
|
|
617
|
+
let own = props;
|
|
618
|
+
if (!type.includes(":") && typeof props.as === "string") {
|
|
619
|
+
const { as, ...rest } = props;
|
|
620
|
+
tag = as;
|
|
621
|
+
own = rest;
|
|
622
|
+
}
|
|
623
|
+
if (sink.rendered && !sink.rendered.has(tag)) sink.unresolved.add(tag);
|
|
624
|
+
const key = freeKey(sink, hint);
|
|
625
|
+
sink.elements[key] = {
|
|
626
|
+
type: tag,
|
|
627
|
+
props: own,
|
|
628
|
+
...children.length ? { children: [...children] } : {},
|
|
629
|
+
...slots && Object.keys(slots).length ? { slots } : {}
|
|
630
|
+
};
|
|
631
|
+
return key;
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Replace one component instance with the tree it is made of.
|
|
635
|
+
*
|
|
636
|
+
* Every class is computed HERE, from the config as it stands, which is the difference between this
|
|
637
|
+
* and a stored spec: an edit to a token, a style rule or a layer shows up on the next render with
|
|
638
|
+
* nothing to regenerate. `content` is the children the instance was given — placed on the root when
|
|
639
|
+
* the component has nowhere else declared to put them.
|
|
640
|
+
*/
|
|
641
|
+
function expandInstance(sink, path, instanceProps, content, hint, instanceSlots) {
|
|
642
|
+
const { config } = sink;
|
|
643
|
+
const body = bodyOf(config, path) ?? {};
|
|
644
|
+
const layers = body.layers ?? {};
|
|
645
|
+
const anatomy = body.anatomy && Object.keys(body.anatomy).length > 0 ? body.anatomy : { root: { element: { __ref: `layer:${rootKeyOf(layers)}` } } };
|
|
646
|
+
const filled = {};
|
|
647
|
+
for (const [from, fills] of Object.entries(instanceSlots ?? {})) if (fills.length > 0 && instanceProps[from] === void 0) filled[from] = true;
|
|
648
|
+
const state = {
|
|
649
|
+
...body.defaultProps,
|
|
650
|
+
...filled,
|
|
651
|
+
...instanceProps
|
|
652
|
+
};
|
|
653
|
+
const consumed = consumedProps(config, path, body);
|
|
654
|
+
const { byLayer: targets } = slotTargets(config, body);
|
|
655
|
+
const supplied = {
|
|
656
|
+
...body.defaultProps ?? {},
|
|
657
|
+
...instanceProps
|
|
658
|
+
};
|
|
659
|
+
const bag = {};
|
|
660
|
+
for (const [name, value] of Object.entries(supplied)) if (!consumed.has(name)) bag[name] = value;
|
|
661
|
+
const { className: styleClasses, rest } = splitStyleProps({
|
|
662
|
+
config,
|
|
663
|
+
bag,
|
|
664
|
+
through: [path]
|
|
665
|
+
});
|
|
666
|
+
const layerProps = instanceProps.layerProps ?? {};
|
|
667
|
+
const forwarded = layerCompositeProps(config, path, state);
|
|
668
|
+
const entryNode = entryKeyOf(anatomy);
|
|
669
|
+
const rootLayer = rootKeyOf(layers);
|
|
670
|
+
const hostNode = Object.keys(anatomy).find((key) => layerOf(anatomy[key]?.element) === rootLayer) ?? entryNode;
|
|
671
|
+
const nested = {
|
|
672
|
+
...sink,
|
|
673
|
+
expanding: [...sink.expanding, path]
|
|
674
|
+
};
|
|
675
|
+
const rootTarget = targets.get(rootLayer);
|
|
676
|
+
const untargetedSlot = rootTarget?.untargeted ? rootTarget.from : void 0;
|
|
677
|
+
let contentPlaced = false;
|
|
678
|
+
const routedFroms = /* @__PURE__ */ new Set();
|
|
679
|
+
for (const target of targets.values()) routedFroms.add(target.from);
|
|
680
|
+
const unroutedFills = Object.entries(instanceSlots ?? {}).filter(([from]) => !routedFroms.has(from)).flatMap(([, fills]) => fills);
|
|
681
|
+
const allContent = [...content, ...unroutedFills];
|
|
682
|
+
const emit = (key, keyHint) => {
|
|
683
|
+
const node = anatomy[key];
|
|
684
|
+
if (!node) return void 0;
|
|
685
|
+
const visible = resolveVisibility(node.visible, state);
|
|
686
|
+
const layer = layerOf(node.element);
|
|
687
|
+
const isRoot = key === hostNode;
|
|
688
|
+
let type = typeOf(layers, node.element);
|
|
689
|
+
const bound = boundPropOf(node.element);
|
|
690
|
+
if (bound) type = boundElementType(config, path, bound, state[bound]);
|
|
691
|
+
if (!type) {
|
|
692
|
+
sink.unresolved.add(bound ? `component:${path}#${key} (${bound})` : `component:${path}#${key}`);
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
if (visible === false) {
|
|
696
|
+
const key = freeKey(nested, keyHint);
|
|
697
|
+
nested.elements[key] = {
|
|
698
|
+
type,
|
|
699
|
+
props: {},
|
|
700
|
+
visible: false
|
|
701
|
+
};
|
|
702
|
+
return key;
|
|
703
|
+
}
|
|
704
|
+
const slot = layer ? targets.get(layer) : void 0;
|
|
705
|
+
const slotValue = slot ? instanceProps[slot.from] : void 0;
|
|
706
|
+
const props = layer ? {
|
|
707
|
+
...authoredProps(config, node.props),
|
|
708
|
+
"data-uds-component": path,
|
|
709
|
+
"data-uds-layer": layer,
|
|
710
|
+
...forwarded[layer],
|
|
711
|
+
...isRoot ? rest : {},
|
|
712
|
+
...slot && slotValue !== void 0 ? { [intoName(slot.into)]: slotValue } : {},
|
|
713
|
+
...layerProps[layer],
|
|
714
|
+
className: classesFor(config, path, layer, state, [
|
|
715
|
+
isRoot ? styleClasses : void 0,
|
|
716
|
+
isRoot ? instanceProps.className : void 0,
|
|
717
|
+
layerProps[layer]?.className
|
|
718
|
+
]) || void 0
|
|
719
|
+
} : authoredProps(config, node.props);
|
|
720
|
+
if (node.text !== void 0) props.children = node.text;
|
|
721
|
+
const children = [];
|
|
722
|
+
for (const child of node.children ?? []) {
|
|
723
|
+
const childSlot = slotAnatomyPropOf(anatomy[child]);
|
|
724
|
+
if (childSlot !== void 0) {
|
|
725
|
+
if (childSlot === untargetedSlot && allContent.length > 0) {
|
|
726
|
+
children.push(...allContent);
|
|
727
|
+
contentPlaced = true;
|
|
728
|
+
}
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
731
|
+
const placed = emit(child, `${keyHint}•${child}`);
|
|
732
|
+
if (placed) children.push(placed);
|
|
733
|
+
}
|
|
734
|
+
if (isRoot && allContent.length > 0 && !contentPlaced) children.push(...allContent);
|
|
735
|
+
const slots = {};
|
|
736
|
+
const routed = slot ? instanceSlots?.[slot.from] : void 0;
|
|
737
|
+
const overridden = routed?.length && slot && !slot.untargeted ? intoName(slot.into) : void 0;
|
|
738
|
+
for (const [name, fills] of Object.entries(node.slots ?? {})) {
|
|
739
|
+
if (name === overridden) continue;
|
|
740
|
+
const placedFills = fills.map((fill) => emit(fill, `${keyHint}•${fill}`)).filter((fillKey) => fillKey !== void 0);
|
|
741
|
+
if (placedFills.length) slots[name] = placedFills;
|
|
742
|
+
}
|
|
743
|
+
if (overridden && routed) slots[overridden] = [...routed];
|
|
744
|
+
if (routed?.length && !overridden) children.push(...routed);
|
|
745
|
+
const placed = place(nested, type, props, children, keyHint, Object.keys(slots).length ? slots : void 0);
|
|
746
|
+
if (visible === void 0) withCondition(nested, placed, { visible: node.visible });
|
|
747
|
+
return placed;
|
|
748
|
+
};
|
|
749
|
+
return emit(entryNode, hint) ?? hint;
|
|
750
|
+
}
|
|
751
|
+
/** Attach a condition the reader couldn't settle to an already-emitted element. */
|
|
752
|
+
function withCondition(sink, key, condition) {
|
|
753
|
+
const element = sink.elements[key];
|
|
754
|
+
if (element && condition.visible !== void 0) sink.elements[key] = {
|
|
755
|
+
...element,
|
|
756
|
+
visible: condition.visible
|
|
757
|
+
};
|
|
758
|
+
return key;
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Expand every component element the renderer can't name into the tree it is made of.
|
|
762
|
+
*
|
|
763
|
+
* The spec's own keys are KEPT — an expanded instance's root is emitted under the key it already
|
|
764
|
+
* had, so a `slots` entry, a selection marker, or anything else that names an element by key
|
|
765
|
+
* still resolves. Only the nodes an expansion adds get new keys, namespaced under the instance they
|
|
766
|
+
* belong to.
|
|
767
|
+
*/
|
|
768
|
+
function expandSpec(config, spec, options = {}) {
|
|
769
|
+
const sink = {
|
|
770
|
+
config,
|
|
771
|
+
rendered: options.rendered,
|
|
772
|
+
elements: {},
|
|
773
|
+
unresolved: /* @__PURE__ */ new Set(),
|
|
774
|
+
expanding: []
|
|
775
|
+
};
|
|
776
|
+
const emit = (key) => {
|
|
777
|
+
const element = spec.elements[key];
|
|
778
|
+
if (!element) return void 0;
|
|
779
|
+
const children = [];
|
|
780
|
+
for (const child of element.children ?? []) {
|
|
781
|
+
const placed = emit(child);
|
|
782
|
+
if (placed) children.push(placed);
|
|
783
|
+
}
|
|
784
|
+
const slots = {};
|
|
785
|
+
for (const [name, fills] of Object.entries(element.slots ?? {})) slots[name] = fills.map((fill) => emit(fill)).filter((placed) => placed !== void 0);
|
|
786
|
+
const placed = place(sink, element.type, { ...element.props }, children, key, slots);
|
|
787
|
+
if (element.visible !== void 0) withCondition(sink, placed, { visible: element.visible });
|
|
788
|
+
return placed;
|
|
789
|
+
};
|
|
790
|
+
return {
|
|
791
|
+
spec: {
|
|
792
|
+
root: emit(spec.root) ?? spec.root,
|
|
793
|
+
elements: sink.elements
|
|
794
|
+
},
|
|
795
|
+
unresolved: [...sink.unresolved]
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* One component instance, expanded from nothing but the config — what a canvas paints for a
|
|
800
|
+
* component created in Studio.
|
|
801
|
+
*
|
|
802
|
+
* The single-element spec this starts from is what a design-file node amounts to, so this is
|
|
803
|
+
* {@link expandSpec} with the ceremony removed.
|
|
804
|
+
*/
|
|
805
|
+
function expandComponent(config, path, props = {}, options = {}) {
|
|
806
|
+
return expandSpec(config, instanceSpec(path, props), options);
|
|
807
|
+
}
|
|
808
|
+
//#endregion
|
|
809
|
+
export { boundElementType, componentSpec, deriveSpec, entryKeyOf, expandComponent, expandSpec, instanceSpec, previewSpec, resolveVisibility, slotAnatomyPropOf, slotTargetsOf, specWithResolvedVisibility, visibilityStateProps, visibilityTerms, withoutTruthyTerm };
|