@yahoo/uds 3.156.2 → 3.157.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/dist/automated-config/dist/generated/autoVariants.cjs +9 -4
  2. package/dist/automated-config/dist/generated/autoVariants.d.cts +2 -1
  3. package/dist/automated-config/dist/generated/autoVariants.d.ts +2 -1
  4. package/dist/automated-config/dist/generated/autoVariants.js +9 -4
  5. package/dist/automated-config/dist/generated/generatedConfigs.cjs +3011 -3038
  6. package/dist/automated-config/dist/generated/generatedConfigs.d.cts +143 -140
  7. package/dist/automated-config/dist/generated/generatedConfigs.d.ts +143 -140
  8. package/dist/automated-config/dist/generated/generatedConfigs.js +3011 -3038
  9. package/dist/automated-config/dist/generated/universalTokensConfigAuto.cjs +1227 -501
  10. package/dist/automated-config/dist/generated/universalTokensConfigAuto.js +1227 -501
  11. package/dist/automated-config/dist/properties.cjs +1 -1
  12. package/dist/automated-config/dist/properties.d.cts +15 -0
  13. package/dist/automated-config/dist/properties.d.ts +15 -0
  14. package/dist/automated-config/dist/properties.js +1 -1
  15. package/dist/automated-config/dist/types/ComponentConfig.d.cts +77 -4
  16. package/dist/automated-config/dist/types/ComponentConfig.d.ts +77 -4
  17. package/dist/automated-config/dist/types/ConfigSchema.d.cts +14 -2
  18. package/dist/automated-config/dist/types/ConfigSchema.d.ts +14 -2
  19. package/dist/automated-config/dist/types/StateAxis.cjs +90 -0
  20. package/dist/automated-config/dist/types/StateAxis.d.cts +70 -0
  21. package/dist/automated-config/dist/types/StateAxis.d.ts +70 -0
  22. package/dist/automated-config/dist/types/StateAxis.js +84 -0
  23. package/dist/automated-config/dist/utils/buildConfigSchema.cjs +98 -82
  24. package/dist/automated-config/dist/utils/buildConfigSchema.d.cts +32 -10
  25. package/dist/automated-config/dist/utils/buildConfigSchema.d.ts +32 -10
  26. package/dist/automated-config/dist/utils/buildConfigSchema.js +99 -83
  27. package/dist/automated-config/dist/utils/canonicalizeStateKey.cjs +32 -0
  28. package/dist/automated-config/dist/utils/canonicalizeStateKey.d.cts +48 -0
  29. package/dist/automated-config/dist/utils/canonicalizeStateKey.d.ts +48 -0
  30. package/dist/automated-config/dist/utils/canonicalizeStateKey.js +31 -0
  31. package/dist/automated-config/dist/utils/getConfigComponentVariant.d.cts +8 -0
  32. package/dist/automated-config/dist/utils/getConfigComponentVariant.d.ts +8 -0
  33. package/dist/automated-config/dist/utils/getConfigVariantProperties.d.cts +3 -3
  34. package/dist/automated-config/dist/utils/getConfigVariantProperties.d.ts +3 -3
  35. package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.cjs +12 -5
  36. package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.d.cts +8 -1
  37. package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.d.ts +8 -1
  38. package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.js +12 -5
  39. package/dist/automated-config/dist/utils/index.cjs +407 -97
  40. package/dist/automated-config/dist/utils/index.d.cts +66 -16
  41. package/dist/automated-config/dist/utils/index.d.ts +66 -16
  42. package/dist/automated-config/dist/utils/index.js +408 -99
  43. package/dist/automated-config/dist/utils/pseudoStateSelectors.cjs +122 -0
  44. package/dist/automated-config/dist/utils/pseudoStateSelectors.d.cts +80 -0
  45. package/dist/automated-config/dist/utils/pseudoStateSelectors.d.ts +80 -0
  46. package/dist/automated-config/dist/utils/pseudoStateSelectors.js +120 -0
  47. package/dist/automated-config/dist/utils/resolvePropertyStates.cjs +131 -0
  48. package/dist/automated-config/dist/utils/resolvePropertyStates.d.cts +49 -0
  49. package/dist/automated-config/dist/utils/resolvePropertyStates.d.ts +49 -0
  50. package/dist/automated-config/dist/utils/resolvePropertyStates.js +130 -0
  51. package/dist/automated-config/dist/utils/resolveSlotByCascade.cjs +118 -0
  52. package/dist/automated-config/dist/utils/resolveSlotByCascade.d.cts +68 -0
  53. package/dist/automated-config/dist/utils/resolveSlotByCascade.d.ts +68 -0
  54. package/dist/automated-config/dist/utils/resolveSlotByCascade.js +117 -0
  55. package/dist/automated-config/dist/utils/variantConfigGuards.d.cts +13 -0
  56. package/dist/automated-config/dist/utils/variantConfigGuards.d.ts +13 -0
  57. package/dist/components/client/Input/Input.cjs +42 -6
  58. package/dist/components/client/Input/Input.d.cts +13 -0
  59. package/dist/components/client/Input/Input.d.ts +13 -0
  60. package/dist/components/client/Input/Input.js +42 -6
  61. package/dist/config/dist/index.cjs +221 -550
  62. package/dist/config/dist/index.js +221 -550
  63. package/dist/css/dist/commands/css.cjs +1 -0
  64. package/dist/css/dist/commands/css.helpers.cjs +6 -0
  65. package/dist/css/dist/commands/css.helpers.js +6 -0
  66. package/dist/css/dist/commands/css.js +1 -0
  67. package/dist/css/dist/css/generate.cjs +4 -2
  68. package/dist/css/dist/css/generate.d.cts +28 -0
  69. package/dist/css/dist/css/generate.d.ts +28 -0
  70. package/dist/css/dist/css/generate.helpers.cjs +5 -1
  71. package/dist/css/dist/css/generate.helpers.js +6 -2
  72. package/dist/css/dist/css/generate.js +4 -2
  73. package/dist/css/dist/css/postcss.cjs +81 -0
  74. package/dist/css/dist/css/postcss.helpers.cjs +60 -0
  75. package/dist/css/dist/css/postcss.helpers.js +59 -1
  76. package/dist/css/dist/css/postcss.js +82 -2
  77. package/dist/css/dist/css/runner.cjs +12 -2
  78. package/dist/css/dist/css/runner.js +12 -2
  79. package/dist/css/dist/css/theme.d.cts +6 -0
  80. package/dist/css/dist/css/theme.d.ts +6 -0
  81. package/dist/css/dist/packages/automated-config/dist/properties.cjs +1 -1
  82. package/dist/css/dist/packages/automated-config/dist/properties.js +1 -1
  83. package/dist/css/dist/packages/automated-config/dist/utils/index.d.cts +6 -0
  84. package/dist/css/dist/packages/automated-config/dist/utils/index.d.ts +6 -0
  85. package/dist/css/dist/packages/config/dist/index.cjs +221 -550
  86. package/dist/css/dist/packages/config/dist/index.js +221 -550
  87. package/dist/css/dist/utils/optimizeCSS.cjs +59 -0
  88. package/dist/css/dist/utils/optimizeCSS.js +59 -0
  89. package/dist/index.cjs +25 -0
  90. package/dist/index.d.cts +10 -3
  91. package/dist/index.d.ts +10 -3
  92. package/dist/index.js +9 -2
  93. package/dist/styles/styler.d.cts +12 -11
  94. package/dist/styles/styler.d.ts +12 -11
  95. package/dist/styles/variants.d.cts +9 -4
  96. package/dist/styles/variants.d.ts +9 -4
  97. package/dist/tailwind-internal/dist/packages/automated-config/dist/generated/generatedConfigs.cjs +3011 -3038
  98. package/dist/tailwind-internal/dist/packages/automated-config/dist/generated/generatedConfigs.js +3011 -3038
  99. package/dist/tailwind-internal/dist/packages/automated-config/dist/properties.cjs +1 -1
  100. package/dist/tailwind-internal/dist/packages/automated-config/dist/properties.js +1 -1
  101. package/dist/tailwind-internal/dist/packages/automated-config/dist/types/StateAxis.cjs +81 -0
  102. package/dist/tailwind-internal/dist/packages/automated-config/dist/types/StateAxis.js +76 -0
  103. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/canonicalizeStateKey.cjs +33 -0
  104. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/canonicalizeStateKey.js +32 -0
  105. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/componentStatePseudoStates.cjs +0 -7
  106. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/componentStatePseudoStates.js +1 -7
  107. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.cjs +354 -97
  108. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.d.cts +6 -0
  109. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.d.ts +6 -0
  110. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.js +355 -98
  111. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/pseudoStateSelectors.cjs +122 -0
  112. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/pseudoStateSelectors.js +121 -0
  113. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolvePropertyStates.cjs +132 -0
  114. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolvePropertyStates.js +131 -0
  115. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolveSlotByCascade.cjs +95 -0
  116. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolveSlotByCascade.js +95 -0
  117. package/dist/tailwind-internal/dist/packages/config/dist/index.cjs +221 -550
  118. package/dist/tailwind-internal/dist/packages/config/dist/index.js +221 -550
  119. package/dist/tailwind-internal/dist/plugins/components.cjs +28 -24
  120. package/dist/tailwind-internal/dist/plugins/components.js +28 -24
  121. package/dist/tailwind-internal/dist/utils/composeTailwindPlugins.d.cts +3 -0
  122. package/dist/tailwind-internal/dist/utils/composeTailwindPlugins.d.ts +3 -0
  123. package/dist/tailwind-internal/dist/utils/getShadowStyles.d.cts +2 -2
  124. package/dist/tailwind-internal/dist/utils/getShadowStyles.d.ts +2 -2
  125. package/dist/tokens/automation/index.cjs +25 -0
  126. package/dist/tokens/automation/index.d.cts +9 -2
  127. package/dist/tokens/automation/index.d.ts +9 -2
  128. package/dist/tokens/automation/index.js +9 -2
  129. package/dist/tokens/index.cjs +25 -0
  130. package/dist/tokens/index.d.cts +10 -3
  131. package/dist/tokens/index.d.ts +10 -3
  132. package/dist/tokens/index.js +9 -2
  133. package/dist/tokens/types.d.cts +1 -1
  134. package/dist/tokens/types.d.ts +1 -1
  135. package/dist/uds/generated/componentData.cjs +2202 -2200
  136. package/dist/uds/generated/componentData.js +2202 -2200
  137. package/dist/uds/generated/migrationSchemaVersion.cjs +1 -1
  138. package/dist/uds/generated/migrationSchemaVersion.js +1 -1
  139. package/dist/uds/generated/tailwindPurge.cjs +79 -78
  140. package/dist/uds/generated/tailwindPurge.js +79 -78
  141. package/generated/componentData.json +2720 -2718
  142. package/generated/migrationSchemaVersion.ts +1 -1
  143. package/generated/tailwindPurge.ts +2 -2
  144. package/package.json +1 -1
@@ -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 };
@@ -15,6 +15,10 @@ let lodash_isFunction_js = require("lodash/isFunction.js");
15
15
  lodash_isFunction_js = require_runtime.__toESM(lodash_isFunction_js);
16
16
  let motion_react = require("motion/react");
17
17
  //#region src/components/client/Input/Input.tsx
18
+ function setNativeInputValue(input, next) {
19
+ (Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set)?.call(input, next);
20
+ input.dispatchEvent(new Event("input", { bubbles: true }));
21
+ }
18
22
  const HelpTextContent = (0, react.memo)(function HelpTextContentOriginal({ helpText, helperTextIcon, spacingStart, spacingTop, size, isFilled, helperTextSlotProps, ...rest }) {
19
23
  if (!helpText) return null;
20
24
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Box.Box, {
@@ -82,7 +86,7 @@ const EndIcon = (0, react.memo)(function EndIcon({ icon, className, iconProps })
82
86
  *
83
87
  * @related [Checkbox](https://uds.build/docs/components/checkbox), [Radio](https://uds.build/docs/components/radio).
84
88
  **/
85
- const Input = (0, react.forwardRef)(function Input({ id, label, type = "text", size = "md", startIcon, endIcon, helpText, helperTextIcon, hasError, width: containerWidth = "full", defaultValue, value: valueProp, required, readOnly, disabled, reduceMotion: forceReduceMotion, onChange, slotProps, style, className, ...otherProps }, forwardedRef) {
89
+ const Input = (0, react.forwardRef)(function Input({ id, label, type = "text", size = "md", startIcon, endIcon, helpText, helperTextIcon, hasError, width: containerWidth = "full", defaultValue, value: valueProp, required, readOnly, disabled, reduceMotion: forceReduceMotion, onChange, onBlur, onFocus, formatOnBlur, parseOnFocus, slotProps, style, className, ...otherProps }, forwardedRef) {
86
90
  const generatedId = (0, react.useId)();
87
91
  const uid = id ?? `uds-input-${generatedId}`;
88
92
  const helpTextId = `uds-input-${uid}-help-text`;
@@ -98,7 +102,36 @@ const Input = (0, react.forwardRef)(function Input({ id, label, type = "text", s
98
102
  const handleChange = (0, react.useCallback)((e) => {
99
103
  if (!isControlled) setValue(e.target.value);
100
104
  onChange?.(e);
101
- }, [isControlled, onChange]);
105
+ slotProps?.input?.onChange?.(e);
106
+ }, [
107
+ isControlled,
108
+ onChange,
109
+ slotProps
110
+ ]);
111
+ const handleBlur = (0, react.useCallback)((e) => {
112
+ if (formatOnBlur && ref.current) {
113
+ const next = formatOnBlur(e.target.value);
114
+ if (next !== e.target.value) setNativeInputValue(ref.current, next);
115
+ }
116
+ onBlur?.(e);
117
+ slotProps?.input?.onBlur?.(e);
118
+ }, [
119
+ formatOnBlur,
120
+ onBlur,
121
+ slotProps
122
+ ]);
123
+ const handleFocus = (0, react.useCallback)((e) => {
124
+ if (parseOnFocus && ref.current) {
125
+ const next = parseOnFocus(e.target.value);
126
+ if (next !== e.target.value) setNativeInputValue(ref.current, next);
127
+ }
128
+ onFocus?.(e);
129
+ slotProps?.input?.onFocus?.(e);
130
+ }, [
131
+ parseOnFocus,
132
+ onFocus,
133
+ slotProps
134
+ ]);
102
135
  const layoutVariant = (0, motion_react.useReducedMotion)() ? "smooth" : "bouncy";
103
136
  const reduceMotion = forceReduceMotion ? "always" : "user";
104
137
  const isInteractive = !readOnly && !disabled;
@@ -110,7 +143,8 @@ const Input = (0, react.forwardRef)(function Input({ id, label, type = "text", s
110
143
  className: require_styles_styler.cx([className, disabled ? "opacity-50" : ""])
111
144
  }),
112
145
  inputWrapper: require_styles_styler.cx(require_styles_styler.getStyles({
113
- inputSizeInputWrapper: size,
146
+ inputSizeInputWrapperStatic: size,
147
+ inputSizeInputWrapperDynamic: size,
114
148
  inputVariantInputWrapper: "default",
115
149
  inputVariantValueInputWrapper: !value ? "empty" : "filled"
116
150
  }), "min-w-[200px]", inputWrapperClassName),
@@ -120,7 +154,7 @@ const Input = (0, react.forwardRef)(function Input({ id, label, type = "text", s
120
154
  inputVariantValueInput: !value ? "empty" : "filled",
121
155
  inputVariantInputPlaceholder: "default",
122
156
  inputVariantValueInputPlaceholder: !value ? "empty" : "filled",
123
- className: require_styles_styler.cx("grow", "uds-hit-target", "bg-clip-text", "focus:outline-none", isInteractive ? "cursor-text" : "cursor-not-allowed", inputClassName)
157
+ className: require_styles_styler.cx("grow", "uds-hit-target", "bg-transparent", "bg-clip-text", "focus-visible:outline-none", isInteractive ? "cursor-text" : "cursor-not-allowed", inputClassName)
124
158
  }),
125
159
  label: require_styles_styler.cx(require_styles_styler.getStyles({
126
160
  inputSizeLabel: size,
@@ -182,10 +216,12 @@ const Input = (0, react.forwardRef)(function Input({ id, label, type = "text", s
182
216
  disabled,
183
217
  "aria-describedby": helpTextId,
184
218
  "aria-invalid": hasError,
185
- onChange: handleChange,
186
219
  className: classNames.input,
187
220
  ...otherProps,
188
- ...inputProps
221
+ ...inputProps,
222
+ onChange: handleChange,
223
+ onBlur: handleBlur,
224
+ onFocus: handleFocus
189
225
  }),
190
226
  endIcon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_HStack.HStack, {
191
227
  alignItems: "center",
@@ -27,6 +27,19 @@ interface InputProps extends NativeInputProps, UniversalInputProps {
27
27
  helperText?: Partial<React.HTMLAttributes<HTMLSpanElement> & DataAttributes>;
28
28
  helperTextIcon?: Partial<IconPropsWithSVGProps & DataAttributes>;
29
29
  };
30
+ /** Called on user-initiated blur. Return the formatted display value.
31
+ * The result flows through `onChange`. Programmatic value changes (autofill,
32
+ * password managers, parent state updates) do NOT trigger this.
33
+ *
34
+ * In controlled mode (`value` prop supplied) the formatted value is surfaced
35
+ * only through `onChange` — the parent MUST fold it back into `value`, or it
36
+ * reverts on the next render. Uncontrolled usage handles this internally. */
37
+ formatOnBlur?: (value: string) => string;
38
+ /** Called on focus. Return the value to display while editing (typically the
39
+ * raw, unformatted form). Omit to leave the value as-is on focus.
40
+ * The result flows through `onChange` (same controlled-mode caveat as
41
+ * `formatOnBlur`: the parent must propagate the value when `value` is set). */
42
+ parseOnFocus?: (value: string) => string;
30
43
  }
31
44
  /**
32
45
  * **📦 An input that allows users to enter text and collect data.**