@yahoo/uds-create-config 1.1.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/dist/AssetGroup.d.ts +77 -0
- package/dist/AssetGroup.js +125 -0
- package/dist/Component.d.ts +308 -0
- package/dist/Component.js +896 -0
- package/dist/ComponentGroup.d.ts +20 -0
- package/dist/ComponentGroup.js +46 -0
- package/dist/CompositeStyle.d.ts +27 -0
- package/dist/CompositeStyle.js +52 -0
- package/dist/Config.d.ts +423 -0
- package/dist/Config.js +1429 -0
- package/dist/Mode.d.ts +41 -0
- package/dist/Mode.js +81 -0
- package/dist/Modifier.d.ts +51 -0
- package/dist/Modifier.js +97 -0
- package/dist/MotionDef.d.ts +49 -0
- package/dist/MotionDef.js +97 -0
- package/dist/Props.d.ts +317 -0
- package/dist/Props.js +35 -0
- package/dist/Provider.d.ts +20 -0
- package/dist/Provider.js +14 -0
- package/dist/StyleProp.d.ts +112 -0
- package/dist/StyleProp.js +197 -0
- package/dist/Token.d.ts +55 -0
- package/dist/Token.js +112 -0
- package/dist/TokenGroup.d.ts +32 -0
- package/dist/TokenGroup.js +68 -0
- package/dist/asset-kind.d.ts +55 -0
- package/dist/asset-kind.js +29 -0
- package/dist/brands.d.ts +30 -0
- package/dist/brands.js +20 -0
- package/dist/captureCallerPath.d.ts +48 -0
- package/dist/captureCallerPath.js +95 -0
- package/dist/colorExpressions.d.ts +131 -0
- package/dist/colorExpressions.js +148 -0
- package/dist/defineAssetGroup.d.ts +166 -0
- package/dist/defineAssetGroup.js +264 -0
- package/dist/defineProvider.d.ts +29 -0
- package/dist/defineProvider.js +60 -0
- package/dist/element-marker.d.ts +54 -0
- package/dist/element-marker.js +113 -0
- package/dist/entity-utils.d.ts +56 -0
- package/dist/entity-utils.js +105 -0
- package/dist/factories.d.ts +707 -0
- package/dist/factories.js +393 -0
- package/dist/foreign-component-name.d.ts +21 -0
- package/dist/foreign-component-name.js +42 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +28 -0
- package/dist/interpolate.d.ts +20 -0
- package/dist/interpolate.js +10 -0
- package/dist/jsx/__fixtures__/cross-component-preview.d.ts +3 -0
- package/dist/jsx/__fixtures__/cross-component-preview.js +15 -0
- package/dist/jsx/jsx-dev-runtime.d.ts +15 -0
- package/dist/jsx/jsx-dev-runtime.js +11 -0
- package/dist/jsx/jsx-runtime.d.ts +48 -0
- package/dist/jsx/jsx-runtime.js +305 -0
- package/dist/markers.d.ts +217 -0
- package/dist/markers.js +67 -0
- package/dist/refs.d.ts +158 -0
- package/dist/refs.js +104 -0
- package/dist/renderer/RendererErrorBoundary.d.ts +26 -0
- package/dist/renderer/RendererErrorBoundary.js +30 -0
- package/dist/renderer/UdsRenderer.d.ts +80 -0
- package/dist/renderer/UdsRenderer.js +33 -0
- package/dist/renderer/assetRenderable.d.ts +13 -0
- package/dist/renderer/assetRenderable.js +13 -0
- package/dist/renderer/index.d.ts +14 -0
- package/dist/renderer/index.js +14 -0
- package/dist/renderer/makeRegistry.d.ts +34 -0
- package/dist/renderer/makeRegistry.js +52 -0
- package/dist/renderer/makeUdsRenderer.d.ts +29 -0
- package/dist/renderer/makeUdsRenderer.js +35 -0
- package/dist/renderer/primitives/FragmentRenderer.d.ts +12 -0
- package/dist/renderer/primitives/FragmentRenderer.js +11 -0
- package/dist/renderer/primitives/SlotRenderer.d.ts +21 -0
- package/dist/renderer/primitives/SlotRenderer.js +20 -0
- package/dist/renderer/wrapRegistry.d.ts +64 -0
- package/dist/renderer/wrapRegistry.js +39 -0
- package/dist/renderer/wrappers/component-slots.d.ts +41 -0
- package/dist/renderer/wrappers/component-slots.js +66 -0
- package/dist/renderer/wrappers/event-bridge.d.ts +20 -0
- package/dist/renderer/wrappers/event-bridge.js +78 -0
- package/dist/renderer/wrappers/hex-normalize.d.ts +14 -0
- package/dist/renderer/wrappers/hex-normalize.js +35 -0
- package/dist/renderer/wrappers/html-aliases.d.ts +24 -0
- package/dist/renderer/wrappers/html-aliases.js +62 -0
- package/dist/renderer/wrappers/inline-styles.d.ts +16 -0
- package/dist/renderer/wrappers/inline-styles.js +104 -0
- package/dist/renderer/wrappers/slot-resolution.d.ts +25 -0
- package/dist/renderer/wrappers/slot-resolution.js +68 -0
- package/dist/renderer/wrappers/void-elements.d.ts +23 -0
- package/dist/renderer/wrappers/void-elements.js +28 -0
- package/dist/spec/apply-forced-modifiers.d.ts +19 -0
- package/dist/spec/apply-forced-modifiers.js +49 -0
- package/dist/spec/asset-jsx.d.ts +48 -0
- package/dist/spec/asset-jsx.js +48 -0
- package/dist/spec/collapse-text-labels.d.ts +44 -0
- package/dist/spec/collapse-text-labels.js +107 -0
- package/dist/spec/empty-node-slots.d.ts +51 -0
- package/dist/spec/empty-node-slots.js +141 -0
- package/dist/spec/index.d.ts +10 -0
- package/dist/spec/index.js +10 -0
- package/dist/spec/jsxToSpec.d.ts +48 -0
- package/dist/spec/jsxToSpec.js +506 -0
- package/dist/spec/layer-props.d.ts +52 -0
- package/dist/spec/layer-props.js +149 -0
- package/dist/spec/preview-controls.d.ts +44 -0
- package/dist/spec/preview-controls.js +139 -0
- package/dist/spec/slot-refs.d.ts +39 -0
- package/dist/spec/slot-refs.js +56 -0
- package/dist/spec/specToJsx.d.ts +35 -0
- package/dist/spec/specToJsx.js +127 -0
- package/dist/token-override-rows.d.ts +66 -0
- package/dist/token-override-rows.js +223 -0
- package/dist/tokenValueType.d.ts +34 -0
- package/dist/tokenValueType.js +138 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/css-properties.d.ts +232 -0
- package/dist/types/css-properties.js +14 -0
- package/dist/types/css-property-keywords.d.ts +156 -0
- package/dist/types/css-property-keywords.js +616 -0
- package/dist/types/css-values.d.ts +63 -0
- package/dist/types/css-values.js +16 -0
- package/dist/types.d.ts +708 -0
- package/dist/types.js +12 -0
- package/dist/units.d.ts +14 -0
- package/dist/units.js +16 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +4 -0
- package/package.json +81 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { makeTokenRefToCss } from "../entity-utils.js";
|
|
2
|
+
import { isTokenRef } from "../refs.js";
|
|
3
|
+
import { isColorExpression, resolveColorExpression } from "../colorExpressions.js";
|
|
4
|
+
//#region src/spec/layer-props.ts
|
|
5
|
+
const REST_STATE = "rest";
|
|
6
|
+
const isStateKey = (key) => key.startsWith("_");
|
|
7
|
+
/**
|
|
8
|
+
* Resolve the styles a layer effectively has for a set of prop-axis values and
|
|
9
|
+
* (optionally) one forced interaction state, walking the authoring surfaces
|
|
10
|
+
* codegen reads:
|
|
11
|
+
*
|
|
12
|
+
* 1. `component.base[layer]` — the always-on baseline.
|
|
13
|
+
* 2. `component.props[axis].values[value][layer]` — per axis key in `propValues`.
|
|
14
|
+
* 3. `component.compoundProps[].layers[layer]` — for entries whose `when`
|
|
15
|
+
* is a subset of `propValues` (multi-axis overrides).
|
|
16
|
+
* 4. State overlay — when `state` names a modifier sub-object (`_hover`) on
|
|
17
|
+
* the merged surface, its props overlay the base so a static preview
|
|
18
|
+
* shows that state.
|
|
19
|
+
*
|
|
20
|
+
* `propValues` are plain axis names → values (no reserved keys). `state` is a
|
|
21
|
+
* single forced interaction modifier (`'_hover'`); pass it directly rather than
|
|
22
|
+
* smuggling it into `propValues`. **Modes are intentionally not a parameter** —
|
|
23
|
+
* mode visuals ride a scoped wrapper class (`.dark &`) and flip live, so they
|
|
24
|
+
* are never folded into the static base here.
|
|
25
|
+
*/
|
|
26
|
+
function resolveAuthoredLayerProps(component, layer, propValues, state) {
|
|
27
|
+
if (!component) return {};
|
|
28
|
+
const merged = { ...component.base?.[layer] ?? {} };
|
|
29
|
+
for (const [key, value] of Object.entries(propValues)) {
|
|
30
|
+
const binding = component.props?.[key];
|
|
31
|
+
if (!binding || typeof binding !== "object") continue;
|
|
32
|
+
const layerStyles = pickAxisLayers(binding, value)?.[layer];
|
|
33
|
+
if (layerStyles) mergeLayerStyles(merged, layerStyles);
|
|
34
|
+
}
|
|
35
|
+
if (component.compoundProps) for (const entry of component.compoundProps) {
|
|
36
|
+
if (!matchesContext(entry.when, propValues)) continue;
|
|
37
|
+
const layerStyles = entry.layers[layer];
|
|
38
|
+
if (layerStyles && typeof layerStyles === "object") mergeLayerStyles(merged, layerStyles);
|
|
39
|
+
}
|
|
40
|
+
if (state && state !== REST_STATE && isStateKey(state)) {
|
|
41
|
+
const stateStyles = merged[state];
|
|
42
|
+
if (stateStyles && typeof stateStyles === "object") Object.assign(merged, stateStyles);
|
|
43
|
+
}
|
|
44
|
+
return merged;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolve per-layer style bags for a set of prop values, mirroring the config's
|
|
48
|
+
* variant resolution. `buildRenderSpec` calls this once per variant combo to
|
|
49
|
+
* bake the bags onto the render spec.
|
|
50
|
+
*
|
|
51
|
+
* `state` is left `null` by the bake path — modifier blocks ride through as
|
|
52
|
+
* nested style props and forced state is applied per cell (the render-spec
|
|
53
|
+
* consumers fold the active modifier into props), so folding it into the base
|
|
54
|
+
* here would be redundant. (Derived callers like the layers panel also pass no
|
|
55
|
+
* state.) `cssVarPrefix` resolves color expressions to a `var(...)`-referencing
|
|
56
|
+
* arbitrary value.
|
|
57
|
+
*/
|
|
58
|
+
function resolveRenderedLayerProps(component, propValues, state, cssVarPrefix) {
|
|
59
|
+
if (!component) return {};
|
|
60
|
+
const layerStyles = {};
|
|
61
|
+
for (const layerName of component.layers.keys()) layerStyles[layerName] = finalizeLayerProps(resolveAuthoredLayerProps(component, layerName, propValues, state), state, cssVarPrefix);
|
|
62
|
+
return layerStyles;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Fold the active (selected) state's styles into the flat bag so the static
|
|
66
|
+
* preview shows that state without real interaction, KEEP modifier blocks as
|
|
67
|
+
* nested objects so the component renderer emits their classes (`_hover:bg-x`,
|
|
68
|
+
* `_stackedChildren:ms-[-6px]`) — structural modifiers (child combinators,
|
|
69
|
+
* attrs) apply and interaction states stay live — and bake color expressions
|
|
70
|
+
* into arbitrary style-prop values so they flow through the class system
|
|
71
|
+
* instead of an inline `style`.
|
|
72
|
+
*/
|
|
73
|
+
function finalizeLayerProps(styles, state, cssVarPrefix) {
|
|
74
|
+
const merged = { ...styles };
|
|
75
|
+
const stateStyles = state && isStateKey(state) ? styles[state] : void 0;
|
|
76
|
+
if (stateStyles && typeof stateStyles === "object") Object.assign(merged, stateStyles);
|
|
77
|
+
return bakeColorValues(merged, makeTokenRefToCss(cssVarPrefix));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Resolve every color expression / token ref in a layer's style map to an
|
|
81
|
+
* arbitrary `[…]` value, recursing into nested modifier blocks. A
|
|
82
|
+
* `color-mix(...)` / `var(--…)` isn't a pre-compiled token class, so the
|
|
83
|
+
* bracket form lets `getClassName` encode it into a class the same way it
|
|
84
|
+
* handles `[16px]` — keeping styling renderer-agnostic (no inline `style` whose
|
|
85
|
+
* `var(...)` the build would tree-shake).
|
|
86
|
+
*/
|
|
87
|
+
function bakeColorValues(styles, tokenRefToCss) {
|
|
88
|
+
const out = {};
|
|
89
|
+
for (const [key, value] of Object.entries(styles)) if (isColorExpression(value)) out[key] = `[${resolveColorExpression(value, tokenRefToCss)}]`;
|
|
90
|
+
else if (isTokenRef(value)) out[key] = `[${tokenRefToCss(value.ref)}]`;
|
|
91
|
+
else if (value !== null && typeof value === "object" && !Array.isArray(value)) out[key] = bakeColorValues(value, tokenRefToCss);
|
|
92
|
+
else out[key] = value;
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Layer `from` onto `into`, deep-merging interaction-state modifier blocks
|
|
97
|
+
* (`_hover`, `_disabled`, …) instead of replacing them. A variant's
|
|
98
|
+
* `_disabled: { borderColor }` must combine with `base`'s `_disabled: { opacity }`
|
|
99
|
+
* — a shallow `Object.assign` would drop the base opacity, so the
|
|
100
|
+
* secondary+disabled cell loses its dimming. Non-modifier props still
|
|
101
|
+
* shallow-replace (a later authoring surface wins that property outright).
|
|
102
|
+
*/
|
|
103
|
+
function mergeLayerStyles(into, from) {
|
|
104
|
+
for (const [key, value] of Object.entries(from)) {
|
|
105
|
+
const existing = into[key];
|
|
106
|
+
if (isStateKey(key) && isStyleMap(existing) && isStyleMap(value)) into[key] = {
|
|
107
|
+
...existing,
|
|
108
|
+
...value
|
|
109
|
+
};
|
|
110
|
+
else into[key] = value;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Pull the per-layer style map keyed by `value` off a `variant` / `boolean`
|
|
115
|
+
* marker. Booleans accept a real boolean or its string form.
|
|
116
|
+
*/
|
|
117
|
+
function pickAxisLayers(binding, value) {
|
|
118
|
+
if (binding.__kind === "variant") {
|
|
119
|
+
const values = binding.values;
|
|
120
|
+
if (!values) return void 0;
|
|
121
|
+
const entry = values[String(value)];
|
|
122
|
+
return isStyleMap(entry) ? entry : void 0;
|
|
123
|
+
}
|
|
124
|
+
if (binding.__kind === "boolean") {
|
|
125
|
+
const values = binding.values;
|
|
126
|
+
if (!values) return void 0;
|
|
127
|
+
const key = coerceBooleanKey(value);
|
|
128
|
+
if (key === void 0) return void 0;
|
|
129
|
+
const entry = values[key];
|
|
130
|
+
return isStyleMap(entry) ? entry : void 0;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function isStyleMap(value) {
|
|
134
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
135
|
+
}
|
|
136
|
+
function coerceBooleanKey(value) {
|
|
137
|
+
if (value === true || value === "true") return "true";
|
|
138
|
+
if (value === false || value === "false") return "false";
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* `when` is a subset of `context` when every key in `when` is present in
|
|
142
|
+
* `context` with the same stringified value.
|
|
143
|
+
*/
|
|
144
|
+
function matchesContext(when, context) {
|
|
145
|
+
for (const [k, v] of Object.entries(when)) if (String(context[k]) !== String(v)) return false;
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
//#endregion
|
|
149
|
+
export { resolveAuthoredLayerProps, resolveRenderedLayerProps };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Component } from "../Component.js";
|
|
2
|
+
import { Config } from "../Config.js";
|
|
3
|
+
|
|
4
|
+
//#region src/spec/preview-controls.d.ts
|
|
5
|
+
type PreviewControlKind = 'variant' | 'boolean' | 'string';
|
|
6
|
+
interface PreviewControl {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
readonly kind: PreviewControlKind;
|
|
9
|
+
readonly defaultValue: unknown;
|
|
10
|
+
/** Selectable values — set for `variant` controls (incl. composite/styleProp). */
|
|
11
|
+
readonly options?: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The selectable values for a prop, or `undefined` when it isn't option-backed.
|
|
15
|
+
* Resolves by the prop's declared kind: a `variant()`'s declared values, a
|
|
16
|
+
* `composite()`'s registered style keys (e.g. `textVariant` → its variant
|
|
17
|
+
* names), or a `styleProp()`'s token + keyword names. A name that isn't a
|
|
18
|
+
* declared prop falls back to a same-named registered style prop (the Box
|
|
19
|
+
* surface — e.g. `color`/`bg` set only via `preview.defaultProps`).
|
|
20
|
+
*/
|
|
21
|
+
declare function propOptions(config: Config, component: Component, name: string): readonly string[] | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Editable preview controls for a component:
|
|
24
|
+
*
|
|
25
|
+
* - `variant` → dropdown of declared values
|
|
26
|
+
* - `composite` → dropdown, options from the registered composite
|
|
27
|
+
* - `boolean` → toggle; `string` / `number` → text input
|
|
28
|
+
* - `slot` → text input when string-typed/seeded (`children: 'Badge'`); a
|
|
29
|
+
* ReactNode slot is omitted (its content is a keyed spec element, not a
|
|
30
|
+
* scalar prop — it can't be set from a value control)
|
|
31
|
+
* - `styleProp` / `function` → omitted from the main walk (the Box style
|
|
32
|
+
* surface would flood; event handlers aren't value-editable)
|
|
33
|
+
* - any scalar `preview.defaultProps` entry not already covered (e.g.
|
|
34
|
+
* `children` on a primitive) → text/toggle, or a dropdown when the prop is
|
|
35
|
+
* option-backed (e.g. a `color` styleProp default → token dropdown)
|
|
36
|
+
*
|
|
37
|
+
* `seed` is the rendered instance's current root props (the authored example /
|
|
38
|
+
* live override); a seed value wins over `defaultProps` for the shown default.
|
|
39
|
+
* Variant/slot stay unset when nothing seeds them so the row reads "Default".
|
|
40
|
+
* Sorted variant-first, then the rest, each group alphabetized.
|
|
41
|
+
*/
|
|
42
|
+
declare function derivePreviewControls(config: Config, component: Component, seed?: Record<string, unknown>): PreviewControl[];
|
|
43
|
+
//#endregion
|
|
44
|
+
export { PreviewControl, PreviewControlKind, derivePreviewControls, propOptions };
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
//#region src/spec/preview-controls.ts
|
|
2
|
+
/**
|
|
3
|
+
* The selectable values for a prop, or `undefined` when it isn't option-backed.
|
|
4
|
+
* Resolves by the prop's declared kind: a `variant()`'s declared values, a
|
|
5
|
+
* `composite()`'s registered style keys (e.g. `textVariant` → its variant
|
|
6
|
+
* names), or a `styleProp()`'s token + keyword names. A name that isn't a
|
|
7
|
+
* declared prop falls back to a same-named registered style prop (the Box
|
|
8
|
+
* surface — e.g. `color`/`bg` set only via `preview.defaultProps`).
|
|
9
|
+
*/
|
|
10
|
+
function propOptions(config, component, name) {
|
|
11
|
+
const info = component.derived.props.find((p) => p.name === name);
|
|
12
|
+
if (!info) return optionsForStyleProp(config, name);
|
|
13
|
+
switch (info.kind) {
|
|
14
|
+
case "variant": return info.values.map(String);
|
|
15
|
+
case "composite": {
|
|
16
|
+
const composite = config.compositeStyles.get(info.compositeName);
|
|
17
|
+
return composite ? [...composite.styles.keys()] : void 0;
|
|
18
|
+
}
|
|
19
|
+
case "styleProp": return optionsForStyleProp(config, info.stylePropName);
|
|
20
|
+
default: return;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function optionsForStyleProp(config, propName) {
|
|
24
|
+
const styleProp = config.styleProps.get(propName);
|
|
25
|
+
if (!styleProp) return void 0;
|
|
26
|
+
const values = [];
|
|
27
|
+
for (const token of styleProp.derived.tokens) values.push(token.name);
|
|
28
|
+
for (const key of styleProp.derived.keywords.keys()) if (typeof key === "string") values.push(key);
|
|
29
|
+
return values.length > 0 ? values : void 0;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Editable preview controls for a component:
|
|
33
|
+
*
|
|
34
|
+
* - `variant` → dropdown of declared values
|
|
35
|
+
* - `composite` → dropdown, options from the registered composite
|
|
36
|
+
* - `boolean` → toggle; `string` / `number` → text input
|
|
37
|
+
* - `slot` → text input when string-typed/seeded (`children: 'Badge'`); a
|
|
38
|
+
* ReactNode slot is omitted (its content is a keyed spec element, not a
|
|
39
|
+
* scalar prop — it can't be set from a value control)
|
|
40
|
+
* - `styleProp` / `function` → omitted from the main walk (the Box style
|
|
41
|
+
* surface would flood; event handlers aren't value-editable)
|
|
42
|
+
* - any scalar `preview.defaultProps` entry not already covered (e.g.
|
|
43
|
+
* `children` on a primitive) → text/toggle, or a dropdown when the prop is
|
|
44
|
+
* option-backed (e.g. a `color` styleProp default → token dropdown)
|
|
45
|
+
*
|
|
46
|
+
* `seed` is the rendered instance's current root props (the authored example /
|
|
47
|
+
* live override); a seed value wins over `defaultProps` for the shown default.
|
|
48
|
+
* Variant/slot stay unset when nothing seeds them so the row reads "Default".
|
|
49
|
+
* Sorted variant-first, then the rest, each group alphabetized.
|
|
50
|
+
*/
|
|
51
|
+
function derivePreviewControls(config, component, seed = {}) {
|
|
52
|
+
const controls = [];
|
|
53
|
+
const seen = /* @__PURE__ */ new Set();
|
|
54
|
+
const declaredDefaults = component.defaultProps ?? {};
|
|
55
|
+
for (const info of component.derived.props) {
|
|
56
|
+
const control = describeProp(config, component, info, declaredDefaults[info.name], seed[info.name]);
|
|
57
|
+
if (!control) continue;
|
|
58
|
+
controls.push(control);
|
|
59
|
+
seen.add(info.name);
|
|
60
|
+
}
|
|
61
|
+
const previewDefaults = component.preview?.defaultProps ?? {};
|
|
62
|
+
for (const [name, value] of Object.entries(previewDefaults)) {
|
|
63
|
+
if (seen.has(name)) continue;
|
|
64
|
+
const options = propOptions(config, component, name);
|
|
65
|
+
if (options && options.length > 0) {
|
|
66
|
+
controls.push({
|
|
67
|
+
name,
|
|
68
|
+
kind: "variant",
|
|
69
|
+
defaultValue: seed[name] ?? value,
|
|
70
|
+
options
|
|
71
|
+
});
|
|
72
|
+
seen.add(name);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const kind = scalarKind(value);
|
|
76
|
+
if (!kind) continue;
|
|
77
|
+
controls.push({
|
|
78
|
+
name,
|
|
79
|
+
kind,
|
|
80
|
+
defaultValue: seed[name] ?? value
|
|
81
|
+
});
|
|
82
|
+
seen.add(name);
|
|
83
|
+
}
|
|
84
|
+
return sortPreviewControls(controls);
|
|
85
|
+
}
|
|
86
|
+
function describeProp(config, component, info, declaredDefault, seed) {
|
|
87
|
+
switch (info.kind) {
|
|
88
|
+
case "variant": return {
|
|
89
|
+
name: info.name,
|
|
90
|
+
kind: "variant",
|
|
91
|
+
defaultValue: seed ?? declaredDefault ?? void 0,
|
|
92
|
+
options: info.values.map(String)
|
|
93
|
+
};
|
|
94
|
+
case "boolean": return {
|
|
95
|
+
name: info.name,
|
|
96
|
+
kind: "boolean",
|
|
97
|
+
defaultValue: seed ?? declaredDefault ?? false
|
|
98
|
+
};
|
|
99
|
+
case "string":
|
|
100
|
+
case "number": return {
|
|
101
|
+
name: info.name,
|
|
102
|
+
kind: "string",
|
|
103
|
+
defaultValue: seed ?? declaredDefault ?? ""
|
|
104
|
+
};
|
|
105
|
+
case "composite": {
|
|
106
|
+
const options = propOptions(config, component, info.name);
|
|
107
|
+
if (!options || options.length === 0) return null;
|
|
108
|
+
return {
|
|
109
|
+
name: info.name,
|
|
110
|
+
kind: "variant",
|
|
111
|
+
defaultValue: seed ?? declaredDefault ?? void 0,
|
|
112
|
+
options
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
case "slot": {
|
|
116
|
+
const value = seed ?? declaredDefault;
|
|
117
|
+
if (info.authoredType === "string" || typeof value === "string") return {
|
|
118
|
+
name: info.name,
|
|
119
|
+
kind: "string",
|
|
120
|
+
defaultValue: typeof value === "string" ? value : ""
|
|
121
|
+
};
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
default: return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function scalarKind(value) {
|
|
128
|
+
if (typeof value === "boolean") return "boolean";
|
|
129
|
+
if (typeof value === "string" || typeof value === "number") return "string";
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
function sortPreviewControls(controls) {
|
|
133
|
+
const byName = (a, b) => a.name.localeCompare(b.name);
|
|
134
|
+
const variants = controls.filter((c) => c.kind === "variant").sort(byName);
|
|
135
|
+
const rest = controls.filter((c) => c.kind !== "variant").sort(byName);
|
|
136
|
+
return [...variants, ...rest];
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
export { derivePreviewControls, propOptions };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//#region src/spec/slot-refs.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* `$slot` resolution utilities for json-render specs.
|
|
4
|
+
*
|
|
5
|
+
* UDS extends json-render's prop-value markers with a `$slot` reference
|
|
6
|
+
* that points to another element in the same Spec's `elements` map:
|
|
7
|
+
*
|
|
8
|
+
* { "$slot": "btn1" } // single ref
|
|
9
|
+
* { "$slot": ["btn1", "btn2"] } // multi-ref (e.g. Card.actions)
|
|
10
|
+
*
|
|
11
|
+
* Used for composition patterns where a slot prop on one component
|
|
12
|
+
* (Card.actions, Button.startIcon) receives whole sub-trees of other
|
|
13
|
+
* elements. The shared helpers here are consumed by:
|
|
14
|
+
*
|
|
15
|
+
* - `wrapSlotResolution` (renderer wrapper) — resolves $slot refs to
|
|
16
|
+
* rendered React children at paint time.
|
|
17
|
+
* - `prepareRenderSpec` (studio) — injects $slot-referenced keys
|
|
18
|
+
* into the parent's `children` array so json-render's `Renderer`
|
|
19
|
+
* traverses them (otherwise an unreferenced element in the
|
|
20
|
+
* `elements` map is never visited).
|
|
21
|
+
* - Inspection / debug surfaces (layers panel, dev code panel,
|
|
22
|
+
* control inference, RPC routes) that need to skip past $slot
|
|
23
|
+
* prop markers when inspecting "real" prop values.
|
|
24
|
+
*/
|
|
25
|
+
declare function isSlotRef(value: unknown): value is {
|
|
26
|
+
$slot: string | string[];
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Walk a Spec's `elements` map and return, for each parent element
|
|
30
|
+
* that has at least one `$slot`-prop reference, the set of element
|
|
31
|
+
* keys it references. Used by render-time and edit-time code that
|
|
32
|
+
* needs to know "which sibling elements does this parent pull in
|
|
33
|
+
* through slot props."
|
|
34
|
+
*/
|
|
35
|
+
declare function collectSlotRefs(elements: Record<string, {
|
|
36
|
+
props?: Record<string, unknown>;
|
|
37
|
+
} | undefined>): Map<string, Set<string>>;
|
|
38
|
+
//#endregion
|
|
39
|
+
export { collectSlotRefs, isSlotRef };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//#region src/spec/slot-refs.ts
|
|
2
|
+
/**
|
|
3
|
+
* `$slot` resolution utilities for json-render specs.
|
|
4
|
+
*
|
|
5
|
+
* UDS extends json-render's prop-value markers with a `$slot` reference
|
|
6
|
+
* that points to another element in the same Spec's `elements` map:
|
|
7
|
+
*
|
|
8
|
+
* { "$slot": "btn1" } // single ref
|
|
9
|
+
* { "$slot": ["btn1", "btn2"] } // multi-ref (e.g. Card.actions)
|
|
10
|
+
*
|
|
11
|
+
* Used for composition patterns where a slot prop on one component
|
|
12
|
+
* (Card.actions, Button.startIcon) receives whole sub-trees of other
|
|
13
|
+
* elements. The shared helpers here are consumed by:
|
|
14
|
+
*
|
|
15
|
+
* - `wrapSlotResolution` (renderer wrapper) — resolves $slot refs to
|
|
16
|
+
* rendered React children at paint time.
|
|
17
|
+
* - `prepareRenderSpec` (studio) — injects $slot-referenced keys
|
|
18
|
+
* into the parent's `children` array so json-render's `Renderer`
|
|
19
|
+
* traverses them (otherwise an unreferenced element in the
|
|
20
|
+
* `elements` map is never visited).
|
|
21
|
+
* - Inspection / debug surfaces (layers panel, dev code panel,
|
|
22
|
+
* control inference, RPC routes) that need to skip past $slot
|
|
23
|
+
* prop markers when inspecting "real" prop values.
|
|
24
|
+
*/
|
|
25
|
+
function isSlotRef(value) {
|
|
26
|
+
if (typeof value !== "object" || value === null) return false;
|
|
27
|
+
if (!("$slot" in value)) return false;
|
|
28
|
+
const inner = value.$slot;
|
|
29
|
+
return typeof inner === "string" || Array.isArray(inner);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Walk a Spec's `elements` map and return, for each parent element
|
|
33
|
+
* that has at least one `$slot`-prop reference, the set of element
|
|
34
|
+
* keys it references. Used by render-time and edit-time code that
|
|
35
|
+
* needs to know "which sibling elements does this parent pull in
|
|
36
|
+
* through slot props."
|
|
37
|
+
*/
|
|
38
|
+
function collectSlotRefs(elements) {
|
|
39
|
+
const parentToSlotKeys = /* @__PURE__ */ new Map();
|
|
40
|
+
for (const [key, el] of Object.entries(elements)) {
|
|
41
|
+
if (!el?.props) continue;
|
|
42
|
+
for (const value of Object.values(el.props)) {
|
|
43
|
+
if (!isSlotRef(value)) continue;
|
|
44
|
+
const refs = Array.isArray(value.$slot) ? value.$slot : [value.$slot];
|
|
45
|
+
let slotKeys = parentToSlotKeys.get(key);
|
|
46
|
+
if (!slotKeys) {
|
|
47
|
+
slotKeys = /* @__PURE__ */ new Set();
|
|
48
|
+
parentToSlotKeys.set(key, slotKeys);
|
|
49
|
+
}
|
|
50
|
+
for (const ref of refs) slotKeys.add(ref);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return parentToSlotKeys;
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { collectSlotRefs, isSlotRef };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { AssetComponentMap } from "./asset-jsx.js";
|
|
2
|
+
import { Spec } from "@json-render/core";
|
|
3
|
+
|
|
4
|
+
//#region src/spec/specToJsx.d.ts
|
|
5
|
+
interface SpecToJsxOptions {
|
|
6
|
+
/** Component import source (default: './uds/components') */
|
|
7
|
+
importSource?: string;
|
|
8
|
+
/**
|
|
9
|
+
* When true, skip the leading `import { ... } from '...';` line and
|
|
10
|
+
* return only the JSX expression in `jsx`. Callers that need to
|
|
11
|
+
* assemble bespoke import lines (e.g. per-component kebab-cased
|
|
12
|
+
* paths for downloadable file exports) can read the type names
|
|
13
|
+
* from the returned `imports` array.
|
|
14
|
+
*/
|
|
15
|
+
omitImports?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* `asset group slug → component tag used for JSX export`
|
|
18
|
+
* (e.g. `{ icons: 'Icon' }`).
|
|
19
|
+
*
|
|
20
|
+
* This is derived from `Config.assetGroups[*].component`. It does not affect
|
|
21
|
+
* asset registration or rendering; it only controls whether a stored asset node
|
|
22
|
+
* like `yahoo-os:asset:icons/Trophy` prints as `<Icon name="Trophy" />`
|
|
23
|
+
* instead of the fallback `<Trophy />`.
|
|
24
|
+
*/
|
|
25
|
+
assetComponents?: AssetComponentMap;
|
|
26
|
+
}
|
|
27
|
+
interface SpecToJsxResult {
|
|
28
|
+
/** Generated JSX source code */
|
|
29
|
+
jsx: string;
|
|
30
|
+
/** Component names used (for imports) */
|
|
31
|
+
imports: string[];
|
|
32
|
+
}
|
|
33
|
+
declare function specToJsx(spec: Spec, options?: SpecToJsxOptions): SpecToJsxResult;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { SpecToJsxOptions, SpecToJsxResult, specToJsx };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { isSlotRef } from "./slot-refs.js";
|
|
2
|
+
import { assetJsxForm } from "./asset-jsx.js";
|
|
3
|
+
import { collectUsedComponents, serializeProps } from "@json-render/codegen";
|
|
4
|
+
//#region src/spec/specToJsx.ts
|
|
5
|
+
function statePathToExpression(path) {
|
|
6
|
+
return `state${path.split("/").filter(Boolean).map((s) => `.${s}`).join("")}`;
|
|
7
|
+
}
|
|
8
|
+
function isDynamicValue(value) {
|
|
9
|
+
return typeof value === "object" && value !== null && "$state" in value && typeof value.$state === "string";
|
|
10
|
+
}
|
|
11
|
+
/** Collect all element keys referenced via $slot in a spec element's props */
|
|
12
|
+
function collectSlotKeys(props) {
|
|
13
|
+
const keys = /* @__PURE__ */ new Set();
|
|
14
|
+
for (const value of Object.values(props)) if (isSlotRef(value)) {
|
|
15
|
+
const refs = Array.isArray(value.$slot) ? value.$slot : [value.$slot];
|
|
16
|
+
for (const ref of refs) keys.add(ref);
|
|
17
|
+
}
|
|
18
|
+
return keys;
|
|
19
|
+
}
|
|
20
|
+
function resolveDynamicValue(value) {
|
|
21
|
+
if (isDynamicValue(value)) return statePathToExpression(value.$state);
|
|
22
|
+
return String(value);
|
|
23
|
+
}
|
|
24
|
+
function renderActionBinding(binding) {
|
|
25
|
+
if (Array.isArray(binding)) return `{() => { ${binding.map((b) => b.action).map((a) => `${a}()`).join("; ")} }}`;
|
|
26
|
+
return `{${binding.action}}`;
|
|
27
|
+
}
|
|
28
|
+
function eventNameToJsx(name) {
|
|
29
|
+
return `on${name.charAt(0).toUpperCase()}${name.slice(1)}`;
|
|
30
|
+
}
|
|
31
|
+
function renderElement(key, spec, depth, assetComponents) {
|
|
32
|
+
const element = spec.elements[key];
|
|
33
|
+
if (!element) return "";
|
|
34
|
+
const indent = " ".repeat(depth);
|
|
35
|
+
const { children: _children, ...restProps } = element.props ?? {};
|
|
36
|
+
const slotKeys = collectSlotKeys(restProps);
|
|
37
|
+
const slotAttrs = [];
|
|
38
|
+
const propsWithoutSlots = {};
|
|
39
|
+
for (const [propName, propValue] of Object.entries(restProps)) if (isSlotRef(propValue)) {
|
|
40
|
+
const refs = Array.isArray(propValue.$slot) ? propValue.$slot : [propValue.$slot];
|
|
41
|
+
if (refs.length === 1) {
|
|
42
|
+
const rendered = renderElement(refs[0], spec, 0, assetComponents).trim();
|
|
43
|
+
slotAttrs.push(`${propName}={${rendered}}`);
|
|
44
|
+
} else {
|
|
45
|
+
const rendered = refs.map((r) => renderElement(r, spec, 0, assetComponents).trim()).filter(Boolean);
|
|
46
|
+
slotAttrs.push(`${propName}={<>${rendered.join("")}</>}`);
|
|
47
|
+
}
|
|
48
|
+
} else propsWithoutSlots[propName] = propValue;
|
|
49
|
+
const assetForm = assetJsxForm(element.type, assetComponents);
|
|
50
|
+
const tag = assetForm?.tag ?? element.type;
|
|
51
|
+
const nameAttr = assetForm?.nameAttr ? `name="${assetForm.nameAttr}"` : "";
|
|
52
|
+
if (assetForm?.nameAttr) delete propsWithoutSlots.name;
|
|
53
|
+
const staticAttrs = Object.keys(propsWithoutSlots).length > 0 ? serializeProps(propsWithoutSlots) : "";
|
|
54
|
+
const eventAttrs = [];
|
|
55
|
+
if (element.on && typeof element.on === "object") {
|
|
56
|
+
const onRecord = element.on;
|
|
57
|
+
for (const [eventName, binding] of Object.entries(onRecord)) eventAttrs.push(`${eventNameToJsx(eventName)}=${renderActionBinding(binding)}`);
|
|
58
|
+
}
|
|
59
|
+
const dynamicAttrs = [];
|
|
60
|
+
for (const [propName, propValue] of Object.entries(propsWithoutSlots)) if (isDynamicValue(propValue)) dynamicAttrs.push(`${propName}={${resolveDynamicValue(propValue)}}`);
|
|
61
|
+
let propsStr;
|
|
62
|
+
if (dynamicAttrs.length > 0 || slotAttrs.length > 0) {
|
|
63
|
+
const staticOnly = {};
|
|
64
|
+
for (const [k, v] of Object.entries(propsWithoutSlots)) if (!isDynamicValue(v)) staticOnly[k] = v;
|
|
65
|
+
const parts = [
|
|
66
|
+
nameAttr,
|
|
67
|
+
Object.keys(staticOnly).length > 0 ? serializeProps(staticOnly) : "",
|
|
68
|
+
...dynamicAttrs,
|
|
69
|
+
...slotAttrs,
|
|
70
|
+
...eventAttrs
|
|
71
|
+
].filter(Boolean);
|
|
72
|
+
propsStr = parts.length > 0 ? ` ${parts.join(" ")}` : "";
|
|
73
|
+
} else {
|
|
74
|
+
const parts = [
|
|
75
|
+
nameAttr,
|
|
76
|
+
staticAttrs,
|
|
77
|
+
...eventAttrs
|
|
78
|
+
].filter(Boolean);
|
|
79
|
+
propsStr = parts.length > 0 ? ` ${parts.join(" ")}` : "";
|
|
80
|
+
}
|
|
81
|
+
const rawChildren = element.props?.children;
|
|
82
|
+
const textContent = typeof rawChildren === "string" ? rawChildren : null;
|
|
83
|
+
const dynamicChildren = isDynamicValue(rawChildren) ? `{${resolveDynamicValue(rawChildren)}}` : null;
|
|
84
|
+
const inlineContent = textContent ?? dynamicChildren;
|
|
85
|
+
const childKeys = (element.children ?? []).filter((k) => !slotKeys.has(k));
|
|
86
|
+
const hasChildren = childKeys.length > 0 || inlineContent !== null;
|
|
87
|
+
let jsxLine;
|
|
88
|
+
if (!hasChildren) jsxLine = `${indent}<${tag}${propsStr} />`;
|
|
89
|
+
else if (inlineContent !== null && childKeys.length === 0) jsxLine = `${indent}<${tag}${propsStr}>${inlineContent}</${tag}>`;
|
|
90
|
+
else {
|
|
91
|
+
const childLines = [];
|
|
92
|
+
if (inlineContent !== null) childLines.push(`${" ".repeat(depth + 1)}${inlineContent}`);
|
|
93
|
+
for (const childKey of childKeys) {
|
|
94
|
+
if (!spec.elements[childKey]) continue;
|
|
95
|
+
childLines.push(renderElement(childKey, spec, depth + 1, assetComponents));
|
|
96
|
+
}
|
|
97
|
+
jsxLine = `${indent}<${tag}${propsStr}>\n${childLines.join("\n")}\n${indent}</${tag}>`;
|
|
98
|
+
}
|
|
99
|
+
if (element.repeat && typeof element.repeat === "object") {
|
|
100
|
+
const repeat = element.repeat;
|
|
101
|
+
const stateExpr = statePathToExpression(repeat.statePath);
|
|
102
|
+
const keyProp = repeat.key ? `key={item.${repeat.key}}` : "key={index}";
|
|
103
|
+
jsxLine = `${indent}{${stateExpr}.map((item, index) => (\n${jsxLine.replace(new RegExp(`^${indent}`), `${indent} `)}\n${indent}))}`.replace(`<${tag}`, `<${tag} ${keyProp}`);
|
|
104
|
+
}
|
|
105
|
+
if (element.visible != null) jsxLine = `${indent}{${isDynamicValue(element.visible) ? resolveDynamicValue(element.visible) : String(element.visible)} && (\n${jsxLine.replace(new RegExp(`^${indent}`), `${indent} `)}\n${indent})}`;
|
|
106
|
+
return jsxLine;
|
|
107
|
+
}
|
|
108
|
+
function specToJsx(spec, options) {
|
|
109
|
+
const importSource = options?.importSource ?? "./uds/components";
|
|
110
|
+
if (!spec.elements[spec.root]) return {
|
|
111
|
+
jsx: "",
|
|
112
|
+
imports: []
|
|
113
|
+
};
|
|
114
|
+
const assetComponents = options?.assetComponents;
|
|
115
|
+
const sortedImports = [...new Set([...collectUsedComponents(spec)].map((type) => assetJsxForm(type, assetComponents)?.importName ?? type))].sort();
|
|
116
|
+
const jsxTree = renderElement(spec.root, spec, 0, assetComponents);
|
|
117
|
+
if (options?.omitImports) return {
|
|
118
|
+
jsx: jsxTree,
|
|
119
|
+
imports: sortedImports
|
|
120
|
+
};
|
|
121
|
+
return {
|
|
122
|
+
jsx: `${`import { ${sortedImports.join(", ")} } from '${importSource}';`}\n\n${jsxTree}`,
|
|
123
|
+
imports: sortedImports
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
export { specToJsx };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { TokenModifierKey } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/token-override-rows.d.ts
|
|
4
|
+
type OverrideModeRef = `${string}.${string}`;
|
|
5
|
+
type ModifierTreeValue<TValue> = TValue | {
|
|
6
|
+
value?: TValue;
|
|
7
|
+
[modifier: TokenModifierKey]: ModifierTreeValue<TValue> | undefined;
|
|
8
|
+
};
|
|
9
|
+
type ModifierTree<TValue> = Partial<Record<TokenModifierKey, ModifierTreeValue<TValue>>>;
|
|
10
|
+
type ModifierUpdateValue<TValue> = TValue | null | ModifierUpdateObject<TValue>;
|
|
11
|
+
interface ModifierUpdateObject<TValue> {
|
|
12
|
+
value?: TValue | null;
|
|
13
|
+
[key: string]: ModifierUpdateValue<TValue> | undefined;
|
|
14
|
+
[modifier: TokenModifierKey]: ModifierUpdateValue<TValue> | undefined;
|
|
15
|
+
}
|
|
16
|
+
type ModifierUpdate<TValue> = Record<string, ModifierUpdateValue<TValue> | undefined>;
|
|
17
|
+
interface ModeBackedModifierInput {
|
|
18
|
+
modifier: TokenModifierKey;
|
|
19
|
+
mode: string;
|
|
20
|
+
option: string;
|
|
21
|
+
label?: string;
|
|
22
|
+
modeOrder: number;
|
|
23
|
+
optionOrder: number;
|
|
24
|
+
}
|
|
25
|
+
interface ModeBackedModifierDefinition extends ModeBackedModifierInput {
|
|
26
|
+
modeRef: OverrideModeRef;
|
|
27
|
+
}
|
|
28
|
+
interface ModeBackedModifierLookup {
|
|
29
|
+
byModifier: ReadonlyMap<TokenModifierKey, ModeBackedModifierDefinition>;
|
|
30
|
+
byModeRef: ReadonlyMap<OverrideModeRef, ModeBackedModifierDefinition>;
|
|
31
|
+
}
|
|
32
|
+
interface OverrideRow<TValue = string> {
|
|
33
|
+
modes: OverrideModeRef[];
|
|
34
|
+
value: TValue;
|
|
35
|
+
sourceModifierPath: TokenModifierKey[];
|
|
36
|
+
sourceOrder: number;
|
|
37
|
+
}
|
|
38
|
+
interface ModeBackedModeMetadata {
|
|
39
|
+
name: string;
|
|
40
|
+
options: readonly {
|
|
41
|
+
name: string;
|
|
42
|
+
label?: string;
|
|
43
|
+
}[];
|
|
44
|
+
}
|
|
45
|
+
interface ModeBackedModifierMetadata {
|
|
46
|
+
modifier: TokenModifierKey;
|
|
47
|
+
selector?: unknown;
|
|
48
|
+
}
|
|
49
|
+
declare function createModeBackedModifierLookup(inputs: readonly ModeBackedModifierInput[]): ModeBackedModifierLookup;
|
|
50
|
+
declare function buildModeBackedModifierLookup(modes: readonly ModeBackedModeMetadata[], modifiers: readonly ModeBackedModifierMetadata[]): ModeBackedModifierLookup;
|
|
51
|
+
declare function modifierTreeToOverrideRows<TValue>(modifiers: ModifierTree<TValue> | undefined, lookup: ModeBackedModifierLookup): OverrideRow<TValue>[];
|
|
52
|
+
declare function overrideRowsToModifierTree<TValue>(rows: readonly OverrideRow<TValue>[], lookup: ModeBackedModifierLookup): ModifierTree<TValue>;
|
|
53
|
+
declare function resolveOverrideRowValue<TValue>(baseValue: TValue, rows: readonly Pick<OverrideRow<TValue>, 'modes' | 'value'>[], activeModes: Iterable<OverrideModeRef>): TValue;
|
|
54
|
+
declare function modifierPathToId(path: readonly TokenModifierKey[]): string;
|
|
55
|
+
declare function modifierIdToPath(id: string): TokenModifierKey[];
|
|
56
|
+
declare function buildNestedModifierUpdate<TValue>(modifierPath: readonly TokenModifierKey[], value: ModifierUpdateValue<TValue>): Record<TokenModifierKey, ModifierUpdateValue<TValue>>;
|
|
57
|
+
/**
|
|
58
|
+
* Combines multiple token-modifier update payloads into one patch
|
|
59
|
+
* object. Later updates win on scalar/null leaves; object branches
|
|
60
|
+
* deep-merge so swap operations can delete one nested row and set
|
|
61
|
+
* another nested row under the same parent modifier in a single
|
|
62
|
+
* patch.
|
|
63
|
+
*/
|
|
64
|
+
declare function mergeModifierUpdates<TValue>(...updates: readonly ModifierUpdate<TValue>[]): Record<string, ModifierUpdateValue<TValue>>;
|
|
65
|
+
//#endregion
|
|
66
|
+
export { ModeBackedModifierDefinition, ModeBackedModifierInput, ModeBackedModifierLookup, OverrideModeRef, OverrideRow, buildModeBackedModifierLookup, buildNestedModifierUpdate, createModeBackedModifierLookup, mergeModifierUpdates, modifierIdToPath, modifierPathToId, modifierTreeToOverrideRows, overrideRowsToModifierTree, resolveOverrideRowValue };
|