@yahoo/uds 3.156.2 → 3.157.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/automated-config/dist/generated/autoVariants.cjs +9 -4
- package/dist/automated-config/dist/generated/autoVariants.d.cts +2 -1
- package/dist/automated-config/dist/generated/autoVariants.d.ts +2 -1
- package/dist/automated-config/dist/generated/autoVariants.js +9 -4
- package/dist/automated-config/dist/generated/generatedConfigs.cjs +3078 -3089
- package/dist/automated-config/dist/generated/generatedConfigs.d.cts +158 -141
- package/dist/automated-config/dist/generated/generatedConfigs.d.ts +158 -141
- package/dist/automated-config/dist/generated/generatedConfigs.js +3078 -3089
- package/dist/automated-config/dist/generated/universalTokensConfigAuto.cjs +1217 -491
- package/dist/automated-config/dist/generated/universalTokensConfigAuto.d.cts +4 -1
- package/dist/automated-config/dist/generated/universalTokensConfigAuto.d.ts +4 -1
- package/dist/automated-config/dist/generated/universalTokensConfigAuto.js +1217 -491
- package/dist/automated-config/dist/properties.cjs +9 -2
- package/dist/automated-config/dist/properties.d.cts +23 -0
- package/dist/automated-config/dist/properties.d.ts +23 -0
- package/dist/automated-config/dist/properties.js +9 -2
- package/dist/automated-config/dist/types/ComponentConfig.d.cts +77 -4
- package/dist/automated-config/dist/types/ComponentConfig.d.ts +77 -4
- package/dist/automated-config/dist/types/ConfigSchema.d.cts +14 -2
- package/dist/automated-config/dist/types/ConfigSchema.d.ts +14 -2
- package/dist/automated-config/dist/types/StateAxis.cjs +90 -0
- package/dist/automated-config/dist/types/StateAxis.d.cts +70 -0
- package/dist/automated-config/dist/types/StateAxis.d.ts +70 -0
- package/dist/automated-config/dist/types/StateAxis.js +84 -0
- package/dist/automated-config/dist/utils/buildConfigSchema.cjs +105 -82
- package/dist/automated-config/dist/utils/buildConfigSchema.d.cts +41 -12
- package/dist/automated-config/dist/utils/buildConfigSchema.d.ts +41 -12
- package/dist/automated-config/dist/utils/buildConfigSchema.js +106 -83
- package/dist/automated-config/dist/utils/canonicalizeStateKey.cjs +32 -0
- package/dist/automated-config/dist/utils/canonicalizeStateKey.d.cts +48 -0
- package/dist/automated-config/dist/utils/canonicalizeStateKey.d.ts +48 -0
- package/dist/automated-config/dist/utils/canonicalizeStateKey.js +31 -0
- package/dist/automated-config/dist/utils/getButtonControlHeight.cjs +129 -0
- package/dist/automated-config/dist/utils/getButtonControlHeight.d.cts +133 -0
- package/dist/automated-config/dist/utils/getButtonControlHeight.d.ts +133 -0
- package/dist/automated-config/dist/utils/getButtonControlHeight.js +122 -0
- package/dist/automated-config/dist/utils/getButtonControlHeightPx.cjs +114 -0
- package/dist/automated-config/dist/utils/getButtonControlHeightPx.d.cts +78 -0
- package/dist/automated-config/dist/utils/getButtonControlHeightPx.d.ts +78 -0
- package/dist/automated-config/dist/utils/getButtonControlHeightPx.js +108 -0
- package/dist/automated-config/dist/utils/getConfigComponentVariant.d.cts +8 -0
- package/dist/automated-config/dist/utils/getConfigComponentVariant.d.ts +8 -0
- package/dist/automated-config/dist/utils/getConfigVariantProperties.d.cts +3 -3
- package/dist/automated-config/dist/utils/getConfigVariantProperties.d.ts +3 -3
- package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.cjs +12 -5
- package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.d.cts +8 -1
- package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.d.ts +8 -1
- package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.js +12 -5
- package/dist/automated-config/dist/utils/iconButtonMatchButtonHeight.cjs +110 -0
- package/dist/automated-config/dist/utils/iconButtonMatchButtonHeight.d.cts +19 -0
- package/dist/automated-config/dist/utils/iconButtonMatchButtonHeight.d.ts +19 -0
- package/dist/automated-config/dist/utils/iconButtonMatchButtonHeight.js +108 -0
- package/dist/automated-config/dist/utils/index.cjs +478 -111
- package/dist/automated-config/dist/utils/index.d.cts +76 -15
- package/dist/automated-config/dist/utils/index.d.ts +76 -15
- package/dist/automated-config/dist/utils/index.js +479 -113
- package/dist/automated-config/dist/utils/isConfiguratorPropertyVisible.cjs +15 -0
- package/dist/automated-config/dist/utils/isConfiguratorPropertyVisible.d.cts +20 -0
- package/dist/automated-config/dist/utils/isConfiguratorPropertyVisible.d.ts +20 -0
- package/dist/automated-config/dist/utils/isConfiguratorPropertyVisible.js +15 -0
- package/dist/automated-config/dist/utils/pseudoStateSelectors.cjs +122 -0
- package/dist/automated-config/dist/utils/pseudoStateSelectors.d.cts +80 -0
- package/dist/automated-config/dist/utils/pseudoStateSelectors.d.ts +80 -0
- package/dist/automated-config/dist/utils/pseudoStateSelectors.js +120 -0
- package/dist/automated-config/dist/utils/resolvePropertyStates.cjs +131 -0
- package/dist/automated-config/dist/utils/resolvePropertyStates.d.cts +49 -0
- package/dist/automated-config/dist/utils/resolvePropertyStates.d.ts +49 -0
- package/dist/automated-config/dist/utils/resolvePropertyStates.js +130 -0
- package/dist/automated-config/dist/utils/resolveSlotByCascade.cjs +118 -0
- package/dist/automated-config/dist/utils/resolveSlotByCascade.d.cts +68 -0
- package/dist/automated-config/dist/utils/resolveSlotByCascade.d.ts +68 -0
- package/dist/automated-config/dist/utils/resolveSlotByCascade.js +117 -0
- package/dist/automated-config/dist/utils/variantConfigGuards.d.cts +13 -0
- package/dist/automated-config/dist/utils/variantConfigGuards.d.ts +13 -0
- package/dist/components/client/Button.cjs +3 -3
- package/dist/components/client/Button.js +3 -3
- package/dist/components/client/Input/Input.cjs +42 -6
- package/dist/components/client/Input/Input.d.cts +13 -0
- package/dist/components/client/Input/Input.d.ts +13 -0
- package/dist/components/client/Input/Input.js +42 -6
- package/dist/components/client/Menu/Menu.ItemCheckbox.d.cts +1 -1
- package/dist/components/client/Menu/Menu.ItemCheckbox.d.ts +1 -1
- package/dist/config/dist/index.cjs +224 -553
- package/dist/config/dist/index.js +224 -553
- package/dist/css/dist/commands/css.cjs +1 -0
- package/dist/css/dist/commands/css.helpers.cjs +6 -0
- package/dist/css/dist/commands/css.helpers.js +6 -0
- package/dist/css/dist/commands/css.js +1 -0
- package/dist/css/dist/css/generate.cjs +4 -2
- package/dist/css/dist/css/generate.d.cts +28 -0
- package/dist/css/dist/css/generate.d.ts +28 -0
- package/dist/css/dist/css/generate.helpers.cjs +5 -1
- package/dist/css/dist/css/generate.helpers.js +6 -2
- package/dist/css/dist/css/generate.js +4 -2
- package/dist/css/dist/css/postcss.cjs +81 -0
- package/dist/css/dist/css/postcss.helpers.cjs +60 -0
- package/dist/css/dist/css/postcss.helpers.js +59 -1
- package/dist/css/dist/css/postcss.js +82 -2
- package/dist/css/dist/css/runner.cjs +12 -2
- package/dist/css/dist/css/runner.js +12 -2
- package/dist/css/dist/css/theme.d.cts +6 -0
- package/dist/css/dist/css/theme.d.ts +6 -0
- package/dist/css/dist/packages/automated-config/dist/properties.cjs +9 -2
- package/dist/css/dist/packages/automated-config/dist/properties.js +9 -2
- package/dist/css/dist/packages/automated-config/dist/utils/index.d.cts +6 -0
- package/dist/css/dist/packages/automated-config/dist/utils/index.d.ts +6 -0
- package/dist/css/dist/packages/config/dist/index.cjs +224 -553
- package/dist/css/dist/packages/config/dist/index.js +224 -553
- package/dist/css/dist/utils/optimizeCSS.cjs +59 -0
- package/dist/css/dist/utils/optimizeCSS.js +59 -0
- package/dist/css-tokens/dist/index.cjs +2 -0
- package/dist/css-tokens/dist/index.d.cts +2 -1
- package/dist/css-tokens/dist/index.d.ts +2 -1
- package/dist/css-tokens/dist/index.js +2 -1
- package/dist/index.cjs +48 -0
- package/dist/index.d.cts +15 -4
- package/dist/index.d.ts +15 -4
- package/dist/index.js +14 -3
- package/dist/styles/styler.d.cts +8 -7
- package/dist/styles/styler.d.ts +8 -7
- package/dist/styles/variants.d.cts +9 -4
- package/dist/styles/variants.d.ts +9 -4
- package/dist/tailwind-internal/dist/packages/automated-config/dist/generated/generatedConfigs.cjs +3078 -3089
- package/dist/tailwind-internal/dist/packages/automated-config/dist/generated/generatedConfigs.js +3078 -3089
- package/dist/tailwind-internal/dist/packages/automated-config/dist/properties.cjs +9 -2
- package/dist/tailwind-internal/dist/packages/automated-config/dist/properties.js +9 -2
- package/dist/tailwind-internal/dist/packages/automated-config/dist/types/StateAxis.cjs +81 -0
- package/dist/tailwind-internal/dist/packages/automated-config/dist/types/StateAxis.js +76 -0
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/canonicalizeStateKey.cjs +33 -0
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/canonicalizeStateKey.js +32 -0
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/componentStatePseudoStates.cjs +0 -7
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/componentStatePseudoStates.js +1 -7
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/getButtonControlHeight.cjs +126 -0
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/getButtonControlHeight.js +123 -0
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/iconButtonMatchButtonHeight.cjs +20 -0
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/iconButtonMatchButtonHeight.js +20 -0
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.cjs +422 -111
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.d.cts +6 -0
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.d.ts +6 -0
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.js +423 -112
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/pseudoStateSelectors.cjs +122 -0
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/pseudoStateSelectors.js +121 -0
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolvePropertyStates.cjs +132 -0
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolvePropertyStates.js +131 -0
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolveSlotByCascade.cjs +95 -0
- package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolveSlotByCascade.js +95 -0
- package/dist/tailwind-internal/dist/packages/config/dist/index.cjs +224 -553
- package/dist/tailwind-internal/dist/packages/config/dist/index.js +224 -553
- package/dist/tailwind-internal/dist/packages/css-tokens/dist/index.cjs +2 -0
- package/dist/tailwind-internal/dist/packages/css-tokens/dist/index.js +2 -1
- package/dist/tailwind-internal/dist/plugins/components.cjs +28 -24
- package/dist/tailwind-internal/dist/plugins/components.js +28 -24
- package/dist/tailwind-internal/dist/utils/composeTailwindPlugins.d.cts +3 -0
- package/dist/tailwind-internal/dist/utils/composeTailwindPlugins.d.ts +3 -0
- package/dist/tailwind-internal/dist/utils/getShadowStyles.d.cts +2 -2
- package/dist/tailwind-internal/dist/utils/getShadowStyles.d.ts +2 -2
- package/dist/tokens/automation/index.cjs +47 -0
- package/dist/tokens/automation/index.d.cts +13 -2
- package/dist/tokens/automation/index.d.ts +13 -2
- package/dist/tokens/automation/index.js +13 -2
- package/dist/tokens/consts/cssTokens.cjs +1 -0
- package/dist/tokens/consts/cssTokens.d.cts +2 -2
- package/dist/tokens/consts/cssTokens.d.ts +2 -2
- package/dist/tokens/consts/cssTokens.js +2 -2
- package/dist/tokens/index.cjs +48 -0
- package/dist/tokens/index.d.cts +15 -4
- package/dist/tokens/index.d.ts +15 -4
- package/dist/tokens/index.js +14 -3
- package/dist/tokens/types.d.cts +1 -1
- package/dist/tokens/types.d.ts +1 -1
- package/dist/uds/generated/componentData.cjs +2177 -2172
- package/dist/uds/generated/componentData.js +2177 -2172
- package/dist/uds/generated/migrationSchemaVersion.cjs +1 -1
- package/dist/uds/generated/migrationSchemaVersion.js +1 -1
- package/dist/uds/generated/tailwindPurge.cjs +79 -78
- package/dist/uds/generated/tailwindPurge.js +79 -78
- package/generated/componentData.json +2718 -2713
- package/generated/migrationSchemaVersion.ts +1 -1
- package/generated/tailwindPurge.ts +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
|
|
2
|
+
import { ConfigurablePropertiesName, SelectedConfigurableProperty } from "../properties.js";
|
|
3
|
+
import { LayerConfig, PossibleStatesWithRest } from "../types/ComponentConfig.js";
|
|
4
|
+
|
|
5
|
+
//#region ../automated-config/dist/utils/resolvePropertyStates.d.ts
|
|
6
|
+
//#region src/utils/resolvePropertyStates.d.ts
|
|
7
|
+
type AnyProperty = SelectedConfigurableProperty<ConfigurablePropertiesName, string>;
|
|
8
|
+
/**
|
|
9
|
+
* Yields every modifier subset of size 1..maxSize, preserving input order
|
|
10
|
+
* within each subset. Used by both the resolver and the docs-class
|
|
11
|
+
* enumeration so the depth they support stays in lock-step.
|
|
12
|
+
*
|
|
13
|
+
* Generic over the element type so callers passing `ModifierAtomic[]` get
|
|
14
|
+
* `Generator<readonly ModifierAtomic[]>` back — keeps the inferred type chain
|
|
15
|
+
* tight enough for downstream consumers to avoid widening to `string`.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Returns all canonical state keys a property reacts to, in cascade order:
|
|
19
|
+
*
|
|
20
|
+
* rest → atomics (sorted by global `STATE_PRIORITY`, weakest first)
|
|
21
|
+
* → 2-atom compounds → 3-atom compounds → ...
|
|
22
|
+
*
|
|
23
|
+
* Cross-rank ordering (atom < 2-atom compound < 3-atom compound < ...) is
|
|
24
|
+
* enforced by CSS class-count specificity. Each atom selector contributes
|
|
25
|
+
* (0,1,0), so a 2-atom compound at (0,3,0) strictly beats a 1-atom rule at
|
|
26
|
+
* (0,2,0) — the cascade-cancellation feature is encoded in the selector
|
|
27
|
+
* itself rather than rule source order.
|
|
28
|
+
*
|
|
29
|
+
* Intra-rank ties (which atom wins among the priority-sorted set, or which
|
|
30
|
+
* 2-atom compound wins among siblings of the same size) fall through to
|
|
31
|
+
* source order at the same specificity. Emission order follows the global
|
|
32
|
+
* `STATE_PRIORITY` array — to change which atom wins between any two
|
|
33
|
+
* simultaneously-matching atoms, reorder that array.
|
|
34
|
+
*
|
|
35
|
+
* Resolution sources, in order of precedence:
|
|
36
|
+
*
|
|
37
|
+
* 1. The new system: `layer.atomicStates` declares the atoms this layer can
|
|
38
|
+
* compose; `property.excludeAtomics` opts the property out of selected
|
|
39
|
+
* atoms. Compounds are auto-generated from the resulting set via
|
|
40
|
+
* modifier × interactive expansion.
|
|
41
|
+
* 2. The legacy system: `property.pseudoStates` (no compound generation; the
|
|
42
|
+
* array is used verbatim). Used when `layer.atomicStates` is absent so
|
|
43
|
+
* unmigrated component configs continue to behave exactly as before.
|
|
44
|
+
*
|
|
45
|
+
* `'rest'` is always included unless the property has `skipRestState: true`.
|
|
46
|
+
*/
|
|
47
|
+
declare function resolvePropertyStates(layer: LayerConfig, property: AnyProperty): PossibleStatesWithRest[]; //#endregion
|
|
48
|
+
//#endregion
|
|
49
|
+
export { resolvePropertyStates };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
|
|
2
|
+
import { comparePriority, isInteractiveAtomic, isModifierAtomic } from "../types/StateAxis.js";
|
|
3
|
+
import { canonicalizeStateKey } from "./canonicalizeStateKey.js";
|
|
4
|
+
//#region ../automated-config/dist/utils/resolvePropertyStates.js
|
|
5
|
+
/*! © 2026 Yahoo, Inc. UDS Default Config v0.0.0-development */
|
|
6
|
+
/**
|
|
7
|
+
* Yields every modifier subset of size 1..maxSize, preserving input order
|
|
8
|
+
* within each subset. Used by both the resolver and the docs-class
|
|
9
|
+
* enumeration so the depth they support stays in lock-step.
|
|
10
|
+
*
|
|
11
|
+
* Generic over the element type so callers passing `ModifierAtomic[]` get
|
|
12
|
+
* `Generator<readonly ModifierAtomic[]>` back — keeps the inferred type chain
|
|
13
|
+
* tight enough for downstream consumers to avoid widening to `string`.
|
|
14
|
+
*/
|
|
15
|
+
function* modifierSubsets(modifiers, maxSize) {
|
|
16
|
+
const indices = [];
|
|
17
|
+
function* recurse(start) {
|
|
18
|
+
if (indices.length > 0) yield indices.map((i) => modifiers[i]);
|
|
19
|
+
if (indices.length === maxSize) return;
|
|
20
|
+
for (let i = start; i < modifiers.length; i++) {
|
|
21
|
+
indices.push(i);
|
|
22
|
+
yield* recurse(i + 1);
|
|
23
|
+
indices.pop();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
yield* recurse(0);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Yields the interactive subsets that can be simultaneously active on one
|
|
30
|
+
* element, in `modifierSubsets` order. `hover` and `pressed` are mutually
|
|
31
|
+
* exclusive — the `hover` selector carries `:where(:not(:active …))`, so a
|
|
32
|
+
* `hover&pressed` rule could never match — so any subset containing BOTH is
|
|
33
|
+
* dropped. Every other subset is kept (notably `focus-within`/`focus-visible`
|
|
34
|
+
* CAN coexist with a pointer state: you can hover or press a focused element).
|
|
35
|
+
*
|
|
36
|
+
* `interactives` MUST already be sorted by `comparePriority`.
|
|
37
|
+
*/
|
|
38
|
+
function* coOccurringInteractiveSubsets(interactives) {
|
|
39
|
+
for (const subset of modifierSubsets(interactives, interactives.length)) {
|
|
40
|
+
if (subset.includes("hover") && subset.includes("pressed")) continue;
|
|
41
|
+
yield subset;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Yields a property's atomic + compound state keys in cascade order from a
|
|
46
|
+
* priority-sorted atomic set:
|
|
47
|
+
*
|
|
48
|
+
* atoms (in the given order)
|
|
49
|
+
* → for each modifier subset: the pure-modifier compound (size ≥ 2) then each
|
|
50
|
+
* modifier-subset × SINGLE-interactive compound
|
|
51
|
+
* → each pure interactive-pair compound (size ≥ 2, co-occurring)
|
|
52
|
+
* → each modifier-subset × interactive-PAIR compound
|
|
53
|
+
*
|
|
54
|
+
* The interactive-pair compounds are APPENDED after the original keys so every
|
|
55
|
+
* pre-existing key keeps its rank — `resolveSlotByCascade`'s tie-break and
|
|
56
|
+
* Figma's published values for already-declared states are therefore unchanged;
|
|
57
|
+
* only genuinely new co-occurring combinations (e.g. `focus-within&hover`) gain
|
|
58
|
+
* a key. Each is a real declared state with its own rule, so a property whose
|
|
59
|
+
* `focus-within` and `hover` resolve to different values renders the
|
|
60
|
+
* per-property-correct blend at `(0, 1+atomCount, 0)` specificity in BOTH emit
|
|
61
|
+
* modes — closing the only exhaustive/selective divergence (two interactives
|
|
62
|
+
* co-occurring with no compound to arbitrate them). `hover&pressed` pairs are
|
|
63
|
+
* never generated (they can't co-occur; see `coOccurringInteractiveSubsets`).
|
|
64
|
+
*
|
|
65
|
+
* `'rest'` is NOT included — callers prepend it (and decide whether to, e.g.
|
|
66
|
+
* `skipRestState`). `atomics` MUST already be sorted by `comparePriority`
|
|
67
|
+
* (weakest first) so the strongest atom emits last and wins on intra-rank
|
|
68
|
+
* source-order ties.
|
|
69
|
+
*
|
|
70
|
+
* Shared by `resolvePropertyStates` (the CSS-emission source of truth) and
|
|
71
|
+
* `resolveSlotByCascade`'s cascade-rank computation, so the emission order —
|
|
72
|
+
* and therefore the cascade tie-break it implies — stays identical across both.
|
|
73
|
+
* Change emission order here, not in either caller.
|
|
74
|
+
*/
|
|
75
|
+
function* atomicAndCompoundStateKeys(atomics) {
|
|
76
|
+
for (const atom of atomics) yield atom;
|
|
77
|
+
const modifiers = atomics.filter(isModifierAtomic);
|
|
78
|
+
const interactives = atomics.filter(isInteractiveAtomic);
|
|
79
|
+
for (const subset of modifierSubsets(modifiers, 3)) {
|
|
80
|
+
if (subset.length >= 2) yield canonicalizeStateKey(subset);
|
|
81
|
+
for (const interactive of interactives) yield canonicalizeStateKey([...subset, interactive]);
|
|
82
|
+
}
|
|
83
|
+
const interactivePairs = [...coOccurringInteractiveSubsets(interactives)].filter((subset) => subset.length >= 2);
|
|
84
|
+
for (const interactiveSubset of interactivePairs) yield canonicalizeStateKey(interactiveSubset);
|
|
85
|
+
for (const subset of modifierSubsets(modifiers, 3)) for (const interactiveSubset of interactivePairs) yield canonicalizeStateKey([...subset, ...interactiveSubset]);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Returns all canonical state keys a property reacts to, in cascade order:
|
|
89
|
+
*
|
|
90
|
+
* rest → atomics (sorted by global `STATE_PRIORITY`, weakest first)
|
|
91
|
+
* → 2-atom compounds → 3-atom compounds → ...
|
|
92
|
+
*
|
|
93
|
+
* Cross-rank ordering (atom < 2-atom compound < 3-atom compound < ...) is
|
|
94
|
+
* enforced by CSS class-count specificity. Each atom selector contributes
|
|
95
|
+
* (0,1,0), so a 2-atom compound at (0,3,0) strictly beats a 1-atom rule at
|
|
96
|
+
* (0,2,0) — the cascade-cancellation feature is encoded in the selector
|
|
97
|
+
* itself rather than rule source order.
|
|
98
|
+
*
|
|
99
|
+
* Intra-rank ties (which atom wins among the priority-sorted set, or which
|
|
100
|
+
* 2-atom compound wins among siblings of the same size) fall through to
|
|
101
|
+
* source order at the same specificity. Emission order follows the global
|
|
102
|
+
* `STATE_PRIORITY` array — to change which atom wins between any two
|
|
103
|
+
* simultaneously-matching atoms, reorder that array.
|
|
104
|
+
*
|
|
105
|
+
* Resolution sources, in order of precedence:
|
|
106
|
+
*
|
|
107
|
+
* 1. The new system: `layer.atomicStates` declares the atoms this layer can
|
|
108
|
+
* compose; `property.excludeAtomics` opts the property out of selected
|
|
109
|
+
* atoms. Compounds are auto-generated from the resulting set via
|
|
110
|
+
* modifier × interactive expansion.
|
|
111
|
+
* 2. The legacy system: `property.pseudoStates` (no compound generation; the
|
|
112
|
+
* array is used verbatim). Used when `layer.atomicStates` is absent so
|
|
113
|
+
* unmigrated component configs continue to behave exactly as before.
|
|
114
|
+
*
|
|
115
|
+
* `'rest'` is always included unless the property has `skipRestState: true`.
|
|
116
|
+
*/
|
|
117
|
+
function resolvePropertyStates(layer, property) {
|
|
118
|
+
const states = [];
|
|
119
|
+
if (!(property.skipRestState === true)) states.push("rest");
|
|
120
|
+
if (layer.atomicStates && layer.atomicStates.length > 0) {
|
|
121
|
+
const exclude = new Set(property.excludeAtomics ?? []);
|
|
122
|
+
const atomics = [...layer.atomicStates].filter((atom) => !exclude.has(atom)).sort(comparePriority);
|
|
123
|
+
for (const stateKey of atomicAndCompoundStateKeys(atomics)) states.push(stateKey);
|
|
124
|
+
return states;
|
|
125
|
+
}
|
|
126
|
+
if (property.pseudoStates) for (const s of property.pseudoStates) states.push(s);
|
|
127
|
+
return states;
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
export { atomicAndCompoundStateKeys, resolvePropertyStates };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
|
|
2
|
+
const require_StateAxis = require("../types/StateAxis.cjs");
|
|
3
|
+
const require_resolvePropertyStates = require("./resolvePropertyStates.cjs");
|
|
4
|
+
//#region ../automated-config/dist/utils/resolveSlotByCascade.js
|
|
5
|
+
/*! © 2026 Yahoo, Inc. UDS Default Config v0.0.0-development */
|
|
6
|
+
/**
|
|
7
|
+
* Mirrors `resolvePropertyStates`'s emission order using only the atoms that
|
|
8
|
+
* appear in the property's actual state map, so consumers that need a cascade
|
|
9
|
+
* tie-break can compare these indices (later = stronger). The atom + compound
|
|
10
|
+
* ordering comes from the shared `atomicAndCompoundStateKeys` generator, which
|
|
11
|
+
* keeps this rank identical to the CSS the generator emits.
|
|
12
|
+
*/
|
|
13
|
+
function computeCascadeOrder(propertyStates) {
|
|
14
|
+
const presentAtoms = /* @__PURE__ */ new Set();
|
|
15
|
+
for (const stateName of Object.keys(propertyStates)) {
|
|
16
|
+
if (stateName === "rest" || propertyStates[stateName] == null) continue;
|
|
17
|
+
for (const atom of stateName.split("&")) presentAtoms.add(atom);
|
|
18
|
+
}
|
|
19
|
+
const atomics = [...presentAtoms].filter(require_StateAxis.isAtomicState).sort(require_StateAxis.comparePriority);
|
|
20
|
+
const order = ["rest"];
|
|
21
|
+
for (const stateKey of require_resolvePropertyStates.atomicAndCompoundStateKeys(atomics)) order.push(stateKey);
|
|
22
|
+
return order;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Resolves a state slot following CSS-cascade semantics: a `rest` slot or a
|
|
26
|
+
* non-rest slot with `isEnabled === true` resolves to itself; an unflagged
|
|
27
|
+
* non-rest slot resolves to the most-specific enabled state whose atoms are a
|
|
28
|
+
* subset of the requested state's atoms. A present-but-disabled single MODIFIER
|
|
29
|
+
* atom is the exception: it participates at the `rest` value (never its own
|
|
30
|
+
* stored value), so a dropped higher-priority modifier (e.g. `invalid`, left
|
|
31
|
+
* flag-less because it equals `rest`) suppresses a lower-priority enabled atom
|
|
32
|
+
* inside a compound rather than letting it brighten through — an invalid field
|
|
33
|
+
* shouldn't get the hover color when `invalid` resets to `rest`. A disabled
|
|
34
|
+
* INTERACTIVE atom does NOT participate: it contributes no rule of its own, so
|
|
35
|
+
* an enabled lower-priority interactive survives (e.g. `focus-within` styling
|
|
36
|
+
* persists while `hover`/`pressed`, left at `rest`, is active). Disabled
|
|
37
|
+
* COMPOUNDS and absent atoms do not participate either.
|
|
38
|
+
*
|
|
39
|
+
* Same-size candidates are tie-broken by the cascade emission order from
|
|
40
|
+
* `resolvePropertyStates` — i.e. the rule that the browser would have applied
|
|
41
|
+
* last under equal specificity. The single rule is: later in emission order =
|
|
42
|
+
* stronger. At the 1-atom level that makes the higher-`STATE_PRIORITY` atom
|
|
43
|
+
* win, so modifiers (`invalid`, `readonly`) beat interactives (`hover`,
|
|
44
|
+
* `pressed`) because they sit later in `STATE_PRIORITY`. At the 2-atom level a
|
|
45
|
+
* pure-modifier compound from subset `[m1, m2]` beats any modifier+interactive
|
|
46
|
+
* compound built from an *earlier* single-modifier subset (e.g.
|
|
47
|
+
* `invalid&readonly` beats `invalid&hover`), since `modifierSubsets` emits the
|
|
48
|
+
* size-2 subset after the size-1 subsets that precede it; compounds from a
|
|
49
|
+
* *later* single-modifier subset (e.g. `readonly&hover`) still emit afterward
|
|
50
|
+
* and win. Falls back to `rest` when no enabled subset exists.
|
|
51
|
+
*
|
|
52
|
+
* Two consumers rely on this:
|
|
53
|
+
* - `generateDeclaration` (exhaustive emission for `atomicStates` layers) resolves
|
|
54
|
+
* the value for every declared state — including the many compounds the
|
|
55
|
+
* sparse stored config never persists — so each emitted rule carries the
|
|
56
|
+
* value the cascade would have produced, with no gap a nested foreign theme
|
|
57
|
+
* could fill.
|
|
58
|
+
* - The Figma plugin emits a single library variable value per (component,
|
|
59
|
+
* layer, property, state) matching that same CSS, so disabled slots don't
|
|
60
|
+
* show up as orphaned customizations in the Figma library.
|
|
61
|
+
*/
|
|
62
|
+
function resolveSlotByCascade(stateName, propertyStates) {
|
|
63
|
+
const direct = propertyStates[stateName];
|
|
64
|
+
if (stateName === "rest") return direct;
|
|
65
|
+
if (direct?.isEnabled === true) return direct;
|
|
66
|
+
const cascadeOrder = computeCascadeOrder(propertyStates);
|
|
67
|
+
const rankOf = /* @__PURE__ */ new Map();
|
|
68
|
+
cascadeOrder.forEach((s, i) => rankOf.set(s, i));
|
|
69
|
+
const targetAtoms = new Set(stateName.split("&"));
|
|
70
|
+
let best = null;
|
|
71
|
+
const restSlot = propertyStates.rest;
|
|
72
|
+
for (const [candidateKey, slot] of Object.entries(propertyStates)) {
|
|
73
|
+
if (!slot) continue;
|
|
74
|
+
if (candidateKey === stateName) continue;
|
|
75
|
+
const atoms = candidateKey === "rest" ? [] : candidateKey.split("&");
|
|
76
|
+
if (!atoms.every((a) => targetAtoms.has(a))) continue;
|
|
77
|
+
const enabled = candidateKey === "rest" || slot.isEnabled === true;
|
|
78
|
+
let effectiveSlot;
|
|
79
|
+
if (enabled) effectiveSlot = slot;
|
|
80
|
+
else if (atoms.length === 1 && require_StateAxis.isModifierAtomic(atoms[0])) effectiveSlot = restSlot;
|
|
81
|
+
else continue;
|
|
82
|
+
if (!effectiveSlot) continue;
|
|
83
|
+
const rank = rankOf.get(candidateKey) ?? -1;
|
|
84
|
+
const size = atoms.length;
|
|
85
|
+
if (!best || size > best.size || size === best.size && rank > best.rank) best = {
|
|
86
|
+
rank,
|
|
87
|
+
size,
|
|
88
|
+
slot: effectiveSlot
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return best?.slot ?? propertyStates.rest;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Whether a property's state map is on the new per-state `isEnabled` system.
|
|
95
|
+
*
|
|
96
|
+
* The schema build only writes an `isEnabled` flag for the new `atomicStates`
|
|
97
|
+
* path (and for `skipRestState` properties, where it's always `true`); legacy
|
|
98
|
+
* per-property `pseudoStates` slots never carry one. So the presence of
|
|
99
|
+
* `isEnabled` on any slot reliably distinguishes a migrated/new-system property
|
|
100
|
+
* from a legacy one.
|
|
101
|
+
*
|
|
102
|
+
* Callers that only have the flattened config data — notably the Figma plugin,
|
|
103
|
+
* which can't see the source `ComponentConfig`'s `atomicStates` — use this to
|
|
104
|
+
* decide whether to cascade-resolve a state (new system) or read its slot
|
|
105
|
+
* directly (legacy). Cascade resolution treats an unflagged non-rest slot as
|
|
106
|
+
* disabled and falls back to `rest`, which would wipe a legacy component's real
|
|
107
|
+
* hover/pressed customizations; legacy maps must therefore be read directly.
|
|
108
|
+
*/
|
|
109
|
+
function propertyUsesPerStateEnabled(propertyStates) {
|
|
110
|
+
for (const key of Object.keys(propertyStates)) {
|
|
111
|
+
const slot = propertyStates[key];
|
|
112
|
+
if (slot && "isEnabled" in slot) return true;
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
exports.propertyUsesPerStateEnabled = propertyUsesPerStateEnabled;
|
|
118
|
+
exports.resolveSlotByCascade = resolveSlotByCascade;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
|
|
2
|
+
//#region ../automated-config/dist/utils/resolveSlotByCascade.d.ts
|
|
3
|
+
//#region src/utils/resolveSlotByCascade.d.ts
|
|
4
|
+
type StateSlot = {
|
|
5
|
+
value: unknown;
|
|
6
|
+
isEnabled?: boolean;
|
|
7
|
+
type?: string;
|
|
8
|
+
valueType?: string;
|
|
9
|
+
};
|
|
10
|
+
type PropertyStates = Record<string, StateSlot | undefined>;
|
|
11
|
+
/**
|
|
12
|
+
* Resolves a state slot following CSS-cascade semantics: a `rest` slot or a
|
|
13
|
+
* non-rest slot with `isEnabled === true` resolves to itself; an unflagged
|
|
14
|
+
* non-rest slot resolves to the most-specific enabled state whose atoms are a
|
|
15
|
+
* subset of the requested state's atoms. A present-but-disabled single MODIFIER
|
|
16
|
+
* atom is the exception: it participates at the `rest` value (never its own
|
|
17
|
+
* stored value), so a dropped higher-priority modifier (e.g. `invalid`, left
|
|
18
|
+
* flag-less because it equals `rest`) suppresses a lower-priority enabled atom
|
|
19
|
+
* inside a compound rather than letting it brighten through — an invalid field
|
|
20
|
+
* shouldn't get the hover color when `invalid` resets to `rest`. A disabled
|
|
21
|
+
* INTERACTIVE atom does NOT participate: it contributes no rule of its own, so
|
|
22
|
+
* an enabled lower-priority interactive survives (e.g. `focus-within` styling
|
|
23
|
+
* persists while `hover`/`pressed`, left at `rest`, is active). Disabled
|
|
24
|
+
* COMPOUNDS and absent atoms do not participate either.
|
|
25
|
+
*
|
|
26
|
+
* Same-size candidates are tie-broken by the cascade emission order from
|
|
27
|
+
* `resolvePropertyStates` — i.e. the rule that the browser would have applied
|
|
28
|
+
* last under equal specificity. The single rule is: later in emission order =
|
|
29
|
+
* stronger. At the 1-atom level that makes the higher-`STATE_PRIORITY` atom
|
|
30
|
+
* win, so modifiers (`invalid`, `readonly`) beat interactives (`hover`,
|
|
31
|
+
* `pressed`) because they sit later in `STATE_PRIORITY`. At the 2-atom level a
|
|
32
|
+
* pure-modifier compound from subset `[m1, m2]` beats any modifier+interactive
|
|
33
|
+
* compound built from an *earlier* single-modifier subset (e.g.
|
|
34
|
+
* `invalid&readonly` beats `invalid&hover`), since `modifierSubsets` emits the
|
|
35
|
+
* size-2 subset after the size-1 subsets that precede it; compounds from a
|
|
36
|
+
* *later* single-modifier subset (e.g. `readonly&hover`) still emit afterward
|
|
37
|
+
* and win. Falls back to `rest` when no enabled subset exists.
|
|
38
|
+
*
|
|
39
|
+
* Two consumers rely on this:
|
|
40
|
+
* - `generateDeclaration` (exhaustive emission for `atomicStates` layers) resolves
|
|
41
|
+
* the value for every declared state — including the many compounds the
|
|
42
|
+
* sparse stored config never persists — so each emitted rule carries the
|
|
43
|
+
* value the cascade would have produced, with no gap a nested foreign theme
|
|
44
|
+
* could fill.
|
|
45
|
+
* - The Figma plugin emits a single library variable value per (component,
|
|
46
|
+
* layer, property, state) matching that same CSS, so disabled slots don't
|
|
47
|
+
* show up as orphaned customizations in the Figma library.
|
|
48
|
+
*/
|
|
49
|
+
declare function resolveSlotByCascade(stateName: string, propertyStates: PropertyStates): StateSlot | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Whether a property's state map is on the new per-state `isEnabled` system.
|
|
52
|
+
*
|
|
53
|
+
* The schema build only writes an `isEnabled` flag for the new `atomicStates`
|
|
54
|
+
* path (and for `skipRestState` properties, where it's always `true`); legacy
|
|
55
|
+
* per-property `pseudoStates` slots never carry one. So the presence of
|
|
56
|
+
* `isEnabled` on any slot reliably distinguishes a migrated/new-system property
|
|
57
|
+
* from a legacy one.
|
|
58
|
+
*
|
|
59
|
+
* Callers that only have the flattened config data — notably the Figma plugin,
|
|
60
|
+
* which can't see the source `ComponentConfig`'s `atomicStates` — use this to
|
|
61
|
+
* decide whether to cascade-resolve a state (new system) or read its slot
|
|
62
|
+
* directly (legacy). Cascade resolution treats an unflagged non-rest slot as
|
|
63
|
+
* disabled and falls back to `rest`, which would wipe a legacy component's real
|
|
64
|
+
* hover/pressed customizations; legacy maps must therefore be read directly.
|
|
65
|
+
*/
|
|
66
|
+
declare function propertyUsesPerStateEnabled(propertyStates: PropertyStates): boolean; //#endregion
|
|
67
|
+
//#endregion
|
|
68
|
+
export { type PropertyStates, type StateSlot, propertyUsesPerStateEnabled, resolveSlotByCascade };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
|
|
2
|
+
//#region ../automated-config/dist/utils/resolveSlotByCascade.d.ts
|
|
3
|
+
//#region src/utils/resolveSlotByCascade.d.ts
|
|
4
|
+
type StateSlot = {
|
|
5
|
+
value: unknown;
|
|
6
|
+
isEnabled?: boolean;
|
|
7
|
+
type?: string;
|
|
8
|
+
valueType?: string;
|
|
9
|
+
};
|
|
10
|
+
type PropertyStates = Record<string, StateSlot | undefined>;
|
|
11
|
+
/**
|
|
12
|
+
* Resolves a state slot following CSS-cascade semantics: a `rest` slot or a
|
|
13
|
+
* non-rest slot with `isEnabled === true` resolves to itself; an unflagged
|
|
14
|
+
* non-rest slot resolves to the most-specific enabled state whose atoms are a
|
|
15
|
+
* subset of the requested state's atoms. A present-but-disabled single MODIFIER
|
|
16
|
+
* atom is the exception: it participates at the `rest` value (never its own
|
|
17
|
+
* stored value), so a dropped higher-priority modifier (e.g. `invalid`, left
|
|
18
|
+
* flag-less because it equals `rest`) suppresses a lower-priority enabled atom
|
|
19
|
+
* inside a compound rather than letting it brighten through — an invalid field
|
|
20
|
+
* shouldn't get the hover color when `invalid` resets to `rest`. A disabled
|
|
21
|
+
* INTERACTIVE atom does NOT participate: it contributes no rule of its own, so
|
|
22
|
+
* an enabled lower-priority interactive survives (e.g. `focus-within` styling
|
|
23
|
+
* persists while `hover`/`pressed`, left at `rest`, is active). Disabled
|
|
24
|
+
* COMPOUNDS and absent atoms do not participate either.
|
|
25
|
+
*
|
|
26
|
+
* Same-size candidates are tie-broken by the cascade emission order from
|
|
27
|
+
* `resolvePropertyStates` — i.e. the rule that the browser would have applied
|
|
28
|
+
* last under equal specificity. The single rule is: later in emission order =
|
|
29
|
+
* stronger. At the 1-atom level that makes the higher-`STATE_PRIORITY` atom
|
|
30
|
+
* win, so modifiers (`invalid`, `readonly`) beat interactives (`hover`,
|
|
31
|
+
* `pressed`) because they sit later in `STATE_PRIORITY`. At the 2-atom level a
|
|
32
|
+
* pure-modifier compound from subset `[m1, m2]` beats any modifier+interactive
|
|
33
|
+
* compound built from an *earlier* single-modifier subset (e.g.
|
|
34
|
+
* `invalid&readonly` beats `invalid&hover`), since `modifierSubsets` emits the
|
|
35
|
+
* size-2 subset after the size-1 subsets that precede it; compounds from a
|
|
36
|
+
* *later* single-modifier subset (e.g. `readonly&hover`) still emit afterward
|
|
37
|
+
* and win. Falls back to `rest` when no enabled subset exists.
|
|
38
|
+
*
|
|
39
|
+
* Two consumers rely on this:
|
|
40
|
+
* - `generateDeclaration` (exhaustive emission for `atomicStates` layers) resolves
|
|
41
|
+
* the value for every declared state — including the many compounds the
|
|
42
|
+
* sparse stored config never persists — so each emitted rule carries the
|
|
43
|
+
* value the cascade would have produced, with no gap a nested foreign theme
|
|
44
|
+
* could fill.
|
|
45
|
+
* - The Figma plugin emits a single library variable value per (component,
|
|
46
|
+
* layer, property, state) matching that same CSS, so disabled slots don't
|
|
47
|
+
* show up as orphaned customizations in the Figma library.
|
|
48
|
+
*/
|
|
49
|
+
declare function resolveSlotByCascade(stateName: string, propertyStates: PropertyStates): StateSlot | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Whether a property's state map is on the new per-state `isEnabled` system.
|
|
52
|
+
*
|
|
53
|
+
* The schema build only writes an `isEnabled` flag for the new `atomicStates`
|
|
54
|
+
* path (and for `skipRestState` properties, where it's always `true`); legacy
|
|
55
|
+
* per-property `pseudoStates` slots never carry one. So the presence of
|
|
56
|
+
* `isEnabled` on any slot reliably distinguishes a migrated/new-system property
|
|
57
|
+
* from a legacy one.
|
|
58
|
+
*
|
|
59
|
+
* Callers that only have the flattened config data — notably the Figma plugin,
|
|
60
|
+
* which can't see the source `ComponentConfig`'s `atomicStates` — use this to
|
|
61
|
+
* decide whether to cascade-resolve a state (new system) or read its slot
|
|
62
|
+
* directly (legacy). Cascade resolution treats an unflagged non-rest slot as
|
|
63
|
+
* disabled and falls back to `rest`, which would wipe a legacy component's real
|
|
64
|
+
* hover/pressed customizations; legacy maps must therefore be read directly.
|
|
65
|
+
*/
|
|
66
|
+
declare function propertyUsesPerStateEnabled(propertyStates: PropertyStates): boolean; //#endregion
|
|
67
|
+
//#endregion
|
|
68
|
+
export { type PropertyStates, type StateSlot, propertyUsesPerStateEnabled, resolveSlotByCascade };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
|
|
2
|
+
import { comparePriority, isAtomicState, isModifierAtomic } from "../types/StateAxis.js";
|
|
3
|
+
import { atomicAndCompoundStateKeys } from "./resolvePropertyStates.js";
|
|
4
|
+
//#region ../automated-config/dist/utils/resolveSlotByCascade.js
|
|
5
|
+
/*! © 2026 Yahoo, Inc. UDS Default Config v0.0.0-development */
|
|
6
|
+
/**
|
|
7
|
+
* Mirrors `resolvePropertyStates`'s emission order using only the atoms that
|
|
8
|
+
* appear in the property's actual state map, so consumers that need a cascade
|
|
9
|
+
* tie-break can compare these indices (later = stronger). The atom + compound
|
|
10
|
+
* ordering comes from the shared `atomicAndCompoundStateKeys` generator, which
|
|
11
|
+
* keeps this rank identical to the CSS the generator emits.
|
|
12
|
+
*/
|
|
13
|
+
function computeCascadeOrder(propertyStates) {
|
|
14
|
+
const presentAtoms = /* @__PURE__ */ new Set();
|
|
15
|
+
for (const stateName of Object.keys(propertyStates)) {
|
|
16
|
+
if (stateName === "rest" || propertyStates[stateName] == null) continue;
|
|
17
|
+
for (const atom of stateName.split("&")) presentAtoms.add(atom);
|
|
18
|
+
}
|
|
19
|
+
const atomics = [...presentAtoms].filter(isAtomicState).sort(comparePriority);
|
|
20
|
+
const order = ["rest"];
|
|
21
|
+
for (const stateKey of atomicAndCompoundStateKeys(atomics)) order.push(stateKey);
|
|
22
|
+
return order;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Resolves a state slot following CSS-cascade semantics: a `rest` slot or a
|
|
26
|
+
* non-rest slot with `isEnabled === true` resolves to itself; an unflagged
|
|
27
|
+
* non-rest slot resolves to the most-specific enabled state whose atoms are a
|
|
28
|
+
* subset of the requested state's atoms. A present-but-disabled single MODIFIER
|
|
29
|
+
* atom is the exception: it participates at the `rest` value (never its own
|
|
30
|
+
* stored value), so a dropped higher-priority modifier (e.g. `invalid`, left
|
|
31
|
+
* flag-less because it equals `rest`) suppresses a lower-priority enabled atom
|
|
32
|
+
* inside a compound rather than letting it brighten through — an invalid field
|
|
33
|
+
* shouldn't get the hover color when `invalid` resets to `rest`. A disabled
|
|
34
|
+
* INTERACTIVE atom does NOT participate: it contributes no rule of its own, so
|
|
35
|
+
* an enabled lower-priority interactive survives (e.g. `focus-within` styling
|
|
36
|
+
* persists while `hover`/`pressed`, left at `rest`, is active). Disabled
|
|
37
|
+
* COMPOUNDS and absent atoms do not participate either.
|
|
38
|
+
*
|
|
39
|
+
* Same-size candidates are tie-broken by the cascade emission order from
|
|
40
|
+
* `resolvePropertyStates` — i.e. the rule that the browser would have applied
|
|
41
|
+
* last under equal specificity. The single rule is: later in emission order =
|
|
42
|
+
* stronger. At the 1-atom level that makes the higher-`STATE_PRIORITY` atom
|
|
43
|
+
* win, so modifiers (`invalid`, `readonly`) beat interactives (`hover`,
|
|
44
|
+
* `pressed`) because they sit later in `STATE_PRIORITY`. At the 2-atom level a
|
|
45
|
+
* pure-modifier compound from subset `[m1, m2]` beats any modifier+interactive
|
|
46
|
+
* compound built from an *earlier* single-modifier subset (e.g.
|
|
47
|
+
* `invalid&readonly` beats `invalid&hover`), since `modifierSubsets` emits the
|
|
48
|
+
* size-2 subset after the size-1 subsets that precede it; compounds from a
|
|
49
|
+
* *later* single-modifier subset (e.g. `readonly&hover`) still emit afterward
|
|
50
|
+
* and win. Falls back to `rest` when no enabled subset exists.
|
|
51
|
+
*
|
|
52
|
+
* Two consumers rely on this:
|
|
53
|
+
* - `generateDeclaration` (exhaustive emission for `atomicStates` layers) resolves
|
|
54
|
+
* the value for every declared state — including the many compounds the
|
|
55
|
+
* sparse stored config never persists — so each emitted rule carries the
|
|
56
|
+
* value the cascade would have produced, with no gap a nested foreign theme
|
|
57
|
+
* could fill.
|
|
58
|
+
* - The Figma plugin emits a single library variable value per (component,
|
|
59
|
+
* layer, property, state) matching that same CSS, so disabled slots don't
|
|
60
|
+
* show up as orphaned customizations in the Figma library.
|
|
61
|
+
*/
|
|
62
|
+
function resolveSlotByCascade(stateName, propertyStates) {
|
|
63
|
+
const direct = propertyStates[stateName];
|
|
64
|
+
if (stateName === "rest") return direct;
|
|
65
|
+
if (direct?.isEnabled === true) return direct;
|
|
66
|
+
const cascadeOrder = computeCascadeOrder(propertyStates);
|
|
67
|
+
const rankOf = /* @__PURE__ */ new Map();
|
|
68
|
+
cascadeOrder.forEach((s, i) => rankOf.set(s, i));
|
|
69
|
+
const targetAtoms = new Set(stateName.split("&"));
|
|
70
|
+
let best = null;
|
|
71
|
+
const restSlot = propertyStates.rest;
|
|
72
|
+
for (const [candidateKey, slot] of Object.entries(propertyStates)) {
|
|
73
|
+
if (!slot) continue;
|
|
74
|
+
if (candidateKey === stateName) continue;
|
|
75
|
+
const atoms = candidateKey === "rest" ? [] : candidateKey.split("&");
|
|
76
|
+
if (!atoms.every((a) => targetAtoms.has(a))) continue;
|
|
77
|
+
const enabled = candidateKey === "rest" || slot.isEnabled === true;
|
|
78
|
+
let effectiveSlot;
|
|
79
|
+
if (enabled) effectiveSlot = slot;
|
|
80
|
+
else if (atoms.length === 1 && isModifierAtomic(atoms[0])) effectiveSlot = restSlot;
|
|
81
|
+
else continue;
|
|
82
|
+
if (!effectiveSlot) continue;
|
|
83
|
+
const rank = rankOf.get(candidateKey) ?? -1;
|
|
84
|
+
const size = atoms.length;
|
|
85
|
+
if (!best || size > best.size || size === best.size && rank > best.rank) best = {
|
|
86
|
+
rank,
|
|
87
|
+
size,
|
|
88
|
+
slot: effectiveSlot
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return best?.slot ?? propertyStates.rest;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Whether a property's state map is on the new per-state `isEnabled` system.
|
|
95
|
+
*
|
|
96
|
+
* The schema build only writes an `isEnabled` flag for the new `atomicStates`
|
|
97
|
+
* path (and for `skipRestState` properties, where it's always `true`); legacy
|
|
98
|
+
* per-property `pseudoStates` slots never carry one. So the presence of
|
|
99
|
+
* `isEnabled` on any slot reliably distinguishes a migrated/new-system property
|
|
100
|
+
* from a legacy one.
|
|
101
|
+
*
|
|
102
|
+
* Callers that only have the flattened config data — notably the Figma plugin,
|
|
103
|
+
* which can't see the source `ComponentConfig`'s `atomicStates` — use this to
|
|
104
|
+
* decide whether to cascade-resolve a state (new system) or read its slot
|
|
105
|
+
* directly (legacy). Cascade resolution treats an unflagged non-rest slot as
|
|
106
|
+
* disabled and falls back to `rest`, which would wipe a legacy component's real
|
|
107
|
+
* hover/pressed customizations; legacy maps must therefore be read directly.
|
|
108
|
+
*/
|
|
109
|
+
function propertyUsesPerStateEnabled(propertyStates) {
|
|
110
|
+
for (const key of Object.keys(propertyStates)) {
|
|
111
|
+
const slot = propertyStates[key];
|
|
112
|
+
if (slot && "isEnabled" in slot) return true;
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { propertyUsesPerStateEnabled, resolveSlotByCascade };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
import { VariantConfig, VariantConfigWithComponentStates, VariantConfigWithProperties } from "../types/ComponentConfig.cjs";
|
|
3
|
+
|
|
4
|
+
//#region ../automated-config/dist/utils/variantConfigGuards.d.ts
|
|
5
|
+
//#region src/utils/variantConfigGuards.d.ts
|
|
6
|
+
declare const isVariantConfigWithProperties: (v: VariantConfig) => v is VariantConfigWithProperties & {
|
|
7
|
+
layers: NonNullable<VariantConfigWithProperties["layers"]>;
|
|
8
|
+
};
|
|
9
|
+
declare const isVariantConfigWithComponentStates: (v: VariantConfig) => v is VariantConfigWithComponentStates & {
|
|
10
|
+
componentStates: NonNullable<VariantConfigWithComponentStates["componentStates"]>;
|
|
11
|
+
}; //#endregion
|
|
12
|
+
//#endregion
|
|
13
|
+
export { isVariantConfigWithComponentStates, isVariantConfigWithProperties };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
import { VariantConfig, VariantConfigWithComponentStates, VariantConfigWithProperties } from "../types/ComponentConfig.js";
|
|
3
|
+
|
|
4
|
+
//#region ../automated-config/dist/utils/variantConfigGuards.d.ts
|
|
5
|
+
//#region src/utils/variantConfigGuards.d.ts
|
|
6
|
+
declare const isVariantConfigWithProperties: (v: VariantConfig) => v is VariantConfigWithProperties & {
|
|
7
|
+
layers: NonNullable<VariantConfigWithProperties["layers"]>;
|
|
8
|
+
};
|
|
9
|
+
declare const isVariantConfigWithComponentStates: (v: VariantConfig) => v is VariantConfigWithComponentStates & {
|
|
10
|
+
componentStates: NonNullable<VariantConfigWithComponentStates["componentStates"]>;
|
|
11
|
+
}; //#endregion
|
|
12
|
+
//#endregion
|
|
13
|
+
export { isVariantConfigWithComponentStates, isVariantConfigWithProperties };
|
|
@@ -146,7 +146,7 @@ const Button = (0, react.forwardRef)(function Button({ size, variant, startIcon,
|
|
|
146
146
|
className: require_styles_styler.cx([
|
|
147
147
|
"uds-ring",
|
|
148
148
|
"uds-hit-target",
|
|
149
|
-
"inline-flex overflow-hidden justify-center items-center whitespace-nowrap select-none",
|
|
149
|
+
"inline-flex max-w-full overflow-hidden justify-center items-center whitespace-nowrap select-none",
|
|
150
150
|
"[transform-origin:center]",
|
|
151
151
|
"[backface-visibility:hidden]",
|
|
152
152
|
"[transition-property:color,background-color,border-color,box-shadow,text-decoration-color,fill,stroke]",
|
|
@@ -161,14 +161,14 @@ const Button = (0, react.forwardRef)(function Button({ size, variant, startIcon,
|
|
|
161
161
|
className
|
|
162
162
|
])
|
|
163
163
|
}),
|
|
164
|
-
iconContainer: "flex overflow-clip uds-button-icon-container",
|
|
164
|
+
iconContainer: "flex shrink-0 overflow-clip uds-button-icon-container",
|
|
165
165
|
icon: require_styles_styler.getStyles({ className: require_styles_styler.cx([iconSizeClass, iconVariantClass]) }),
|
|
166
166
|
loadingIcon: require_styles_styler.getStyles({ className: require_styles_styler.cx([
|
|
167
167
|
"animate-spin",
|
|
168
168
|
iconSizeClass,
|
|
169
169
|
iconVariantClass
|
|
170
170
|
]) }),
|
|
171
|
-
content: require_styles_styler.cx(["truncate"])
|
|
171
|
+
content: require_styles_styler.cx(["min-w-0 truncate"])
|
|
172
172
|
}), [
|
|
173
173
|
className,
|
|
174
174
|
disableEffects,
|
|
@@ -143,7 +143,7 @@ const Button = forwardRef(function Button({ size, variant, startIcon, endIcon, i
|
|
|
143
143
|
className: cx([
|
|
144
144
|
"uds-ring",
|
|
145
145
|
"uds-hit-target",
|
|
146
|
-
"inline-flex overflow-hidden justify-center items-center whitespace-nowrap select-none",
|
|
146
|
+
"inline-flex max-w-full overflow-hidden justify-center items-center whitespace-nowrap select-none",
|
|
147
147
|
"[transform-origin:center]",
|
|
148
148
|
"[backface-visibility:hidden]",
|
|
149
149
|
"[transition-property:color,background-color,border-color,box-shadow,text-decoration-color,fill,stroke]",
|
|
@@ -158,14 +158,14 @@ const Button = forwardRef(function Button({ size, variant, startIcon, endIcon, i
|
|
|
158
158
|
className
|
|
159
159
|
])
|
|
160
160
|
}),
|
|
161
|
-
iconContainer: "flex overflow-clip uds-button-icon-container",
|
|
161
|
+
iconContainer: "flex shrink-0 overflow-clip uds-button-icon-container",
|
|
162
162
|
icon: getStyles({ className: cx([iconSizeClass, iconVariantClass]) }),
|
|
163
163
|
loadingIcon: getStyles({ className: cx([
|
|
164
164
|
"animate-spin",
|
|
165
165
|
iconSizeClass,
|
|
166
166
|
iconVariantClass
|
|
167
167
|
]) }),
|
|
168
|
-
content: cx(["truncate"])
|
|
168
|
+
content: cx(["min-w-0 truncate"])
|
|
169
169
|
}), [
|
|
170
170
|
className,
|
|
171
171
|
disableEffects,
|