@seekalfred/primitives 0.7.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/README.md +315 -4
  2. package/dist/alert/alert.d.ts +2 -2
  3. package/dist/alert/alert.js +1 -1
  4. package/dist/alert/alert.js.map +1 -1
  5. package/dist/alert-dialog/alert-dialog.d.ts +14 -0
  6. package/dist/alert-dialog/alert-dialog.js +52 -27
  7. package/dist/alert-dialog/alert-dialog.js.map +1 -1
  8. package/dist/announcement-bar/announcement-bar.d.ts +15 -4
  9. package/dist/announcement-bar/announcement-bar.js +31 -6
  10. package/dist/announcement-bar/announcement-bar.js.map +1 -1
  11. package/dist/avatar/avatar.d.ts +7 -0
  12. package/dist/avatar/avatar.js +33 -12
  13. package/dist/avatar/avatar.js.map +1 -1
  14. package/dist/badge/badge.d.ts +16 -1
  15. package/dist/badge/badge.js +12 -5
  16. package/dist/badge/badge.js.map +1 -1
  17. package/dist/bar-chart/bar-chart-series.d.ts +75 -0
  18. package/dist/bar-chart/bar-chart-series.js +401 -0
  19. package/dist/bar-chart/bar-chart-series.js.map +1 -0
  20. package/dist/bar-chart/bar-chart.d.ts +25 -1
  21. package/dist/bar-chart/bar-chart.js +26 -1
  22. package/dist/bar-chart/bar-chart.js.map +1 -1
  23. package/dist/bar-chart/index.d.ts +2 -1
  24. package/dist/button/button.d.ts +24 -2
  25. package/dist/button/button.js +62 -13
  26. package/dist/button/button.js.map +1 -1
  27. package/dist/card/card.js +1 -1
  28. package/dist/card/card.js.map +1 -1
  29. package/dist/checkbox/checkbox.d.ts +25 -1
  30. package/dist/checkbox/checkbox.js +103 -28
  31. package/dist/checkbox/checkbox.js.map +1 -1
  32. package/dist/checkbox/index.d.ts +2 -2
  33. package/dist/checkbox/index.js +2 -2
  34. package/dist/chip-group/chip-group.d.ts +11 -0
  35. package/dist/chip-group/chip-group.js +50 -34
  36. package/dist/chip-group/chip-group.js.map +1 -1
  37. package/dist/chip-group/index.d.ts +2 -2
  38. package/dist/code-block/code-block.d.ts +45 -0
  39. package/dist/code-block/code-block.js +74 -0
  40. package/dist/code-block/code-block.js.map +1 -0
  41. package/dist/code-block/index.d.ts +2 -0
  42. package/dist/code-block/index.js +2 -0
  43. package/dist/command-palette/command-palette.d.ts +128 -0
  44. package/dist/command-palette/command-palette.js +371 -0
  45. package/dist/command-palette/command-palette.js.map +1 -0
  46. package/dist/command-palette/index.d.ts +2 -0
  47. package/dist/command-palette/index.js +2 -0
  48. package/dist/copy-button/copy-button.d.ts +19 -3
  49. package/dist/copy-button/copy-button.js +7 -4
  50. package/dist/copy-button/copy-button.js.map +1 -1
  51. package/dist/dialog/dialog.js +1 -1
  52. package/dist/dialog/dialog.js.map +1 -1
  53. package/dist/empty-state/empty-state.d.ts +17 -0
  54. package/dist/empty-state/empty-state.js +9 -7
  55. package/dist/empty-state/empty-state.js.map +1 -1
  56. package/dist/file-drop-zone/file-drop-zone.d.ts +29 -3
  57. package/dist/file-drop-zone/file-drop-zone.js +26 -8
  58. package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
  59. package/dist/gauge/gauge.d.ts +18 -1
  60. package/dist/gauge/gauge.js +35 -21
  61. package/dist/gauge/gauge.js.map +1 -1
  62. package/dist/gauge/index.d.ts +2 -2
  63. package/dist/index.d.ts +14 -9
  64. package/dist/index.js +14 -10
  65. package/dist/info-tooltip/index.d.ts +2 -0
  66. package/dist/info-tooltip/index.js +2 -0
  67. package/dist/info-tooltip/info-tooltip.d.ts +35 -0
  68. package/dist/info-tooltip/info-tooltip.js +49 -0
  69. package/dist/info-tooltip/info-tooltip.js.map +1 -0
  70. package/dist/input/input.d.ts +7 -0
  71. package/dist/input/input.js +4 -2
  72. package/dist/input/input.js.map +1 -1
  73. package/dist/lib/field-shell.js +2 -6
  74. package/dist/lib/field-shell.js.map +1 -1
  75. package/dist/lib/required-marker.js +20 -0
  76. package/dist/lib/required-marker.js.map +1 -0
  77. package/dist/lib/selection-control.js +32 -1
  78. package/dist/lib/selection-control.js.map +1 -1
  79. package/dist/line-chart/index.d.ts +2 -2
  80. package/dist/line-chart/line-chart.d.ts +106 -3
  81. package/dist/line-chart/line-chart.js +185 -46
  82. package/dist/line-chart/line-chart.js.map +1 -1
  83. package/dist/mention-input/mention-input.js +1 -1
  84. package/dist/money-input/index.d.ts +2 -2
  85. package/dist/money-input/money-input.d.ts +14 -0
  86. package/dist/money-input/money-input.js +17 -7
  87. package/dist/money-input/money-input.js.map +1 -1
  88. package/dist/morph-select/morph-select.d.ts +8 -1
  89. package/dist/morph-select/morph-select.js +8 -5
  90. package/dist/morph-select/morph-select.js.map +1 -1
  91. package/dist/multi-select/multi-select.d.ts +31 -0
  92. package/dist/multi-select/multi-select.js +123 -6
  93. package/dist/multi-select/multi-select.js.map +1 -1
  94. package/dist/number-field/number-field.js +1 -1
  95. package/dist/password-field/password-field.js +1 -1
  96. package/dist/password-strength/password-strength.js +1 -1
  97. package/dist/phone-input/phone-input.js +1 -1
  98. package/dist/popover/popover.js +1 -1
  99. package/dist/popover/popover.js.map +1 -1
  100. package/dist/progress/index.d.ts +2 -2
  101. package/dist/progress/index.js +2 -2
  102. package/dist/progress/progress.d.ts +53 -2
  103. package/dist/progress/progress.js +146 -36
  104. package/dist/progress/progress.js.map +1 -1
  105. package/dist/radio-group/radio-group.d.ts +12 -3
  106. package/dist/radio-group/radio-group.js +62 -52
  107. package/dist/radio-group/radio-group.js.map +1 -1
  108. package/dist/rich-text-editor/rich-text-editor.js +1 -1
  109. package/dist/scroll-area/scroll-area.d.ts +1 -1
  110. package/dist/scrub-input/scrub-input.d.ts +11 -1
  111. package/dist/scrub-input/scrub-input.js +22 -10
  112. package/dist/scrub-input/scrub-input.js.map +1 -1
  113. package/dist/search-field/search-field.js +1 -1
  114. package/dist/select/select.d.ts +10 -1
  115. package/dist/select/select.js +4 -3
  116. package/dist/select/select.js.map +1 -1
  117. package/dist/separator/separator.d.ts +1 -1
  118. package/dist/shortcut-recorder/shortcut-recorder.js +1 -1
  119. package/dist/sparkline/sparkline.d.ts +27 -2
  120. package/dist/sparkline/sparkline.js +115 -18
  121. package/dist/sparkline/sparkline.js.map +1 -1
  122. package/dist/stat-card/index.d.ts +2 -2
  123. package/dist/stat-card/index.js +2 -2
  124. package/dist/stat-card/stat-card.d.ts +32 -1
  125. package/dist/stat-card/stat-card.js +52 -9
  126. package/dist/stat-card/stat-card.js.map +1 -1
  127. package/dist/styles.css +1 -1
  128. package/dist/table/index.d.ts +2 -2
  129. package/dist/table/index.js +2 -2
  130. package/dist/table/table.d.ts +211 -3
  131. package/dist/table/table.js +473 -93
  132. package/dist/table/table.js.map +1 -1
  133. package/dist/tabs/tabs.d.ts +4 -2
  134. package/dist/tabs/tabs.js +7 -5
  135. package/dist/tabs/tabs.js.map +1 -1
  136. package/dist/tag-input/tag-input.js +1 -1
  137. package/dist/text-shimmer/text-shimmer.d.ts +18 -2
  138. package/dist/text-shimmer/text-shimmer.js +5 -2
  139. package/dist/text-shimmer/text-shimmer.js.map +1 -1
  140. package/dist/textarea/textarea.d.ts +7 -0
  141. package/dist/textarea/textarea.js +4 -2
  142. package/dist/textarea/textarea.js.map +1 -1
  143. package/dist/toast/toast.d.ts +2 -2
  144. package/dist/toggle-button/index.d.ts +2 -0
  145. package/dist/toggle-button/index.js +2 -0
  146. package/dist/toggle-button/toggle-button.d.ts +93 -0
  147. package/dist/toggle-button/toggle-button.js +199 -0
  148. package/dist/toggle-button/toggle-button.js.map +1 -0
  149. package/package.json +19 -3
@@ -1 +1 @@
1
- {"version":3,"file":"money-input.js","names":["AriaGroup","AriaInput","AriaSelect","AriaButton","AriaSelectValue","AriaPopover","AriaListBox","Button"],"sources":["../../src/money-input/money-input.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useId,\n useImperativeHandle,\n useMemo,\n useReducer,\n useRef,\n useState,\n type ClipboardEvent,\n type FocusEvent,\n type KeyboardEvent,\n} from \"react\";\nimport {\n Button as AriaButton,\n Group as AriaGroup,\n Input as AriaInput,\n ListBox as AriaListBox,\n Popover as AriaPopover,\n Select as AriaSelect,\n SelectValue as AriaSelectValue,\n type Key,\n} from \"react-aria-components\";\n\nimport { Button } from \"../button/button.js\";\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { FieldShell, useFieldWiring } from \"../lib/field-shell.js\";\nimport { FieldMessage, RollingNumber } from \"../lib/field-message.js\";\nimport { fieldSurfaceVariants } from \"../lib/field-surface.js\";\nimport { useIsomorphicLayoutEffect, useMorphWidth } from \"../lib/motion.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\nimport { SelectItem, selectPopoverVariants } from \"../select/select.js\";\n\n/** What `onValueChange` hands over beside the minor-unit amount. */\nexport interface MoneyInputDetails {\n currency: string;\n /** The amount in major units, such as 12.5 for $12.50. */\n major: number | null;\n /** The amount as a full currency string, such as \"$12.50\". */\n formatted: string;\n}\n\n/**\n * MoneyInput: a currency amount. The value is in minor units, so $12.50 is 1250\n * and 500 yen is 500. Digits group as they are typed without moving the caret,\n * the cents that are still missing wait as muted ghosts so the amount never\n * changes width when it settles, a long amount scales down to fit instead of\n * scrolling, and the quick-add buttons and the arrow keys roll the digits. A\n * currency menu switches the currency (pass one code to hide it); the symbol\n * swaps in place and the digits are kept. Amounts and the currency come from\n * `Intl.NumberFormat`, so the symbol, its side, the grouping and the number of\n * decimals follow the currency and the locale.\n *\n * `min` is checked when the field loses focus; `max` is enforced as you type: a\n * digit that would pass it is refused and the amount strains toward it. Wording\n * for the range comes from the currency, and `error` replaces it.\n *\n * `isInvalid` and `errorMessage` are the pair Select and MultiSelect take: the\n * app's own validation marks the field invalid, and the message shows only while\n * it is (or while the amount is out of range, whose wording it then replaces).\n */\nexport interface MoneyInputProps {\n /** What the amount is, and the field's accessible name. */\n label: string;\n /** Render the label for screen readers only. */\n labelHidden?: boolean;\n /** Amount in minor units (cents for USD, yen for JPY). `null` is empty. */\n value?: number | null;\n defaultValue?: number | null;\n onValueChange?: (value: number | null, details: MoneyInputDetails) => void;\n /** ISO 4217 code, such as \"USD\". */\n currency?: string;\n defaultCurrency?: string;\n onCurrencyChange?: (currency: string) => void;\n /** Codes offered by the currency menu. Pass one code to hide the menu. */\n currencies?: string[];\n /** Lowest amount in minor units. Checked when the field loses focus. */\n min?: number;\n /** Highest amount in minor units. Typing past it is refused with a nudge. */\n max?: number;\n /** Quick-add buttons in major units. Pass an empty array to hide them. */\n quickAdd?: number[];\n /** Arrow keys move by this many minor units. Defaults to one major unit; Shift moves ten times as far. */\n step?: number;\n /** Formatting locale. Fixed by default so server and client render the same digits. */\n locale?: string;\n /** Helper text under the field. A string, because the copy is animated word by word. */\n description?: string;\n /** Replaces the built-in range message. Shown whenever it is set. */\n error?: string;\n /**\n * Marks the amount invalid: the destructive border and `aria-invalid`, with or\n * without a message. An amount outside `min`/`max` also becomes invalid on its\n * own, once the field loses focus. Without `errorMessage` no text is shown, so\n * the app must say what is wrong itself (WCAG 3.3.1).\n *\n * @default false\n */\n isInvalid?: boolean;\n /**\n * Why the amount is wrong, shown under the field while it is invalid\n * (`isInvalid`, or out of range, whose built-in wording it replaces) and tied\n * to the input as its error. A string, because the copy is animated word by\n * word: unlike Select and MultiSelect, whose `errorMessage` is a `ReactNode`.\n * `error`, when set, wins.\n */\n errorMessage?: string;\n disabled?: boolean;\n /** Adds a hidden input carrying the amount in minor units. */\n name?: string;\n id?: string;\n /** Extra classes for the wrapper. Merged last. */\n className?: string;\n onBlur?: (event: FocusEvent<HTMLInputElement>) => void;\n}\n\ntype Source = \"type\" | \"chip\" | \"key\" | \"external\";\ninterface CurrencyInfo {\n code: string;\n symbol: string;\n position: \"prefix\" | \"suffix\";\n digits: number;\n name: string;\n whole: Intl.NumberFormat;\n full: Intl.NumberFormat;\n}\n\nconst MAX_INTEGER_DIGITS = 12;\n/** How far, in px, the amount is pushed toward a limit it just met. */\nconst STRAIN_PX = 6;\n\nconst infoCache = new Map<string, CurrencyInfo>();\nfunction currencyInfo(code: string, locale: string): CurrencyInfo {\n const cacheKey = `${locale}:${code}`;\n const cached = infoCache.get(cacheKey);\n if (cached) return cached;\n const full = new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency: code,\n currencyDisplay: \"narrowSymbol\",\n numberingSystem: \"latn\",\n });\n const parts = full.formatToParts(1);\n const symbolAt = parts.findIndex((part) => part.type === \"currency\");\n const numberAt = parts.findIndex((part) => part.type === \"integer\");\n const digits = full.resolvedOptions().maximumFractionDigits ?? 2;\n let name = code;\n try {\n name = new Intl.DisplayNames([locale], { type: \"currency\" }).of(code) ?? code;\n } catch {\n // Older engines keep the code.\n }\n const info: CurrencyInfo = {\n code,\n symbol: parts[symbolAt]?.value ?? code,\n position: symbolAt < numberAt ? \"prefix\" : \"suffix\",\n digits,\n name: name.charAt(0).toUpperCase() + name.slice(1),\n full,\n whole: new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency: code,\n currencyDisplay: \"narrowSymbol\",\n maximumFractionDigits: 0,\n numberingSystem: \"latn\",\n }),\n };\n infoCache.set(cacheKey, info);\n return info;\n}\n\nfunction localeSymbols(locale: string) {\n const parts = new Intl.NumberFormat(locale, { numberingSystem: \"latn\" }).formatToParts(12345.6);\n return {\n group: parts.find((part) => part.type === \"group\")?.value ?? \",\",\n decimal: parts.find((part) => part.type === \"decimal\")?.value ?? \".\",\n };\n}\n\n/* A draft is the amount as typed in a canonical form: digits with an optional \".\", such as \"1234.5\". */\nconst pow10 = (digits: number) => 10 ** digits;\nfunction toMinor(draft: string, digits: number): number | null {\n if (!draft) return null;\n const [whole, fraction = \"\"] = draft.split(\".\");\n return (\n Number(whole || \"0\") * pow10(digits) + Number(fraction.padEnd(digits, \"0\").slice(0, digits) || \"0\")\n );\n}\nfunction fromMinor(minor: number | null | undefined, digits: number) {\n if (minor === null || minor === undefined || !Number.isFinite(minor)) return \"\";\n const text = String(Math.round(Math.abs(minor))).padStart(digits + 1, \"0\");\n return digits ? `${text.slice(0, text.length - digits)}.${text.slice(text.length - digits)}` : text;\n}\n/** Settles a draft the way it will be read back: fraction padded to the currency, leading zeros gone. */\nfunction settle(draft: string, digits: number) {\n return draft ? fromMinor(toMinor(draft, digits), digits) : \"\";\n}\n/** Carries an amount across currencies with different minor units, keeping the major amount. */\nfunction convertDraft(draft: string, digits: number) {\n if (!draft) return \"\";\n const [whole = \"\", fraction] = draft.split(\".\");\n if (!digits) return whole || \"0\";\n return fraction === undefined ? whole : `${whole}.${fraction.slice(0, digits)}`;\n}\n\nconst groupInteger = (whole: string, group: string) =>\n whole.replace(/\\B(?=(\\d{3})+(?!\\d))/g, group);\nfunction displayOf(draft: string, symbols: { group: string; decimal: string }) {\n if (!draft) return \"\";\n const [whole = \"\", fraction] = draft.split(\".\");\n return groupInteger(whole, symbols.group) + (fraction !== undefined ? symbols.decimal + fraction : \"\");\n}\n\nconst isSignificant = (char: string, decimal: string) => /\\d/.test(char) || char === decimal;\nfunction positionAfter(text: string, count: number, decimal: string) {\n if (count <= 0) return 0;\n let seen = 0;\n for (let index = 0; index < text.length; index++) {\n if (isSignificant(text[index]!, decimal) && ++seen === count) return index + 1;\n }\n return text.length;\n}\n\n/** The currency symbol swaps with a short rise while its slot springs to the new width. */\nfunction SymbolSlot({ symbol, side }: { symbol: string; side: \"prefix\" | \"suffix\" }) {\n const content = useRef<HTMLSpanElement>(null);\n useMorphWidth(content, symbol);\n return (\n <span\n aria-hidden=\"true\"\n data-slot=\"money-input-symbol\"\n className={cnAll(\"button-slot text-text-secondary\", side === \"prefix\" ? \"me-1\" : \"ms-1.5\")}\n >\n <span ref={content} className=\"inline-flex flex-none items-center whitespace-pre\">\n <SwapText swapKey={symbol} motion=\"rise\" className=\"inline-block\">\n {symbol}\n </SwapText>\n </span>\n </span>\n );\n}\n\n/** The amount the eye sees: whole part, separator and the cents, with the missing ones as muted ghosts. */\nfunction Amount({\n draft,\n digits,\n symbols,\n direction,\n still,\n}: {\n draft: string;\n digits: number;\n symbols: { group: string; decimal: string };\n direction: 1 | -1;\n still: boolean;\n}) {\n const [whole = \"\", fraction] = draft ? draft.split(\".\") : [];\n const typed = fraction ?? \"\";\n const ghostWhole = !whole;\n return (\n <>\n <span data-ghost={ghostWhole || undefined} className=\"inline-flex data-[ghost]:text-muted-foreground\">\n <RollingNumber\n word={groupInteger(whole || \"0\", symbols.group)}\n direction={direction}\n still={still}\n />\n </span>\n {digits > 0 ? (\n <>\n <span\n data-ghost={fraction === undefined || undefined}\n className=\"data-[ghost]:text-muted-foreground\"\n >\n {symbols.decimal}\n </span>\n {typed ? (\n <span className=\"inline-flex\">\n <RollingNumber word={typed} direction={direction} still={still} />\n </span>\n ) : null}\n {typed.length < digits ? (\n <span className=\"text-muted-foreground\">{\"0\".repeat(digits - typed.length)}</span>\n ) : null}\n </>\n ) : null}\n </>\n );\n}\n\nconst triggerClass =\n \"inline-flex h-9 flex-none cursor-pointer items-center gap-1 rounded-pill border-0 bg-transparent ps-3 pe-2 font-sans text-sm font-medium tabular-nums text-foreground outline-none motion-safe:transition-[background-color] motion-safe:duration-(--duration-quick) motion-safe:ease-settle data-[hovered]:bg-muted data-[pressed]:bg-muted data-[disabled]:cursor-not-allowed data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring)\";\n\nconst shellClass = cnAll(\n fieldSurfaceVariants(),\n \"flex h-19 items-center gap-2 rounded-panel ps-4.5 pe-2\",\n // Focus turns the box's own border dark whenever the amount holds focus; no separate ring.\n \"has-[input:focus]:not-data-[invalid]:border-foreground\",\n \"forced-colors:has-[input:focus]:outline-solid forced-colors:has-[input:focus]:outline-(length:--focus-ring-width) forced-colors:has-[input:focus]:outline-offset-(--focus-ring-offset)\",\n);\n\nexport const MoneyInput = forwardRef<HTMLInputElement, MoneyInputProps>(function MoneyInput(\n {\n label,\n labelHidden = false,\n value,\n defaultValue = null,\n onValueChange,\n currency: currencyProp,\n defaultCurrency = \"USD\",\n onCurrencyChange,\n currencies = [\"USD\", \"EUR\", \"GBP\", \"JPY\", \"CAD\", \"AUD\", \"CHF\", \"INR\"],\n min,\n max,\n quickAdd = [10, 50, 100],\n step: stepProp,\n locale = \"en-US\",\n description,\n error,\n isInvalid = false,\n errorMessage,\n disabled = false,\n name,\n id,\n className,\n onBlur,\n },\n forwardedRef,\n) {\n const generated = useId();\n const inputId = id ?? `${generated}-amount`;\n const rangeId = `${inputId}-range`;\n\n const [innerCurrency, setInnerCurrency] = useState(defaultCurrency);\n const code = currencyProp ?? innerCurrency;\n const info = useMemo(() => currencyInfo(code, locale), [code, locale]);\n const symbols = useMemo(() => localeSymbols(locale), [locale]);\n const step = stepProp && stepProp > 0 ? stepProp : pow10(info.digits);\n\n const [draft, setDraft] = useState(() => fromMinor(value !== undefined ? value : defaultValue, info.digits));\n const minor = toMinor(draft, info.digits);\n\n const [seenValue, setSeenValue] = useState(value);\n const [source, setSource] = useState<Source>(\"external\");\n const [direction, setDirection] = useState<1 | -1>(1);\n if (value !== seenValue) {\n setSeenValue(value);\n if (value !== undefined && value !== minor) {\n setDraft(fromMinor(value, info.digits));\n setSource(\"external\");\n setDirection((value ?? 0) >= (minor ?? 0) ? 1 : -1);\n }\n }\n\n const [focused, setFocused] = useState(false);\n const [touched, setTouched] = useState(false);\n const [flash, setFlash] = useState(false);\n const [announcement, setAnnouncement] = useState(\"\");\n const [, rerender] = useReducer((count: number) => count + 1, 0);\n\n const inputRef = useRef<HTMLInputElement>(null);\n useImperativeHandle(forwardedRef, () => inputRef.current as HTMLInputElement);\n const pendingCaret = useRef<number | null>(null);\n const latest = useRef({ minor, draft });\n useIsomorphicLayoutEffect(() => {\n latest.current = { minor, draft };\n });\n\n const money = useCallback(\n (amount: number | null) => (amount === null ? \"\" : info.full.format(amount / pow10(info.digits))),\n [info],\n );\n\n /* ---------------------------------------------- Limits and feedback ---------------------------------------------- */\n\n const groupRef = useRef<HTMLDivElement>(null);\n const flashTimer = useRef<number | undefined>(undefined);\n function refuse(toward: 1 | -1, limit: number) {\n const node = groupRef.current;\n if (node) {\n node.style.setProperty(\"--strain-x\", `${toward * STRAIN_PX}px`);\n // Restart the animation: the attribute has to leave and come back across a style recalculation.\n node.removeAttribute(\"data-strain\");\n void node.offsetWidth;\n node.setAttribute(\"data-strain\", \"\");\n }\n setFlash(true);\n window.clearTimeout(flashTimer.current);\n flashTimer.current = window.setTimeout(() => setFlash(false), 1400);\n setAnnouncement(`${toward > 0 ? \"Maximum\" : \"Minimum\"} is ${money(limit)}`);\n }\n\n function emit(nextMinor: number | null, nextInfo = info) {\n onValueChange?.(nextMinor, {\n currency: nextInfo.code,\n major: nextMinor === null ? null : nextMinor / pow10(nextInfo.digits),\n formatted:\n nextMinor === null ? \"\" : nextInfo.full.format(nextMinor / pow10(nextInfo.digits)),\n });\n }\n\n function setAmount(nextDraft: string, nextSource: Source) {\n const nextMinor = toMinor(nextDraft, info.digits);\n setDirection((nextMinor ?? 0) >= (latest.current.minor ?? 0) ? 1 : -1);\n setSource(nextSource);\n setDraft(nextDraft);\n latest.current = { minor: nextMinor, draft: nextDraft };\n if (nextMinor !== minor) emit(nextMinor);\n if (nextSource !== \"type\") setAnnouncement(money(nextMinor));\n }\n\n /* ---------------------------------------------- Typing ---------------------------------------------- */\n\n /** Reads what the input holds into a draft and counts the digits and decimal before the caret, so the caret can return to the same spot. */\n function sanitize(raw: string, caret: number) {\n let out = \"\";\n let before = 0;\n let dot = false;\n for (let index = 0; index < raw.length; index++) {\n const char = raw[index]!;\n let kept = \"\";\n if (/\\d/.test(char)) kept = char;\n else if (\n (char === symbols.decimal || (char === \".\" && symbols.group !== \".\")) &&\n info.digits > 0 &&\n !dot\n ) {\n kept = \".\";\n dot = true;\n }\n if (!kept) continue;\n out += kept;\n if (index < caret) before++;\n }\n const pieces = out.split(\".\");\n let whole = pieces[0]!;\n const fraction = pieces[1];\n const stripped = whole.length - whole.replace(/^0+/, \"\").length;\n whole = whole.replace(/^0+/, \"\");\n let shift = -Math.min(stripped, before);\n if (fraction !== undefined && !whole) {\n whole = \"0\";\n shift += 1;\n }\n if (fraction === undefined && !whole && stripped) {\n whole = \"0\";\n shift += 1;\n }\n if (fraction !== undefined && fraction.length > info.digits) return null;\n if (whole.length > MAX_INTEGER_DIGITS) return null;\n return {\n draft: fraction !== undefined ? `${whole}.${fraction}` : whole,\n before: Math.max(0, before + shift),\n };\n }\n\n function applyRaw(raw: string, caret: number) {\n const result = sanitize(raw, caret);\n // A refused keystroke keeps the caret where it was: one significant character before where the browser left it.\n if (!result) {\n pendingCaret.current = Math.max(\n 0,\n [...raw.slice(0, caret)].filter((char) => isSignificant(char, symbols.decimal) || char === \".\")\n .length - 1,\n );\n rerender();\n return;\n }\n const nextMinor = toMinor(result.draft, info.digits);\n if (max !== undefined && nextMinor !== null && nextMinor > max) {\n pendingCaret.current = Math.max(0, result.before - 1);\n refuse(1, max);\n rerender();\n return;\n }\n pendingCaret.current = result.before;\n setAmount(result.draft, \"type\");\n }\n\n useIsomorphicLayoutEffect(() => {\n const input = inputRef.current;\n const count = pendingCaret.current;\n pendingCaret.current = null;\n if (!input || count === null || document.activeElement !== input) return;\n const position = positionAfter(input.value, count, symbols.decimal);\n input.setSelectionRange(position, position);\n });\n\n function nudge(amount: number, nextSource: Source) {\n const from = latest.current.minor ?? 0;\n const floor = min ?? 0;\n const ceiling = max ?? Number.MAX_SAFE_INTEGER;\n let next = from + amount;\n if (next > ceiling) {\n if (from >= ceiling) {\n refuse(1, ceiling);\n return;\n }\n next = ceiling;\n refuse(1, ceiling);\n }\n if (next < floor) {\n if (from <= floor && latest.current.minor !== null) {\n refuse(-1, floor);\n return;\n }\n next = floor;\n }\n pendingCaret.current = Number.MAX_SAFE_INTEGER;\n setAmount(fromMinor(next, info.digits), nextSource);\n }\n\n function onKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n const input = event.currentTarget;\n const moves: Record<string, number> = { ArrowUp: 1, ArrowDown: -1, PageUp: 10, PageDown: -10 };\n if (event.key in moves) {\n event.preventDefault();\n const unit = moves[event.key]!;\n nudge(unit * step * (event.shiftKey && Math.abs(unit) === 1 ? 10 : 1), \"key\");\n return;\n }\n if (event.key === \"Enter\") {\n setDraft(settle(draft, info.digits));\n pendingCaret.current = Number.MAX_SAFE_INTEGER;\n return;\n }\n const start = input.selectionStart ?? 0;\n const end = input.selectionEnd ?? 0;\n if (start !== end || event.metaKey || event.ctrlKey || event.altKey) return;\n // Deleting a group separator deletes the digit before it instead of doing nothing.\n if (event.key === \"Backspace\" && start > 1 && input.value[start - 1] === symbols.group) {\n event.preventDefault();\n applyRaw(input.value.slice(0, start - 2) + input.value.slice(start - 1), start - 2);\n }\n if (event.key === \"Delete\" && input.value[start] === symbols.group) {\n event.preventDefault();\n applyRaw(input.value.slice(0, start + 1) + input.value.slice(start + 2), start);\n }\n }\n\n function onPaste(event: ClipboardEvent<HTMLInputElement>) {\n const text = event.clipboardData.getData(\"text\").trim();\n if (!text) return;\n event.preventDefault();\n // \"$1,234.56\", \"1.234,56 EUR\", \"1234.5\": a final separator followed by one or two digits is the decimal; the rest are groups.\n const match = /[.,](\\d{1,2})\\s*\\D*$/.exec(text);\n const whole = (match ? text.slice(0, match.index) : text).replace(/\\D/g, \"\");\n const pasted = match && info.digits ? `${whole}.${match[1]}` : whole;\n if (!/\\d/.test(pasted)) return;\n const input = event.currentTarget;\n const start = input.selectionStart ?? 0;\n const end = input.selectionEnd ?? 0;\n const inserted = pasted.replace(\".\", symbols.decimal);\n const replaceAll = (start === 0 && end === input.value.length) || !input.value;\n const raw = replaceAll ? inserted : input.value.slice(0, start) + inserted + input.value.slice(end);\n applyRaw(raw, replaceAll ? raw.length : start + inserted.length);\n }\n\n /* ---------------------------------------------- Fit to width ---------------------------------------------- */\n\n // A long amount scales down to fit its room instead of scrolling, so it is always read whole. The first fit\n // is not animated; later ones follow on the smooth spring (the group's `data-ready` turns the transition on).\n const wrapRef = useRef<HTMLDivElement>(null);\n useIsomorphicLayoutEffect(() => {\n const wrap = wrapRef.current;\n const group = groupRef.current;\n if (!wrap || !group || typeof ResizeObserver === \"undefined\") return;\n const fit = () => {\n const natural = group.offsetWidth;\n const room = wrap.clientWidth;\n group.style.scale = String(natural > 0 && room > 0 ? Math.min(1, room / natural) : 1);\n if (!group.hasAttribute(\"data-ready\")) {\n void group.offsetWidth;\n group.setAttribute(\"data-ready\", \"\");\n }\n };\n fit();\n const observer = new ResizeObserver(fit);\n observer.observe(wrap);\n observer.observe(group);\n return () => observer.disconnect();\n }, []);\n\n /* ---------------------------------------------- Currency ---------------------------------------------- */\n\n const options = useMemo(() => currencies.map((entry) => currencyInfo(entry, locale)), [currencies, locale]);\n\n function chooseCurrency(next: CurrencyInfo) {\n if (next.code === code) return;\n const nextDraft = focused\n ? convertDraft(draft, next.digits)\n : settle(convertDraft(draft, next.digits), next.digits);\n const nextMinor = toMinor(nextDraft, next.digits);\n if (currencyProp === undefined) setInnerCurrency(next.code);\n onCurrencyChange?.(next.code);\n setSource(\"external\");\n setDraft(nextDraft);\n latest.current = { minor: nextMinor, draft: nextDraft };\n emit(nextMinor, next);\n setAnnouncement(\n `${next.name}${nextMinor === null ? \"\" : `, ${next.full.format(nextMinor / pow10(next.digits))}`}`,\n );\n }\n\n /* ---------------------------------------------- Messages ---------------------------------------------- */\n\n const range =\n min !== undefined && max !== undefined\n ? `${money(min)} to ${money(max)}`\n : max !== undefined\n ? `Up to ${money(max)}`\n : min !== undefined\n ? `At least ${money(min)}`\n : \"\";\n const belowMin = minor !== null && min !== undefined && minor < min;\n const aboveMax = minor !== null && max !== undefined && minor > max;\n const builtIn =\n touched && !focused && (belowMin || aboveMax)\n ? belowMin\n ? `The minimum is ${money(min ?? 0)}`\n : `The maximum is ${money(max ?? 0)}`\n : undefined;\n const invalid = isInvalid || builtIn !== undefined;\n const message = error ?? (invalid ? (errorMessage ?? builtIn) : undefined);\n const chips = quickAdd.filter((amount) => amount > 0);\n\n const field = useFieldWiring({\n enabled: true,\n id: inputId,\n describedBy: range ? rangeId : undefined,\n invalid: invalid || undefined,\n description,\n error: message,\n });\n\n const typing = source === \"type\";\n const display = displayOf(draft, symbols);\n const canSwitch = options.length > 1;\n\n const control = (\n <>\n <AriaGroup\n role=\"presentation\"\n isDisabled={disabled}\n isInvalid={invalid || Boolean(message)}\n data-slot=\"money-input-shell\"\n className={shellClass}\n >\n <div\n ref={wrapRef}\n className=\"relative flex h-full min-w-0 flex-1 cursor-text items-center overflow-hidden\"\n onMouseDown={(event) => {\n if (event.target !== inputRef.current) event.preventDefault();\n }}\n onClick={(event) => {\n if (disabled || event.target === inputRef.current) return;\n const input = inputRef.current;\n input?.focus();\n input?.setSelectionRange(input.value.length, input.value.length);\n }}\n >\n <div\n ref={groupRef}\n data-slot=\"money-input-amount\"\n className=\"inline-flex flex-none origin-left items-baseline whitespace-nowrap font-display text-h2 font-medium tabular-nums text-foreground data-[ready]:motion-safe:transition-[scale] data-[ready]:motion-safe:duration-(--duration-smooth) data-[ready]:motion-safe:ease-smooth data-[strain]:motion-safe:animate-field-strain\"\n >\n {info.position === \"prefix\" ? <SymbolSlot symbol={info.symbol} side=\"prefix\" /> : null}\n <span className=\"relative inline-flex\">\n {/* The digits the eye sees. The input over them draws only the caret and the selection. */}\n <span aria-hidden=\"true\" data-slot=\"money-input-display\" className=\"inline-flex\">\n <Amount\n draft={draft}\n digits={info.digits}\n symbols={symbols}\n direction={direction}\n still={typing}\n />\n </span>\n <AriaInput\n ref={inputRef}\n {...field.controlProps}\n data-slot=\"money-input-input\"\n type=\"text\"\n role=\"spinbutton\"\n inputMode={info.digits ? \"decimal\" : \"numeric\"}\n autoComplete=\"off\"\n spellCheck={false}\n value={display}\n disabled={disabled}\n aria-label={labelHidden ? label : undefined}\n aria-valuenow={minor === null ? undefined : minor / pow10(info.digits)}\n aria-valuetext={minor === null ? \"Empty\" : money(minor)}\n aria-valuemin={min === undefined ? undefined : min / pow10(info.digits)}\n aria-valuemax={max === undefined ? undefined : max / pow10(info.digits)}\n onChange={(event) =>\n applyRaw(\n event.currentTarget.value,\n event.currentTarget.selectionStart ?? event.currentTarget.value.length,\n )\n }\n onKeyDown={onKeyDown}\n onPaste={onPaste}\n onFocus={() => setFocused(true)}\n onBlur={(event) => {\n setFocused(false);\n setTouched(true);\n if (draft) setDraft(settle(draft, info.digits));\n onBlur?.(event);\n }}\n className=\"absolute top-0 left-0 z-10 m-0 h-full w-[calc(100%+0.5em)] border-0 bg-transparent p-0 [font:inherit] text-transparent caret-foreground outline-none selection:bg-primary/20 selection:text-transparent data-[disabled]:cursor-not-allowed\"\n />\n </span>\n {info.position === \"suffix\" ? <SymbolSlot symbol={info.symbol} side=\"suffix\" /> : null}\n </div>\n </div>\n\n {canSwitch ? (\n <AriaSelect\n aria-label=\"Currency\"\n selectedKey={code}\n isDisabled={disabled}\n onSelectionChange={(key: Key | null) => {\n const next = options.find((option) => option.code === key);\n if (next) chooseCurrency(next);\n }}\n data-slot=\"money-input-currency\"\n className=\"flex-none\"\n >\n <AriaButton className={cnAll(triggerClass, \"group/trigger\")}>\n <AriaSelectValue>{() => code}</AriaSelectValue>\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 text-muted-foreground motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180\"\n >\n <path d=\"m4 6 4 4 4-4\" />\n </svg>\n </AriaButton>\n <AriaPopover\n placement=\"bottom end\"\n offset={12}\n data-slot=\"money-input-currency-popover\"\n className={cnAll(selectPopoverVariants(), \"w-64\")}\n >\n <AriaListBox aria-label=\"Currency\" className=\"outline-none\">\n {options.map((option) => (\n <SelectItem key={option.code} id={option.code} textValue={option.code}>\n <span aria-hidden=\"true\" className=\"w-6 flex-none text-center text-muted-foreground\">\n {option.symbol}\n </span>\n <span className=\"flex-none font-medium tabular-nums\">{option.code}</span>\n <span className=\"min-w-0 flex-1 truncate text-xs text-muted-foreground\">\n {option.name}\n </span>\n </SelectItem>\n ))}\n </AriaListBox>\n </AriaPopover>\n </AriaSelect>\n ) : (\n <span\n data-slot=\"money-input-currency\"\n className=\"flex-none px-2.5 text-sm font-medium text-text-secondary\"\n >\n {code}\n </span>\n )}\n </AriaGroup>\n\n {/* The error sits directly under the amount, above the quick-add row. */}\n <FieldMessage id={field.errorId} text={message} tone=\"error\" />\n\n {chips.length > 0 || range ? (\n <div className=\"flex flex-wrap items-center justify-between gap-x-3 gap-y-2 pt-3\">\n {chips.length > 0 ? (\n <div role=\"group\" aria-label=\"Quick add\" className=\"flex flex-wrap gap-1.5\">\n {chips.map((amount) => (\n <Button\n key={amount}\n variant=\"outlineNeutral\"\n size=\"sm\"\n isDisabled={disabled || (max !== undefined && (minor ?? 0) >= max)}\n aria-controls={inputId}\n aria-label={`Add ${info.whole.format(amount)}`}\n onPress={() => nudge(amount * pow10(info.digits), \"chip\")}\n >\n +{info.whole.format(amount)}\n </Button>\n ))}\n </div>\n ) : null}\n {range ? (\n <span\n id={rangeId}\n data-flash={flash || undefined}\n className=\"text-xs tabular-nums text-muted-foreground motion-safe:transition-colors motion-safe:duration-(--duration-standard) data-[flash]:text-destructive-strong motion-safe:data-[flash]:duration-(--duration-instant)\"\n >\n {range}\n </span>\n ) : null}\n </div>\n ) : null}\n\n {name ? <input type=\"hidden\" name={name} value={minor ?? \"\"} /> : null}\n <span className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {announcement}\n </span>\n </>\n );\n\n return (\n <div className={cn(\"w-full min-w-0 font-sans\", className)} data-slot=\"money-input\">\n <FieldShell\n label={labelHidden ? null : label}\n controlId={field.controlId}\n description={description}\n hintId={field.hintId}\n error={undefined}\n errorId={undefined}\n >\n {control}\n </FieldShell>\n </div>\n );\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"money-input.js","names":["AriaGroup","AriaInput","AriaSelect","AriaButton","AriaSelectValue","AriaPopover","AriaListBox","Button"],"sources":["../../src/money-input/money-input.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useId,\n useImperativeHandle,\n useMemo,\n useReducer,\n useRef,\n useState,\n type ClipboardEvent,\n type FocusEvent,\n type KeyboardEvent,\n} from \"react\";\nimport {\n Button as AriaButton,\n Group as AriaGroup,\n Input as AriaInput,\n ListBox as AriaListBox,\n Popover as AriaPopover,\n Select as AriaSelect,\n SelectValue as AriaSelectValue,\n type Key,\n} from \"react-aria-components\";\n\nimport { Button } from \"../button/button.js\";\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { FieldShell, useFieldWiring } from \"../lib/field-shell.js\";\nimport { FieldMessage, RollingNumber } from \"../lib/field-message.js\";\nimport { fieldBoxSize, fieldSurfaceVariants, fieldTextSize } from \"../lib/field-surface.js\";\nimport { useIsomorphicLayoutEffect, useMorphWidth } from \"../lib/motion.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\nimport { SelectItem, selectPopoverVariants } from \"../select/select.js\";\n\n/** What `onValueChange` hands over beside the minor-unit amount. */\nexport interface MoneyInputDetails {\n currency: string;\n /** The amount in major units, such as 12.5 for $12.50. */\n major: number | null;\n /** The amount as a full currency string, such as \"$12.50\". */\n formatted: string;\n}\n\n/** The field sizes: the large `\"hero\"` amount, or one of Input's `sm`, `md` and `lg` rungs. */\nexport type MoneyInputSize = \"hero\" | \"sm\" | \"md\" | \"lg\";\n\n/**\n * MoneyInput: a currency amount. The value is in minor units, so $12.50 is 1250\n * and 500 yen is 500. Digits group as they are typed without moving the caret,\n * the cents that are still missing wait as muted ghosts so the amount never\n * changes width when it settles, a long amount scales down to fit instead of\n * scrolling, and the quick-add buttons and the arrow keys roll the digits. A\n * currency menu switches the currency (pass one code to hide it); the symbol\n * swaps in place and the digits are kept. Amounts and the currency come from\n * `Intl.NumberFormat`, so the symbol, its side, the grouping and the number of\n * decimals follow the currency and the locale.\n *\n * `min` is checked when the field loses focus; `max` is enforced as you type: a\n * digit that would pass it is refused and the amount strains toward it. Wording\n * for the range comes from the currency, and `error` replaces it.\n *\n * `isInvalid` and `errorMessage` are the pair Select and MultiSelect take: the\n * app's own validation marks the field invalid, and the message shows only while\n * it is (or while the amount is out of range, whose wording it then replaces).\n */\nexport interface MoneyInputProps {\n /**\n * How big the field is. `\"hero\"` is the large amount field: a 76px panel\n * with the amount at the type scale's h2 size. `\"sm\"`, `\"md\"` and `\"lg\"`\n * are Input's own rungs: the same height and inline padding (the\n * `--density-control-*` tokens), the same text size, the same radius and\n * the same border, so the amount sits level with an Input, a Select or a\n * Button of that size in a form row. The currency menu shrinks to fit inside\n * the box; the quick-add row and the messages are unchanged.\n *\n * @default \"hero\"\n */\n size?: MoneyInputSize;\n /** What the amount is, and the field's accessible name. */\n label: string;\n /** Render the label for screen readers only. */\n labelHidden?: boolean;\n /** Amount in minor units (cents for USD, yen for JPY). `null` is empty. */\n value?: number | null;\n defaultValue?: number | null;\n onValueChange?: (value: number | null, details: MoneyInputDetails) => void;\n /** ISO 4217 code, such as \"USD\". */\n currency?: string;\n defaultCurrency?: string;\n onCurrencyChange?: (currency: string) => void;\n /** Codes offered by the currency menu. Pass one code to hide the menu. */\n currencies?: string[];\n /** Lowest amount in minor units. Checked when the field loses focus. */\n min?: number;\n /** Highest amount in minor units. Typing past it is refused with a nudge. */\n max?: number;\n /** Quick-add buttons in major units. Pass an empty array to hide them. */\n quickAdd?: number[];\n /** Arrow keys move by this many minor units. Defaults to one major unit; Shift moves ten times as far. */\n step?: number;\n /** Formatting locale. Fixed by default so server and client render the same digits. */\n locale?: string;\n /** Helper text under the field. A string, because the copy is animated word by word. */\n description?: string;\n /** Replaces the built-in range message. Shown whenever it is set. */\n error?: string;\n /**\n * Marks the amount invalid: the destructive border and `aria-invalid`, with or\n * without a message. An amount outside `min`/`max` also becomes invalid on its\n * own, once the field loses focus. Without `errorMessage` no text is shown, so\n * the app must say what is wrong itself (WCAG 3.3.1).\n *\n * @default false\n */\n isInvalid?: boolean;\n /**\n * Why the amount is wrong, shown under the field while it is invalid\n * (`isInvalid`, or out of range, whose built-in wording it replaces) and tied\n * to the input as its error. A string, because the copy is animated word by\n * word: unlike Select and MultiSelect, whose `errorMessage` is a `ReactNode`.\n * `error`, when set, wins.\n */\n errorMessage?: string;\n disabled?: boolean;\n /** Adds a hidden input carrying the amount in minor units. */\n name?: string;\n id?: string;\n /** Extra classes for the wrapper. Merged last. */\n className?: string;\n onBlur?: (event: FocusEvent<HTMLInputElement>) => void;\n}\n\ntype Source = \"type\" | \"chip\" | \"key\" | \"external\";\ninterface CurrencyInfo {\n code: string;\n symbol: string;\n position: \"prefix\" | \"suffix\";\n digits: number;\n name: string;\n whole: Intl.NumberFormat;\n full: Intl.NumberFormat;\n}\n\nconst MAX_INTEGER_DIGITS = 12;\n/** How far, in px, the amount is pushed toward a limit it just met. */\nconst STRAIN_PX = 6;\n\nconst infoCache = new Map<string, CurrencyInfo>();\nfunction currencyInfo(code: string, locale: string): CurrencyInfo {\n const cacheKey = `${locale}:${code}`;\n const cached = infoCache.get(cacheKey);\n if (cached) return cached;\n const full = new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency: code,\n currencyDisplay: \"narrowSymbol\",\n numberingSystem: \"latn\",\n });\n const parts = full.formatToParts(1);\n const symbolAt = parts.findIndex((part) => part.type === \"currency\");\n const numberAt = parts.findIndex((part) => part.type === \"integer\");\n const digits = full.resolvedOptions().maximumFractionDigits ?? 2;\n let name = code;\n try {\n name = new Intl.DisplayNames([locale], { type: \"currency\" }).of(code) ?? code;\n } catch {\n // Older engines keep the code.\n }\n const info: CurrencyInfo = {\n code,\n symbol: parts[symbolAt]?.value ?? code,\n position: symbolAt < numberAt ? \"prefix\" : \"suffix\",\n digits,\n name: name.charAt(0).toUpperCase() + name.slice(1),\n full,\n whole: new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency: code,\n currencyDisplay: \"narrowSymbol\",\n maximumFractionDigits: 0,\n numberingSystem: \"latn\",\n }),\n };\n infoCache.set(cacheKey, info);\n return info;\n}\n\nfunction localeSymbols(locale: string) {\n const parts = new Intl.NumberFormat(locale, { numberingSystem: \"latn\" }).formatToParts(12345.6);\n return {\n group: parts.find((part) => part.type === \"group\")?.value ?? \",\",\n decimal: parts.find((part) => part.type === \"decimal\")?.value ?? \".\",\n };\n}\n\n/* A draft is the amount as typed in a canonical form: digits with an optional \".\", such as \"1234.5\". */\nconst pow10 = (digits: number) => 10 ** digits;\nfunction toMinor(draft: string, digits: number): number | null {\n if (!draft) return null;\n const [whole, fraction = \"\"] = draft.split(\".\");\n return (\n Number(whole || \"0\") * pow10(digits) + Number(fraction.padEnd(digits, \"0\").slice(0, digits) || \"0\")\n );\n}\nfunction fromMinor(minor: number | null | undefined, digits: number) {\n if (minor === null || minor === undefined || !Number.isFinite(minor)) return \"\";\n const text = String(Math.round(Math.abs(minor))).padStart(digits + 1, \"0\");\n return digits ? `${text.slice(0, text.length - digits)}.${text.slice(text.length - digits)}` : text;\n}\n/** Settles a draft the way it will be read back: fraction padded to the currency, leading zeros gone. */\nfunction settle(draft: string, digits: number) {\n return draft ? fromMinor(toMinor(draft, digits), digits) : \"\";\n}\n/** Carries an amount across currencies with different minor units, keeping the major amount. */\nfunction convertDraft(draft: string, digits: number) {\n if (!draft) return \"\";\n const [whole = \"\", fraction] = draft.split(\".\");\n if (!digits) return whole || \"0\";\n return fraction === undefined ? whole : `${whole}.${fraction.slice(0, digits)}`;\n}\n\nconst groupInteger = (whole: string, group: string) =>\n whole.replace(/\\B(?=(\\d{3})+(?!\\d))/g, group);\nfunction displayOf(draft: string, symbols: { group: string; decimal: string }) {\n if (!draft) return \"\";\n const [whole = \"\", fraction] = draft.split(\".\");\n return groupInteger(whole, symbols.group) + (fraction !== undefined ? symbols.decimal + fraction : \"\");\n}\n\nconst isSignificant = (char: string, decimal: string) => /\\d/.test(char) || char === decimal;\nfunction positionAfter(text: string, count: number, decimal: string) {\n if (count <= 0) return 0;\n let seen = 0;\n for (let index = 0; index < text.length; index++) {\n if (isSignificant(text[index]!, decimal) && ++seen === count) return index + 1;\n }\n return text.length;\n}\n\n/** The currency symbol swaps with a short rise while its slot springs to the new width. */\nfunction SymbolSlot({ symbol, side }: { symbol: string; side: \"prefix\" | \"suffix\" }) {\n const content = useRef<HTMLSpanElement>(null);\n useMorphWidth(content, symbol);\n return (\n <span\n aria-hidden=\"true\"\n data-slot=\"money-input-symbol\"\n className={cnAll(\"button-slot text-text-secondary\", side === \"prefix\" ? \"me-1\" : \"ms-1.5\")}\n >\n <span ref={content} className=\"inline-flex flex-none items-center whitespace-pre\">\n <SwapText swapKey={symbol} motion=\"rise\" className=\"inline-block\">\n {symbol}\n </SwapText>\n </span>\n </span>\n );\n}\n\n/** The amount the eye sees: whole part, separator and the cents, with the missing ones as muted ghosts. */\nfunction Amount({\n draft,\n digits,\n symbols,\n direction,\n still,\n}: {\n draft: string;\n digits: number;\n symbols: { group: string; decimal: string };\n direction: 1 | -1;\n still: boolean;\n}) {\n const [whole = \"\", fraction] = draft ? draft.split(\".\") : [];\n const typed = fraction ?? \"\";\n const ghostWhole = !whole;\n return (\n <>\n <span data-ghost={ghostWhole || undefined} className=\"inline-flex data-[ghost]:text-muted-foreground\">\n <RollingNumber\n word={groupInteger(whole || \"0\", symbols.group)}\n direction={direction}\n still={still}\n />\n </span>\n {digits > 0 ? (\n <>\n <span\n data-ghost={fraction === undefined || undefined}\n className=\"data-[ghost]:text-muted-foreground\"\n >\n {symbols.decimal}\n </span>\n {typed ? (\n <span className=\"inline-flex\">\n <RollingNumber word={typed} direction={direction} still={still} />\n </span>\n ) : null}\n {typed.length < digits ? (\n <span className=\"text-muted-foreground\">{\"0\".repeat(digits - typed.length)}</span>\n ) : null}\n </>\n ) : null}\n </>\n );\n}\n\nconst triggerClass =\n \"inline-flex h-9 flex-none cursor-pointer items-center gap-1 rounded-pill border-0 bg-transparent ps-3 pe-2 font-sans text-sm font-medium tabular-nums text-foreground outline-none motion-safe:transition-[background-color] motion-safe:duration-(--duration-quick) motion-safe:ease-settle data-[hovered]:bg-muted data-[pressed]:bg-muted data-[disabled]:cursor-not-allowed data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring)\";\n\n// Focus turns the box's own border dark whenever the amount holds focus; no separate ring.\nconst shellFocus = [\n \"has-[input:focus]:not-data-[invalid]:border-foreground\",\n \"forced-colors:has-[input:focus]:outline-solid forced-colors:has-[input:focus]:outline-(length:--focus-ring-width) forced-colors:has-[input:focus]:outline-offset-(--focus-ring-offset)\",\n];\n\nconst shellClass = cnAll(\n fieldSurfaceVariants(),\n \"flex h-19 items-center gap-2 rounded-panel ps-4.5 pe-2\",\n ...shellFocus,\n);\n\n/*\n * The field sizes take Input's box whole: fieldSurfaceVariants (border, radius\n * `rounded-control`, ground) plus fieldBoxSize (height and inline padding from\n * the `--density-control-*` tokens), so the two cannot drift apart. With a\n * currency menu the end padding drops to a 4px inset and the menu button\n * stretches to the box's inner height less that inset, so it sits centred in\n * the field at every rung and density.\n */\nconst fieldShellClass = (size: Exclude<MoneyInputSize, \"hero\">, hasMenu: boolean) =>\n cnAll(\n fieldSurfaceVariants(),\n \"flex items-center gap-2\",\n fieldBoxSize[size],\n ...shellFocus,\n hasMenu && \"pe-1\",\n );\nconst fieldTriggerClass = cnAll(triggerClass, \"h-auto self-stretch my-1 ps-2.5 pe-1.5\");\n\n/* The amount's type: the h2 display face for the hero, Input's own text rule for a field size. */\nconst amountBase = \"inline-flex flex-none origin-left items-baseline whitespace-nowrap\";\nconst amountRest =\n \"tabular-nums text-foreground data-[ready]:motion-safe:transition-[scale] data-[ready]:motion-safe:duration-(--duration-smooth) data-[ready]:motion-safe:ease-smooth data-[strain]:motion-safe:animate-field-strain\";\n// A field size sets no face or weight of its own, as Input sets none: both inherit the wrapper's.\nconst amountClass = (size: MoneyInputSize) =>\n [\n amountBase,\n size === \"hero\" ? \"font-display text-h2 font-medium\" : fieldTextSize[size],\n amountRest,\n ].join(\" \");\n\nexport const MoneyInput = forwardRef<HTMLInputElement, MoneyInputProps>(function MoneyInput(\n {\n size = \"hero\",\n label,\n labelHidden = false,\n value,\n defaultValue = null,\n onValueChange,\n currency: currencyProp,\n defaultCurrency = \"USD\",\n onCurrencyChange,\n currencies = [\"USD\", \"EUR\", \"GBP\", \"JPY\", \"CAD\", \"AUD\", \"CHF\", \"INR\"],\n min,\n max,\n quickAdd = [10, 50, 100],\n step: stepProp,\n locale = \"en-US\",\n description,\n error,\n isInvalid = false,\n errorMessage,\n disabled = false,\n name,\n id,\n className,\n onBlur,\n },\n forwardedRef,\n) {\n const generated = useId();\n const inputId = id ?? `${generated}-amount`;\n const rangeId = `${inputId}-range`;\n\n const [innerCurrency, setInnerCurrency] = useState(defaultCurrency);\n const code = currencyProp ?? innerCurrency;\n const info = useMemo(() => currencyInfo(code, locale), [code, locale]);\n const symbols = useMemo(() => localeSymbols(locale), [locale]);\n const step = stepProp && stepProp > 0 ? stepProp : pow10(info.digits);\n\n const [draft, setDraft] = useState(() => fromMinor(value !== undefined ? value : defaultValue, info.digits));\n const minor = toMinor(draft, info.digits);\n\n const [seenValue, setSeenValue] = useState(value);\n const [source, setSource] = useState<Source>(\"external\");\n const [direction, setDirection] = useState<1 | -1>(1);\n if (value !== seenValue) {\n setSeenValue(value);\n if (value !== undefined && value !== minor) {\n setDraft(fromMinor(value, info.digits));\n setSource(\"external\");\n setDirection((value ?? 0) >= (minor ?? 0) ? 1 : -1);\n }\n }\n\n const [focused, setFocused] = useState(false);\n const [touched, setTouched] = useState(false);\n const [flash, setFlash] = useState(false);\n const [announcement, setAnnouncement] = useState(\"\");\n const [, rerender] = useReducer((count: number) => count + 1, 0);\n\n const inputRef = useRef<HTMLInputElement>(null);\n useImperativeHandle(forwardedRef, () => inputRef.current as HTMLInputElement);\n const pendingCaret = useRef<number | null>(null);\n const latest = useRef({ minor, draft });\n useIsomorphicLayoutEffect(() => {\n latest.current = { minor, draft };\n });\n\n const money = useCallback(\n (amount: number | null) => (amount === null ? \"\" : info.full.format(amount / pow10(info.digits))),\n [info],\n );\n\n /* ---------------------------------------------- Limits and feedback ---------------------------------------------- */\n\n const groupRef = useRef<HTMLDivElement>(null);\n const flashTimer = useRef<number | undefined>(undefined);\n function refuse(toward: 1 | -1, limit: number) {\n const node = groupRef.current;\n if (node) {\n node.style.setProperty(\"--strain-x\", `${toward * STRAIN_PX}px`);\n // Restart the animation: the attribute has to leave and come back across a style recalculation.\n node.removeAttribute(\"data-strain\");\n void node.offsetWidth;\n node.setAttribute(\"data-strain\", \"\");\n }\n setFlash(true);\n window.clearTimeout(flashTimer.current);\n flashTimer.current = window.setTimeout(() => setFlash(false), 1400);\n setAnnouncement(`${toward > 0 ? \"Maximum\" : \"Minimum\"} is ${money(limit)}`);\n }\n\n function emit(nextMinor: number | null, nextInfo = info) {\n onValueChange?.(nextMinor, {\n currency: nextInfo.code,\n major: nextMinor === null ? null : nextMinor / pow10(nextInfo.digits),\n formatted:\n nextMinor === null ? \"\" : nextInfo.full.format(nextMinor / pow10(nextInfo.digits)),\n });\n }\n\n function setAmount(nextDraft: string, nextSource: Source) {\n const nextMinor = toMinor(nextDraft, info.digits);\n setDirection((nextMinor ?? 0) >= (latest.current.minor ?? 0) ? 1 : -1);\n setSource(nextSource);\n setDraft(nextDraft);\n latest.current = { minor: nextMinor, draft: nextDraft };\n if (nextMinor !== minor) emit(nextMinor);\n if (nextSource !== \"type\") setAnnouncement(money(nextMinor));\n }\n\n /* ---------------------------------------------- Typing ---------------------------------------------- */\n\n /** Reads what the input holds into a draft and counts the digits and decimal before the caret, so the caret can return to the same spot. */\n function sanitize(raw: string, caret: number) {\n let out = \"\";\n let before = 0;\n let dot = false;\n for (let index = 0; index < raw.length; index++) {\n const char = raw[index]!;\n let kept = \"\";\n if (/\\d/.test(char)) kept = char;\n else if (\n (char === symbols.decimal || (char === \".\" && symbols.group !== \".\")) &&\n info.digits > 0 &&\n !dot\n ) {\n kept = \".\";\n dot = true;\n }\n if (!kept) continue;\n out += kept;\n if (index < caret) before++;\n }\n const pieces = out.split(\".\");\n let whole = pieces[0]!;\n const fraction = pieces[1];\n const stripped = whole.length - whole.replace(/^0+/, \"\").length;\n whole = whole.replace(/^0+/, \"\");\n let shift = -Math.min(stripped, before);\n if (fraction !== undefined && !whole) {\n whole = \"0\";\n shift += 1;\n }\n if (fraction === undefined && !whole && stripped) {\n whole = \"0\";\n shift += 1;\n }\n if (fraction !== undefined && fraction.length > info.digits) return null;\n if (whole.length > MAX_INTEGER_DIGITS) return null;\n return {\n draft: fraction !== undefined ? `${whole}.${fraction}` : whole,\n before: Math.max(0, before + shift),\n };\n }\n\n function applyRaw(raw: string, caret: number) {\n const result = sanitize(raw, caret);\n // A refused keystroke keeps the caret where it was: one significant character before where the browser left it.\n if (!result) {\n pendingCaret.current = Math.max(\n 0,\n [...raw.slice(0, caret)].filter((char) => isSignificant(char, symbols.decimal) || char === \".\")\n .length - 1,\n );\n rerender();\n return;\n }\n const nextMinor = toMinor(result.draft, info.digits);\n if (max !== undefined && nextMinor !== null && nextMinor > max) {\n pendingCaret.current = Math.max(0, result.before - 1);\n refuse(1, max);\n rerender();\n return;\n }\n pendingCaret.current = result.before;\n setAmount(result.draft, \"type\");\n }\n\n useIsomorphicLayoutEffect(() => {\n const input = inputRef.current;\n const count = pendingCaret.current;\n pendingCaret.current = null;\n if (!input || count === null || document.activeElement !== input) return;\n const position = positionAfter(input.value, count, symbols.decimal);\n input.setSelectionRange(position, position);\n });\n\n function nudge(amount: number, nextSource: Source) {\n const from = latest.current.minor ?? 0;\n const floor = min ?? 0;\n const ceiling = max ?? Number.MAX_SAFE_INTEGER;\n let next = from + amount;\n if (next > ceiling) {\n if (from >= ceiling) {\n refuse(1, ceiling);\n return;\n }\n next = ceiling;\n refuse(1, ceiling);\n }\n if (next < floor) {\n if (from <= floor && latest.current.minor !== null) {\n refuse(-1, floor);\n return;\n }\n next = floor;\n }\n pendingCaret.current = Number.MAX_SAFE_INTEGER;\n setAmount(fromMinor(next, info.digits), nextSource);\n }\n\n function onKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n const input = event.currentTarget;\n const moves: Record<string, number> = { ArrowUp: 1, ArrowDown: -1, PageUp: 10, PageDown: -10 };\n if (event.key in moves) {\n event.preventDefault();\n const unit = moves[event.key]!;\n nudge(unit * step * (event.shiftKey && Math.abs(unit) === 1 ? 10 : 1), \"key\");\n return;\n }\n if (event.key === \"Enter\") {\n setDraft(settle(draft, info.digits));\n pendingCaret.current = Number.MAX_SAFE_INTEGER;\n return;\n }\n const start = input.selectionStart ?? 0;\n const end = input.selectionEnd ?? 0;\n if (start !== end || event.metaKey || event.ctrlKey || event.altKey) return;\n // Deleting a group separator deletes the digit before it instead of doing nothing.\n if (event.key === \"Backspace\" && start > 1 && input.value[start - 1] === symbols.group) {\n event.preventDefault();\n applyRaw(input.value.slice(0, start - 2) + input.value.slice(start - 1), start - 2);\n }\n if (event.key === \"Delete\" && input.value[start] === symbols.group) {\n event.preventDefault();\n applyRaw(input.value.slice(0, start + 1) + input.value.slice(start + 2), start);\n }\n }\n\n function onPaste(event: ClipboardEvent<HTMLInputElement>) {\n const text = event.clipboardData.getData(\"text\").trim();\n if (!text) return;\n event.preventDefault();\n // \"$1,234.56\", \"1.234,56 EUR\", \"1234.5\": a final separator followed by one or two digits is the decimal; the rest are groups.\n const match = /[.,](\\d{1,2})\\s*\\D*$/.exec(text);\n const whole = (match ? text.slice(0, match.index) : text).replace(/\\D/g, \"\");\n const pasted = match && info.digits ? `${whole}.${match[1]}` : whole;\n if (!/\\d/.test(pasted)) return;\n const input = event.currentTarget;\n const start = input.selectionStart ?? 0;\n const end = input.selectionEnd ?? 0;\n const inserted = pasted.replace(\".\", symbols.decimal);\n const replaceAll = (start === 0 && end === input.value.length) || !input.value;\n const raw = replaceAll ? inserted : input.value.slice(0, start) + inserted + input.value.slice(end);\n applyRaw(raw, replaceAll ? raw.length : start + inserted.length);\n }\n\n /* ---------------------------------------------- Fit to width ---------------------------------------------- */\n\n // A long amount scales down to fit its room instead of scrolling, so it is always read whole. The first fit\n // is not animated; later ones follow on the smooth spring (the group's `data-ready` turns the transition on).\n const wrapRef = useRef<HTMLDivElement>(null);\n useIsomorphicLayoutEffect(() => {\n const wrap = wrapRef.current;\n const group = groupRef.current;\n if (!wrap || !group || typeof ResizeObserver === \"undefined\") return;\n const fit = () => {\n const natural = group.offsetWidth;\n const room = wrap.clientWidth;\n group.style.scale = String(natural > 0 && room > 0 ? Math.min(1, room / natural) : 1);\n if (!group.hasAttribute(\"data-ready\")) {\n void group.offsetWidth;\n group.setAttribute(\"data-ready\", \"\");\n }\n };\n fit();\n const observer = new ResizeObserver(fit);\n observer.observe(wrap);\n observer.observe(group);\n return () => observer.disconnect();\n }, []);\n\n /* ---------------------------------------------- Currency ---------------------------------------------- */\n\n const options = useMemo(() => currencies.map((entry) => currencyInfo(entry, locale)), [currencies, locale]);\n\n function chooseCurrency(next: CurrencyInfo) {\n if (next.code === code) return;\n const nextDraft = focused\n ? convertDraft(draft, next.digits)\n : settle(convertDraft(draft, next.digits), next.digits);\n const nextMinor = toMinor(nextDraft, next.digits);\n if (currencyProp === undefined) setInnerCurrency(next.code);\n onCurrencyChange?.(next.code);\n setSource(\"external\");\n setDraft(nextDraft);\n latest.current = { minor: nextMinor, draft: nextDraft };\n emit(nextMinor, next);\n setAnnouncement(\n `${next.name}${nextMinor === null ? \"\" : `, ${next.full.format(nextMinor / pow10(next.digits))}`}`,\n );\n }\n\n /* ---------------------------------------------- Messages ---------------------------------------------- */\n\n const range =\n min !== undefined && max !== undefined\n ? `${money(min)} to ${money(max)}`\n : max !== undefined\n ? `Up to ${money(max)}`\n : min !== undefined\n ? `At least ${money(min)}`\n : \"\";\n const belowMin = minor !== null && min !== undefined && minor < min;\n const aboveMax = minor !== null && max !== undefined && minor > max;\n const builtIn =\n touched && !focused && (belowMin || aboveMax)\n ? belowMin\n ? `The minimum is ${money(min ?? 0)}`\n : `The maximum is ${money(max ?? 0)}`\n : undefined;\n const invalid = isInvalid || builtIn !== undefined;\n const message = error ?? (invalid ? (errorMessage ?? builtIn) : undefined);\n const chips = quickAdd.filter((amount) => amount > 0);\n\n const field = useFieldWiring({\n enabled: true,\n id: inputId,\n describedBy: range ? rangeId : undefined,\n invalid: invalid || undefined,\n description,\n error: message,\n });\n\n const typing = source === \"type\";\n const display = displayOf(draft, symbols);\n const canSwitch = options.length > 1;\n\n const control = (\n <>\n <AriaGroup\n role=\"presentation\"\n isDisabled={disabled}\n isInvalid={invalid || Boolean(message)}\n data-slot=\"money-input-shell\"\n className={size === \"hero\" ? shellClass : fieldShellClass(size, canSwitch)}\n >\n <div\n ref={wrapRef}\n className=\"relative flex h-full min-w-0 flex-1 cursor-text items-center overflow-hidden\"\n onMouseDown={(event) => {\n if (event.target !== inputRef.current) event.preventDefault();\n }}\n onClick={(event) => {\n if (disabled || event.target === inputRef.current) return;\n const input = inputRef.current;\n input?.focus();\n input?.setSelectionRange(input.value.length, input.value.length);\n }}\n >\n <div ref={groupRef} data-slot=\"money-input-amount\" className={amountClass(size)}>\n {info.position === \"prefix\" ? <SymbolSlot symbol={info.symbol} side=\"prefix\" /> : null}\n <span className=\"relative inline-flex\">\n {/* The digits the eye sees. The input over them draws only the caret and the selection. */}\n <span aria-hidden=\"true\" data-slot=\"money-input-display\" className=\"inline-flex\">\n <Amount\n draft={draft}\n digits={info.digits}\n symbols={symbols}\n direction={direction}\n still={typing}\n />\n </span>\n <AriaInput\n ref={inputRef}\n {...field.controlProps}\n data-slot=\"money-input-input\"\n type=\"text\"\n role=\"spinbutton\"\n inputMode={info.digits ? \"decimal\" : \"numeric\"}\n autoComplete=\"off\"\n spellCheck={false}\n value={display}\n disabled={disabled}\n aria-label={labelHidden ? label : undefined}\n aria-valuenow={minor === null ? undefined : minor / pow10(info.digits)}\n aria-valuetext={minor === null ? \"Empty\" : money(minor)}\n aria-valuemin={min === undefined ? undefined : min / pow10(info.digits)}\n aria-valuemax={max === undefined ? undefined : max / pow10(info.digits)}\n onChange={(event) =>\n applyRaw(\n event.currentTarget.value,\n event.currentTarget.selectionStart ?? event.currentTarget.value.length,\n )\n }\n onKeyDown={onKeyDown}\n onPaste={onPaste}\n onFocus={() => setFocused(true)}\n onBlur={(event) => {\n setFocused(false);\n setTouched(true);\n if (draft) setDraft(settle(draft, info.digits));\n onBlur?.(event);\n }}\n className=\"absolute top-0 left-0 z-10 m-0 h-full w-[calc(100%+0.5em)] border-0 bg-transparent p-0 [font:inherit] text-transparent caret-foreground outline-none selection:bg-primary/20 selection:text-transparent data-[disabled]:cursor-not-allowed\"\n />\n </span>\n {info.position === \"suffix\" ? <SymbolSlot symbol={info.symbol} side=\"suffix\" /> : null}\n </div>\n </div>\n\n {canSwitch ? (\n <AriaSelect\n aria-label=\"Currency\"\n selectedKey={code}\n isDisabled={disabled}\n onSelectionChange={(key: Key | null) => {\n const next = options.find((option) => option.code === key);\n if (next) chooseCurrency(next);\n }}\n data-slot=\"money-input-currency\"\n className=\"flex-none\"\n >\n <AriaButton\n className={cnAll(size === \"hero\" ? triggerClass : fieldTriggerClass, \"group/trigger\")}\n >\n <AriaSelectValue>{() => code}</AriaSelectValue>\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 text-muted-foreground motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180\"\n >\n <path d=\"m4 6 4 4 4-4\" />\n </svg>\n </AriaButton>\n <AriaPopover\n placement=\"bottom end\"\n offset={12}\n data-slot=\"money-input-currency-popover\"\n className={cnAll(selectPopoverVariants(), \"w-64\")}\n >\n <AriaListBox aria-label=\"Currency\" className=\"outline-none\">\n {options.map((option) => (\n <SelectItem key={option.code} id={option.code} textValue={option.code}>\n <span aria-hidden=\"true\" className=\"w-6 flex-none text-center text-muted-foreground\">\n {option.symbol}\n </span>\n <span className=\"flex-none font-medium tabular-nums\">{option.code}</span>\n <span className=\"min-w-0 flex-1 truncate text-xs text-muted-foreground\">\n {option.name}\n </span>\n </SelectItem>\n ))}\n </AriaListBox>\n </AriaPopover>\n </AriaSelect>\n ) : (\n <span\n data-slot=\"money-input-currency\"\n className={cnAll(\n \"flex-none px-2.5 text-sm font-medium text-text-secondary\",\n size !== \"hero\" && \"pe-0\",\n )}\n >\n {code}\n </span>\n )}\n </AriaGroup>\n\n {/* The error sits directly under the amount, above the quick-add row. */}\n <FieldMessage id={field.errorId} text={message} tone=\"error\" />\n\n {chips.length > 0 || range ? (\n <div className=\"flex flex-wrap items-center justify-between gap-x-3 gap-y-2 pt-3\">\n {chips.length > 0 ? (\n <div role=\"group\" aria-label=\"Quick add\" className=\"flex flex-wrap gap-1.5\">\n {chips.map((amount) => (\n <Button\n key={amount}\n variant=\"outlineNeutral\"\n size=\"sm\"\n isDisabled={disabled || (max !== undefined && (minor ?? 0) >= max)}\n aria-controls={inputId}\n aria-label={`Add ${info.whole.format(amount)}`}\n onPress={() => nudge(amount * pow10(info.digits), \"chip\")}\n >\n +{info.whole.format(amount)}\n </Button>\n ))}\n </div>\n ) : null}\n {range ? (\n <span\n id={rangeId}\n data-flash={flash || undefined}\n className=\"text-xs tabular-nums text-muted-foreground motion-safe:transition-colors motion-safe:duration-(--duration-standard) data-[flash]:text-destructive-strong motion-safe:data-[flash]:duration-(--duration-instant)\"\n >\n {range}\n </span>\n ) : null}\n </div>\n ) : null}\n\n {name ? <input type=\"hidden\" name={name} value={minor ?? \"\"} /> : null}\n <span className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {announcement}\n </span>\n </>\n );\n\n return (\n <div className={cn(\"w-full min-w-0 font-sans\", className)} data-slot=\"money-input\">\n <FieldShell\n label={labelHidden ? null : label}\n controlId={field.controlId}\n description={description}\n hintId={field.hintId}\n error={undefined}\n errorId={undefined}\n >\n {control}\n </FieldShell>\n </div>\n );\n});\n"],"mappings":"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"}
@@ -81,9 +81,16 @@ export interface MorphSelectProps {
81
81
  * is set, and is named in the trigger's and the search field's `aria-describedby`.
82
82
  */
83
83
  errorMessage?: ReactNode;
84
+ /**
85
+ * Stretches the trigger to fill its container, like Input and Select, instead of hugging
86
+ * its content up to 20rem. The open surface is then as wide as the trigger. `panelWidth`
87
+ * does not apply.
88
+ * @default false
89
+ */
90
+ fullWidth?: boolean;
84
91
  /** Extra classes for the wrapper. Merged last. */
85
92
  className?: string;
86
93
  }
87
- export declare function MorphSelect({ label, labelHidden, items, value, defaultValue, onValueChange, placeholder, searchable, searchPlaceholder, panelWidth, maxListHeight, align, size, name, disabled, id, isInvalid, errorMessage, className }: MorphSelectProps): import("react").JSX.Element;
94
+ export declare function MorphSelect({ label, labelHidden, items, value, defaultValue, onValueChange, placeholder, searchable, searchPlaceholder, panelWidth, maxListHeight, align, size, name, disabled, id, isInvalid, errorMessage, fullWidth, className }: MorphSelectProps): import("react").JSX.Element;
88
95
  //#endregion
89
96
  //# sourceMappingURL=morph-select.d.ts.map
@@ -72,7 +72,7 @@ function LeavingLayer({ id, rect, dir, fly, onDone, children }) {
72
72
  children
73
73
  });
74
74
  }
75
- function MorphSelect({ label, labelHidden = false, items, value, defaultValue = null, onValueChange, placeholder = "Select", searchable = "auto", searchPlaceholder, panelWidth = 272, maxListHeight = 296, align = "start", size = "md", name, disabled = false, id, isInvalid = false, errorMessage, className }) {
75
+ function MorphSelect({ label, labelHidden = false, items, value, defaultValue = null, onValueChange, placeholder = "Select", searchable = "auto", searchPlaceholder, panelWidth = 272, maxListHeight = 296, align = "start", size = "md", name, disabled = false, id, isInvalid = false, errorMessage, fullWidth = false, className }) {
76
76
  const uid = useId();
77
77
  const labelId = `${uid}-label`;
78
78
  const errorId = `${uid}-error`;
@@ -124,7 +124,8 @@ function MorphSelect({ label, labelHidden = false, items, value, defaultValue =
124
124
  });
125
125
  const live = useRef({
126
126
  open: false,
127
- anchorW: 0
127
+ anchorW: 0,
128
+ full: fullWidth
128
129
  });
129
130
  const place = useCallback(() => {
130
131
  const { trigger, lid, listW, listH } = sizes.current;
@@ -136,7 +137,7 @@ function MorphSelect({ label, labelHidden = false, items, value, defaultValue =
136
137
  anchor.toggleAttribute("data-widening", trigger > live.current.anchorW);
137
138
  live.current.anchorW = trigger;
138
139
  }
139
- anchor.style.width = `${trigger}px`;
140
+ anchor.style.width = live.current.full ? "" : `${trigger}px`;
140
141
  shape.style.width = `${isOpen ? Math.max(trigger, listW) : trigger}px`;
141
142
  shape.style.height = `${isOpen ? lid + listH : lid}px`;
142
143
  }, []);
@@ -156,6 +157,7 @@ function MorphSelect({ label, labelHidden = false, items, value, defaultValue =
156
157
  place();
157
158
  }, [place]);
158
159
  useIsomorphicLayoutEffect(() => {
160
+ live.current.full = fullWidth;
159
161
  const measure = measureRef.current;
160
162
  const face = faceRef.current;
161
163
  const root = rootRef.current;
@@ -168,7 +170,7 @@ function MorphSelect({ label, labelHidden = false, items, value, defaultValue =
168
170
  observer.observe(measure);
169
171
  observer.observe(face);
170
172
  return () => observer.disconnect();
171
- }, [read]);
173
+ }, [read, fullWidth]);
172
174
  useIsomorphicLayoutEffect(() => {
173
175
  if (live.current.open === open) return;
174
176
  live.current.open = open;
@@ -417,12 +419,13 @@ function MorphSelect({ label, labelHidden = false, items, value, defaultValue =
417
419
  "data-slot": "morph-select",
418
420
  "data-open": open || void 0,
419
421
  "data-align": align,
422
+ "data-full": fullWidth || void 0,
420
423
  "data-size": size,
421
424
  "data-disabled": disabled || void 0,
422
425
  "data-invalid": isInvalid || void 0,
423
426
  style: rootStyle,
424
427
  onBlur,
425
- className: cn("morph-select-root group/ms relative inline-grid min-w-0 justify-items-start gap-2 font-sans leading-(--leading-normal) text-foreground data-[align=end]:justify-items-end data-[open]:z-30 data-[size=sm]:text-sm data-[size=md]:text-base data-[size=lg]:text-base", className),
428
+ className: cn("morph-select-root group/ms relative inline-grid data-[full]:grid data-[full]:w-full min-w-0 justify-items-start gap-2 font-sans leading-(--leading-normal) text-foreground data-[align=end]:justify-items-end data-[open]:z-30 data-[size=sm]:text-sm data-[size=md]:text-base data-[size=lg]:text-base", className),
426
429
  children: [
427
430
  /* @__PURE__ */ jsx("span", {
428
431
  id: labelId,
@@ -1 +1 @@
1
- {"version":3,"file":"morph-select.js","names":[],"sources":["../../src/morph-select/morph-select.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\n\nimport { labelVariants } from \"../label/label.js\";\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport {\n prefersReducedMotion,\n useExitBackstop,\n useIsomorphicLayoutEffect,\n useLeavingItems,\n type Rect,\n} from \"../lib/motion.js\";\n\n/** One choice. */\nexport interface MorphSelectOption {\n value: string;\n label: string;\n /** A 20px mark shown before the label, in the list and in the trigger. An avatar or a plain icon. */\n icon?: ReactNode;\n /** Short trailing detail such as an offset or a count. Rendered with tabular numerals. */\n meta?: string;\n /** Extra words that match this option when searching. */\n keywords?: string;\n disabled?: boolean;\n}\n\n/** A labelled run of options. */\nexport interface MorphSelectGroup {\n label: string;\n options: MorphSelectOption[];\n}\n\nexport type MorphSelectItem = MorphSelectOption | MorphSelectGroup;\n\n/**\n * MorphSelect: a select whose trigger is the list. Opening grows the trigger itself\n * into the options surface (its width, height and corners spring from the closed\n * control to the open panel), a highlight glides between options, and the chosen\n * option's label lifts out of the list and settles into the trigger while the trigger\n * springs to its new width. Supports groups, type-ahead, search for long lists, and the\n * full listbox keyboard model: arrows, Home, End, PageUp, PageDown, Enter, Space,\n * Escape and Alt+ArrowUp.\n *\n * Use it for compact property pickers and form fields with up to a few hundred options.\n * Prefer a combobox when people mostly type free text, and Select when the list should\n * float over the page instead of growing from the trigger.\n *\n * It is its own listbox rather than a React Aria Select: a Select's list lives in a\n * popover, a separate surface that cannot be the trigger.\n */\nexport interface MorphSelectProps {\n /** Visible label, also the accessible name of the trigger and the list. */\n label: string;\n /** Keeps the label for assistive technology only. */\n labelHidden?: boolean;\n items: MorphSelectItem[];\n /** The chosen value. Controlled when given; `null` is nothing chosen. */\n value?: string | null;\n defaultValue?: string | null;\n onValueChange?: (value: string, option: MorphSelectOption) => void;\n /** Shown while nothing is chosen. @default \"Select\" */\n placeholder?: string;\n /** Shows a search field in place of the trigger when open. \"auto\" turns it on above eight options. @default \"auto\" */\n searchable?: boolean | \"auto\";\n searchPlaceholder?: string;\n /** Width of the open surface in px. It never gets narrower than the trigger. @default 272 */\n panelWidth?: number;\n /** Tallest the option list gets before it scrolls, in px. @default 296 */\n maxListHeight?: number;\n /** Which edge of the trigger stays put while the surface grows. @default \"start\" */\n align?: \"start\" | \"end\";\n /**\n * Control scale: the same heights as Select and Input.\n * @default \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Adds a hidden input for native form submission. */\n name?: string;\n disabled?: boolean;\n /**\n * The trigger's `id`, so a `<label htmlFor>` outside the select (or a form\n * library that focuses a field by id) reaches the real control. When such a\n * label exists it NAMES the trigger: the trigger then drops its\n * `aria-labelledby` to the inner `label`, which would otherwise outrank the\n * outside label in the accessible-name computation.\n */\n id?: string;\n /**\n * Marks the choice as wrong: the trigger takes the invalid border and is\n * `aria-invalid`. Pair it with `errorMessage`, or the state is shown visually in colour only.\n */\n isInvalid?: boolean;\n /**\n * Why the choice is wrong, under the control. It shows only while `isInvalid`\n * is set, and is named in the trigger's and the search field's `aria-describedby`.\n */\n errorMessage?: ReactNode;\n /** Extra classes for the wrapper. Merged last. */\n className?: string;\n}\n\ninterface Section {\n key: string;\n label?: string;\n options: MorphSelectOption[];\n}\ninterface Change {\n value: string | null;\n dir: number;\n fly: { x: number; y: number } | null;\n}\n\nconst TYPEAHEAD_RESET = 600;\nconst PAGE = 8;\n\nconst isGroup = (item: MorphSelectItem): item is MorphSelectGroup => \"options\" in item;\n\n/** Loose options between groups gather into unlabelled sections, so the list keeps the order it was given. */\nfunction normalize(items: MorphSelectItem[]): Section[] {\n const out: Section[] = [];\n items.forEach((item, index) => {\n if (isGroup(item)) {\n out.push({ key: `group-${index}`, label: item.label, options: item.options });\n return;\n }\n const last = out[out.length - 1];\n if (last && !last.label) last.options.push(item);\n else out.push({ key: `loose-${index}`, options: [item] });\n });\n return out;\n}\n\nconst matches = (option: MorphSelectOption, needle: string) =>\n `${option.label} ${option.meta ?? \"\"} ${option.keywords ?? \"\"}`.toLowerCase().includes(needle);\n\n/** The closed trigger's width, rounded up. offsetWidth rounds to the nearest pixel, and half a pixel short is enough to ellipsize the label. */\nfunction triggerWidth(node: HTMLElement) {\n const exact = node.getBoundingClientRect().width;\n const rounded = node.offsetWidth;\n // Under a scaled ancestor the rect is scaled too, so fall back to the layout width with a pixel of slack.\n return Math.abs(exact - rounded) <= 0.5 ? Math.ceil(exact - 0.01) : rounded + 1;\n}\n\n/** The layout the closed trigger and its invisible measuring copy share, so the copy's width is the trigger's. */\nconst triggerLayout =\n \"box-border flex h-(--ms-h) items-center gap-2 ps-(--ms-pad) pe-10 font-medium whitespace-nowrap\";\n\nconst iconClass =\n \"grid size-5 flex-none place-items-center text-text-secondary [&>svg]:size-4 [&>img]:size-5 [&>img]:rounded-full [&>img]:object-cover\";\n\n/** The label a layer shows: the icon, then the text, or the placeholder. */\nfunction Value({ option, placeholder }: { option: MorphSelectOption | undefined; placeholder: string }) {\n if (!option) return <span className=\"font-normal text-muted-foreground\">{placeholder}</span>;\n return (\n <>\n {option.icon ? (\n <span className={iconClass} aria-hidden=\"true\">\n {option.icon}\n </span>\n ) : null}\n <span className=\"min-w-0 truncate\">{option.label}</span>\n </>\n );\n}\n\n/** A replaced label on its way out, popped out of flow where it stood and lifting away. */\nfunction LeavingLayer({\n id,\n rect,\n dir,\n fly,\n onDone,\n children,\n}: {\n id: number;\n rect: Rect;\n dir: number;\n fly: boolean;\n onDone: (id: number) => void;\n children: ReactNode;\n}) {\n const remove = useCallback(() => onDone(id), [onDone, id]);\n useExitBackstop(true, remove);\n return (\n <span\n aria-hidden=\"true\"\n data-leaving=\"\"\n onAnimationEnd={(event) => {\n if (event.target === event.currentTarget) remove();\n }}\n // field-swap-out lifts by 0.3em times --dir: a label that flew to the trigger lifts 0.3em, one that rolled lifts 0.45em the way the list ran.\n style={{ ...rect, \"--dir\": fly ? 1 : dir * 1.5 } as CSSProperties}\n className=\"absolute flex min-w-0 items-center gap-2 motion-safe:animate-field-swap-out motion-reduce:animate-fade-out\"\n >\n {children}\n </span>\n );\n}\n\nexport function MorphSelect({\n label,\n labelHidden = false,\n items,\n value,\n defaultValue = null,\n onValueChange,\n placeholder = \"Select\",\n searchable = \"auto\",\n searchPlaceholder,\n panelWidth = 272,\n maxListHeight = 296,\n align = \"start\",\n size = \"md\",\n name,\n disabled = false,\n id,\n isInvalid = false,\n errorMessage,\n className,\n}: MorphSelectProps) {\n const uid = useId();\n const labelId = `${uid}-label`;\n const errorId = `${uid}-error`;\n // As on Select: the message shows only while the field is invalid.\n const showError = isInvalid && errorMessage != null;\n const listId = `${uid}-list`;\n const optionId = (index: number) => `${uid}-option-${index}`;\n\n const rootRef = useRef<HTMLDivElement>(null);\n const anchorRef = useRef<HTMLDivElement>(null);\n const shapeRef = useRef<HTMLDivElement>(null);\n const measureRef = useRef<HTMLSpanElement>(null);\n const faceRef = useRef<HTMLDivElement>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n // An outside <label htmlFor={id}> names the trigger natively. aria-labelledby\n // outranks it, so while one exists the trigger does not point at the inner label.\n const [outsideLabel, setOutsideLabel] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n const slotRef = useRef<HTMLSpanElement>(null);\n const layerRef = useRef<HTMLSpanElement>(null);\n const highlightRef = useRef<HTMLSpanElement>(null);\n const searchRowRef = useRef<HTMLDivElement>(null);\n const optionRefs = useRef(new Map<string, HTMLElement>());\n\n const sections = useMemo(() => normalize(items), [items]);\n const all = useMemo(() => sections.flatMap((section) => section.options), [sections]);\n const indexOf = useMemo(() => new Map(all.map((option, index) => [option.value, index])), [all]);\n const canSearch = searchable === \"auto\" ? all.length > 8 : searchable;\n\n const [inner, setInner] = useState<string | null>(defaultValue);\n const selected = value !== undefined ? value : inner;\n const selectedOption = selected === null ? undefined : all.find((option) => option.value === selected);\n\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [active, setActive] = useState<string | null>(null);\n const [change, setChange] = useState<Change>({ value: null, dir: 1, fly: null });\n\n const needle = query.trim().toLowerCase();\n const visibleSections = useMemo(\n () =>\n needle\n ? sections\n .map((section) => ({\n ...section,\n options: section.options.filter((option) => matches(option, needle)),\n }))\n .filter((section) => section.options.length)\n : sections,\n [needle, sections],\n );\n const enabled = useMemo(\n () => visibleSections.flatMap((section) => section.options).filter((option) => !option.disabled),\n [visibleSections],\n );\n const current =\n active !== null && enabled.some((option) => option.value === active)\n ? active\n : (enabled[0]?.value ?? null);\n const resultCount = visibleSections.reduce((sum, section) => sum + section.options.length, 0);\n\n /* ---------------------------------------------- The shape ---------------------------------------------- */\n\n // One surface whose width and height follow the trigger and the open list. The sizes are written straight to the\n // two boxes, and the transitions in CSS do the moving, so a new label never waits on a render. Nothing animates\n // until the first measurement has landed (data-ready).\n const sizes = useRef({ trigger: 0, lid: 0, listW: 0, listH: 0 });\n const live = useRef({ open: false, anchorW: 0 });\n\n const place = useCallback(() => {\n const { trigger, lid, listW, listH } = sizes.current;\n const shape = shapeRef.current;\n const anchor = anchorRef.current;\n if (!trigger || !lid || !shape || !anchor) return;\n const isOpen = live.current.open;\n // A trigger that widens springs; one that narrows never overshoots, so it never dips under its label.\n if (trigger !== live.current.anchorW) {\n anchor.toggleAttribute(\"data-widening\", trigger > live.current.anchorW);\n live.current.anchorW = trigger;\n }\n anchor.style.width = `${trigger}px`;\n shape.style.width = `${isOpen ? Math.max(trigger, listW) : trigger}px`;\n shape.style.height = `${isOpen ? lid + listH : lid}px`;\n }, []);\n\n const read = useCallback(() => {\n const measure = measureRef.current;\n const face = faceRef.current;\n const root = rootRef.current;\n if (!measure || !face || !root) return;\n // The list is never narrower than the trigger; the width goes to CSS directly so a new label never waits on a render.\n const trigger = triggerWidth(measure);\n root.style.setProperty(\"--ms-trigger-w\", `${trigger}px`);\n sizes.current = {\n trigger,\n lid: measure.offsetHeight,\n listW: face.offsetWidth,\n listH: face.offsetHeight,\n };\n place();\n }, [place]);\n\n useIsomorphicLayoutEffect(() => {\n const measure = measureRef.current;\n const face = faceRef.current;\n const root = rootRef.current;\n if (!measure || !face || !root) return;\n read();\n // The first sizes are in place: from here on a change of size moves.\n void shapeRef.current?.offsetWidth;\n root.setAttribute(\"data-ready\", \"\");\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(read);\n observer.observe(measure);\n observer.observe(face);\n return () => observer.disconnect();\n }, [read]);\n\n useIsomorphicLayoutEffect(() => {\n if (live.current.open === open) return;\n live.current.open = open;\n // A pick changes the label and closes in the same commit, so both faces are read fresh before the shape moves.\n read();\n }, [open, read]);\n\n /* ---------------------------------------------- The highlight ---------------------------------------------- */\n\n const scrollIntent = useRef(false);\n useIsomorphicLayoutEffect(() => {\n const highlight = highlightRef.current;\n if (!highlight) return;\n const node = open && current !== null ? optionRefs.current.get(current) : undefined;\n if (!node) {\n highlight.removeAttribute(\"data-on\");\n return;\n }\n const top = node.offsetTop;\n const height = node.offsetHeight;\n // It appears where it is needed, and glides from there.\n highlight.toggleAttribute(\"data-jump\", !highlight.hasAttribute(\"data-on\") || prefersReducedMotion());\n highlight.style.translate = `0 ${top}px`;\n highlight.style.height = `${height}px`;\n highlight.setAttribute(\"data-on\", \"\");\n const scroller = scrollRef.current;\n if (scroller && scrollIntent.current) {\n scrollIntent.current = false;\n const pad = 6;\n if (top < scroller.scrollTop + pad) scroller.scrollTop = top - pad;\n else if (top + height > scroller.scrollTop + scroller.clientHeight - pad)\n scroller.scrollTop = top + height - scroller.clientHeight + pad;\n }\n }, [current, open, visibleSections]);\n\n /* ---------------------------------------------- Focus and keys ---------------------------------------------- */\n\n useIsomorphicLayoutEffect(() => {\n setOutsideLabel(id != null && (triggerRef.current?.labels?.length ?? 0) > 0);\n }, [id]);\n\n const showSearch = open && canSearch;\n // Written as attributes, since React 18 and 19 disagree about the `inert` prop and both agree about the attribute: the\n // closed list and the idle search row take no focus and are hidden from assistive technology; a search row in use replaces the trigger.\n useIsomorphicLayoutEffect(() => {\n triggerRef.current?.toggleAttribute(\"inert\", showSearch);\n searchRowRef.current?.toggleAttribute(\"inert\", !showSearch);\n const face = faceRef.current;\n if (!face) return;\n face.toggleAttribute(\"inert\", !open);\n if (open) face.removeAttribute(\"aria-hidden\");\n else face.setAttribute(\"aria-hidden\", \"true\");\n }, [open, showSearch]);\n\n // Focus lands in the lid: the search field when there is one, otherwise the trigger that keeps the active option.\n const pendingFocus = useRef<\"trigger\" | \"input\" | null>(null);\n useIsomorphicLayoutEffect(() => {\n const target = pendingFocus.current;\n pendingFocus.current = null;\n if (target === \"input\") inputRef.current?.focus({ preventScroll: true });\n if (target === \"trigger\") triggerRef.current?.focus({ preventScroll: true });\n }, [open]);\n\n const typeahead = useRef({ buffer: \"\", at: 0 });\n /** Space opens and picks on keydown; the click a button fires on the matching keyup must not undo it. */\n const suppressClick = useRef(false);\n const [announcement, setAnnouncement] = useState(\"\");\n\n function openList(focus?: string | null, seed = \"\") {\n if (disabled || open) return;\n setQuery(seed);\n setActive(focus !== undefined ? focus : selected);\n scrollIntent.current = true;\n pendingFocus.current = canSearch ? \"input\" : \"trigger\";\n setAnnouncement(\"\");\n setOpen(true);\n }\n\n const close = useCallback((restoreFocus: boolean) => {\n typeahead.current.buffer = \"\";\n if (restoreFocus) pendingFocus.current = \"trigger\";\n setOpen(false);\n }, []);\n\n function commit(option: MorphSelectOption | undefined, source?: HTMLElement | null) {\n if (!option || option.disabled) return;\n if (option.value !== selected) {\n // The chosen label starts where it stood in the list and settles into the trigger.\n let fly: Change[\"fly\"] = null;\n const slot = slotRef.current;\n const part = source?.querySelector<HTMLElement>(\"[data-part='value']\");\n if (!prefersReducedMotion() && part && slot) {\n const from = part.getBoundingClientRect();\n const to = slot.getBoundingClientRect();\n fly = { x: from.left - to.left, y: from.top + from.height / 2 - (to.top + to.height / 2) };\n }\n const from = selected === null ? -1 : (indexOf.get(selected) ?? -1);\n const to = indexOf.get(option.value) ?? 0;\n setChange({ value: option.value, dir: from < 0 ? 1 : Math.sign(to - from) || 1, fly });\n if (value === undefined) setInner(option.value);\n onValueChange?.(option.value, option);\n }\n close(true);\n }\n\n function typeTo(char: string) {\n const now = performance.now();\n const state = typeahead.current;\n state.buffer = now - state.at > TYPEAHEAD_RESET ? char : state.buffer + char;\n state.at = now;\n const buffer = state.buffer.toLowerCase();\n const repeated = buffer.split(\"\").every((letter) => letter === buffer[0]);\n const search = repeated ? buffer[0]! : buffer;\n const pool = all.filter((option) => !option.disabled);\n const from = open ? current : selected;\n const at = pool.findIndex((option) => option.value === from);\n const offset = repeated || buffer.length === 1 ? 1 : 0;\n const ordered = [...pool.slice(at + offset), ...pool.slice(0, at + offset)];\n return ordered.find((option) => option.label.toLowerCase().startsWith(search));\n }\n\n function move(to: string | null | undefined) {\n if (to === undefined || to === null) return;\n scrollIntent.current = true;\n setActive(to);\n }\n\n function onKey(event: KeyboardEvent<HTMLElement>, fromInput: boolean) {\n const { key } = event;\n if (key === \" \" && !fromInput) suppressClick.current = true;\n const printable = key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey;\n const typing =\n performance.now() - typeahead.current.at < TYPEAHEAD_RESET && typeahead.current.buffer.length > 0;\n\n if (!open) {\n if (key === \"ArrowDown\" || key === \"ArrowUp\" || key === \"Enter\" || (key === \" \" && !typing)) {\n event.preventDefault();\n openList();\n return;\n }\n if (key === \"Home\" || key === \"End\") {\n event.preventDefault();\n const pool = all.filter((option) => !option.disabled);\n openList((key === \"Home\" ? pool[0] : pool[pool.length - 1])?.value ?? null);\n return;\n }\n if (printable) {\n event.preventDefault();\n if (canSearch) openList(undefined, key);\n else {\n const hit = typeTo(key);\n openList(hit?.value ?? selected);\n }\n }\n return;\n }\n\n const at = enabled.findIndex((option) => option.value === current);\n const caretKeys = fromInput && query.length > 0;\n const pick = () => commit(enabled[at], optionRefs.current.get(current ?? \"\"));\n switch (key) {\n case \"ArrowDown\":\n event.preventDefault();\n move(enabled[Math.min(enabled.length - 1, at + 1)]?.value);\n return;\n case \"ArrowUp\":\n event.preventDefault();\n if (event.altKey) {\n pick();\n return;\n }\n move(enabled[Math.max(0, at - 1)]?.value);\n return;\n case \"PageDown\":\n event.preventDefault();\n move(enabled[Math.min(enabled.length - 1, at + PAGE)]?.value);\n return;\n case \"PageUp\":\n event.preventDefault();\n move(enabled[Math.max(0, at - PAGE)]?.value);\n return;\n case \"Home\":\n if (caretKeys) return;\n event.preventDefault();\n move(enabled[0]?.value);\n return;\n case \"End\":\n if (caretKeys) return;\n event.preventDefault();\n move(enabled[enabled.length - 1]?.value);\n return;\n case \"Enter\":\n event.preventDefault();\n pick();\n return;\n case \"Escape\":\n event.preventDefault();\n event.stopPropagation();\n close(true);\n return;\n case \"Tab\":\n close(false);\n return;\n }\n if (fromInput) return;\n if (key === \" \" && !typing) {\n event.preventDefault();\n pick();\n return;\n }\n if (printable) {\n event.preventDefault();\n move(typeTo(key)?.value);\n }\n }\n\n // A press outside or focus leaving the control folds the list back into the trigger.\n useEffect(() => {\n if (!open) return;\n const down = (event: PointerEvent) => {\n if (!rootRef.current?.contains(event.target as Node)) close(false);\n };\n document.addEventListener(\"pointerdown\", down);\n return () => document.removeEventListener(\"pointerdown\", down);\n }, [close, open]);\n function onBlur(event: FocusEvent<HTMLDivElement>) {\n const next = event.relatedTarget as Node | null;\n if (open && next && !event.currentTarget.contains(next)) close(false);\n }\n\n function onQuery(next: string) {\n setQuery(next);\n setActive(null);\n scrollIntent.current = true;\n if (scrollRef.current) scrollRef.current.scrollTop = 0;\n const text = next.trim().toLowerCase();\n const count = text ? all.filter((option) => matches(option, text)).length : all.length;\n setAnnouncement(text ? (count ? `${count} ${count === 1 ? \"option\" : \"options\"}` : \"No matches\") : \"\");\n }\n\n /* ---------------------------------------------- The chosen label ---------------------------------------------- */\n\n // The trigger's value is a layer: a new one flies in from the list (or rolls, when it cannot) while the old one lifts away.\n const layerKey = selected ?? \"__empty\";\n const flying = change.value === selected ? change.fly : null;\n const { leaving, changed, remove } = useLeavingItems(\n layerKey,\n { key: layerKey, node: <Value option={selectedOption} placeholder={placeholder} />, dir: change.dir, fly: Boolean(flying) },\n () => layerRef.current,\n );\n\n const rootStyle = {\n \"--ms-panel-w\": `${panelWidth}px`,\n \"--ms-list-max\": `${maxListHeight}px`,\n } as CSSProperties;\n const layerStyle = {\n \"--dir\": change.dir,\n ...(flying ? { \"--fly-x\": `${flying.x}px`, \"--fly-y\": `${flying.y}px` } : {}),\n } as CSSProperties;\n\n return (\n <div\n ref={rootRef}\n data-slot=\"morph-select\"\n data-open={open || undefined}\n data-align={align}\n data-size={size}\n data-disabled={disabled || undefined}\n data-invalid={isInvalid || undefined}\n style={rootStyle}\n onBlur={onBlur}\n className={cn(\n \"morph-select-root group/ms relative inline-grid min-w-0 justify-items-start gap-2 font-sans leading-(--leading-normal) text-foreground data-[align=end]:justify-items-end data-[open]:z-30 data-[size=sm]:text-sm data-[size=md]:text-base data-[size=lg]:text-base\",\n className,\n )}\n >\n <span id={labelId} data-slot=\"morph-select-label\" className={cnAll(labelVariants(), labelHidden && \"sr-only\")}>\n {label}\n </span>\n\n <div ref={anchorRef} className=\"morph-select-anchor\">\n {/* Sizes the closed trigger. It holds the chosen label with the trigger's own padding, so the width has a target before anything moves. */}\n <span ref={measureRef} aria-hidden=\"true\" className={cnAll(triggerLayout, \"morph-select-measure\")}>\n <Value option={selectedOption} placeholder={placeholder} />\n </span>\n\n <div\n ref={shapeRef}\n data-slot=\"morph-select-shape\"\n data-open={open || undefined}\n data-disabled={disabled || undefined}\n data-invalid={isInvalid || undefined}\n className=\"morph-select-shape\"\n >\n {/* The lid is the trigger row. It stays at the top of the surface in both states and sits above the list, so a lifted label passes over it. */}\n <div className=\"absolute inset-x-0 top-0 z-10 h-(--ms-h)\">\n <button\n ref={triggerRef}\n id={id}\n type=\"button\"\n role=\"combobox\"\n data-slot=\"morph-select-trigger\"\n aria-labelledby={outsideLabel ? undefined : labelId}\n aria-invalid={isInvalid || undefined}\n aria-describedby={showError ? errorId : undefined}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-controls={listId}\n aria-activedescendant={\n open && !canSearch && current !== null ? optionId(indexOf.get(current) ?? 0) : undefined\n }\n disabled={disabled}\n tabIndex={showSearch ? -1 : 0}\n onClick={() => {\n if (suppressClick.current) {\n suppressClick.current = false;\n return;\n }\n if (open) close(true);\n else openList();\n }}\n onKeyDown={(event) => onKey(event, false)}\n onKeyUp={(event) => {\n if (event.key === \" \")\n window.setTimeout(() => {\n suppressClick.current = false;\n });\n }}\n className={cnAll(\n triggerLayout,\n \"absolute inset-0 w-full cursor-pointer border-0 bg-transparent text-start text-foreground outline-none [font-family:inherit] [font-size:inherit] [line-height:inherit] [letter-spacing:inherit] [-webkit-tap-highlight-color:transparent] [touch-action:manipulation] disabled:cursor-not-allowed\",\n )}\n >\n <span\n ref={slotRef}\n data-hidden={showSearch || undefined}\n className=\"relative block h-full min-w-0 flex-1 data-[hidden]:opacity-0 motion-safe:transition-opacity motion-safe:duration-(--duration-instant) motion-safe:ease-settle\"\n >\n {leaving.map((item) => (\n <LeavingLayer\n key={item.id}\n id={item.id}\n rect={item.rect}\n dir={item.value.dir}\n fly={item.value.fly}\n onDone={remove}\n >\n {item.value.node}\n </LeavingLayer>\n ))}\n <span\n key={layerKey}\n ref={layerRef}\n style={layerStyle}\n className={cnAll(\n \"absolute inset-0 flex min-w-0 items-center gap-2\",\n changed &&\n (flying\n ? \"motion-safe:animate-morph-select-fly motion-reduce:animate-fade-in\"\n : \"motion-safe:animate-select-value-in motion-reduce:animate-fade-in\"),\n )}\n >\n <Value option={selectedOption} placeholder={placeholder} />\n </span>\n </span>\n </button>\n\n {canSearch ? (\n <div\n ref={searchRowRef}\n className={cnAll(\n \"absolute inset-0 flex items-center gap-2 ps-(--ms-pad) pe-1.5 motion-safe:transition-opacity motion-safe:duration-(--duration-quick) motion-safe:ease-settle\",\n showSearch ? \"opacity-100\" : \"pointer-events-none opacity-0\",\n )}\n >\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 flex-none text-muted-foreground\"\n >\n <circle cx=\"7\" cy=\"7\" r=\"4.25\" />\n <path d=\"m10.5 10.5 3 3\" />\n </svg>\n <input\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n data-slot=\"morph-select-search\"\n aria-label={`Search ${label.toLowerCase()}`}\n aria-invalid={isInvalid || undefined}\n aria-describedby={showError ? errorId : undefined}\n aria-expanded={open}\n aria-controls={listId}\n aria-autocomplete=\"list\"\n aria-activedescendant={\n open && current !== null ? optionId(indexOf.get(current) ?? 0) : undefined\n }\n placeholder={searchPlaceholder ?? selectedOption?.label ?? \"Search\"}\n value={query}\n autoComplete=\"off\"\n spellCheck={false}\n onChange={(event) => onQuery(event.target.value)}\n onKeyDown={(event) => onKey(event, true)}\n className=\"h-full min-w-0 flex-1 border-0 bg-transparent p-0 [font:inherit] text-foreground outline-none placeholder:text-muted-foreground\"\n />\n {query ? (\n <button\n type=\"button\"\n aria-label=\"Clear search\"\n onPointerDown={(event) => event.preventDefault()}\n onClick={() => {\n onQuery(\"\");\n inputRef.current?.focus();\n }}\n className=\"grid size-6 flex-none cursor-pointer place-items-center rounded-pill border-0 bg-muted p-0 text-text-secondary outline-none motion-safe:animate-field-clear-in motion-reduce:animate-fade-in hover:text-foreground\"\n >\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n className=\"size-3.5\"\n >\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </svg>\n </button>\n ) : null}\n {/* Sits exactly over the shared chevron, so the chevron closes the list in both lid states. */}\n <button\n type=\"button\"\n aria-label={`Close ${label.toLowerCase()}`}\n onClick={() => close(true)}\n className=\"size-9 flex-none cursor-pointer rounded-pill border-0 bg-transparent p-0 outline-none motion-safe:transition-[background-color] motion-safe:duration-(--duration-quick) motion-safe:ease-settle hover:bg-muted\"\n />\n </div>\n ) : null}\n\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"pointer-events-none absolute end-4 top-1/2 -mt-2 size-4 text-muted-foreground motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-data-[open]/ms:rotate-180\"\n >\n <path d=\"m4 6 4 4 4-4\" />\n </svg>\n </div>\n\n <div\n ref={faceRef}\n data-open={open || undefined}\n className=\"morph-select-face\"\n >\n <div\n ref={scrollRef}\n className=\"max-h-(--ms-list-max) overflow-y-auto overscroll-contain pt-1.5 [scrollbar-width:thin]\"\n >\n <div id={listId} role=\"listbox\" aria-labelledby={labelId} className=\"relative grid grid-cols-1 gap-0.5\">\n <span ref={highlightRef} aria-hidden=\"true\" className=\"morph-select-highlight\" />\n {visibleSections.map((section) => {\n const rows = section.options.map((option) => {\n const index = indexOf.get(option.value) ?? 0;\n const isSelected = option.value === selected;\n return (\n <div\n key={option.value}\n id={optionId(index)}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || undefined}\n data-slot=\"morph-select-option\"\n data-active={option.value === current || undefined}\n ref={(node) => {\n if (node) optionRefs.current.set(option.value, node);\n else optionRefs.current.delete(option.value);\n }}\n onPointerMove={(event) => {\n if (event.pointerType === \"mouse\" && !option.disabled && option.value !== current)\n setActive(option.value);\n }}\n onPointerDown={(event) => event.preventDefault()}\n onClick={(event) => commit(option, event.currentTarget)}\n className=\"morph-select-option relative box-border flex min-h-9 cursor-pointer select-none items-center gap-2 rounded-lg text-text-secondary outline-none data-[active]:text-foreground aria-selected:text-foreground aria-disabled:cursor-not-allowed aria-disabled:opacity-(--opacity-disabled) motion-safe:transition-colors motion-safe:duration-(--duration-quick) motion-safe:ease-settle\"\n >\n <span data-part=\"value\" className=\"flex min-w-0 flex-1 items-center gap-2 font-medium\">\n {option.icon ? (\n <span className={iconClass} aria-hidden=\"true\">\n {option.icon}\n </span>\n ) : null}\n <span className=\"min-w-0 truncate\">{option.label}</span>\n </span>\n {option.meta ? (\n <span className=\"flex-none text-xs tabular-nums text-muted-foreground\">\n {option.meta}\n </span>\n ) : null}\n <span\n aria-hidden=\"true\"\n data-on={isSelected || undefined}\n className=\"grid size-4 flex-none scale-60 place-items-center text-foreground opacity-0 data-[on]:scale-100 data-[on]:opacity-100 motion-safe:transition-[opacity,scale] motion-safe:duration-(--duration-quick) motion-safe:ease-settle\"\n >\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4\"\n >\n <path d=\"m3.5 8.5 3 3 6-6\" />\n </svg>\n </span>\n </div>\n );\n });\n return section.label ? (\n <div\n key={section.key}\n role=\"group\"\n aria-labelledby={`${uid}-${section.key}`}\n className=\"grid grid-cols-1 gap-0.5 not-first:mt-1\"\n >\n <div\n id={`${uid}-${section.key}`}\n role=\"presentation\"\n className=\"px-2.5 pt-2 pb-1 text-xs text-muted-foreground\"\n >\n {section.label}\n </div>\n {rows}\n </div>\n ) : (\n <div key={section.key} role=\"presentation\" className=\"grid grid-cols-1 gap-0.5 not-first:mt-1\">\n {rows}\n </div>\n );\n })}\n {resultCount === 0 ? (\n <p className=\"m-0 px-2.5 pt-3 pb-3.5 text-sm text-muted-foreground\">\n No matches for “{query.trim()}”\n </p>\n ) : null}\n </div>\n </div>\n </div>\n </div>\n </div>\n\n {showError ? (\n <span\n id={errorId}\n data-slot=\"morph-select-error\"\n className=\"text-sm leading-(--leading-normal) text-destructive-strong\"\n >\n {errorMessage}\n </span>\n ) : null}\n {name ? <input type=\"hidden\" name={name} value={selected ?? \"\"} /> : null}\n <span className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {announcement}\n </span>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"morph-select.js","names":[],"sources":["../../src/morph-select/morph-select.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\n\nimport { labelVariants } from \"../label/label.js\";\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport {\n prefersReducedMotion,\n useExitBackstop,\n useIsomorphicLayoutEffect,\n useLeavingItems,\n type Rect,\n} from \"../lib/motion.js\";\n\n/** One choice. */\nexport interface MorphSelectOption {\n value: string;\n label: string;\n /** A 20px mark shown before the label, in the list and in the trigger. An avatar or a plain icon. */\n icon?: ReactNode;\n /** Short trailing detail such as an offset or a count. Rendered with tabular numerals. */\n meta?: string;\n /** Extra words that match this option when searching. */\n keywords?: string;\n disabled?: boolean;\n}\n\n/** A labelled run of options. */\nexport interface MorphSelectGroup {\n label: string;\n options: MorphSelectOption[];\n}\n\nexport type MorphSelectItem = MorphSelectOption | MorphSelectGroup;\n\n/**\n * MorphSelect: a select whose trigger is the list. Opening grows the trigger itself\n * into the options surface (its width, height and corners spring from the closed\n * control to the open panel), a highlight glides between options, and the chosen\n * option's label lifts out of the list and settles into the trigger while the trigger\n * springs to its new width. Supports groups, type-ahead, search for long lists, and the\n * full listbox keyboard model: arrows, Home, End, PageUp, PageDown, Enter, Space,\n * Escape and Alt+ArrowUp.\n *\n * Use it for compact property pickers and form fields with up to a few hundred options.\n * Prefer a combobox when people mostly type free text, and Select when the list should\n * float over the page instead of growing from the trigger.\n *\n * It is its own listbox rather than a React Aria Select: a Select's list lives in a\n * popover, a separate surface that cannot be the trigger.\n */\nexport interface MorphSelectProps {\n /** Visible label, also the accessible name of the trigger and the list. */\n label: string;\n /** Keeps the label for assistive technology only. */\n labelHidden?: boolean;\n items: MorphSelectItem[];\n /** The chosen value. Controlled when given; `null` is nothing chosen. */\n value?: string | null;\n defaultValue?: string | null;\n onValueChange?: (value: string, option: MorphSelectOption) => void;\n /** Shown while nothing is chosen. @default \"Select\" */\n placeholder?: string;\n /** Shows a search field in place of the trigger when open. \"auto\" turns it on above eight options. @default \"auto\" */\n searchable?: boolean | \"auto\";\n searchPlaceholder?: string;\n /** Width of the open surface in px. It never gets narrower than the trigger. @default 272 */\n panelWidth?: number;\n /** Tallest the option list gets before it scrolls, in px. @default 296 */\n maxListHeight?: number;\n /** Which edge of the trigger stays put while the surface grows. @default \"start\" */\n align?: \"start\" | \"end\";\n /**\n * Control scale: the same heights as Select and Input.\n * @default \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Adds a hidden input for native form submission. */\n name?: string;\n disabled?: boolean;\n /**\n * The trigger's `id`, so a `<label htmlFor>` outside the select (or a form\n * library that focuses a field by id) reaches the real control. When such a\n * label exists it NAMES the trigger: the trigger then drops its\n * `aria-labelledby` to the inner `label`, which would otherwise outrank the\n * outside label in the accessible-name computation.\n */\n id?: string;\n /**\n * Marks the choice as wrong: the trigger takes the invalid border and is\n * `aria-invalid`. Pair it with `errorMessage`, or the state is shown visually in colour only.\n */\n isInvalid?: boolean;\n /**\n * Why the choice is wrong, under the control. It shows only while `isInvalid`\n * is set, and is named in the trigger's and the search field's `aria-describedby`.\n */\n errorMessage?: ReactNode;\n /**\n * Stretches the trigger to fill its container, like Input and Select, instead of hugging\n * its content up to 20rem. The open surface is then as wide as the trigger. `panelWidth`\n * does not apply.\n * @default false\n */\n fullWidth?: boolean;\n /** Extra classes for the wrapper. Merged last. */\n className?: string;\n}\n\ninterface Section {\n key: string;\n label?: string;\n options: MorphSelectOption[];\n}\ninterface Change {\n value: string | null;\n dir: number;\n fly: { x: number; y: number } | null;\n}\n\nconst TYPEAHEAD_RESET = 600;\nconst PAGE = 8;\n\nconst isGroup = (item: MorphSelectItem): item is MorphSelectGroup => \"options\" in item;\n\n/** Loose options between groups gather into unlabelled sections, so the list keeps the order it was given. */\nfunction normalize(items: MorphSelectItem[]): Section[] {\n const out: Section[] = [];\n items.forEach((item, index) => {\n if (isGroup(item)) {\n out.push({ key: `group-${index}`, label: item.label, options: item.options });\n return;\n }\n const last = out[out.length - 1];\n if (last && !last.label) last.options.push(item);\n else out.push({ key: `loose-${index}`, options: [item] });\n });\n return out;\n}\n\nconst matches = (option: MorphSelectOption, needle: string) =>\n `${option.label} ${option.meta ?? \"\"} ${option.keywords ?? \"\"}`.toLowerCase().includes(needle);\n\n/** The closed trigger's width, rounded up. offsetWidth rounds to the nearest pixel, and half a pixel short is enough to ellipsize the label. */\nfunction triggerWidth(node: HTMLElement) {\n const exact = node.getBoundingClientRect().width;\n const rounded = node.offsetWidth;\n // Under a scaled ancestor the rect is scaled too, so fall back to the layout width with a pixel of slack.\n return Math.abs(exact - rounded) <= 0.5 ? Math.ceil(exact - 0.01) : rounded + 1;\n}\n\n/** The layout the closed trigger and its invisible measuring copy share, so the copy's width is the trigger's. */\nconst triggerLayout =\n \"box-border flex h-(--ms-h) items-center gap-2 ps-(--ms-pad) pe-10 font-medium whitespace-nowrap\";\n\nconst iconClass =\n \"grid size-5 flex-none place-items-center text-text-secondary [&>svg]:size-4 [&>img]:size-5 [&>img]:rounded-full [&>img]:object-cover\";\n\n/** The label a layer shows: the icon, then the text, or the placeholder. */\nfunction Value({ option, placeholder }: { option: MorphSelectOption | undefined; placeholder: string }) {\n if (!option) return <span className=\"font-normal text-muted-foreground\">{placeholder}</span>;\n return (\n <>\n {option.icon ? (\n <span className={iconClass} aria-hidden=\"true\">\n {option.icon}\n </span>\n ) : null}\n <span className=\"min-w-0 truncate\">{option.label}</span>\n </>\n );\n}\n\n/** A replaced label on its way out, popped out of flow where it stood and lifting away. */\nfunction LeavingLayer({\n id,\n rect,\n dir,\n fly,\n onDone,\n children,\n}: {\n id: number;\n rect: Rect;\n dir: number;\n fly: boolean;\n onDone: (id: number) => void;\n children: ReactNode;\n}) {\n const remove = useCallback(() => onDone(id), [onDone, id]);\n useExitBackstop(true, remove);\n return (\n <span\n aria-hidden=\"true\"\n data-leaving=\"\"\n onAnimationEnd={(event) => {\n if (event.target === event.currentTarget) remove();\n }}\n // field-swap-out lifts by 0.3em times --dir: a label that flew to the trigger lifts 0.3em, one that rolled lifts 0.45em the way the list ran.\n style={{ ...rect, \"--dir\": fly ? 1 : dir * 1.5 } as CSSProperties}\n className=\"absolute flex min-w-0 items-center gap-2 motion-safe:animate-field-swap-out motion-reduce:animate-fade-out\"\n >\n {children}\n </span>\n );\n}\n\nexport function MorphSelect({\n label,\n labelHidden = false,\n items,\n value,\n defaultValue = null,\n onValueChange,\n placeholder = \"Select\",\n searchable = \"auto\",\n searchPlaceholder,\n panelWidth = 272,\n maxListHeight = 296,\n align = \"start\",\n size = \"md\",\n name,\n disabled = false,\n id,\n isInvalid = false,\n errorMessage,\n fullWidth = false,\n className,\n}: MorphSelectProps) {\n const uid = useId();\n const labelId = `${uid}-label`;\n const errorId = `${uid}-error`;\n // As on Select: the message shows only while the field is invalid.\n const showError = isInvalid && errorMessage != null;\n const listId = `${uid}-list`;\n const optionId = (index: number) => `${uid}-option-${index}`;\n\n const rootRef = useRef<HTMLDivElement>(null);\n const anchorRef = useRef<HTMLDivElement>(null);\n const shapeRef = useRef<HTMLDivElement>(null);\n const measureRef = useRef<HTMLSpanElement>(null);\n const faceRef = useRef<HTMLDivElement>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n // An outside <label htmlFor={id}> names the trigger natively. aria-labelledby\n // outranks it, so while one exists the trigger does not point at the inner label.\n const [outsideLabel, setOutsideLabel] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n const slotRef = useRef<HTMLSpanElement>(null);\n const layerRef = useRef<HTMLSpanElement>(null);\n const highlightRef = useRef<HTMLSpanElement>(null);\n const searchRowRef = useRef<HTMLDivElement>(null);\n const optionRefs = useRef(new Map<string, HTMLElement>());\n\n const sections = useMemo(() => normalize(items), [items]);\n const all = useMemo(() => sections.flatMap((section) => section.options), [sections]);\n const indexOf = useMemo(() => new Map(all.map((option, index) => [option.value, index])), [all]);\n const canSearch = searchable === \"auto\" ? all.length > 8 : searchable;\n\n const [inner, setInner] = useState<string | null>(defaultValue);\n const selected = value !== undefined ? value : inner;\n const selectedOption = selected === null ? undefined : all.find((option) => option.value === selected);\n\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [active, setActive] = useState<string | null>(null);\n const [change, setChange] = useState<Change>({ value: null, dir: 1, fly: null });\n\n const needle = query.trim().toLowerCase();\n const visibleSections = useMemo(\n () =>\n needle\n ? sections\n .map((section) => ({\n ...section,\n options: section.options.filter((option) => matches(option, needle)),\n }))\n .filter((section) => section.options.length)\n : sections,\n [needle, sections],\n );\n const enabled = useMemo(\n () => visibleSections.flatMap((section) => section.options).filter((option) => !option.disabled),\n [visibleSections],\n );\n const current =\n active !== null && enabled.some((option) => option.value === active)\n ? active\n : (enabled[0]?.value ?? null);\n const resultCount = visibleSections.reduce((sum, section) => sum + section.options.length, 0);\n\n /* ---------------------------------------------- The shape ---------------------------------------------- */\n\n // One surface whose width and height follow the trigger and the open list. The sizes are written straight to the\n // two boxes, and the transitions in CSS do the moving, so a new label never waits on a render. Nothing animates\n // until the first measurement has landed (data-ready).\n const sizes = useRef({ trigger: 0, lid: 0, listW: 0, listH: 0 });\n const live = useRef({ open: false, anchorW: 0, full: fullWidth });\n\n const place = useCallback(() => {\n const { trigger, lid, listW, listH } = sizes.current;\n const shape = shapeRef.current;\n const anchor = anchorRef.current;\n if (!trigger || !lid || !shape || !anchor) return;\n const isOpen = live.current.open;\n // A trigger that widens springs; one that narrows never overshoots, so it never dips under its label.\n if (trigger !== live.current.anchorW) {\n anchor.toggleAttribute(\"data-widening\", trigger > live.current.anchorW);\n live.current.anchorW = trigger;\n }\n // Full width: the anchor is 100% in CSS, so it follows the container; only the shape needs the pixel width.\n anchor.style.width = live.current.full ? \"\" : `${trigger}px`;\n shape.style.width = `${isOpen ? Math.max(trigger, listW) : trigger}px`;\n shape.style.height = `${isOpen ? lid + listH : lid}px`;\n }, []);\n\n const read = useCallback(() => {\n const measure = measureRef.current;\n const face = faceRef.current;\n const root = rootRef.current;\n if (!measure || !face || !root) return;\n // The list is never narrower than the trigger; the width goes to CSS directly so a new label never waits on a render.\n const trigger = triggerWidth(measure);\n root.style.setProperty(\"--ms-trigger-w\", `${trigger}px`);\n sizes.current = {\n trigger,\n lid: measure.offsetHeight,\n listW: face.offsetWidth,\n listH: face.offsetHeight,\n };\n place();\n }, [place]);\n\n useIsomorphicLayoutEffect(() => {\n live.current.full = fullWidth;\n const measure = measureRef.current;\n const face = faceRef.current;\n const root = rootRef.current;\n if (!measure || !face || !root) return;\n read();\n // The first sizes are in place: from here on a change of size moves.\n void shapeRef.current?.offsetWidth;\n root.setAttribute(\"data-ready\", \"\");\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(read);\n observer.observe(measure);\n observer.observe(face);\n return () => observer.disconnect();\n }, [read, fullWidth]);\n\n useIsomorphicLayoutEffect(() => {\n if (live.current.open === open) return;\n live.current.open = open;\n // A pick changes the label and closes in the same commit, so both faces are read fresh before the shape moves.\n read();\n }, [open, read]);\n\n /* ---------------------------------------------- The highlight ---------------------------------------------- */\n\n const scrollIntent = useRef(false);\n useIsomorphicLayoutEffect(() => {\n const highlight = highlightRef.current;\n if (!highlight) return;\n const node = open && current !== null ? optionRefs.current.get(current) : undefined;\n if (!node) {\n highlight.removeAttribute(\"data-on\");\n return;\n }\n const top = node.offsetTop;\n const height = node.offsetHeight;\n // It appears where it is needed, and glides from there.\n highlight.toggleAttribute(\"data-jump\", !highlight.hasAttribute(\"data-on\") || prefersReducedMotion());\n highlight.style.translate = `0 ${top}px`;\n highlight.style.height = `${height}px`;\n highlight.setAttribute(\"data-on\", \"\");\n const scroller = scrollRef.current;\n if (scroller && scrollIntent.current) {\n scrollIntent.current = false;\n const pad = 6;\n if (top < scroller.scrollTop + pad) scroller.scrollTop = top - pad;\n else if (top + height > scroller.scrollTop + scroller.clientHeight - pad)\n scroller.scrollTop = top + height - scroller.clientHeight + pad;\n }\n }, [current, open, visibleSections]);\n\n /* ---------------------------------------------- Focus and keys ---------------------------------------------- */\n\n useIsomorphicLayoutEffect(() => {\n setOutsideLabel(id != null && (triggerRef.current?.labels?.length ?? 0) > 0);\n }, [id]);\n\n const showSearch = open && canSearch;\n // Written as attributes, since React 18 and 19 disagree about the `inert` prop and both agree about the attribute: the\n // closed list and the idle search row take no focus and are hidden from assistive technology; a search row in use replaces the trigger.\n useIsomorphicLayoutEffect(() => {\n triggerRef.current?.toggleAttribute(\"inert\", showSearch);\n searchRowRef.current?.toggleAttribute(\"inert\", !showSearch);\n const face = faceRef.current;\n if (!face) return;\n face.toggleAttribute(\"inert\", !open);\n if (open) face.removeAttribute(\"aria-hidden\");\n else face.setAttribute(\"aria-hidden\", \"true\");\n }, [open, showSearch]);\n\n // Focus lands in the lid: the search field when there is one, otherwise the trigger that keeps the active option.\n const pendingFocus = useRef<\"trigger\" | \"input\" | null>(null);\n useIsomorphicLayoutEffect(() => {\n const target = pendingFocus.current;\n pendingFocus.current = null;\n if (target === \"input\") inputRef.current?.focus({ preventScroll: true });\n if (target === \"trigger\") triggerRef.current?.focus({ preventScroll: true });\n }, [open]);\n\n const typeahead = useRef({ buffer: \"\", at: 0 });\n /** Space opens and picks on keydown; the click a button fires on the matching keyup must not undo it. */\n const suppressClick = useRef(false);\n const [announcement, setAnnouncement] = useState(\"\");\n\n function openList(focus?: string | null, seed = \"\") {\n if (disabled || open) return;\n setQuery(seed);\n setActive(focus !== undefined ? focus : selected);\n scrollIntent.current = true;\n pendingFocus.current = canSearch ? \"input\" : \"trigger\";\n setAnnouncement(\"\");\n setOpen(true);\n }\n\n const close = useCallback((restoreFocus: boolean) => {\n typeahead.current.buffer = \"\";\n if (restoreFocus) pendingFocus.current = \"trigger\";\n setOpen(false);\n }, []);\n\n function commit(option: MorphSelectOption | undefined, source?: HTMLElement | null) {\n if (!option || option.disabled) return;\n if (option.value !== selected) {\n // The chosen label starts where it stood in the list and settles into the trigger.\n let fly: Change[\"fly\"] = null;\n const slot = slotRef.current;\n const part = source?.querySelector<HTMLElement>(\"[data-part='value']\");\n if (!prefersReducedMotion() && part && slot) {\n const from = part.getBoundingClientRect();\n const to = slot.getBoundingClientRect();\n fly = { x: from.left - to.left, y: from.top + from.height / 2 - (to.top + to.height / 2) };\n }\n const from = selected === null ? -1 : (indexOf.get(selected) ?? -1);\n const to = indexOf.get(option.value) ?? 0;\n setChange({ value: option.value, dir: from < 0 ? 1 : Math.sign(to - from) || 1, fly });\n if (value === undefined) setInner(option.value);\n onValueChange?.(option.value, option);\n }\n close(true);\n }\n\n function typeTo(char: string) {\n const now = performance.now();\n const state = typeahead.current;\n state.buffer = now - state.at > TYPEAHEAD_RESET ? char : state.buffer + char;\n state.at = now;\n const buffer = state.buffer.toLowerCase();\n const repeated = buffer.split(\"\").every((letter) => letter === buffer[0]);\n const search = repeated ? buffer[0]! : buffer;\n const pool = all.filter((option) => !option.disabled);\n const from = open ? current : selected;\n const at = pool.findIndex((option) => option.value === from);\n const offset = repeated || buffer.length === 1 ? 1 : 0;\n const ordered = [...pool.slice(at + offset), ...pool.slice(0, at + offset)];\n return ordered.find((option) => option.label.toLowerCase().startsWith(search));\n }\n\n function move(to: string | null | undefined) {\n if (to === undefined || to === null) return;\n scrollIntent.current = true;\n setActive(to);\n }\n\n function onKey(event: KeyboardEvent<HTMLElement>, fromInput: boolean) {\n const { key } = event;\n if (key === \" \" && !fromInput) suppressClick.current = true;\n const printable = key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey;\n const typing =\n performance.now() - typeahead.current.at < TYPEAHEAD_RESET && typeahead.current.buffer.length > 0;\n\n if (!open) {\n if (key === \"ArrowDown\" || key === \"ArrowUp\" || key === \"Enter\" || (key === \" \" && !typing)) {\n event.preventDefault();\n openList();\n return;\n }\n if (key === \"Home\" || key === \"End\") {\n event.preventDefault();\n const pool = all.filter((option) => !option.disabled);\n openList((key === \"Home\" ? pool[0] : pool[pool.length - 1])?.value ?? null);\n return;\n }\n if (printable) {\n event.preventDefault();\n if (canSearch) openList(undefined, key);\n else {\n const hit = typeTo(key);\n openList(hit?.value ?? selected);\n }\n }\n return;\n }\n\n const at = enabled.findIndex((option) => option.value === current);\n const caretKeys = fromInput && query.length > 0;\n const pick = () => commit(enabled[at], optionRefs.current.get(current ?? \"\"));\n switch (key) {\n case \"ArrowDown\":\n event.preventDefault();\n move(enabled[Math.min(enabled.length - 1, at + 1)]?.value);\n return;\n case \"ArrowUp\":\n event.preventDefault();\n if (event.altKey) {\n pick();\n return;\n }\n move(enabled[Math.max(0, at - 1)]?.value);\n return;\n case \"PageDown\":\n event.preventDefault();\n move(enabled[Math.min(enabled.length - 1, at + PAGE)]?.value);\n return;\n case \"PageUp\":\n event.preventDefault();\n move(enabled[Math.max(0, at - PAGE)]?.value);\n return;\n case \"Home\":\n if (caretKeys) return;\n event.preventDefault();\n move(enabled[0]?.value);\n return;\n case \"End\":\n if (caretKeys) return;\n event.preventDefault();\n move(enabled[enabled.length - 1]?.value);\n return;\n case \"Enter\":\n event.preventDefault();\n pick();\n return;\n case \"Escape\":\n event.preventDefault();\n event.stopPropagation();\n close(true);\n return;\n case \"Tab\":\n close(false);\n return;\n }\n if (fromInput) return;\n if (key === \" \" && !typing) {\n event.preventDefault();\n pick();\n return;\n }\n if (printable) {\n event.preventDefault();\n move(typeTo(key)?.value);\n }\n }\n\n // A press outside or focus leaving the control folds the list back into the trigger.\n useEffect(() => {\n if (!open) return;\n const down = (event: PointerEvent) => {\n if (!rootRef.current?.contains(event.target as Node)) close(false);\n };\n document.addEventListener(\"pointerdown\", down);\n return () => document.removeEventListener(\"pointerdown\", down);\n }, [close, open]);\n function onBlur(event: FocusEvent<HTMLDivElement>) {\n const next = event.relatedTarget as Node | null;\n if (open && next && !event.currentTarget.contains(next)) close(false);\n }\n\n function onQuery(next: string) {\n setQuery(next);\n setActive(null);\n scrollIntent.current = true;\n if (scrollRef.current) scrollRef.current.scrollTop = 0;\n const text = next.trim().toLowerCase();\n const count = text ? all.filter((option) => matches(option, text)).length : all.length;\n setAnnouncement(text ? (count ? `${count} ${count === 1 ? \"option\" : \"options\"}` : \"No matches\") : \"\");\n }\n\n /* ---------------------------------------------- The chosen label ---------------------------------------------- */\n\n // The trigger's value is a layer: a new one flies in from the list (or rolls, when it cannot) while the old one lifts away.\n const layerKey = selected ?? \"__empty\";\n const flying = change.value === selected ? change.fly : null;\n const { leaving, changed, remove } = useLeavingItems(\n layerKey,\n { key: layerKey, node: <Value option={selectedOption} placeholder={placeholder} />, dir: change.dir, fly: Boolean(flying) },\n () => layerRef.current,\n );\n\n const rootStyle = {\n \"--ms-panel-w\": `${panelWidth}px`,\n \"--ms-list-max\": `${maxListHeight}px`,\n } as CSSProperties;\n const layerStyle = {\n \"--dir\": change.dir,\n ...(flying ? { \"--fly-x\": `${flying.x}px`, \"--fly-y\": `${flying.y}px` } : {}),\n } as CSSProperties;\n\n return (\n <div\n ref={rootRef}\n data-slot=\"morph-select\"\n data-open={open || undefined}\n data-align={align}\n data-full={fullWidth || undefined}\n data-size={size}\n data-disabled={disabled || undefined}\n data-invalid={isInvalid || undefined}\n style={rootStyle}\n onBlur={onBlur}\n className={cn(\n \"morph-select-root group/ms relative inline-grid data-[full]:grid data-[full]:w-full min-w-0 justify-items-start gap-2 font-sans leading-(--leading-normal) text-foreground data-[align=end]:justify-items-end data-[open]:z-30 data-[size=sm]:text-sm data-[size=md]:text-base data-[size=lg]:text-base\",\n className,\n )}\n >\n <span id={labelId} data-slot=\"morph-select-label\" className={cnAll(labelVariants(), labelHidden && \"sr-only\")}>\n {label}\n </span>\n\n <div ref={anchorRef} className=\"morph-select-anchor\">\n {/* Sizes the closed trigger. It holds the chosen label with the trigger's own padding, so the width has a target before anything moves. */}\n <span ref={measureRef} aria-hidden=\"true\" className={cnAll(triggerLayout, \"morph-select-measure\")}>\n <Value option={selectedOption} placeholder={placeholder} />\n </span>\n\n <div\n ref={shapeRef}\n data-slot=\"morph-select-shape\"\n data-open={open || undefined}\n data-disabled={disabled || undefined}\n data-invalid={isInvalid || undefined}\n className=\"morph-select-shape\"\n >\n {/* The lid is the trigger row. It stays at the top of the surface in both states and sits above the list, so a lifted label passes over it. */}\n <div className=\"absolute inset-x-0 top-0 z-10 h-(--ms-h)\">\n <button\n ref={triggerRef}\n id={id}\n type=\"button\"\n role=\"combobox\"\n data-slot=\"morph-select-trigger\"\n aria-labelledby={outsideLabel ? undefined : labelId}\n aria-invalid={isInvalid || undefined}\n aria-describedby={showError ? errorId : undefined}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-controls={listId}\n aria-activedescendant={\n open && !canSearch && current !== null ? optionId(indexOf.get(current) ?? 0) : undefined\n }\n disabled={disabled}\n tabIndex={showSearch ? -1 : 0}\n onClick={() => {\n if (suppressClick.current) {\n suppressClick.current = false;\n return;\n }\n if (open) close(true);\n else openList();\n }}\n onKeyDown={(event) => onKey(event, false)}\n onKeyUp={(event) => {\n if (event.key === \" \")\n window.setTimeout(() => {\n suppressClick.current = false;\n });\n }}\n className={cnAll(\n triggerLayout,\n \"absolute inset-0 w-full cursor-pointer border-0 bg-transparent text-start text-foreground outline-none [font-family:inherit] [font-size:inherit] [line-height:inherit] [letter-spacing:inherit] [-webkit-tap-highlight-color:transparent] [touch-action:manipulation] disabled:cursor-not-allowed\",\n )}\n >\n <span\n ref={slotRef}\n data-hidden={showSearch || undefined}\n className=\"relative block h-full min-w-0 flex-1 data-[hidden]:opacity-0 motion-safe:transition-opacity motion-safe:duration-(--duration-instant) motion-safe:ease-settle\"\n >\n {leaving.map((item) => (\n <LeavingLayer\n key={item.id}\n id={item.id}\n rect={item.rect}\n dir={item.value.dir}\n fly={item.value.fly}\n onDone={remove}\n >\n {item.value.node}\n </LeavingLayer>\n ))}\n <span\n key={layerKey}\n ref={layerRef}\n style={layerStyle}\n className={cnAll(\n \"absolute inset-0 flex min-w-0 items-center gap-2\",\n changed &&\n (flying\n ? \"motion-safe:animate-morph-select-fly motion-reduce:animate-fade-in\"\n : \"motion-safe:animate-select-value-in motion-reduce:animate-fade-in\"),\n )}\n >\n <Value option={selectedOption} placeholder={placeholder} />\n </span>\n </span>\n </button>\n\n {canSearch ? (\n <div\n ref={searchRowRef}\n className={cnAll(\n \"absolute inset-0 flex items-center gap-2 ps-(--ms-pad) pe-1.5 motion-safe:transition-opacity motion-safe:duration-(--duration-quick) motion-safe:ease-settle\",\n showSearch ? \"opacity-100\" : \"pointer-events-none opacity-0\",\n )}\n >\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 flex-none text-muted-foreground\"\n >\n <circle cx=\"7\" cy=\"7\" r=\"4.25\" />\n <path d=\"m10.5 10.5 3 3\" />\n </svg>\n <input\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n data-slot=\"morph-select-search\"\n aria-label={`Search ${label.toLowerCase()}`}\n aria-invalid={isInvalid || undefined}\n aria-describedby={showError ? errorId : undefined}\n aria-expanded={open}\n aria-controls={listId}\n aria-autocomplete=\"list\"\n aria-activedescendant={\n open && current !== null ? optionId(indexOf.get(current) ?? 0) : undefined\n }\n placeholder={searchPlaceholder ?? selectedOption?.label ?? \"Search\"}\n value={query}\n autoComplete=\"off\"\n spellCheck={false}\n onChange={(event) => onQuery(event.target.value)}\n onKeyDown={(event) => onKey(event, true)}\n className=\"h-full min-w-0 flex-1 border-0 bg-transparent p-0 [font:inherit] text-foreground outline-none placeholder:text-muted-foreground\"\n />\n {query ? (\n <button\n type=\"button\"\n aria-label=\"Clear search\"\n onPointerDown={(event) => event.preventDefault()}\n onClick={() => {\n onQuery(\"\");\n inputRef.current?.focus();\n }}\n className=\"grid size-6 flex-none cursor-pointer place-items-center rounded-pill border-0 bg-muted p-0 text-text-secondary outline-none motion-safe:animate-field-clear-in motion-reduce:animate-fade-in hover:text-foreground\"\n >\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n className=\"size-3.5\"\n >\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </svg>\n </button>\n ) : null}\n {/* Sits exactly over the shared chevron, so the chevron closes the list in both lid states. */}\n <button\n type=\"button\"\n aria-label={`Close ${label.toLowerCase()}`}\n onClick={() => close(true)}\n className=\"size-9 flex-none cursor-pointer rounded-pill border-0 bg-transparent p-0 outline-none motion-safe:transition-[background-color] motion-safe:duration-(--duration-quick) motion-safe:ease-settle hover:bg-muted\"\n />\n </div>\n ) : null}\n\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"pointer-events-none absolute end-4 top-1/2 -mt-2 size-4 text-muted-foreground motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-data-[open]/ms:rotate-180\"\n >\n <path d=\"m4 6 4 4 4-4\" />\n </svg>\n </div>\n\n <div\n ref={faceRef}\n data-open={open || undefined}\n className=\"morph-select-face\"\n >\n <div\n ref={scrollRef}\n className=\"max-h-(--ms-list-max) overflow-y-auto overscroll-contain pt-1.5 [scrollbar-width:thin]\"\n >\n <div id={listId} role=\"listbox\" aria-labelledby={labelId} className=\"relative grid grid-cols-1 gap-0.5\">\n <span ref={highlightRef} aria-hidden=\"true\" className=\"morph-select-highlight\" />\n {visibleSections.map((section) => {\n const rows = section.options.map((option) => {\n const index = indexOf.get(option.value) ?? 0;\n const isSelected = option.value === selected;\n return (\n <div\n key={option.value}\n id={optionId(index)}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || undefined}\n data-slot=\"morph-select-option\"\n data-active={option.value === current || undefined}\n ref={(node) => {\n if (node) optionRefs.current.set(option.value, node);\n else optionRefs.current.delete(option.value);\n }}\n onPointerMove={(event) => {\n if (event.pointerType === \"mouse\" && !option.disabled && option.value !== current)\n setActive(option.value);\n }}\n onPointerDown={(event) => event.preventDefault()}\n onClick={(event) => commit(option, event.currentTarget)}\n className=\"morph-select-option relative box-border flex min-h-9 cursor-pointer select-none items-center gap-2 rounded-lg text-text-secondary outline-none data-[active]:text-foreground aria-selected:text-foreground aria-disabled:cursor-not-allowed aria-disabled:opacity-(--opacity-disabled) motion-safe:transition-colors motion-safe:duration-(--duration-quick) motion-safe:ease-settle\"\n >\n <span data-part=\"value\" className=\"flex min-w-0 flex-1 items-center gap-2 font-medium\">\n {option.icon ? (\n <span className={iconClass} aria-hidden=\"true\">\n {option.icon}\n </span>\n ) : null}\n <span className=\"min-w-0 truncate\">{option.label}</span>\n </span>\n {option.meta ? (\n <span className=\"flex-none text-xs tabular-nums text-muted-foreground\">\n {option.meta}\n </span>\n ) : null}\n <span\n aria-hidden=\"true\"\n data-on={isSelected || undefined}\n className=\"grid size-4 flex-none scale-60 place-items-center text-foreground opacity-0 data-[on]:scale-100 data-[on]:opacity-100 motion-safe:transition-[opacity,scale] motion-safe:duration-(--duration-quick) motion-safe:ease-settle\"\n >\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4\"\n >\n <path d=\"m3.5 8.5 3 3 6-6\" />\n </svg>\n </span>\n </div>\n );\n });\n return section.label ? (\n <div\n key={section.key}\n role=\"group\"\n aria-labelledby={`${uid}-${section.key}`}\n className=\"grid grid-cols-1 gap-0.5 not-first:mt-1\"\n >\n <div\n id={`${uid}-${section.key}`}\n role=\"presentation\"\n className=\"px-2.5 pt-2 pb-1 text-xs text-muted-foreground\"\n >\n {section.label}\n </div>\n {rows}\n </div>\n ) : (\n <div key={section.key} role=\"presentation\" className=\"grid grid-cols-1 gap-0.5 not-first:mt-1\">\n {rows}\n </div>\n );\n })}\n {resultCount === 0 ? (\n <p className=\"m-0 px-2.5 pt-3 pb-3.5 text-sm text-muted-foreground\">\n No matches for “{query.trim()}”\n </p>\n ) : null}\n </div>\n </div>\n </div>\n </div>\n </div>\n\n {showError ? (\n <span\n id={errorId}\n data-slot=\"morph-select-error\"\n className=\"text-sm leading-(--leading-normal) text-destructive-strong\"\n >\n {errorMessage}\n </span>\n ) : null}\n {name ? <input type=\"hidden\" name={name} value={selected ?? \"\"} /> : null}\n <span className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {announcement}\n </span>\n </div>\n );\n}\n"],"mappings":"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"}