@spark-ui/components 17.15.4 → 17.15.5

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 (245) hide show
  1. package/dist/{DialogContent.styles-BSxCCi-u.mjs → DialogContent.styles-ClKRO43s.mjs} +1 -1
  2. package/dist/{DialogContent.styles-BSxCCi-u.mjs.map → DialogContent.styles-ClKRO43s.mjs.map} +1 -1
  3. package/dist/DialogContent.styles-FnwXayCC.js +2 -0
  4. package/dist/{DialogContent.styles-CAhJtUud.js.map → DialogContent.styles-FnwXayCC.js.map} +1 -1
  5. package/dist/{FormFieldRequiredIndicator-CzdpinIz.mjs → FormFieldRequiredIndicator-COKYxTNs.mjs} +2 -2
  6. package/dist/{FormFieldRequiredIndicator-CzdpinIz.mjs.map → FormFieldRequiredIndicator-COKYxTNs.mjs.map} +1 -1
  7. package/dist/FormFieldRequiredIndicator-DY3STDp1.js +2 -0
  8. package/dist/{FormFieldRequiredIndicator-CEB8Ez-q.js.map → FormFieldRequiredIndicator-DY3STDp1.js.map} +1 -1
  9. package/dist/accordion/index.js +1 -1
  10. package/dist/accordion/index.js.map +1 -1
  11. package/dist/accordion/index.mjs +1 -1
  12. package/dist/accordion/index.mjs.map +1 -1
  13. package/dist/alert-dialog/index.js +1 -1
  14. package/dist/alert-dialog/index.js.map +1 -1
  15. package/dist/alert-dialog/index.mjs +1 -1
  16. package/dist/alert-dialog/index.mjs.map +1 -1
  17. package/dist/avatar/index.js +1 -1
  18. package/dist/avatar/index.js.map +1 -1
  19. package/dist/avatar/index.mjs +2 -2
  20. package/dist/avatar/index.mjs.map +1 -1
  21. package/dist/badge/index.js +1 -1
  22. package/dist/badge/index.js.map +1 -1
  23. package/dist/badge/index.mjs.map +1 -1
  24. package/dist/breadcrumb/index.js +1 -1
  25. package/dist/breadcrumb/index.js.map +1 -1
  26. package/dist/breadcrumb/index.mjs +1 -1
  27. package/dist/breadcrumb/index.mjs.map +1 -1
  28. package/dist/button/index.js +1 -1
  29. package/dist/button/index.mjs +1 -1
  30. package/dist/{button-CzHtbgj7.mjs → button-BeLA-Evk.mjs} +2 -2
  31. package/dist/{button-CzHtbgj7.mjs.map → button-BeLA-Evk.mjs.map} +1 -1
  32. package/dist/button-pb2JbL37.js +2 -0
  33. package/dist/{button-jlscsCw0.js.map → button-pb2JbL37.js.map} +1 -1
  34. package/dist/card/index.js +1 -1
  35. package/dist/card/index.js.map +1 -1
  36. package/dist/card/index.mjs.map +1 -1
  37. package/dist/carousel/index.js +1 -1
  38. package/dist/carousel/index.js.map +1 -1
  39. package/dist/carousel/index.mjs +2 -2
  40. package/dist/carousel/index.mjs.map +1 -1
  41. package/dist/checkbox/index.js +1 -1
  42. package/dist/checkbox/index.mjs +1 -1
  43. package/dist/checkbox-Cbewcw6G.js +2 -0
  44. package/dist/{checkbox-hVGRiWC_.js.map → checkbox-Cbewcw6G.js.map} +1 -1
  45. package/dist/{checkbox-CbMh-21q.mjs → checkbox-uSINHQYe.mjs} +3 -3
  46. package/dist/{checkbox-CbMh-21q.mjs.map → checkbox-uSINHQYe.mjs.map} +1 -1
  47. package/dist/chip/index.js +1 -1
  48. package/dist/chip/index.js.map +1 -1
  49. package/dist/chip/index.mjs +1 -1
  50. package/dist/chip/index.mjs.map +1 -1
  51. package/dist/circular-meter/index.js +1 -1
  52. package/dist/circular-meter/index.js.map +1 -1
  53. package/dist/circular-meter/index.mjs.map +1 -1
  54. package/dist/collapsible/index.js +1 -1
  55. package/dist/collapsible/index.js.map +1 -1
  56. package/dist/collapsible/index.mjs.map +1 -1
  57. package/dist/combobox/index.js +1 -1
  58. package/dist/combobox/index.js.map +1 -1
  59. package/dist/combobox/index.mjs +9 -6
  60. package/dist/combobox/index.mjs.map +1 -1
  61. package/dist/dialog/index.js +1 -1
  62. package/dist/dialog/index.js.map +1 -1
  63. package/dist/dialog/index.mjs +4 -4
  64. package/dist/dialog/index.mjs.map +1 -1
  65. package/dist/divider/index.js +1 -1
  66. package/dist/divider/index.js.map +1 -1
  67. package/dist/divider/index.mjs.map +1 -1
  68. package/dist/drawer/index.js +1 -1
  69. package/dist/drawer/index.js.map +1 -1
  70. package/dist/drawer/index.mjs +3 -3
  71. package/dist/drawer/index.mjs.map +1 -1
  72. package/dist/dropdown/index.js +1 -1
  73. package/dist/dropdown/index.js.map +1 -1
  74. package/dist/dropdown/index.mjs +2 -2
  75. package/dist/dropdown/index.mjs.map +1 -1
  76. package/dist/file-upload/index.js +1 -1
  77. package/dist/file-upload/index.js.map +1 -1
  78. package/dist/file-upload/index.mjs +7 -7
  79. package/dist/file-upload/index.mjs.map +1 -1
  80. package/dist/form-field/index.js +1 -1
  81. package/dist/form-field/index.mjs +1 -1
  82. package/dist/form-field-B38U0zp8.js +2 -0
  83. package/dist/{form-field-BCqHBvWN.js.map → form-field-B38U0zp8.js.map} +1 -1
  84. package/dist/{form-field-ByLHXtzx.mjs → form-field-DCKyhBmv.mjs} +4 -4
  85. package/dist/{form-field-ByLHXtzx.mjs.map → form-field-DCKyhBmv.mjs.map} +1 -1
  86. package/dist/global.d.ts +12 -0
  87. package/dist/icon/index.js +1 -1
  88. package/dist/icon/index.mjs +1 -1
  89. package/dist/{icon-D05Uqh8_.mjs → icon-Bs-39J-H.mjs} +1 -1
  90. package/dist/{icon-D05Uqh8_.mjs.map → icon-Bs-39J-H.mjs.map} +1 -1
  91. package/dist/icon-DY4l9nsc.js +2 -0
  92. package/dist/{icon-CRPcdgYp.js.map → icon-DY4l9nsc.js.map} +1 -1
  93. package/dist/icon-button/index.js +1 -1
  94. package/dist/icon-button/index.mjs +1 -1
  95. package/dist/icon-button-4kkwICAr.js +2 -0
  96. package/dist/{icon-button-Iv9kq84M.js.map → icon-button-4kkwICAr.js.map} +1 -1
  97. package/dist/{icon-button-BLG7VjR7.mjs → icon-button-DQAzrC_Q.mjs} +2 -2
  98. package/dist/{icon-button-BLG7VjR7.mjs.map → icon-button-DQAzrC_Q.mjs.map} +1 -1
  99. package/dist/input/index.js +1 -1
  100. package/dist/input/index.mjs +1 -1
  101. package/dist/input-Bmkrsnjd.js +2 -0
  102. package/dist/{input-BSCMbnO4.js.map → input-Bmkrsnjd.js.map} +1 -1
  103. package/dist/{input-cKdm2R3n.mjs → input-Dy5t-hhE.mjs} +2 -2
  104. package/dist/{input-cKdm2R3n.mjs.map → input-Dy5t-hhE.mjs.map} +1 -1
  105. package/dist/input-otp/index.js +1 -1
  106. package/dist/input-otp/index.js.map +1 -1
  107. package/dist/input-otp/index.mjs.map +1 -1
  108. package/dist/kbd/index.js +1 -1
  109. package/dist/kbd/index.js.map +1 -1
  110. package/dist/kbd/index.mjs.map +1 -1
  111. package/dist/label/index.js +1 -1
  112. package/dist/label/index.mjs +1 -1
  113. package/dist/label-DO48yda7.js +2 -0
  114. package/dist/{label-BCSEss4U.js.map → label-DO48yda7.js.map} +1 -1
  115. package/dist/{label-DDBRKLUX.mjs → label-RKaPen31.mjs} +1 -1
  116. package/dist/{label-DDBRKLUX.mjs.map → label-RKaPen31.mjs.map} +1 -1
  117. package/dist/link-box/index.js +1 -1
  118. package/dist/link-box/index.js.map +1 -1
  119. package/dist/link-box/index.mjs.map +1 -1
  120. package/dist/menu/index.js +1 -1
  121. package/dist/menu/index.js.map +1 -1
  122. package/dist/menu/index.mjs +1 -1
  123. package/dist/menu/index.mjs.map +1 -1
  124. package/dist/meter/index.js +1 -1
  125. package/dist/meter/index.js.map +1 -1
  126. package/dist/meter/index.mjs.map +1 -1
  127. package/dist/pagination/index.js +1 -1
  128. package/dist/pagination/index.js.map +1 -1
  129. package/dist/pagination/index.mjs +3 -3
  130. package/dist/pagination/index.mjs.map +1 -1
  131. package/dist/popover/index.js +1 -1
  132. package/dist/popover/index.mjs +1 -1
  133. package/dist/popover-DK2-MWUF.js +2 -0
  134. package/dist/{popover-qoueDrzR.js.map → popover-DK2-MWUF.js.map} +1 -1
  135. package/dist/{popover-Ds6xXjgT.mjs → popover-Dj95Y-ex.mjs} +3 -3
  136. package/dist/{popover-Ds6xXjgT.mjs.map → popover-Dj95Y-ex.mjs.map} +1 -1
  137. package/dist/portal/index.js +1 -1
  138. package/dist/portal/index.js.map +1 -1
  139. package/dist/portal/index.mjs.map +1 -1
  140. package/dist/progress/index.js +1 -1
  141. package/dist/progress/index.mjs +1 -1
  142. package/dist/progress-B_zfJmtJ.js +2 -0
  143. package/dist/{progress-DBD2FiQ1.js.map → progress-B_zfJmtJ.js.map} +1 -1
  144. package/dist/progress-tracker/index.js +1 -1
  145. package/dist/progress-tracker/index.js.map +1 -1
  146. package/dist/progress-tracker/index.mjs +1 -1
  147. package/dist/progress-tracker/index.mjs.map +1 -1
  148. package/dist/{progress-BfpWgeE1.mjs → progress-xbOXCGXi.mjs} +1 -1
  149. package/dist/{progress-BfpWgeE1.mjs.map → progress-xbOXCGXi.mjs.map} +1 -1
  150. package/dist/radio-group/index.js +1 -1
  151. package/dist/radio-group/index.js.map +1 -1
  152. package/dist/radio-group/index.mjs.map +1 -1
  153. package/dist/rating/index.js +1 -1
  154. package/dist/rating/index.js.map +1 -1
  155. package/dist/rating/index.mjs +5 -3
  156. package/dist/rating/index.mjs.map +1 -1
  157. package/dist/rating-display/index.js +1 -1
  158. package/dist/rating-display/index.js.map +1 -1
  159. package/dist/rating-display/index.mjs +1 -1
  160. package/dist/rating-display/index.mjs.map +1 -1
  161. package/dist/{chunk-C91j1N6u.js → rolldown-runtime-CMqjfN_6.js} +1 -1
  162. package/dist/scrolling-list/index.js +1 -1
  163. package/dist/scrolling-list/index.js.map +1 -1
  164. package/dist/scrolling-list/index.mjs +3 -3
  165. package/dist/scrolling-list/index.mjs.map +1 -1
  166. package/dist/segmented-control/index.js +1 -1
  167. package/dist/segmented-control/index.js.map +1 -1
  168. package/dist/segmented-control/index.mjs.map +1 -1
  169. package/dist/segmented-gauge/index.js +1 -1
  170. package/dist/segmented-gauge/index.js.map +1 -1
  171. package/dist/segmented-gauge/index.mjs.map +1 -1
  172. package/dist/select/index.js +1 -1
  173. package/dist/select/index.js.map +1 -1
  174. package/dist/select/index.mjs +1 -1
  175. package/dist/select/index.mjs.map +1 -1
  176. package/dist/skeleton/index.js +1 -1
  177. package/dist/skeleton/index.js.map +1 -1
  178. package/dist/skeleton/index.mjs.map +1 -1
  179. package/dist/slider/index.js +1 -1
  180. package/dist/slider/index.js.map +1 -1
  181. package/dist/slider/index.mjs +2 -2
  182. package/dist/slider/index.mjs.map +1 -1
  183. package/dist/slot/index.js +1 -1
  184. package/dist/slot/index.js.map +1 -1
  185. package/dist/slot/index.mjs.map +1 -1
  186. package/dist/spinner/index.js +1 -1
  187. package/dist/spinner/index.mjs +1 -1
  188. package/dist/spinner-CmPypfJR.js +2 -0
  189. package/dist/{spinner-DFUoYvmm.js.map → spinner-CmPypfJR.js.map} +1 -1
  190. package/dist/{spinner-DULLiM6a.mjs → spinner-TfGcjNm3.mjs} +1 -1
  191. package/dist/{spinner-DULLiM6a.mjs.map → spinner-TfGcjNm3.mjs.map} +1 -1
  192. package/dist/stepper/index.js +1 -1
  193. package/dist/stepper/index.js.map +1 -1
  194. package/dist/stepper/index.mjs +3 -3
  195. package/dist/stepper/index.mjs.map +1 -1
  196. package/dist/switch/index.js +1 -1
  197. package/dist/switch/index.js.map +1 -1
  198. package/dist/switch/index.mjs +2 -2
  199. package/dist/switch/index.mjs.map +1 -1
  200. package/dist/table/index.js +1 -1
  201. package/dist/table/index.js.map +1 -1
  202. package/dist/table/index.mjs +3 -3
  203. package/dist/table/index.mjs.map +1 -1
  204. package/dist/tabs/index.js +1 -1
  205. package/dist/tabs/index.js.map +1 -1
  206. package/dist/tabs/index.mjs +4 -4
  207. package/dist/tabs/index.mjs.map +1 -1
  208. package/dist/tag/index.js +1 -1
  209. package/dist/tag/index.js.map +1 -1
  210. package/dist/tag/index.mjs.map +1 -1
  211. package/dist/text-link/index.js +1 -1
  212. package/dist/text-link/index.js.map +1 -1
  213. package/dist/text-link/index.mjs.map +1 -1
  214. package/dist/textarea/index.js +1 -1
  215. package/dist/textarea/index.js.map +1 -1
  216. package/dist/textarea/index.mjs +1 -1
  217. package/dist/textarea/index.mjs.map +1 -1
  218. package/dist/toast/index.js +1 -1
  219. package/dist/toast/index.js.map +1 -1
  220. package/dist/toast/index.mjs +3 -3
  221. package/dist/toast/index.mjs.map +1 -1
  222. package/dist/toggle-group/index.js +1 -1
  223. package/dist/toggle-group/index.js.map +1 -1
  224. package/dist/toggle-group/index.mjs.map +1 -1
  225. package/dist/{useRenderSlot-DKIwoqpO.mjs → useRenderSlot-5CPC9Lkj.mjs} +1 -1
  226. package/dist/{useRenderSlot-DKIwoqpO.mjs.map → useRenderSlot-5CPC9Lkj.mjs.map} +1 -1
  227. package/dist/useRenderSlot-DHV0CegV.js +2 -0
  228. package/dist/{useRenderSlot-C4UVWhDN.js.map → useRenderSlot-DHV0CegV.js.map} +1 -1
  229. package/dist/visually-hidden/index.js +1 -1
  230. package/dist/visually-hidden/index.js.map +1 -1
  231. package/dist/visually-hidden/index.mjs.map +1 -1
  232. package/package.json +5 -5
  233. package/dist/DialogContent.styles-CAhJtUud.js +0 -2
  234. package/dist/FormFieldRequiredIndicator-CEB8Ez-q.js +0 -2
  235. package/dist/button-jlscsCw0.js +0 -2
  236. package/dist/checkbox-hVGRiWC_.js +0 -2
  237. package/dist/form-field-BCqHBvWN.js +0 -2
  238. package/dist/icon-CRPcdgYp.js +0 -2
  239. package/dist/icon-button-Iv9kq84M.js +0 -2
  240. package/dist/input-BSCMbnO4.js +0 -2
  241. package/dist/label-BCSEss4U.js +0 -2
  242. package/dist/popover-qoueDrzR.js +0 -2
  243. package/dist/progress-DBD2FiQ1.js +0 -2
  244. package/dist/spinner-DFUoYvmm.js +0 -2
  245. package/dist/useRenderSlot-C4UVWhDN.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/segmented-control/SegmentedControl.styles.ts","../../src/segmented-control/SegmentedControlContext.tsx","../../src/segmented-control/useSegmentedControlNavigation.ts","../../src/segmented-control/SegmentedControl.tsx","../../src/segmented-control/SegmentedControlIndicator.tsx","../../src/segmented-control/SegmentedControlItem.tsx","../../src/segmented-control/index.ts"],"sourcesContent":["import { cva, VariantProps } from 'class-variance-authority'\n\nexport const rootStyles = cva([\n 'default:self-start',\n 'group inline-flex flex-wrap',\n 'relative items-stretch min-w-max',\n 'rounded-xl p-sm',\n 'bg-surface border-sm border-outline',\n])\n\nexport const itemStyles = cva([\n 'relative z-raised min-h-sz-44 focus-visible:outline-none',\n 'flex flex-auto items-center justify-center gap-md',\n 'default:px-lg default:py-md',\n 'rounded-[20px]',\n 'cursor-pointer select-none',\n 'font-medium',\n 'transition-colors duration-150',\n 'outline-none',\n 'focus-visible:u-outline',\n 'data-disabled:cursor-not-allowed data-disabled:opacity-dim-3',\n 'data-checked:text-on-support-container',\n // Avoid layout shift: simulate \"bold\" without changing font metrics.\n // Apply only to wrapped text nodes (not arbitrary nested JSX like Tag).\n 'data-checked:[&>[data-spark-segmented-control-text]]:[text-shadow:0.35px_0_currentColor,-0.35px_0_currentColor]',\n])\n\nexport const indicatorStyles = cva([\n 'absolute z-base',\n 'rounded-[20px]',\n 'bg-support-container border-md border-support',\n 'group-has-focus-visible:border-focus',\n 'transition-[left,top,width,height] duration-200 ease-in-out',\n 'pointer-events-none',\n])\n\nexport type SegmentedControlStylesProps = VariantProps<typeof itemStyles>\n","import { createContext, RefObject, useContext } from 'react'\n\nexport interface SegmentedControlContextInterface {\n checkedValue: string | null\n containerRef: RefObject<HTMLDivElement | null>\n onValueChange: (value: string) => void\n name?: string\n rowLength?: number\n itemValues: string[]\n}\n\nexport const SegmentedControlContext = createContext<SegmentedControlContextInterface>(\n {} as SegmentedControlContextInterface\n)\n\nexport const useSegmentedControlContext = () => {\n const context = useContext(SegmentedControlContext)\n\n if (!context) {\n throw Error('useSegmentedControlContext must be used within a SegmentedControlContext Provider')\n }\n\n return context\n}\n","import { KeyboardEvent, RefObject } from 'react'\n\ninterface UseSegmentedControlNavigationProps {\n itemValues: string[]\n containerRef: RefObject<HTMLDivElement | null>\n onValueChange: (value: string) => void\n}\n\n/**\n * Custom hook that handles keyboard navigation for SegmentedControl.\n * Uses sequential left/right navigation.\n */\nexport const useSegmentedControlNavigation = ({\n itemValues,\n containerRef,\n onValueChange,\n}: UseSegmentedControlNavigationProps) => {\n const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n // Find the currently focused item (which may differ from checkedValue during keyboard navigation)\n const focusedElement = e.target as HTMLElement\n const focusedValue = focusedElement.getAttribute('data-value')\n\n if (!focusedValue) return\n\n const currentIndex = itemValues.indexOf(focusedValue)\n if (currentIndex === -1) return\n\n let nextIndex: number | null = null\n\n // Always use 1D sequential navigation (left/right only)\n nextIndex = calculate1DNavigation(e.key, currentIndex, itemValues.length)\n\n if (nextIndex !== null && nextIndex !== currentIndex) {\n e.preventDefault()\n\n // Skip disabled items\n let attempts = 0\n const maxAttempts = itemValues.length\n\n while (attempts < maxAttempts) {\n const nextValue = itemValues[nextIndex]\n if (!nextValue) return\n\n const nextItem = containerRef.current?.querySelector<HTMLElement>(\n `[data-value=\"${CSS.escape(nextValue)}\"]`\n )\n\n // If the item is not disabled, focus it and update the value\n if (nextItem && !nextItem.hasAttribute('data-disabled')) {\n nextItem.focus()\n onValueChange(nextValue)\n return\n }\n\n // If disabled, continue in the same direction\n const direction = e.key === 'ArrowRight' || e.key === 'ArrowDown' ? 1 : -1\n nextIndex = (nextIndex + direction + itemValues.length) % itemValues.length\n attempts++\n }\n }\n }\n\n return { handleKeyDown }\n}\n\n/**\n * Calculate next index for 1D sequential navigation.\n * Navigation wraps around at the boundaries.\n */\nfunction calculate1DNavigation(\n key: string,\n currentIndex: number,\n totalItems: number\n): number | null {\n switch (key) {\n case 'ArrowRight':\n case 'ArrowDown':\n return (currentIndex + 1) % totalItems\n case 'ArrowLeft':\n case 'ArrowUp':\n return (currentIndex - 1 + totalItems) % totalItems\n default:\n return null\n }\n}\n","import { useFormFieldControl } from '@spark-ui/components/form-field'\nimport { useMergeRefs } from '@spark-ui/hooks/use-merge-refs'\nimport {\n Children,\n type ComponentProps,\n CSSProperties,\n isValidElement,\n Ref,\n useRef,\n useState,\n} from 'react'\n\nimport type { SegmentedControlStylesProps } from './SegmentedControl.styles'\nimport { rootStyles } from './SegmentedControl.styles'\nimport { SegmentedControlContext } from './SegmentedControlContext'\nimport { useSegmentedControlNavigation } from './useSegmentedControlNavigation'\n\nexport interface SegmentedControlProps\n extends Omit<ComponentProps<'div'>, 'onValueChange'>, SegmentedControlStylesProps {\n /**\n * The controlled selected value.\n */\n value?: string\n /**\n * The uncontrolled default selected value.\n */\n defaultValue?: string\n /**\n * Callback fired when the selected value changes.\n */\n onValueChange?: (value: string) => void\n /**\n * Number of items per row in multi-row layout.\n * When undefined, items display in a single row (default behavior).\n * @default undefined\n * @example\n * // Create 3-column grid with wrapping rows\n * <SegmentedControl rowLength={3}>\n */\n rowLength?: number\n /**\n * The name attribute for the radio group (used in form submissions).\n */\n name?: string\n ref?: Ref<HTMLDivElement>\n}\n\nconst getFirstItemValue = (children: React.ReactNode): string | null => {\n let firstValue: string | null = null\n\n Children.forEach(children, child => {\n if (firstValue !== null) return\n if (isValidElement(child) && typeof (child.props as { value?: string }).value === 'string') {\n firstValue = (child.props as { value: string }).value\n }\n })\n\n return firstValue\n}\n\nexport const SegmentedControl = ({\n value,\n defaultValue,\n onValueChange,\n className,\n children,\n rowLength,\n name: nameProp,\n ref,\n ...rest\n}: SegmentedControlProps) => {\n const containerRef = useRef<HTMLDivElement | null>(null)\n const mergedRef = useMergeRefs(containerRef, ref)\n\n const firstValue = getFirstItemValue(children)\n\n const isControlled = value !== undefined\n const [internalValue, setInternalValue] = useState<string | null>(\n () => defaultValue ?? firstValue\n )\n const checkedValue = isControlled ? (value ?? null) : internalValue\n\n const handleValueChange = (newValue: string) => {\n if (!isControlled) {\n setInternalValue(newValue)\n }\n\n onValueChange?.(newValue)\n }\n\n const { labelId, description, isRequired, isInvalid, name: nameFromField } = useFormFieldControl()\n const name = nameProp ?? nameFromField\n\n // Get all item values in order\n const itemValues: string[] = []\n Children.forEach(children, child => {\n if (isValidElement(child) && typeof (child.props as { value?: string }).value === 'string') {\n itemValues.push((child.props as { value: string }).value)\n }\n })\n\n // Keyboard navigation (sequential left/right)\n const { handleKeyDown } = useSegmentedControlNavigation({\n itemValues,\n containerRef,\n onValueChange: handleValueChange,\n })\n\n // Compute dynamic flex styles for multi-row layout\n const flexStyles = rowLength\n ? ({\n '--segmented-control-cols': rowLength,\n rowGap: 'var(--spacing-md)',\n } as CSSProperties)\n : undefined\n\n return (\n <SegmentedControlContext\n value={{\n checkedValue,\n containerRef,\n onValueChange: handleValueChange,\n name,\n rowLength,\n itemValues,\n }}\n >\n <div\n ref={mergedRef}\n role=\"radiogroup\"\n data-spark-component=\"segmented-control\"\n className={rootStyles({ className })}\n style={flexStyles}\n aria-labelledby={labelId}\n aria-describedby={description}\n aria-required={isRequired || undefined}\n aria-invalid={isInvalid || undefined}\n onKeyDown={handleKeyDown}\n {...rest}\n >\n {children}\n </div>\n </SegmentedControlContext>\n )\n}\n\nSegmentedControl.displayName = 'SegmentedControl'\n","import { type ComponentProps, type CSSProperties, Ref, useEffect, useMemo, useState } from 'react'\n\nimport { indicatorStyles } from './SegmentedControl.styles'\nimport { useSegmentedControlContext } from './SegmentedControlContext'\n\ninterface IndicatorRect {\n left: number\n top: number\n width: number\n height: number\n}\n\nexport interface SegmentedControlIndicatorProps extends ComponentProps<'span'> {\n ref?: Ref<HTMLSpanElement>\n}\n\n/** The visual indicator that highlights the selected item. Renders a <span> element. */\nexport const SegmentedControlIndicator = ({\n className,\n ref,\n ...rest\n}: SegmentedControlIndicatorProps) => {\n const { checkedValue, containerRef } = useSegmentedControlContext()\n const [rect, setRect] = useState<IndicatorRect | null>(null)\n\n const selector = useMemo(\n () => (checkedValue ? `[data-value=\"${CSS.escape(checkedValue)}\"]` : null),\n [checkedValue]\n )\n\n useEffect(() => {\n const container = containerRef.current\n\n if (!container) {\n return\n }\n\n const selectedItem = selector ? container.querySelector<HTMLElement>(selector) : null\n\n const update = () => {\n const currentContainer = containerRef.current\n if (!currentContainer || !selector) {\n setRect(null)\n\n return\n }\n\n const currentSelected = currentContainer.querySelector<HTMLElement>(selector)\n if (!currentSelected) {\n setRect(null)\n\n return\n }\n\n const containerRect = currentContainer.getBoundingClientRect()\n const itemRect = currentSelected.getBoundingClientRect()\n\n // Storybook canvas \"zoom\" can be implemented via `transform: scale()`.\n // In that case, `getBoundingClientRect()` returns *scaled* values, but CSS positioning/sizing\n // expects unscaled layout pixels. We infer the scale factor from offset sizes and normalize.\n const scaleX =\n currentSelected.offsetWidth > 0 ? itemRect.width / currentSelected.offsetWidth : 1\n const scaleY =\n currentSelected.offsetHeight > 0 ? itemRect.height / currentSelected.offsetHeight : 1\n\n // `getBoundingClientRect()` is border-box; absolute positioning is relative to the padding box.\n setRect({\n left: (itemRect.left - containerRect.left) / scaleX - currentContainer.clientLeft,\n top: (itemRect.top - containerRect.top) / scaleY - currentContainer.clientTop,\n width: itemRect.width / scaleX,\n height: itemRect.height / scaleY,\n })\n }\n\n update()\n\n const ro =\n typeof ResizeObserver !== 'undefined'\n ? new ResizeObserver(() => {\n update()\n })\n : null\n\n ro?.observe(container)\n if (selectedItem) ro?.observe(selectedItem)\n\n window.addEventListener('resize', update, { passive: true })\n window.visualViewport?.addEventListener('resize', update, { passive: true })\n\n return () => {\n ro?.disconnect()\n window.removeEventListener('resize', update)\n window.visualViewport?.removeEventListener('resize', update)\n }\n }, [containerRef, selector])\n\n if (!rect) return null\n\n const style: CSSProperties = {\n left: rect.left,\n top: rect.top,\n width: rect.width,\n height: rect.height,\n }\n\n return (\n <span\n ref={ref}\n data-spark-component=\"segmented-control-indicator\"\n aria-hidden\n className={indicatorStyles({ className })}\n style={style}\n {...rest}\n />\n )\n}\n\nSegmentedControlIndicator.displayName = 'SegmentedControl.Indicator'\n","import { Children, type ComponentProps, Ref } from 'react'\n\nimport { itemStyles } from './SegmentedControl.styles'\nimport { useSegmentedControlContext } from './SegmentedControlContext'\n\nexport interface SegmentedControlItemProps extends Omit<\n ComponentProps<'button'>,\n 'value' | 'onClick'\n> {\n /**\n * A unique value that identifies this item within the segmented control.\n */\n value: string\n /**\n * When true, prevents the user from interacting with this item.\n * @default false\n */\n disabled?: boolean\n ref?: Ref<HTMLButtonElement>\n}\n\n/** A selectable item in the segmented control. Renders a <button> element. */\nexport const SegmentedControlItem = ({\n value,\n disabled = false,\n children,\n className,\n ref,\n ...rest\n}: SegmentedControlItemProps) => {\n const { checkedValue, onValueChange, name, rowLength, itemValues } = useSegmentedControlContext()\n\n const isChecked = checkedValue === value\n\n const handleClick = () => {\n if (!disabled) {\n onValueChange(value)\n }\n }\n\n const content = Children.toArray(children).map((child, index) => {\n if (typeof child === 'string' || typeof child === 'number') {\n return (\n <span key={`text-${index}`} data-spark-segmented-control-text>\n {child}\n </span>\n )\n }\n\n return child\n })\n\n // Calculate flex-basis for multi-row layout and horizontal separator visibility\n const itemIndex = itemValues.indexOf(value)\n const itemStyle = rowLength\n ? { flexBasis: `calc(100% / var(--segmented-control-cols))` }\n : undefined\n\n let showHorizontalSeparator = false\n\n if (rowLength && rowLength > 0 && itemIndex !== -1) {\n const cols = rowLength\n const currentCol = itemIndex % cols\n const currentRow = Math.floor(itemIndex / cols)\n const totalRows = Math.ceil(itemValues.length / cols)\n\n // Show horizontal separator only on the first column of each row (except last row)\n showHorizontalSeparator = currentRow < totalRows - 1 && currentCol === 0\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"radio\"\n data-spark-component=\"segmented-control-item\"\n data-value={value}\n aria-checked={isChecked}\n data-checked={isChecked || undefined}\n data-disabled={disabled || undefined}\n disabled={disabled}\n tabIndex={isChecked ? 0 : -1}\n className={itemStyles({ className })}\n style={itemStyle}\n onClick={handleClick}\n {...rest}\n >\n {content}\n {/* Hidden input for form submission */}\n {name && isChecked && <input type=\"hidden\" name={name} value={value} />}\n {/* Horizontal separator between rows (full width across all columns) */}\n {showHorizontalSeparator && rowLength && (\n <div\n className=\"bg-outline/dim-3 -mx-sm absolute left-0 h-px\"\n style={{\n bottom: 'calc(var(--spacing-md) / -2)',\n width: `calc(${rowLength * 100}% + var(--spacing-sm) * 2)`,\n }}\n aria-hidden=\"true\"\n />\n )}\n </button>\n )\n}\n\nSegmentedControlItem.displayName = 'SegmentedControl.Item'\n","import { SegmentedControl as Root } from './SegmentedControl'\nimport { SegmentedControlIndicator as Indicator } from './SegmentedControlIndicator'\nimport { SegmentedControlItem as Item } from './SegmentedControlItem'\n\n/**\n * A set of toggle buttons that allows users to select a single option from a group of related choices.\n */\nexport const SegmentedControl: typeof Root & {\n Item: typeof Item\n Indicator: typeof Indicator\n} = Object.assign(Root, {\n Item,\n Indicator,\n})\n\nSegmentedControl.displayName = 'SegmentedControl'\nItem.displayName = 'SegmentedControl.Item'\nIndicator.displayName = 'SegmentedControl.Indicator'\n\nexport type { SegmentedControlProps } from './SegmentedControl'\nexport type { SegmentedControlItemProps } from './SegmentedControlItem'\nexport type { SegmentedControlIndicatorProps } from './SegmentedControlIndicator'\n"],"mappings":"wRAEA,IAAa,GAAA,EAAA,EAAA,KAAiB,CAC5B,qBACA,8BACA,mCACA,kBACA,sCACD,CAAC,CAEW,GAAA,EAAA,EAAA,KAAiB,CAC5B,2DACA,oDACA,8BACA,iBACA,6BACA,cACA,iCACA,eACA,0BACA,+DACA,yCAGA,kHACD,CAAC,CAEW,GAAA,EAAA,EAAA,KAAsB,CACjC,kBACA,iBACA,gDACA,uCACA,8DACA,sBACD,CAAC,CCvBW,GAAA,EAAA,EAAA,eACX,EAAE,CACH,CAEY,MAAmC,CAC9C,IAAM,GAAA,EAAA,EAAA,YAAqB,EAAwB,CAEnD,GAAI,CAAC,EACH,MAAM,MAAM,oFAAoF,CAGlG,OAAO,GCVI,GAAiC,CAC5C,aACA,eACA,oBA+CO,CAAE,cA7Cc,GAAqC,CAG1D,IAAM,EADiB,EAAE,OACW,aAAa,aAAa,CAE9D,GAAI,CAAC,EAAc,OAEnB,IAAM,EAAe,EAAW,QAAQ,EAAa,CACrD,GAAI,IAAiB,GAAI,OAEzB,IAAI,EAA2B,KAK/B,GAFA,EAAY,EAAsB,EAAE,IAAK,EAAc,EAAW,OAAO,CAErE,IAAc,MAAQ,IAAc,EAAc,CACpD,EAAE,gBAAgB,CAGlB,IAAI,EAAW,EACT,EAAc,EAAW,OAE/B,KAAO,EAAW,GAAa,CAC7B,IAAM,EAAY,EAAW,GAC7B,GAAI,CAAC,EAAW,OAEhB,IAAM,EAAW,EAAa,SAAS,cACrC,gBAAgB,IAAI,OAAO,EAAU,CAAC,IACvC,CAGD,GAAI,GAAY,CAAC,EAAS,aAAa,gBAAgB,CAAE,CACvD,EAAS,OAAO,CAChB,EAAc,EAAU,CACxB,OAIF,IAAM,EAAY,EAAE,MAAQ,cAAgB,EAAE,MAAQ,YAAc,EAAI,GACxE,GAAa,EAAY,EAAY,EAAW,QAAU,EAAW,OACrE,OAKkB,EAO1B,SAAS,EACP,EACA,EACA,EACe,CACf,OAAQ,EAAR,CACE,IAAK,aACL,IAAK,YACH,OAAQ,EAAe,GAAK,EAC9B,IAAK,YACL,IAAK,UACH,OAAQ,EAAe,EAAI,GAAc,EAC3C,QACE,OAAO,MCnCb,IAAM,EAAqB,GAA6C,CACtE,IAAI,EAA4B,KAShC,OAPA,EAAA,SAAS,QAAQ,EAAU,GAAS,CAC9B,IAAe,OACnB,EAAA,EAAA,gBAAmB,EAAM,EAAI,OAAQ,EAAM,MAA6B,OAAU,WAChF,EAAc,EAAM,MAA4B,QAElD,CAEK,GAGI,GAAoB,CAC/B,QACA,eACA,gBACA,YACA,WACA,YACA,KAAM,EACN,MACA,GAAG,KACwB,CAC3B,IAAM,GAAA,EAAA,EAAA,QAA6C,KAAK,CAClD,GAAA,EAAA,EAAA,cAAyB,EAAc,EAAI,CAE3C,EAAa,EAAkB,EAAS,CAExC,EAAe,IAAU,IAAA,GACzB,CAAC,EAAe,IAAA,EAAA,EAAA,cACd,GAAgB,EACvB,CACK,EAAe,EAAgB,GAAS,KAAQ,EAEhD,EAAqB,GAAqB,CACzC,GACH,EAAiB,EAAS,CAG5B,IAAgB,EAAS,EAGrB,CAAE,UAAS,cAAa,aAAY,YAAW,KAAM,IAAA,EAAA,EAAA,sBAAuC,CAC5F,EAAO,GAAY,EAGnB,EAAuB,EAAE,CAC/B,EAAA,SAAS,QAAQ,EAAU,GAAS,EAClC,EAAA,EAAA,gBAAmB,EAAM,EAAI,OAAQ,EAAM,MAA6B,OAAU,UAChF,EAAW,KAAM,EAAM,MAA4B,MAAM,EAE3D,CAGF,GAAM,CAAE,iBAAkB,EAA8B,CACtD,aACA,eACA,cAAe,EAChB,CAAC,CAGI,EAAa,EACd,CACC,2BAA4B,EAC5B,OAAQ,oBACT,CACD,IAAA,GAEJ,OACE,EAAA,EAAA,KAAC,EAAD,CACE,MAAO,CACL,eACA,eACA,cAAe,EACf,OACA,YACA,aACD,WAED,EAAA,EAAA,KAAC,MAAD,CACE,IAAK,EACL,KAAK,aACL,uBAAqB,oBACrB,UAAW,EAAW,CAAE,YAAW,CAAC,CACpC,MAAO,EACP,kBAAiB,EACjB,mBAAkB,EAClB,gBAAe,GAAc,IAAA,GAC7B,eAAc,GAAa,IAAA,GAC3B,UAAW,EACX,GAAI,EAEH,WACG,CAAA,CACkB,CAAA,EAI9B,EAAiB,YAAc,mBCjI/B,IAAa,GAA6B,CACxC,YACA,MACA,GAAG,KACiC,CACpC,GAAM,CAAE,eAAc,gBAAiB,GAA4B,CAC7D,CAAC,EAAM,IAAA,EAAA,EAAA,UAA0C,KAAK,CAEtD,GAAA,EAAA,EAAA,aACG,EAAe,gBAAgB,IAAI,OAAO,EAAa,CAAC,IAAM,KACrE,CAAC,EAAa,CACf,CAoED,IAlEA,EAAA,EAAA,eAAgB,CACd,IAAM,EAAY,EAAa,QAE/B,GAAI,CAAC,EACH,OAGF,IAAM,EAAe,EAAW,EAAU,cAA2B,EAAS,CAAG,KAE3E,MAAe,CACnB,IAAM,EAAmB,EAAa,QACtC,GAAI,CAAC,GAAoB,CAAC,EAAU,CAClC,EAAQ,KAAK,CAEb,OAGF,IAAM,EAAkB,EAAiB,cAA2B,EAAS,CAC7E,GAAI,CAAC,EAAiB,CACpB,EAAQ,KAAK,CAEb,OAGF,IAAM,EAAgB,EAAiB,uBAAuB,CACxD,EAAW,EAAgB,uBAAuB,CAKlD,EACJ,EAAgB,YAAc,EAAI,EAAS,MAAQ,EAAgB,YAAc,EAC7E,EACJ,EAAgB,aAAe,EAAI,EAAS,OAAS,EAAgB,aAAe,EAGtF,EAAQ,CACN,MAAO,EAAS,KAAO,EAAc,MAAQ,EAAS,EAAiB,WACvE,KAAM,EAAS,IAAM,EAAc,KAAO,EAAS,EAAiB,UACpE,MAAO,EAAS,MAAQ,EACxB,OAAQ,EAAS,OAAS,EAC3B,CAAC,EAGJ,GAAQ,CAER,IAAM,EACJ,OAAO,eAAmB,IACtB,IAAI,mBAAqB,CACvB,GAAQ,EACR,CACF,KAQN,OANA,GAAI,QAAQ,EAAU,CAClB,GAAc,GAAI,QAAQ,EAAa,CAE3C,OAAO,iBAAiB,SAAU,EAAQ,CAAE,QAAS,GAAM,CAAC,CAC5D,OAAO,gBAAgB,iBAAiB,SAAU,EAAQ,CAAE,QAAS,GAAM,CAAC,KAE/D,CACX,GAAI,YAAY,CAChB,OAAO,oBAAoB,SAAU,EAAO,CAC5C,OAAO,gBAAgB,oBAAoB,SAAU,EAAO,GAE7D,CAAC,EAAc,EAAS,CAAC,CAExB,CAAC,EAAM,OAAO,KAElB,IAAM,EAAuB,CAC3B,KAAM,EAAK,KACX,IAAK,EAAK,IACV,MAAO,EAAK,MACZ,OAAQ,EAAK,OACd,CAED,OACE,EAAA,EAAA,KAAC,OAAD,CACO,MACL,uBAAqB,8BACrB,cAAA,GACA,UAAW,EAAgB,CAAE,YAAW,CAAC,CAClC,QACP,GAAI,EACJ,CAAA,EAIN,EAA0B,YAAc,6BC/FxC,IAAa,GAAwB,CACnC,QACA,WAAW,GACX,WACA,YACA,MACA,GAAG,KAC4B,CAC/B,GAAM,CAAE,eAAc,gBAAe,OAAM,YAAW,cAAe,GAA4B,CAE3F,EAAY,IAAiB,EAE7B,MAAoB,CACnB,GACH,EAAc,EAAM,EAIlB,EAAU,EAAA,SAAS,QAAQ,EAAS,CAAC,KAAK,EAAO,IACjD,OAAO,GAAU,UAAY,OAAO,GAAU,UAE9C,EAAA,EAAA,KAAC,OAAD,CAA4B,oCAAA,YACzB,EACI,CAFI,QAAQ,IAEZ,CAIJ,EACP,CAGI,EAAY,EAAW,QAAQ,EAAM,CACrC,EAAY,EACd,CAAE,UAAW,6CAA8C,CAC3D,IAAA,GAEA,EAA0B,GAE9B,GAAI,GAAa,EAAY,GAAK,IAAc,GAAI,CAClD,IAAM,EAAO,EACP,EAAa,EAAY,EAK/B,EAJmB,KAAK,MAAM,EAAY,EAAK,CAC7B,KAAK,KAAK,EAAW,OAAS,EAAK,CAGF,GAAK,IAAe,EAGzE,OACE,EAAA,EAAA,MAAC,SAAD,CACO,MACL,KAAK,SACL,KAAK,QACL,uBAAqB,yBACrB,aAAY,EACZ,eAAc,EACd,eAAc,GAAa,IAAA,GAC3B,gBAAe,GAAY,IAAA,GACjB,WACV,SAAU,EAAY,EAAI,GAC1B,UAAW,EAAW,CAAE,YAAW,CAAC,CACpC,MAAO,EACP,QAAS,EACT,GAAI,WAdN,CAgBG,EAEA,GAAQ,IAAa,EAAA,EAAA,KAAC,QAAD,CAAO,KAAK,SAAe,OAAa,QAAS,CAAA,CAEtE,GAA2B,IAC1B,EAAA,EAAA,KAAC,MAAD,CACE,UAAU,+CACV,MAAO,CACL,OAAQ,+BACR,MAAO,QAAQ,EAAY,IAAI,4BAChC,CACD,cAAY,OACZ,CAAA,CAEG,IAIb,EAAqB,YAAc,wBClGnC,IAAa,EAGT,OAAO,OAAO,EAAM,CACtB,KAAA,EACA,UAAA,EACD,CAAC,CAEF,EAAiB,YAAc,mBAC/B,EAAK,YAAc,wBACnB,EAAU,YAAc"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/segmented-control/SegmentedControl.styles.ts","../../src/segmented-control/SegmentedControlContext.tsx","../../src/segmented-control/useSegmentedControlNavigation.ts","../../src/segmented-control/SegmentedControl.tsx","../../src/segmented-control/SegmentedControlIndicator.tsx","../../src/segmented-control/SegmentedControlItem.tsx","../../src/segmented-control/index.ts"],"sourcesContent":["import { cva, VariantProps } from 'class-variance-authority'\n\nexport const rootStyles = cva([\n 'default:self-start',\n 'group inline-flex flex-wrap',\n 'relative items-stretch min-w-max',\n 'rounded-xl p-sm',\n 'bg-surface border-sm border-outline',\n])\n\nexport const itemStyles = cva([\n 'relative z-raised min-h-sz-44 focus-visible:outline-none',\n 'flex flex-auto items-center justify-center gap-md',\n 'default:px-lg default:py-md',\n 'rounded-[20px]',\n 'cursor-pointer select-none',\n 'font-medium',\n 'transition-colors duration-150',\n 'outline-none',\n 'focus-visible:u-outline',\n 'data-disabled:cursor-not-allowed data-disabled:opacity-dim-3',\n 'data-checked:text-on-support-container',\n // Avoid layout shift: simulate \"bold\" without changing font metrics.\n // Apply only to wrapped text nodes (not arbitrary nested JSX like Tag).\n 'data-checked:[&>[data-spark-segmented-control-text]]:[text-shadow:0.35px_0_currentColor,-0.35px_0_currentColor]',\n])\n\nexport const indicatorStyles = cva([\n 'absolute z-base',\n 'rounded-[20px]',\n 'bg-support-container border-md border-support',\n 'group-has-focus-visible:border-focus',\n 'transition-[left,top,width,height] duration-200 ease-in-out',\n 'pointer-events-none',\n])\n\nexport type SegmentedControlStylesProps = VariantProps<typeof itemStyles>\n","import { createContext, RefObject, useContext } from 'react'\n\nexport interface SegmentedControlContextInterface {\n checkedValue: string | null\n containerRef: RefObject<HTMLDivElement | null>\n onValueChange: (value: string) => void\n name?: string\n rowLength?: number\n itemValues: string[]\n}\n\nexport const SegmentedControlContext = createContext<SegmentedControlContextInterface>(\n {} as SegmentedControlContextInterface\n)\n\nexport const useSegmentedControlContext = () => {\n const context = useContext(SegmentedControlContext)\n\n if (!context) {\n throw Error('useSegmentedControlContext must be used within a SegmentedControlContext Provider')\n }\n\n return context\n}\n","import { KeyboardEvent, RefObject } from 'react'\n\ninterface UseSegmentedControlNavigationProps {\n itemValues: string[]\n containerRef: RefObject<HTMLDivElement | null>\n onValueChange: (value: string) => void\n}\n\n/**\n * Custom hook that handles keyboard navigation for SegmentedControl.\n * Uses sequential left/right navigation.\n */\nexport const useSegmentedControlNavigation = ({\n itemValues,\n containerRef,\n onValueChange,\n}: UseSegmentedControlNavigationProps) => {\n const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n // Find the currently focused item (which may differ from checkedValue during keyboard navigation)\n const focusedElement = e.target as HTMLElement\n const focusedValue = focusedElement.getAttribute('data-value')\n\n if (!focusedValue) return\n\n const currentIndex = itemValues.indexOf(focusedValue)\n if (currentIndex === -1) return\n\n let nextIndex: number | null = null\n\n // Always use 1D sequential navigation (left/right only)\n nextIndex = calculate1DNavigation(e.key, currentIndex, itemValues.length)\n\n if (nextIndex !== null && nextIndex !== currentIndex) {\n e.preventDefault()\n\n // Skip disabled items\n let attempts = 0\n const maxAttempts = itemValues.length\n\n while (attempts < maxAttempts) {\n const nextValue = itemValues[nextIndex]\n if (!nextValue) return\n\n const nextItem = containerRef.current?.querySelector<HTMLElement>(\n `[data-value=\"${CSS.escape(nextValue)}\"]`\n )\n\n // If the item is not disabled, focus it and update the value\n if (nextItem && !nextItem.hasAttribute('data-disabled')) {\n nextItem.focus()\n onValueChange(nextValue)\n return\n }\n\n // If disabled, continue in the same direction\n const direction = e.key === 'ArrowRight' || e.key === 'ArrowDown' ? 1 : -1\n nextIndex = (nextIndex + direction + itemValues.length) % itemValues.length\n attempts++\n }\n }\n }\n\n return { handleKeyDown }\n}\n\n/**\n * Calculate next index for 1D sequential navigation.\n * Navigation wraps around at the boundaries.\n */\nfunction calculate1DNavigation(\n key: string,\n currentIndex: number,\n totalItems: number\n): number | null {\n switch (key) {\n case 'ArrowRight':\n case 'ArrowDown':\n return (currentIndex + 1) % totalItems\n case 'ArrowLeft':\n case 'ArrowUp':\n return (currentIndex - 1 + totalItems) % totalItems\n default:\n return null\n }\n}\n","import { useFormFieldControl } from '@spark-ui/components/form-field'\nimport { useMergeRefs } from '@spark-ui/hooks/use-merge-refs'\nimport {\n Children,\n type ComponentProps,\n CSSProperties,\n isValidElement,\n Ref,\n useRef,\n useState,\n} from 'react'\n\nimport type { SegmentedControlStylesProps } from './SegmentedControl.styles'\nimport { rootStyles } from './SegmentedControl.styles'\nimport { SegmentedControlContext } from './SegmentedControlContext'\nimport { useSegmentedControlNavigation } from './useSegmentedControlNavigation'\n\nexport interface SegmentedControlProps\n extends Omit<ComponentProps<'div'>, 'onValueChange'>, SegmentedControlStylesProps {\n /**\n * The controlled selected value.\n */\n value?: string\n /**\n * The uncontrolled default selected value.\n */\n defaultValue?: string\n /**\n * Callback fired when the selected value changes.\n */\n onValueChange?: (value: string) => void\n /**\n * Number of items per row in multi-row layout.\n * When undefined, items display in a single row (default behavior).\n * @default undefined\n * @example\n * // Create 3-column grid with wrapping rows\n * <SegmentedControl rowLength={3}>\n */\n rowLength?: number\n /**\n * The name attribute for the radio group (used in form submissions).\n */\n name?: string\n ref?: Ref<HTMLDivElement>\n}\n\nconst getFirstItemValue = (children: React.ReactNode): string | null => {\n let firstValue: string | null = null\n\n Children.forEach(children, child => {\n if (firstValue !== null) return\n if (isValidElement(child) && typeof (child.props as { value?: string }).value === 'string') {\n firstValue = (child.props as { value: string }).value\n }\n })\n\n return firstValue\n}\n\nexport const SegmentedControl = ({\n value,\n defaultValue,\n onValueChange,\n className,\n children,\n rowLength,\n name: nameProp,\n ref,\n ...rest\n}: SegmentedControlProps) => {\n const containerRef = useRef<HTMLDivElement | null>(null)\n const mergedRef = useMergeRefs(containerRef, ref)\n\n const firstValue = getFirstItemValue(children)\n\n const isControlled = value !== undefined\n const [internalValue, setInternalValue] = useState<string | null>(\n () => defaultValue ?? firstValue\n )\n const checkedValue = isControlled ? (value ?? null) : internalValue\n\n const handleValueChange = (newValue: string) => {\n if (!isControlled) {\n setInternalValue(newValue)\n }\n\n onValueChange?.(newValue)\n }\n\n const { labelId, description, isRequired, isInvalid, name: nameFromField } = useFormFieldControl()\n const name = nameProp ?? nameFromField\n\n // Get all item values in order\n const itemValues: string[] = []\n Children.forEach(children, child => {\n if (isValidElement(child) && typeof (child.props as { value?: string }).value === 'string') {\n itemValues.push((child.props as { value: string }).value)\n }\n })\n\n // Keyboard navigation (sequential left/right)\n const { handleKeyDown } = useSegmentedControlNavigation({\n itemValues,\n containerRef,\n onValueChange: handleValueChange,\n })\n\n // Compute dynamic flex styles for multi-row layout\n const flexStyles = rowLength\n ? ({\n '--segmented-control-cols': rowLength,\n rowGap: 'var(--spacing-md)',\n } as CSSProperties)\n : undefined\n\n return (\n <SegmentedControlContext\n value={{\n checkedValue,\n containerRef,\n onValueChange: handleValueChange,\n name,\n rowLength,\n itemValues,\n }}\n >\n <div\n ref={mergedRef}\n role=\"radiogroup\"\n data-spark-component=\"segmented-control\"\n className={rootStyles({ className })}\n style={flexStyles}\n aria-labelledby={labelId}\n aria-describedby={description}\n aria-required={isRequired || undefined}\n aria-invalid={isInvalid || undefined}\n onKeyDown={handleKeyDown}\n {...rest}\n >\n {children}\n </div>\n </SegmentedControlContext>\n )\n}\n\nSegmentedControl.displayName = 'SegmentedControl'\n","import { type ComponentProps, type CSSProperties, Ref, useEffect, useMemo, useState } from 'react'\n\nimport { indicatorStyles } from './SegmentedControl.styles'\nimport { useSegmentedControlContext } from './SegmentedControlContext'\n\ninterface IndicatorRect {\n left: number\n top: number\n width: number\n height: number\n}\n\nexport interface SegmentedControlIndicatorProps extends ComponentProps<'span'> {\n ref?: Ref<HTMLSpanElement>\n}\n\n/** The visual indicator that highlights the selected item. Renders a <span> element. */\nexport const SegmentedControlIndicator = ({\n className,\n ref,\n ...rest\n}: SegmentedControlIndicatorProps) => {\n const { checkedValue, containerRef } = useSegmentedControlContext()\n const [rect, setRect] = useState<IndicatorRect | null>(null)\n\n const selector = useMemo(\n () => (checkedValue ? `[data-value=\"${CSS.escape(checkedValue)}\"]` : null),\n [checkedValue]\n )\n\n useEffect(() => {\n const container = containerRef.current\n\n if (!container) {\n return\n }\n\n const selectedItem = selector ? container.querySelector<HTMLElement>(selector) : null\n\n const update = () => {\n const currentContainer = containerRef.current\n if (!currentContainer || !selector) {\n setRect(null)\n\n return\n }\n\n const currentSelected = currentContainer.querySelector<HTMLElement>(selector)\n if (!currentSelected) {\n setRect(null)\n\n return\n }\n\n const containerRect = currentContainer.getBoundingClientRect()\n const itemRect = currentSelected.getBoundingClientRect()\n\n // Storybook canvas \"zoom\" can be implemented via `transform: scale()`.\n // In that case, `getBoundingClientRect()` returns *scaled* values, but CSS positioning/sizing\n // expects unscaled layout pixels. We infer the scale factor from offset sizes and normalize.\n const scaleX =\n currentSelected.offsetWidth > 0 ? itemRect.width / currentSelected.offsetWidth : 1\n const scaleY =\n currentSelected.offsetHeight > 0 ? itemRect.height / currentSelected.offsetHeight : 1\n\n // `getBoundingClientRect()` is border-box; absolute positioning is relative to the padding box.\n setRect({\n left: (itemRect.left - containerRect.left) / scaleX - currentContainer.clientLeft,\n top: (itemRect.top - containerRect.top) / scaleY - currentContainer.clientTop,\n width: itemRect.width / scaleX,\n height: itemRect.height / scaleY,\n })\n }\n\n update()\n\n const ro =\n typeof ResizeObserver !== 'undefined'\n ? new ResizeObserver(() => {\n update()\n })\n : null\n\n ro?.observe(container)\n if (selectedItem) ro?.observe(selectedItem)\n\n window.addEventListener('resize', update, { passive: true })\n window.visualViewport?.addEventListener('resize', update, { passive: true })\n\n return () => {\n ro?.disconnect()\n window.removeEventListener('resize', update)\n window.visualViewport?.removeEventListener('resize', update)\n }\n }, [containerRef, selector])\n\n if (!rect) return null\n\n const style: CSSProperties = {\n left: rect.left,\n top: rect.top,\n width: rect.width,\n height: rect.height,\n }\n\n return (\n <span\n ref={ref}\n data-spark-component=\"segmented-control-indicator\"\n aria-hidden\n className={indicatorStyles({ className })}\n style={style}\n {...rest}\n />\n )\n}\n\nSegmentedControlIndicator.displayName = 'SegmentedControl.Indicator'\n","import { Children, type ComponentProps, Ref } from 'react'\n\nimport { itemStyles } from './SegmentedControl.styles'\nimport { useSegmentedControlContext } from './SegmentedControlContext'\n\nexport interface SegmentedControlItemProps extends Omit<\n ComponentProps<'button'>,\n 'value' | 'onClick'\n> {\n /**\n * A unique value that identifies this item within the segmented control.\n */\n value: string\n /**\n * When true, prevents the user from interacting with this item.\n * @default false\n */\n disabled?: boolean\n ref?: Ref<HTMLButtonElement>\n}\n\n/** A selectable item in the segmented control. Renders a <button> element. */\nexport const SegmentedControlItem = ({\n value,\n disabled = false,\n children,\n className,\n ref,\n ...rest\n}: SegmentedControlItemProps) => {\n const { checkedValue, onValueChange, name, rowLength, itemValues } = useSegmentedControlContext()\n\n const isChecked = checkedValue === value\n\n const handleClick = () => {\n if (!disabled) {\n onValueChange(value)\n }\n }\n\n const content = Children.toArray(children).map((child, index) => {\n if (typeof child === 'string' || typeof child === 'number') {\n return (\n <span key={`text-${index}`} data-spark-segmented-control-text>\n {child}\n </span>\n )\n }\n\n return child\n })\n\n // Calculate flex-basis for multi-row layout and horizontal separator visibility\n const itemIndex = itemValues.indexOf(value)\n const itemStyle = rowLength\n ? { flexBasis: `calc(100% / var(--segmented-control-cols))` }\n : undefined\n\n let showHorizontalSeparator = false\n\n if (rowLength && rowLength > 0 && itemIndex !== -1) {\n const cols = rowLength\n const currentCol = itemIndex % cols\n const currentRow = Math.floor(itemIndex / cols)\n const totalRows = Math.ceil(itemValues.length / cols)\n\n // Show horizontal separator only on the first column of each row (except last row)\n showHorizontalSeparator = currentRow < totalRows - 1 && currentCol === 0\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"radio\"\n data-spark-component=\"segmented-control-item\"\n data-value={value}\n aria-checked={isChecked}\n data-checked={isChecked || undefined}\n data-disabled={disabled || undefined}\n disabled={disabled}\n tabIndex={isChecked ? 0 : -1}\n className={itemStyles({ className })}\n style={itemStyle}\n onClick={handleClick}\n {...rest}\n >\n {content}\n {/* Hidden input for form submission */}\n {name && isChecked && <input type=\"hidden\" name={name} value={value} />}\n {/* Horizontal separator between rows (full width across all columns) */}\n {showHorizontalSeparator && rowLength && (\n <div\n className=\"bg-outline/dim-3 -mx-sm absolute left-0 h-px\"\n style={{\n bottom: 'calc(var(--spacing-md) / -2)',\n width: `calc(${rowLength * 100}% + var(--spacing-sm) * 2)`,\n }}\n aria-hidden=\"true\"\n />\n )}\n </button>\n )\n}\n\nSegmentedControlItem.displayName = 'SegmentedControl.Item'\n","import { SegmentedControl as Root } from './SegmentedControl'\nimport { SegmentedControlIndicator as Indicator } from './SegmentedControlIndicator'\nimport { SegmentedControlItem as Item } from './SegmentedControlItem'\n\n/**\n * A set of toggle buttons that allows users to select a single option from a group of related choices.\n */\nexport const SegmentedControl: typeof Root & {\n Item: typeof Item\n Indicator: typeof Indicator\n} = Object.assign(Root, {\n Item,\n Indicator,\n})\n\nSegmentedControl.displayName = 'SegmentedControl'\nItem.displayName = 'SegmentedControl.Item'\nIndicator.displayName = 'SegmentedControl.Indicator'\n\nexport type { SegmentedControlProps } from './SegmentedControl'\nexport type { SegmentedControlItemProps } from './SegmentedControlItem'\nexport type { SegmentedControlIndicatorProps } from './SegmentedControlIndicator'\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/segmented-control/SegmentedControl.styles.ts","../../src/segmented-control/SegmentedControlContext.tsx","../../src/segmented-control/useSegmentedControlNavigation.ts","../../src/segmented-control/SegmentedControl.tsx","../../src/segmented-control/SegmentedControlIndicator.tsx","../../src/segmented-control/SegmentedControlItem.tsx","../../src/segmented-control/index.ts"],"sourcesContent":["import { cva, VariantProps } from 'class-variance-authority'\n\nexport const rootStyles = cva([\n 'default:self-start',\n 'group inline-flex flex-wrap',\n 'relative items-stretch min-w-max',\n 'rounded-xl p-sm',\n 'bg-surface border-sm border-outline',\n])\n\nexport const itemStyles = cva([\n 'relative z-raised min-h-sz-44 focus-visible:outline-none',\n 'flex flex-auto items-center justify-center gap-md',\n 'default:px-lg default:py-md',\n 'rounded-[20px]',\n 'cursor-pointer select-none',\n 'font-medium',\n 'transition-colors duration-150',\n 'outline-none',\n 'focus-visible:u-outline',\n 'data-disabled:cursor-not-allowed data-disabled:opacity-dim-3',\n 'data-checked:text-on-support-container',\n // Avoid layout shift: simulate \"bold\" without changing font metrics.\n // Apply only to wrapped text nodes (not arbitrary nested JSX like Tag).\n 'data-checked:[&>[data-spark-segmented-control-text]]:[text-shadow:0.35px_0_currentColor,-0.35px_0_currentColor]',\n])\n\nexport const indicatorStyles = cva([\n 'absolute z-base',\n 'rounded-[20px]',\n 'bg-support-container border-md border-support',\n 'group-has-focus-visible:border-focus',\n 'transition-[left,top,width,height] duration-200 ease-in-out',\n 'pointer-events-none',\n])\n\nexport type SegmentedControlStylesProps = VariantProps<typeof itemStyles>\n","import { createContext, RefObject, useContext } from 'react'\n\nexport interface SegmentedControlContextInterface {\n checkedValue: string | null\n containerRef: RefObject<HTMLDivElement | null>\n onValueChange: (value: string) => void\n name?: string\n rowLength?: number\n itemValues: string[]\n}\n\nexport const SegmentedControlContext = createContext<SegmentedControlContextInterface>(\n {} as SegmentedControlContextInterface\n)\n\nexport const useSegmentedControlContext = () => {\n const context = useContext(SegmentedControlContext)\n\n if (!context) {\n throw Error('useSegmentedControlContext must be used within a SegmentedControlContext Provider')\n }\n\n return context\n}\n","import { KeyboardEvent, RefObject } from 'react'\n\ninterface UseSegmentedControlNavigationProps {\n itemValues: string[]\n containerRef: RefObject<HTMLDivElement | null>\n onValueChange: (value: string) => void\n}\n\n/**\n * Custom hook that handles keyboard navigation for SegmentedControl.\n * Uses sequential left/right navigation.\n */\nexport const useSegmentedControlNavigation = ({\n itemValues,\n containerRef,\n onValueChange,\n}: UseSegmentedControlNavigationProps) => {\n const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n // Find the currently focused item (which may differ from checkedValue during keyboard navigation)\n const focusedElement = e.target as HTMLElement\n const focusedValue = focusedElement.getAttribute('data-value')\n\n if (!focusedValue) return\n\n const currentIndex = itemValues.indexOf(focusedValue)\n if (currentIndex === -1) return\n\n let nextIndex: number | null = null\n\n // Always use 1D sequential navigation (left/right only)\n nextIndex = calculate1DNavigation(e.key, currentIndex, itemValues.length)\n\n if (nextIndex !== null && nextIndex !== currentIndex) {\n e.preventDefault()\n\n // Skip disabled items\n let attempts = 0\n const maxAttempts = itemValues.length\n\n while (attempts < maxAttempts) {\n const nextValue = itemValues[nextIndex]\n if (!nextValue) return\n\n const nextItem = containerRef.current?.querySelector<HTMLElement>(\n `[data-value=\"${CSS.escape(nextValue)}\"]`\n )\n\n // If the item is not disabled, focus it and update the value\n if (nextItem && !nextItem.hasAttribute('data-disabled')) {\n nextItem.focus()\n onValueChange(nextValue)\n return\n }\n\n // If disabled, continue in the same direction\n const direction = e.key === 'ArrowRight' || e.key === 'ArrowDown' ? 1 : -1\n nextIndex = (nextIndex + direction + itemValues.length) % itemValues.length\n attempts++\n }\n }\n }\n\n return { handleKeyDown }\n}\n\n/**\n * Calculate next index for 1D sequential navigation.\n * Navigation wraps around at the boundaries.\n */\nfunction calculate1DNavigation(\n key: string,\n currentIndex: number,\n totalItems: number\n): number | null {\n switch (key) {\n case 'ArrowRight':\n case 'ArrowDown':\n return (currentIndex + 1) % totalItems\n case 'ArrowLeft':\n case 'ArrowUp':\n return (currentIndex - 1 + totalItems) % totalItems\n default:\n return null\n }\n}\n","import { useFormFieldControl } from '@spark-ui/components/form-field'\nimport { useMergeRefs } from '@spark-ui/hooks/use-merge-refs'\nimport {\n Children,\n type ComponentProps,\n CSSProperties,\n isValidElement,\n Ref,\n useRef,\n useState,\n} from 'react'\n\nimport type { SegmentedControlStylesProps } from './SegmentedControl.styles'\nimport { rootStyles } from './SegmentedControl.styles'\nimport { SegmentedControlContext } from './SegmentedControlContext'\nimport { useSegmentedControlNavigation } from './useSegmentedControlNavigation'\n\nexport interface SegmentedControlProps\n extends Omit<ComponentProps<'div'>, 'onValueChange'>, SegmentedControlStylesProps {\n /**\n * The controlled selected value.\n */\n value?: string\n /**\n * The uncontrolled default selected value.\n */\n defaultValue?: string\n /**\n * Callback fired when the selected value changes.\n */\n onValueChange?: (value: string) => void\n /**\n * Number of items per row in multi-row layout.\n * When undefined, items display in a single row (default behavior).\n * @default undefined\n * @example\n * // Create 3-column grid with wrapping rows\n * <SegmentedControl rowLength={3}>\n */\n rowLength?: number\n /**\n * The name attribute for the radio group (used in form submissions).\n */\n name?: string\n ref?: Ref<HTMLDivElement>\n}\n\nconst getFirstItemValue = (children: React.ReactNode): string | null => {\n let firstValue: string | null = null\n\n Children.forEach(children, child => {\n if (firstValue !== null) return\n if (isValidElement(child) && typeof (child.props as { value?: string }).value === 'string') {\n firstValue = (child.props as { value: string }).value\n }\n })\n\n return firstValue\n}\n\nexport const SegmentedControl = ({\n value,\n defaultValue,\n onValueChange,\n className,\n children,\n rowLength,\n name: nameProp,\n ref,\n ...rest\n}: SegmentedControlProps) => {\n const containerRef = useRef<HTMLDivElement | null>(null)\n const mergedRef = useMergeRefs(containerRef, ref)\n\n const firstValue = getFirstItemValue(children)\n\n const isControlled = value !== undefined\n const [internalValue, setInternalValue] = useState<string | null>(\n () => defaultValue ?? firstValue\n )\n const checkedValue = isControlled ? (value ?? null) : internalValue\n\n const handleValueChange = (newValue: string) => {\n if (!isControlled) {\n setInternalValue(newValue)\n }\n\n onValueChange?.(newValue)\n }\n\n const { labelId, description, isRequired, isInvalid, name: nameFromField } = useFormFieldControl()\n const name = nameProp ?? nameFromField\n\n // Get all item values in order\n const itemValues: string[] = []\n Children.forEach(children, child => {\n if (isValidElement(child) && typeof (child.props as { value?: string }).value === 'string') {\n itemValues.push((child.props as { value: string }).value)\n }\n })\n\n // Keyboard navigation (sequential left/right)\n const { handleKeyDown } = useSegmentedControlNavigation({\n itemValues,\n containerRef,\n onValueChange: handleValueChange,\n })\n\n // Compute dynamic flex styles for multi-row layout\n const flexStyles = rowLength\n ? ({\n '--segmented-control-cols': rowLength,\n rowGap: 'var(--spacing-md)',\n } as CSSProperties)\n : undefined\n\n return (\n <SegmentedControlContext\n value={{\n checkedValue,\n containerRef,\n onValueChange: handleValueChange,\n name,\n rowLength,\n itemValues,\n }}\n >\n <div\n ref={mergedRef}\n role=\"radiogroup\"\n data-spark-component=\"segmented-control\"\n className={rootStyles({ className })}\n style={flexStyles}\n aria-labelledby={labelId}\n aria-describedby={description}\n aria-required={isRequired || undefined}\n aria-invalid={isInvalid || undefined}\n onKeyDown={handleKeyDown}\n {...rest}\n >\n {children}\n </div>\n </SegmentedControlContext>\n )\n}\n\nSegmentedControl.displayName = 'SegmentedControl'\n","import { type ComponentProps, type CSSProperties, Ref, useEffect, useMemo, useState } from 'react'\n\nimport { indicatorStyles } from './SegmentedControl.styles'\nimport { useSegmentedControlContext } from './SegmentedControlContext'\n\ninterface IndicatorRect {\n left: number\n top: number\n width: number\n height: number\n}\n\nexport interface SegmentedControlIndicatorProps extends ComponentProps<'span'> {\n ref?: Ref<HTMLSpanElement>\n}\n\n/** The visual indicator that highlights the selected item. Renders a <span> element. */\nexport const SegmentedControlIndicator = ({\n className,\n ref,\n ...rest\n}: SegmentedControlIndicatorProps) => {\n const { checkedValue, containerRef } = useSegmentedControlContext()\n const [rect, setRect] = useState<IndicatorRect | null>(null)\n\n const selector = useMemo(\n () => (checkedValue ? `[data-value=\"${CSS.escape(checkedValue)}\"]` : null),\n [checkedValue]\n )\n\n useEffect(() => {\n const container = containerRef.current\n\n if (!container) {\n return\n }\n\n const selectedItem = selector ? container.querySelector<HTMLElement>(selector) : null\n\n const update = () => {\n const currentContainer = containerRef.current\n if (!currentContainer || !selector) {\n setRect(null)\n\n return\n }\n\n const currentSelected = currentContainer.querySelector<HTMLElement>(selector)\n if (!currentSelected) {\n setRect(null)\n\n return\n }\n\n const containerRect = currentContainer.getBoundingClientRect()\n const itemRect = currentSelected.getBoundingClientRect()\n\n // Storybook canvas \"zoom\" can be implemented via `transform: scale()`.\n // In that case, `getBoundingClientRect()` returns *scaled* values, but CSS positioning/sizing\n // expects unscaled layout pixels. We infer the scale factor from offset sizes and normalize.\n const scaleX =\n currentSelected.offsetWidth > 0 ? itemRect.width / currentSelected.offsetWidth : 1\n const scaleY =\n currentSelected.offsetHeight > 0 ? itemRect.height / currentSelected.offsetHeight : 1\n\n // `getBoundingClientRect()` is border-box; absolute positioning is relative to the padding box.\n setRect({\n left: (itemRect.left - containerRect.left) / scaleX - currentContainer.clientLeft,\n top: (itemRect.top - containerRect.top) / scaleY - currentContainer.clientTop,\n width: itemRect.width / scaleX,\n height: itemRect.height / scaleY,\n })\n }\n\n update()\n\n const ro =\n typeof ResizeObserver !== 'undefined'\n ? new ResizeObserver(() => {\n update()\n })\n : null\n\n ro?.observe(container)\n if (selectedItem) ro?.observe(selectedItem)\n\n window.addEventListener('resize', update, { passive: true })\n window.visualViewport?.addEventListener('resize', update, { passive: true })\n\n return () => {\n ro?.disconnect()\n window.removeEventListener('resize', update)\n window.visualViewport?.removeEventListener('resize', update)\n }\n }, [containerRef, selector])\n\n if (!rect) return null\n\n const style: CSSProperties = {\n left: rect.left,\n top: rect.top,\n width: rect.width,\n height: rect.height,\n }\n\n return (\n <span\n ref={ref}\n data-spark-component=\"segmented-control-indicator\"\n aria-hidden\n className={indicatorStyles({ className })}\n style={style}\n {...rest}\n />\n )\n}\n\nSegmentedControlIndicator.displayName = 'SegmentedControl.Indicator'\n","import { Children, type ComponentProps, Ref } from 'react'\n\nimport { itemStyles } from './SegmentedControl.styles'\nimport { useSegmentedControlContext } from './SegmentedControlContext'\n\nexport interface SegmentedControlItemProps extends Omit<\n ComponentProps<'button'>,\n 'value' | 'onClick'\n> {\n /**\n * A unique value that identifies this item within the segmented control.\n */\n value: string\n /**\n * When true, prevents the user from interacting with this item.\n * @default false\n */\n disabled?: boolean\n ref?: Ref<HTMLButtonElement>\n}\n\n/** A selectable item in the segmented control. Renders a <button> element. */\nexport const SegmentedControlItem = ({\n value,\n disabled = false,\n children,\n className,\n ref,\n ...rest\n}: SegmentedControlItemProps) => {\n const { checkedValue, onValueChange, name, rowLength, itemValues } = useSegmentedControlContext()\n\n const isChecked = checkedValue === value\n\n const handleClick = () => {\n if (!disabled) {\n onValueChange(value)\n }\n }\n\n const content = Children.toArray(children).map((child, index) => {\n if (typeof child === 'string' || typeof child === 'number') {\n return (\n <span key={`text-${index}`} data-spark-segmented-control-text>\n {child}\n </span>\n )\n }\n\n return child\n })\n\n // Calculate flex-basis for multi-row layout and horizontal separator visibility\n const itemIndex = itemValues.indexOf(value)\n const itemStyle = rowLength\n ? { flexBasis: `calc(100% / var(--segmented-control-cols))` }\n : undefined\n\n let showHorizontalSeparator = false\n\n if (rowLength && rowLength > 0 && itemIndex !== -1) {\n const cols = rowLength\n const currentCol = itemIndex % cols\n const currentRow = Math.floor(itemIndex / cols)\n const totalRows = Math.ceil(itemValues.length / cols)\n\n // Show horizontal separator only on the first column of each row (except last row)\n showHorizontalSeparator = currentRow < totalRows - 1 && currentCol === 0\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"radio\"\n data-spark-component=\"segmented-control-item\"\n data-value={value}\n aria-checked={isChecked}\n data-checked={isChecked || undefined}\n data-disabled={disabled || undefined}\n disabled={disabled}\n tabIndex={isChecked ? 0 : -1}\n className={itemStyles({ className })}\n style={itemStyle}\n onClick={handleClick}\n {...rest}\n >\n {content}\n {/* Hidden input for form submission */}\n {name && isChecked && <input type=\"hidden\" name={name} value={value} />}\n {/* Horizontal separator between rows (full width across all columns) */}\n {showHorizontalSeparator && rowLength && (\n <div\n className=\"bg-outline/dim-3 -mx-sm absolute left-0 h-px\"\n style={{\n bottom: 'calc(var(--spacing-md) / -2)',\n width: `calc(${rowLength * 100}% + var(--spacing-sm) * 2)`,\n }}\n aria-hidden=\"true\"\n />\n )}\n </button>\n )\n}\n\nSegmentedControlItem.displayName = 'SegmentedControl.Item'\n","import { SegmentedControl as Root } from './SegmentedControl'\nimport { SegmentedControlIndicator as Indicator } from './SegmentedControlIndicator'\nimport { SegmentedControlItem as Item } from './SegmentedControlItem'\n\n/**\n * A set of toggle buttons that allows users to select a single option from a group of related choices.\n */\nexport const SegmentedControl: typeof Root & {\n Item: typeof Item\n Indicator: typeof Indicator\n} = Object.assign(Root, {\n Item,\n Indicator,\n})\n\nSegmentedControl.displayName = 'SegmentedControl'\nItem.displayName = 'SegmentedControl.Item'\nIndicator.displayName = 'SegmentedControl.Indicator'\n\nexport type { SegmentedControlProps } from './SegmentedControl'\nexport type { SegmentedControlItemProps } from './SegmentedControlItem'\nexport type { SegmentedControlIndicatorProps } from './SegmentedControlIndicator'\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/segmented-control/SegmentedControl.styles.ts","../../src/segmented-control/SegmentedControlContext.tsx","../../src/segmented-control/useSegmentedControlNavigation.ts","../../src/segmented-control/SegmentedControl.tsx","../../src/segmented-control/SegmentedControlIndicator.tsx","../../src/segmented-control/SegmentedControlItem.tsx","../../src/segmented-control/index.ts"],"sourcesContent":["import { cva, VariantProps } from 'class-variance-authority'\n\nexport const rootStyles = cva([\n 'default:self-start',\n 'group inline-flex flex-wrap',\n 'relative items-stretch min-w-max',\n 'rounded-xl p-sm',\n 'bg-surface border-sm border-outline',\n])\n\nexport const itemStyles = cva([\n 'relative z-raised min-h-sz-44 focus-visible:outline-none',\n 'flex flex-auto items-center justify-center gap-md',\n 'default:px-lg default:py-md',\n 'rounded-[20px]',\n 'cursor-pointer select-none',\n 'font-medium',\n 'transition-colors duration-150',\n 'outline-none',\n 'focus-visible:u-outline',\n 'data-disabled:cursor-not-allowed data-disabled:opacity-dim-3',\n 'data-checked:text-on-support-container',\n // Avoid layout shift: simulate \"bold\" without changing font metrics.\n // Apply only to wrapped text nodes (not arbitrary nested JSX like Tag).\n 'data-checked:[&>[data-spark-segmented-control-text]]:[text-shadow:0.35px_0_currentColor,-0.35px_0_currentColor]',\n])\n\nexport const indicatorStyles = cva([\n 'absolute z-base',\n 'rounded-[20px]',\n 'bg-support-container border-md border-support',\n 'group-has-focus-visible:border-focus',\n 'transition-[left,top,width,height] duration-200 ease-in-out',\n 'pointer-events-none',\n])\n\nexport type SegmentedControlStylesProps = VariantProps<typeof itemStyles>\n","import { createContext, RefObject, useContext } from 'react'\n\nexport interface SegmentedControlContextInterface {\n checkedValue: string | null\n containerRef: RefObject<HTMLDivElement | null>\n onValueChange: (value: string) => void\n name?: string\n rowLength?: number\n itemValues: string[]\n}\n\nexport const SegmentedControlContext = createContext<SegmentedControlContextInterface>(\n {} as SegmentedControlContextInterface\n)\n\nexport const useSegmentedControlContext = () => {\n const context = useContext(SegmentedControlContext)\n\n if (!context) {\n throw Error('useSegmentedControlContext must be used within a SegmentedControlContext Provider')\n }\n\n return context\n}\n","import { KeyboardEvent, RefObject } from 'react'\n\ninterface UseSegmentedControlNavigationProps {\n itemValues: string[]\n containerRef: RefObject<HTMLDivElement | null>\n onValueChange: (value: string) => void\n}\n\n/**\n * Custom hook that handles keyboard navigation for SegmentedControl.\n * Uses sequential left/right navigation.\n */\nexport const useSegmentedControlNavigation = ({\n itemValues,\n containerRef,\n onValueChange,\n}: UseSegmentedControlNavigationProps) => {\n const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n // Find the currently focused item (which may differ from checkedValue during keyboard navigation)\n const focusedElement = e.target as HTMLElement\n const focusedValue = focusedElement.getAttribute('data-value')\n\n if (!focusedValue) return\n\n const currentIndex = itemValues.indexOf(focusedValue)\n if (currentIndex === -1) return\n\n let nextIndex: number | null = null\n\n // Always use 1D sequential navigation (left/right only)\n nextIndex = calculate1DNavigation(e.key, currentIndex, itemValues.length)\n\n if (nextIndex !== null && nextIndex !== currentIndex) {\n e.preventDefault()\n\n // Skip disabled items\n let attempts = 0\n const maxAttempts = itemValues.length\n\n while (attempts < maxAttempts) {\n const nextValue = itemValues[nextIndex]\n if (!nextValue) return\n\n const nextItem = containerRef.current?.querySelector<HTMLElement>(\n `[data-value=\"${CSS.escape(nextValue)}\"]`\n )\n\n // If the item is not disabled, focus it and update the value\n if (nextItem && !nextItem.hasAttribute('data-disabled')) {\n nextItem.focus()\n onValueChange(nextValue)\n return\n }\n\n // If disabled, continue in the same direction\n const direction = e.key === 'ArrowRight' || e.key === 'ArrowDown' ? 1 : -1\n nextIndex = (nextIndex + direction + itemValues.length) % itemValues.length\n attempts++\n }\n }\n }\n\n return { handleKeyDown }\n}\n\n/**\n * Calculate next index for 1D sequential navigation.\n * Navigation wraps around at the boundaries.\n */\nfunction calculate1DNavigation(\n key: string,\n currentIndex: number,\n totalItems: number\n): number | null {\n switch (key) {\n case 'ArrowRight':\n case 'ArrowDown':\n return (currentIndex + 1) % totalItems\n case 'ArrowLeft':\n case 'ArrowUp':\n return (currentIndex - 1 + totalItems) % totalItems\n default:\n return null\n }\n}\n","import { useFormFieldControl } from '@spark-ui/components/form-field'\nimport { useMergeRefs } from '@spark-ui/hooks/use-merge-refs'\nimport {\n Children,\n type ComponentProps,\n CSSProperties,\n isValidElement,\n Ref,\n useRef,\n useState,\n} from 'react'\n\nimport type { SegmentedControlStylesProps } from './SegmentedControl.styles'\nimport { rootStyles } from './SegmentedControl.styles'\nimport { SegmentedControlContext } from './SegmentedControlContext'\nimport { useSegmentedControlNavigation } from './useSegmentedControlNavigation'\n\nexport interface SegmentedControlProps\n extends Omit<ComponentProps<'div'>, 'onValueChange'>, SegmentedControlStylesProps {\n /**\n * The controlled selected value.\n */\n value?: string\n /**\n * The uncontrolled default selected value.\n */\n defaultValue?: string\n /**\n * Callback fired when the selected value changes.\n */\n onValueChange?: (value: string) => void\n /**\n * Number of items per row in multi-row layout.\n * When undefined, items display in a single row (default behavior).\n * @default undefined\n * @example\n * // Create 3-column grid with wrapping rows\n * <SegmentedControl rowLength={3}>\n */\n rowLength?: number\n /**\n * The name attribute for the radio group (used in form submissions).\n */\n name?: string\n ref?: Ref<HTMLDivElement>\n}\n\nconst getFirstItemValue = (children: React.ReactNode): string | null => {\n let firstValue: string | null = null\n\n Children.forEach(children, child => {\n if (firstValue !== null) return\n if (isValidElement(child) && typeof (child.props as { value?: string }).value === 'string') {\n firstValue = (child.props as { value: string }).value\n }\n })\n\n return firstValue\n}\n\nexport const SegmentedControl = ({\n value,\n defaultValue,\n onValueChange,\n className,\n children,\n rowLength,\n name: nameProp,\n ref,\n ...rest\n}: SegmentedControlProps) => {\n const containerRef = useRef<HTMLDivElement | null>(null)\n const mergedRef = useMergeRefs(containerRef, ref)\n\n const firstValue = getFirstItemValue(children)\n\n const isControlled = value !== undefined\n const [internalValue, setInternalValue] = useState<string | null>(\n () => defaultValue ?? firstValue\n )\n const checkedValue = isControlled ? (value ?? null) : internalValue\n\n const handleValueChange = (newValue: string) => {\n if (!isControlled) {\n setInternalValue(newValue)\n }\n\n onValueChange?.(newValue)\n }\n\n const { labelId, description, isRequired, isInvalid, name: nameFromField } = useFormFieldControl()\n const name = nameProp ?? nameFromField\n\n // Get all item values in order\n const itemValues: string[] = []\n Children.forEach(children, child => {\n if (isValidElement(child) && typeof (child.props as { value?: string }).value === 'string') {\n itemValues.push((child.props as { value: string }).value)\n }\n })\n\n // Keyboard navigation (sequential left/right)\n const { handleKeyDown } = useSegmentedControlNavigation({\n itemValues,\n containerRef,\n onValueChange: handleValueChange,\n })\n\n // Compute dynamic flex styles for multi-row layout\n const flexStyles = rowLength\n ? ({\n '--segmented-control-cols': rowLength,\n rowGap: 'var(--spacing-md)',\n } as CSSProperties)\n : undefined\n\n return (\n <SegmentedControlContext\n value={{\n checkedValue,\n containerRef,\n onValueChange: handleValueChange,\n name,\n rowLength,\n itemValues,\n }}\n >\n <div\n ref={mergedRef}\n role=\"radiogroup\"\n data-spark-component=\"segmented-control\"\n className={rootStyles({ className })}\n style={flexStyles}\n aria-labelledby={labelId}\n aria-describedby={description}\n aria-required={isRequired || undefined}\n aria-invalid={isInvalid || undefined}\n onKeyDown={handleKeyDown}\n {...rest}\n >\n {children}\n </div>\n </SegmentedControlContext>\n )\n}\n\nSegmentedControl.displayName = 'SegmentedControl'\n","import { type ComponentProps, type CSSProperties, Ref, useEffect, useMemo, useState } from 'react'\n\nimport { indicatorStyles } from './SegmentedControl.styles'\nimport { useSegmentedControlContext } from './SegmentedControlContext'\n\ninterface IndicatorRect {\n left: number\n top: number\n width: number\n height: number\n}\n\nexport interface SegmentedControlIndicatorProps extends ComponentProps<'span'> {\n ref?: Ref<HTMLSpanElement>\n}\n\n/** The visual indicator that highlights the selected item. Renders a <span> element. */\nexport const SegmentedControlIndicator = ({\n className,\n ref,\n ...rest\n}: SegmentedControlIndicatorProps) => {\n const { checkedValue, containerRef } = useSegmentedControlContext()\n const [rect, setRect] = useState<IndicatorRect | null>(null)\n\n const selector = useMemo(\n () => (checkedValue ? `[data-value=\"${CSS.escape(checkedValue)}\"]` : null),\n [checkedValue]\n )\n\n useEffect(() => {\n const container = containerRef.current\n\n if (!container) {\n return\n }\n\n const selectedItem = selector ? container.querySelector<HTMLElement>(selector) : null\n\n const update = () => {\n const currentContainer = containerRef.current\n if (!currentContainer || !selector) {\n setRect(null)\n\n return\n }\n\n const currentSelected = currentContainer.querySelector<HTMLElement>(selector)\n if (!currentSelected) {\n setRect(null)\n\n return\n }\n\n const containerRect = currentContainer.getBoundingClientRect()\n const itemRect = currentSelected.getBoundingClientRect()\n\n // Storybook canvas \"zoom\" can be implemented via `transform: scale()`.\n // In that case, `getBoundingClientRect()` returns *scaled* values, but CSS positioning/sizing\n // expects unscaled layout pixels. We infer the scale factor from offset sizes and normalize.\n const scaleX =\n currentSelected.offsetWidth > 0 ? itemRect.width / currentSelected.offsetWidth : 1\n const scaleY =\n currentSelected.offsetHeight > 0 ? itemRect.height / currentSelected.offsetHeight : 1\n\n // `getBoundingClientRect()` is border-box; absolute positioning is relative to the padding box.\n setRect({\n left: (itemRect.left - containerRect.left) / scaleX - currentContainer.clientLeft,\n top: (itemRect.top - containerRect.top) / scaleY - currentContainer.clientTop,\n width: itemRect.width / scaleX,\n height: itemRect.height / scaleY,\n })\n }\n\n update()\n\n const ro =\n typeof ResizeObserver !== 'undefined'\n ? new ResizeObserver(() => {\n update()\n })\n : null\n\n ro?.observe(container)\n if (selectedItem) ro?.observe(selectedItem)\n\n window.addEventListener('resize', update, { passive: true })\n window.visualViewport?.addEventListener('resize', update, { passive: true })\n\n return () => {\n ro?.disconnect()\n window.removeEventListener('resize', update)\n window.visualViewport?.removeEventListener('resize', update)\n }\n }, [containerRef, selector])\n\n if (!rect) return null\n\n const style: CSSProperties = {\n left: rect.left,\n top: rect.top,\n width: rect.width,\n height: rect.height,\n }\n\n return (\n <span\n ref={ref}\n data-spark-component=\"segmented-control-indicator\"\n aria-hidden\n className={indicatorStyles({ className })}\n style={style}\n {...rest}\n />\n )\n}\n\nSegmentedControlIndicator.displayName = 'SegmentedControl.Indicator'\n","import { Children, type ComponentProps, Ref } from 'react'\n\nimport { itemStyles } from './SegmentedControl.styles'\nimport { useSegmentedControlContext } from './SegmentedControlContext'\n\nexport interface SegmentedControlItemProps extends Omit<\n ComponentProps<'button'>,\n 'value' | 'onClick'\n> {\n /**\n * A unique value that identifies this item within the segmented control.\n */\n value: string\n /**\n * When true, prevents the user from interacting with this item.\n * @default false\n */\n disabled?: boolean\n ref?: Ref<HTMLButtonElement>\n}\n\n/** A selectable item in the segmented control. Renders a <button> element. */\nexport const SegmentedControlItem = ({\n value,\n disabled = false,\n children,\n className,\n ref,\n ...rest\n}: SegmentedControlItemProps) => {\n const { checkedValue, onValueChange, name, rowLength, itemValues } = useSegmentedControlContext()\n\n const isChecked = checkedValue === value\n\n const handleClick = () => {\n if (!disabled) {\n onValueChange(value)\n }\n }\n\n const content = Children.toArray(children).map((child, index) => {\n if (typeof child === 'string' || typeof child === 'number') {\n return (\n <span key={`text-${index}`} data-spark-segmented-control-text>\n {child}\n </span>\n )\n }\n\n return child\n })\n\n // Calculate flex-basis for multi-row layout and horizontal separator visibility\n const itemIndex = itemValues.indexOf(value)\n const itemStyle = rowLength\n ? { flexBasis: `calc(100% / var(--segmented-control-cols))` }\n : undefined\n\n let showHorizontalSeparator = false\n\n if (rowLength && rowLength > 0 && itemIndex !== -1) {\n const cols = rowLength\n const currentCol = itemIndex % cols\n const currentRow = Math.floor(itemIndex / cols)\n const totalRows = Math.ceil(itemValues.length / cols)\n\n // Show horizontal separator only on the first column of each row (except last row)\n showHorizontalSeparator = currentRow < totalRows - 1 && currentCol === 0\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"radio\"\n data-spark-component=\"segmented-control-item\"\n data-value={value}\n aria-checked={isChecked}\n data-checked={isChecked || undefined}\n data-disabled={disabled || undefined}\n disabled={disabled}\n tabIndex={isChecked ? 0 : -1}\n className={itemStyles({ className })}\n style={itemStyle}\n onClick={handleClick}\n {...rest}\n >\n {content}\n {/* Hidden input for form submission */}\n {name && isChecked && <input type=\"hidden\" name={name} value={value} />}\n {/* Horizontal separator between rows (full width across all columns) */}\n {showHorizontalSeparator && rowLength && (\n <div\n className=\"bg-outline/dim-3 -mx-sm absolute left-0 h-px\"\n style={{\n bottom: 'calc(var(--spacing-md) / -2)',\n width: `calc(${rowLength * 100}% + var(--spacing-sm) * 2)`,\n }}\n aria-hidden=\"true\"\n />\n )}\n </button>\n )\n}\n\nSegmentedControlItem.displayName = 'SegmentedControl.Item'\n","import { SegmentedControl as Root } from './SegmentedControl'\nimport { SegmentedControlIndicator as Indicator } from './SegmentedControlIndicator'\nimport { SegmentedControlItem as Item } from './SegmentedControlItem'\n\n/**\n * A set of toggle buttons that allows users to select a single option from a group of related choices.\n */\nexport const SegmentedControl: typeof Root & {\n Item: typeof Item\n Indicator: typeof Indicator\n} = Object.assign(Root, {\n Item,\n Indicator,\n})\n\nSegmentedControl.displayName = 'SegmentedControl'\nItem.displayName = 'SegmentedControl.Item'\nIndicator.displayName = 'SegmentedControl.Indicator'\n\nexport type { SegmentedControlProps } from './SegmentedControl'\nexport type { SegmentedControlItemProps } from './SegmentedControlItem'\nexport type { SegmentedControlIndicatorProps } from './SegmentedControlIndicator'\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../chunk-C91j1N6u.js`);let e=require(`class-variance-authority`),t=require(`react`),n=require(`react/jsx-runtime`);var r=(0,t.createContext)(null),i=()=>{let e=(0,t.useContext)(r);if(!e)throw Error(`useSegmentedGaugeContext must be used within a SegmentedGauge provider`);return e},a=({className:t,children:r,ref:a,id:o,...s})=>{let{labelId:c}=i();return(0,n.jsx)(`span`,{"data-spark-component":`segmented-gauge-label`,"data-testid":`segmented-gauge-label`,ref:a,id:o||c,className:(0,e.cx)(`default:text-on-surface default:text-body-2`,t),...s,children:r})};a.displayName=`SegmentedGauge.Label`;var o=({index:r=0,className:a,children:o,ref:s,...c})=>{let{size:l,intent:u,customColor:d,currentIndex:f}=i(),p=f!==-1&&r<=f,m=f!==-1&&r===f,h=(0,t.useMemo)(()=>{if(d)return d;switch(u){case`main`:return`var(--color-main)`;case`support`:return`var(--color-support)`;case`accent`:return`var(--color-accent)`;case`success`:return`var(--color-success)`;case`alert`:return`var(--color-alert)`;case`danger`:return`var(--color-error)`;case`info`:return`var(--color-info)`;case`neutral`:return`var(--color-neutral)`;default:return`var(--color-neutral)`}},[u,d]),g=(0,e.cx)(`border-outline relative rounded-full`,{"h-sz-8 w-sz-24":l===`sm`,"h-sz-12 w-sz-36":l===`md`,"default:bg-[var(--gauge-color)]":p,"border-sm bg-surface":!p},a),_=(0,e.cx)(`absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2`,`default:bg-surface default:rounded-full`,`border-[var(--gauge-color)]`,{"size-sz-12 border-md":l===`sm`,"size-sz-20 border-lg":l===`md`});return(0,n.jsxs)(`div`,{"data-spark-component":`segmented-gauge-segment`,"data-testid":`segmented-gauge-segment`,"data-active":p,"data-current":m,ref:s,style:{"--gauge-color":h},className:g,...c,children:[o,m&&(0,n.jsx)(`div`,{className:_,"aria-hidden":`true`})]})};o.displayName=`SegmentedGauge.Segment`;var s=({className:t,children:r,ref:i,...a})=>(0,n.jsx)(`div`,{"data-spark-component":`segmented-gauge-track`,ref:i,className:(0,e.cx)(`gap-sm relative flex rounded-full`,t),...a,children:r});s.displayName=`SegmentedGauge.Track`;var c=(e,t,n,r)=>{if(e==null)return-1;let i=Math.max(t,Math.min(n,e)),a=(n-t)/(r-1),o=(i-t)/a;return Math.max(0,Math.min(r-1,Math.round(o)))},l=({value:i,min:l,max:u,description:d,size:f=`md`,intent:p=`neutral`,customColor:m,id:h,"aria-label":g,className:_,ref:v,children:y,...b})=>{let x=u-l+1,S=(0,t.useMemo)(()=>c(i,l,u,x),[i,l,u,x]),C=(0,t.useId)(),w=(0,t.useId)(),T=h||w,E=(0,t.useMemo)(()=>Array.from({length:x},(e,t)=>({isActive:S!==-1&&t<=S,isCurrent:S!==-1&&t===S})),[x,S]),D=(0,t.useMemo)(()=>({value:i,min:l,max:u,segments:x,currentIndex:S,size:f,intent:p,customColor:m,labelId:C,gaugeId:T}),[i,l,u,x,S,f,p,m,C,T]),O=i==null?{role:`status`}:{role:`meter`,"aria-valuenow":i,"aria-valuemin":l,"aria-valuemax":u};return(0,n.jsx)(r,{value:D,children:(0,n.jsx)(`div`,{id:T,"data-spark-component":`segmented-gauge`,ref:v,className:(0,e.cx)(`gap-md flex flex-wrap items-center`,_),...O,"aria-labelledby":h?`${T}-label`:void 0,"aria-label":h?void 0:g,"aria-describedby":C,...b,children:y?y({segments:E,currentIndex:S}):(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(s,{children:E.map((e,t)=>(0,n.jsx)(o,{index:t},t))}),d&&(0,n.jsx)(a,{id:C,children:d})]})})})};l.displayName=`SegmentedGauge`;var u=Object.assign(l,{Track:s,Segment:o,Label:a});u.displayName=`SegmentedGauge`,s.displayName=`SegmentedGauge.Track`,o.displayName=`SegmentedGauge.Segment`,a.displayName=`SegmentedGauge.Label`,exports.SegmentedGauge=u;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("class-variance-authority"),t=require("react"),n=require("react/jsx-runtime");var r=(0,t.createContext)(null),i=()=>{let e=(0,t.useContext)(r);if(!e)throw Error(`useSegmentedGaugeContext must be used within a SegmentedGauge provider`);return e},a=({className:t,children:r,ref:a,id:o,...s})=>{let{labelId:c}=i();return(0,n.jsx)(`span`,{"data-spark-component":`segmented-gauge-label`,"data-testid":`segmented-gauge-label`,ref:a,id:o||c,className:(0,e.cx)(`default:text-on-surface default:text-body-2`,t),...s,children:r})};a.displayName=`SegmentedGauge.Label`;var o=({index:r=0,className:a,children:o,ref:s,...c})=>{let{size:l,intent:u,customColor:d,currentIndex:f}=i(),p=f!==-1&&r<=f,m=f!==-1&&r===f,h=(0,t.useMemo)(()=>{if(d)return d;switch(u){case`main`:return`var(--color-main)`;case`support`:return`var(--color-support)`;case`accent`:return`var(--color-accent)`;case`success`:return`var(--color-success)`;case`alert`:return`var(--color-alert)`;case`danger`:return`var(--color-error)`;case`info`:return`var(--color-info)`;case`neutral`:return`var(--color-neutral)`;default:return`var(--color-neutral)`}},[u,d]),g=(0,e.cx)(`border-outline relative rounded-full`,{"h-sz-8 w-sz-24":l===`sm`,"h-sz-12 w-sz-36":l===`md`,"default:bg-[var(--gauge-color)]":p,"border-sm bg-surface":!p},a),_=(0,e.cx)(`absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2`,`default:bg-surface default:rounded-full`,`border-[var(--gauge-color)]`,{"size-sz-12 border-md":l===`sm`,"size-sz-20 border-lg":l===`md`});return(0,n.jsxs)(`div`,{"data-spark-component":`segmented-gauge-segment`,"data-testid":`segmented-gauge-segment`,"data-active":p,"data-current":m,ref:s,style:{"--gauge-color":h},className:g,...c,children:[o,m&&(0,n.jsx)(`div`,{className:_,"aria-hidden":`true`})]})};o.displayName=`SegmentedGauge.Segment`;var s=({className:t,children:r,ref:i,...a})=>(0,n.jsx)(`div`,{"data-spark-component":`segmented-gauge-track`,ref:i,className:(0,e.cx)(`gap-sm relative flex rounded-full`,t),...a,children:r});s.displayName=`SegmentedGauge.Track`;var c=(e,t,n,r)=>{if(e==null)return-1;let i=Math.max(t,Math.min(n,e)),a=(n-t)/(r-1),o=(i-t)/a;return Math.max(0,Math.min(r-1,Math.round(o)))},l=({value:i,min:l,max:u,description:d,size:f=`md`,intent:p=`neutral`,customColor:m,id:h,"aria-label":g,className:_,ref:v,children:y,...b})=>{let x=u-l+1,S=(0,t.useMemo)(()=>c(i,l,u,x),[i,l,u,x]),C=(0,t.useId)(),w=(0,t.useId)(),T=h||w,E=(0,t.useMemo)(()=>Array.from({length:x},(e,t)=>({isActive:S!==-1&&t<=S,isCurrent:S!==-1&&t===S})),[x,S]),D=(0,t.useMemo)(()=>({value:i,min:l,max:u,segments:x,currentIndex:S,size:f,intent:p,customColor:m,labelId:C,gaugeId:T}),[i,l,u,x,S,f,p,m,C,T]),O=i==null?{role:`status`}:{role:`meter`,"aria-valuenow":i,"aria-valuemin":l,"aria-valuemax":u};return(0,n.jsx)(r,{value:D,children:(0,n.jsx)(`div`,{id:T,"data-spark-component":`segmented-gauge`,ref:v,className:(0,e.cx)(`gap-md flex flex-wrap items-center`,_),...O,"aria-labelledby":h?`${T}-label`:void 0,"aria-label":h?void 0:g,"aria-describedby":C,...b,children:y?y({segments:E,currentIndex:S}):(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(s,{children:E.map((e,t)=>(0,n.jsx)(o,{index:t},t))}),d&&(0,n.jsx)(a,{id:C,children:d})]})})})};l.displayName=`SegmentedGauge`;var u=Object.assign(l,{Track:s,Segment:o,Label:a});u.displayName=`SegmentedGauge`,s.displayName=`SegmentedGauge.Track`,o.displayName=`SegmentedGauge.Segment`,a.displayName=`SegmentedGauge.Label`,exports.SegmentedGauge=u;
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/segmented-gauge/SegmentedGaugeContext.tsx","../../src/segmented-gauge/SegmentedGaugeLabel.tsx","../../src/segmented-gauge/SegmentedGaugeSegment.tsx","../../src/segmented-gauge/SegmentedGaugeTrack.tsx","../../src/segmented-gauge/SegmentedGauge.tsx","../../src/segmented-gauge/index.ts"],"sourcesContent":["import { createContext, useContext } from 'react'\n\nexport interface SegmentedGaugeContextValue {\n value?: number\n min: number\n max: number\n segments: number\n currentIndex: number\n size: 'sm' | 'md'\n intent: 'main' | 'support' | 'accent' | 'success' | 'alert' | 'danger' | 'info' | 'neutral'\n customColor?: string\n labelId: string\n gaugeId: string\n}\n\nexport const SegmentedGaugeContext = createContext<SegmentedGaugeContextValue | null>(null)\n\nexport const useSegmentedGaugeContext = () => {\n const context = useContext(SegmentedGaugeContext)\n\n if (!context) {\n throw new Error('useSegmentedGaugeContext must be used within a SegmentedGauge provider')\n }\n\n return context\n}\n","import { cx } from 'class-variance-authority'\nimport { ComponentProps, Ref } from 'react'\n\nimport { useSegmentedGaugeContext } from './SegmentedGaugeContext'\n\nexport interface SegmentedGaugeLabelProps extends ComponentProps<'span'> {\n ref?: Ref<HTMLSpanElement>\n id?: string\n}\n\n/** The text label for the gauge. Renders a <span> element. */\nexport const SegmentedGaugeLabel = ({\n className,\n children,\n ref,\n id,\n ...props\n}: SegmentedGaugeLabelProps) => {\n const { labelId } = useSegmentedGaugeContext()\n\n return (\n <span\n data-spark-component=\"segmented-gauge-label\"\n data-testid=\"segmented-gauge-label\"\n ref={ref}\n id={id || labelId}\n className={cx('default:text-on-surface default:text-body-2', className)}\n {...props}\n >\n {children}\n </span>\n )\n}\n\nSegmentedGaugeLabel.displayName = 'SegmentedGauge.Label'\n","import { cx } from 'class-variance-authority'\nimport { ComponentProps, Ref, useMemo } from 'react'\n\nimport { useSegmentedGaugeContext } from './SegmentedGaugeContext'\n\nexport interface SegmentedGaugeSegmentProps extends ComponentProps<'div'> {\n /**\n * Index of this segment (0-based)\n */\n index?: number\n ref?: Ref<HTMLDivElement>\n}\n\n/**\n * An individual segment in the gauge. Renders a <div> element.\n */\n\nexport const SegmentedGaugeSegment = ({\n index = 0,\n className,\n children,\n ref,\n ...props\n}: SegmentedGaugeSegmentProps) => {\n const { size, intent, customColor, currentIndex } = useSegmentedGaugeContext()\n\n // Calculate isActive and isCurrent from context and index\n const isActive = currentIndex !== -1 && index <= currentIndex\n const isCurrent = currentIndex !== -1 && index === currentIndex\n\n const gaugeColor = useMemo(() => {\n // If customColor is provided, use it\n if (customColor) {\n return customColor\n }\n\n // Handle predefined intents\n switch (intent) {\n case 'main':\n return 'var(--color-main)'\n case 'support':\n return 'var(--color-support)'\n case 'accent':\n return 'var(--color-accent)'\n case 'success':\n return 'var(--color-success)'\n case 'alert':\n return 'var(--color-alert)'\n case 'danger':\n return 'var(--color-error)'\n case 'info':\n return 'var(--color-info)'\n case 'neutral':\n return 'var(--color-neutral)'\n default:\n return 'var(--color-neutral)'\n }\n }, [intent, customColor])\n\n const segmentClasses = cx(\n 'border-outline relative rounded-full',\n {\n 'h-sz-8 w-sz-24': size === 'sm',\n 'h-sz-12 w-sz-36': size === 'md',\n 'default:bg-[var(--gauge-color)]': isActive,\n 'border-sm bg-surface': !isActive,\n },\n className\n )\n\n const indicatorClasses = cx(\n 'absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2',\n 'default:bg-surface default:rounded-full',\n 'border-[var(--gauge-color)]',\n {\n 'size-sz-12 border-md': size === 'sm',\n 'size-sz-20 border-lg': size === 'md',\n }\n )\n\n return (\n <div\n data-spark-component=\"segmented-gauge-segment\"\n data-testid=\"segmented-gauge-segment\"\n data-active={isActive}\n data-current={isCurrent}\n ref={ref}\n style={\n {\n '--gauge-color': gaugeColor,\n } as React.CSSProperties\n }\n className={segmentClasses}\n {...props}\n >\n {children}\n {isCurrent && <div className={indicatorClasses} aria-hidden=\"true\" />}\n </div>\n )\n}\n\nSegmentedGaugeSegment.displayName = 'SegmentedGauge.Segment'\n","import { cx } from 'class-variance-authority'\nimport { ComponentProps, Ref } from 'react'\n\nexport interface SegmentedGaugeTrackProps extends ComponentProps<'div'> {\n ref?: Ref<HTMLDivElement>\n}\n\n/** The container track for the gauge segments. Renders a <div> element. */\nexport const SegmentedGaugeTrack = ({\n className,\n children,\n ref,\n ...props\n}: SegmentedGaugeTrackProps) => {\n return (\n <div\n data-spark-component=\"segmented-gauge-track\"\n ref={ref}\n className={cx('gap-sm relative flex rounded-full', className)}\n {...props}\n >\n {children}\n </div>\n )\n}\n\nSegmentedGaugeTrack.displayName = 'SegmentedGauge.Track'\n","import { cx } from 'class-variance-authority'\nimport { Ref, useId, useMemo } from 'react'\n\nimport { SegmentedGaugeContext } from './SegmentedGaugeContext'\nimport { SegmentedGaugeLabel } from './SegmentedGaugeLabel'\nimport { SegmentedGaugeSegment } from './SegmentedGaugeSegment'\nimport { SegmentedGaugeTrack } from './SegmentedGaugeTrack'\n\nconst calculateCurrentIndex = (\n value: number | undefined,\n min: number,\n max: number,\n segments: number\n) => {\n // If value is undefined or null, no segment is active\n if (value == null) {\n return -1\n }\n const normalizedValue = Math.max(min, Math.min(max, value))\n const range = max - min\n const segmentSize = range / (segments - 1)\n const rawIndex = (normalizedValue - min) / segmentSize\n\n // Clamp the index to valid range\n return Math.max(0, Math.min(segments - 1, Math.round(rawIndex)))\n}\n\nexport interface SegmentedGaugeProps {\n /**\n * The current value of the gauge\n */\n value?: number\n /**\n * Minimum value of the gauge (aria-valuemin)\n */\n min: number\n /**\n * Maximum value of the gauge (aria-valuemax)\n */\n max: number\n /**\n * Description text for the gauge (aria-describedby)\n */\n description?: string\n /**\n * Size of the gauge\n */\n size?: 'sm' | 'md'\n /**\n * Intent of the gauge - predefined color intent\n */\n intent?: 'main' | 'support' | 'accent' | 'success' | 'alert' | 'danger' | 'info' | 'neutral'\n /**\n * Custom color for the gauge (hex, CSS variable, etc.)\n */\n customColor?: string\n /**\n * ID of the gauge element\n */\n id?: string\n /**\n * Accessible label for the gauge (required if no id is provided)\n */\n 'aria-label'?: string\n /**\n * Textual representation of the current value (aria-valuetext)\n * By default, percentage is used (e.g. \"33%\")\n */\n 'aria-valuetext'?: string\n /**\n * Additional CSS classes\n */\n className?: string\n /**\n * Ref to the root element\n */\n ref?: Ref<HTMLDivElement>\n /**\n * Children render prop for custom rendering\n */\n children?: (props: {\n segments: {\n isActive: boolean\n isCurrent: boolean\n }[]\n currentIndex: number\n }) => React.ReactNode\n}\n\nexport const SegmentedGauge = ({\n value,\n min,\n max,\n description,\n size = 'md',\n intent = 'neutral',\n customColor,\n id,\n 'aria-label': ariaLabel,\n className,\n ref,\n children,\n ...props\n}: SegmentedGaugeProps) => {\n // Calculate segments from min and max\n const segments = max - min + 1\n const currentIndex = useMemo(\n () => calculateCurrentIndex(value, min, max, segments),\n [value, min, max, segments]\n )\n\n // Generate unique IDs\n const internalLabelId = useId()\n const generatedId = useId()\n // Use provided id or generated one for the gauge element\n const gaugeId = id || generatedId\n\n const segmentsData = useMemo(() => {\n return Array.from({ length: segments }, (_, index) => ({\n isActive: currentIndex !== -1 && index <= currentIndex,\n isCurrent: currentIndex !== -1 && index === currentIndex,\n }))\n }, [segments, currentIndex])\n\n const contextValue = useMemo(\n () => ({\n value,\n min,\n max,\n segments,\n currentIndex,\n size,\n intent,\n customColor,\n labelId: internalLabelId,\n gaugeId,\n }),\n [value, min, max, segments, currentIndex, size, intent, customColor, internalLabelId, gaugeId]\n )\n\n /**\n * A `meter` role MUST have a value. If the value is not available, the component uses a `status` role instead.\n */\n const roleProps =\n value != null\n ? {\n role: 'meter',\n 'aria-valuenow': value,\n 'aria-valuemin': min,\n 'aria-valuemax': max,\n }\n : {\n role: 'status',\n }\n\n return (\n <SegmentedGaugeContext value={contextValue}>\n <div\n id={gaugeId}\n data-spark-component=\"segmented-gauge\"\n ref={ref}\n className={cx('gap-md flex flex-wrap items-center', className)}\n {...roleProps}\n aria-labelledby={id ? `${gaugeId}-label` : undefined}\n aria-label={!id ? ariaLabel : undefined}\n aria-describedby={internalLabelId}\n {...props}\n >\n {children ? (\n children({\n segments: segmentsData,\n currentIndex,\n })\n ) : (\n <>\n <SegmentedGaugeTrack>\n {segmentsData.map((_, index) => (\n <SegmentedGaugeSegment key={index} index={index} />\n ))}\n </SegmentedGaugeTrack>\n\n {description && (\n <SegmentedGaugeLabel id={internalLabelId}>{description}</SegmentedGaugeLabel>\n )}\n </>\n )}\n </div>\n </SegmentedGaugeContext>\n )\n}\n\nSegmentedGauge.displayName = 'SegmentedGauge'\n","import { SegmentedGauge as Root } from './SegmentedGauge'\nimport { SegmentedGaugeLabel } from './SegmentedGaugeLabel'\nimport { SegmentedGaugeSegment } from './SegmentedGaugeSegment'\nimport { SegmentedGaugeTrack } from './SegmentedGaugeTrack'\n\n/**\n * A visual indicator that displays a value using discrete segments within a defined range.\n */\nexport const SegmentedGauge: typeof Root & {\n Track: typeof SegmentedGaugeTrack\n Segment: typeof SegmentedGaugeSegment\n Label: typeof SegmentedGaugeLabel\n} = Object.assign(Root, {\n Track: SegmentedGaugeTrack,\n Segment: SegmentedGaugeSegment,\n Label: SegmentedGaugeLabel,\n})\n\nSegmentedGauge.displayName = 'SegmentedGauge'\nSegmentedGaugeTrack.displayName = 'SegmentedGauge.Track'\nSegmentedGaugeSegment.displayName = 'SegmentedGauge.Segment'\nSegmentedGaugeLabel.displayName = 'SegmentedGauge.Label'\n\nexport { type SegmentedGaugeProps } from './SegmentedGauge'\nexport { type SegmentedGaugeTrackProps } from './SegmentedGaugeTrack'\nexport { type SegmentedGaugeSegmentProps } from './SegmentedGaugeSegment'\nexport { type SegmentedGaugeLabelProps } from './SegmentedGaugeLabel'\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/segmented-gauge/SegmentedGaugeContext.tsx","../../src/segmented-gauge/SegmentedGaugeLabel.tsx","../../src/segmented-gauge/SegmentedGaugeSegment.tsx","../../src/segmented-gauge/SegmentedGaugeTrack.tsx","../../src/segmented-gauge/SegmentedGauge.tsx","../../src/segmented-gauge/index.ts"],"sourcesContent":["import { createContext, useContext } from 'react'\n\nexport interface SegmentedGaugeContextValue {\n value?: number\n min: number\n max: number\n segments: number\n currentIndex: number\n size: 'sm' | 'md'\n intent: 'main' | 'support' | 'accent' | 'success' | 'alert' | 'danger' | 'info' | 'neutral'\n customColor?: string\n labelId: string\n gaugeId: string\n}\n\nexport const SegmentedGaugeContext = createContext<SegmentedGaugeContextValue | null>(null)\n\nexport const useSegmentedGaugeContext = () => {\n const context = useContext(SegmentedGaugeContext)\n\n if (!context) {\n throw new Error('useSegmentedGaugeContext must be used within a SegmentedGauge provider')\n }\n\n return context\n}\n","import { cx } from 'class-variance-authority'\nimport { ComponentProps, Ref } from 'react'\n\nimport { useSegmentedGaugeContext } from './SegmentedGaugeContext'\n\nexport interface SegmentedGaugeLabelProps extends ComponentProps<'span'> {\n ref?: Ref<HTMLSpanElement>\n id?: string\n}\n\n/** The text label for the gauge. Renders a <span> element. */\nexport const SegmentedGaugeLabel = ({\n className,\n children,\n ref,\n id,\n ...props\n}: SegmentedGaugeLabelProps) => {\n const { labelId } = useSegmentedGaugeContext()\n\n return (\n <span\n data-spark-component=\"segmented-gauge-label\"\n data-testid=\"segmented-gauge-label\"\n ref={ref}\n id={id || labelId}\n className={cx('default:text-on-surface default:text-body-2', className)}\n {...props}\n >\n {children}\n </span>\n )\n}\n\nSegmentedGaugeLabel.displayName = 'SegmentedGauge.Label'\n","import { cx } from 'class-variance-authority'\nimport { ComponentProps, Ref, useMemo } from 'react'\n\nimport { useSegmentedGaugeContext } from './SegmentedGaugeContext'\n\nexport interface SegmentedGaugeSegmentProps extends ComponentProps<'div'> {\n /**\n * Index of this segment (0-based)\n */\n index?: number\n ref?: Ref<HTMLDivElement>\n}\n\n/**\n * An individual segment in the gauge. Renders a <div> element.\n */\n\nexport const SegmentedGaugeSegment = ({\n index = 0,\n className,\n children,\n ref,\n ...props\n}: SegmentedGaugeSegmentProps) => {\n const { size, intent, customColor, currentIndex } = useSegmentedGaugeContext()\n\n // Calculate isActive and isCurrent from context and index\n const isActive = currentIndex !== -1 && index <= currentIndex\n const isCurrent = currentIndex !== -1 && index === currentIndex\n\n const gaugeColor = useMemo(() => {\n // If customColor is provided, use it\n if (customColor) {\n return customColor\n }\n\n // Handle predefined intents\n switch (intent) {\n case 'main':\n return 'var(--color-main)'\n case 'support':\n return 'var(--color-support)'\n case 'accent':\n return 'var(--color-accent)'\n case 'success':\n return 'var(--color-success)'\n case 'alert':\n return 'var(--color-alert)'\n case 'danger':\n return 'var(--color-error)'\n case 'info':\n return 'var(--color-info)'\n case 'neutral':\n return 'var(--color-neutral)'\n default:\n return 'var(--color-neutral)'\n }\n }, [intent, customColor])\n\n const segmentClasses = cx(\n 'border-outline relative rounded-full',\n {\n 'h-sz-8 w-sz-24': size === 'sm',\n 'h-sz-12 w-sz-36': size === 'md',\n 'default:bg-[var(--gauge-color)]': isActive,\n 'border-sm bg-surface': !isActive,\n },\n className\n )\n\n const indicatorClasses = cx(\n 'absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2',\n 'default:bg-surface default:rounded-full',\n 'border-[var(--gauge-color)]',\n {\n 'size-sz-12 border-md': size === 'sm',\n 'size-sz-20 border-lg': size === 'md',\n }\n )\n\n return (\n <div\n data-spark-component=\"segmented-gauge-segment\"\n data-testid=\"segmented-gauge-segment\"\n data-active={isActive}\n data-current={isCurrent}\n ref={ref}\n style={\n {\n '--gauge-color': gaugeColor,\n } as React.CSSProperties\n }\n className={segmentClasses}\n {...props}\n >\n {children}\n {isCurrent && <div className={indicatorClasses} aria-hidden=\"true\" />}\n </div>\n )\n}\n\nSegmentedGaugeSegment.displayName = 'SegmentedGauge.Segment'\n","import { cx } from 'class-variance-authority'\nimport { ComponentProps, Ref } from 'react'\n\nexport interface SegmentedGaugeTrackProps extends ComponentProps<'div'> {\n ref?: Ref<HTMLDivElement>\n}\n\n/** The container track for the gauge segments. Renders a <div> element. */\nexport const SegmentedGaugeTrack = ({\n className,\n children,\n ref,\n ...props\n}: SegmentedGaugeTrackProps) => {\n return (\n <div\n data-spark-component=\"segmented-gauge-track\"\n ref={ref}\n className={cx('gap-sm relative flex rounded-full', className)}\n {...props}\n >\n {children}\n </div>\n )\n}\n\nSegmentedGaugeTrack.displayName = 'SegmentedGauge.Track'\n","import { cx } from 'class-variance-authority'\nimport { Ref, useId, useMemo } from 'react'\n\nimport { SegmentedGaugeContext } from './SegmentedGaugeContext'\nimport { SegmentedGaugeLabel } from './SegmentedGaugeLabel'\nimport { SegmentedGaugeSegment } from './SegmentedGaugeSegment'\nimport { SegmentedGaugeTrack } from './SegmentedGaugeTrack'\n\nconst calculateCurrentIndex = (\n value: number | undefined,\n min: number,\n max: number,\n segments: number\n) => {\n // If value is undefined or null, no segment is active\n if (value == null) {\n return -1\n }\n const normalizedValue = Math.max(min, Math.min(max, value))\n const range = max - min\n const segmentSize = range / (segments - 1)\n const rawIndex = (normalizedValue - min) / segmentSize\n\n // Clamp the index to valid range\n return Math.max(0, Math.min(segments - 1, Math.round(rawIndex)))\n}\n\nexport interface SegmentedGaugeProps {\n /**\n * The current value of the gauge\n */\n value?: number\n /**\n * Minimum value of the gauge (aria-valuemin)\n */\n min: number\n /**\n * Maximum value of the gauge (aria-valuemax)\n */\n max: number\n /**\n * Description text for the gauge (aria-describedby)\n */\n description?: string\n /**\n * Size of the gauge\n */\n size?: 'sm' | 'md'\n /**\n * Intent of the gauge - predefined color intent\n */\n intent?: 'main' | 'support' | 'accent' | 'success' | 'alert' | 'danger' | 'info' | 'neutral'\n /**\n * Custom color for the gauge (hex, CSS variable, etc.)\n */\n customColor?: string\n /**\n * ID of the gauge element\n */\n id?: string\n /**\n * Accessible label for the gauge (required if no id is provided)\n */\n 'aria-label'?: string\n /**\n * Textual representation of the current value (aria-valuetext)\n * By default, percentage is used (e.g. \"33%\")\n */\n 'aria-valuetext'?: string\n /**\n * Additional CSS classes\n */\n className?: string\n /**\n * Ref to the root element\n */\n ref?: Ref<HTMLDivElement>\n /**\n * Children render prop for custom rendering\n */\n children?: (props: {\n segments: {\n isActive: boolean\n isCurrent: boolean\n }[]\n currentIndex: number\n }) => React.ReactNode\n}\n\nexport const SegmentedGauge = ({\n value,\n min,\n max,\n description,\n size = 'md',\n intent = 'neutral',\n customColor,\n id,\n 'aria-label': ariaLabel,\n className,\n ref,\n children,\n ...props\n}: SegmentedGaugeProps) => {\n // Calculate segments from min and max\n const segments = max - min + 1\n const currentIndex = useMemo(\n () => calculateCurrentIndex(value, min, max, segments),\n [value, min, max, segments]\n )\n\n // Generate unique IDs\n const internalLabelId = useId()\n const generatedId = useId()\n // Use provided id or generated one for the gauge element\n const gaugeId = id || generatedId\n\n const segmentsData = useMemo(() => {\n return Array.from({ length: segments }, (_, index) => ({\n isActive: currentIndex !== -1 && index <= currentIndex,\n isCurrent: currentIndex !== -1 && index === currentIndex,\n }))\n }, [segments, currentIndex])\n\n const contextValue = useMemo(\n () => ({\n value,\n min,\n max,\n segments,\n currentIndex,\n size,\n intent,\n customColor,\n labelId: internalLabelId,\n gaugeId,\n }),\n [value, min, max, segments, currentIndex, size, intent, customColor, internalLabelId, gaugeId]\n )\n\n /**\n * A `meter` role MUST have a value. If the value is not available, the component uses a `status` role instead.\n */\n const roleProps =\n value != null\n ? {\n role: 'meter',\n 'aria-valuenow': value,\n 'aria-valuemin': min,\n 'aria-valuemax': max,\n }\n : {\n role: 'status',\n }\n\n return (\n <SegmentedGaugeContext value={contextValue}>\n <div\n id={gaugeId}\n data-spark-component=\"segmented-gauge\"\n ref={ref}\n className={cx('gap-md flex flex-wrap items-center', className)}\n {...roleProps}\n aria-labelledby={id ? `${gaugeId}-label` : undefined}\n aria-label={!id ? ariaLabel : undefined}\n aria-describedby={internalLabelId}\n {...props}\n >\n {children ? (\n children({\n segments: segmentsData,\n currentIndex,\n })\n ) : (\n <>\n <SegmentedGaugeTrack>\n {segmentsData.map((_, index) => (\n <SegmentedGaugeSegment key={index} index={index} />\n ))}\n </SegmentedGaugeTrack>\n\n {description && (\n <SegmentedGaugeLabel id={internalLabelId}>{description}</SegmentedGaugeLabel>\n )}\n </>\n )}\n </div>\n </SegmentedGaugeContext>\n )\n}\n\nSegmentedGauge.displayName = 'SegmentedGauge'\n","import { SegmentedGauge as Root } from './SegmentedGauge'\nimport { SegmentedGaugeLabel } from './SegmentedGaugeLabel'\nimport { SegmentedGaugeSegment } from './SegmentedGaugeSegment'\nimport { SegmentedGaugeTrack } from './SegmentedGaugeTrack'\n\n/**\n * A visual indicator that displays a value using discrete segments within a defined range.\n */\nexport const SegmentedGauge: typeof Root & {\n Track: typeof SegmentedGaugeTrack\n Segment: typeof SegmentedGaugeSegment\n Label: typeof SegmentedGaugeLabel\n} = Object.assign(Root, {\n Track: SegmentedGaugeTrack,\n Segment: SegmentedGaugeSegment,\n Label: SegmentedGaugeLabel,\n})\n\nSegmentedGauge.displayName = 'SegmentedGauge'\nSegmentedGaugeTrack.displayName = 'SegmentedGauge.Track'\nSegmentedGaugeSegment.displayName = 'SegmentedGauge.Segment'\nSegmentedGaugeLabel.displayName = 'SegmentedGauge.Label'\n\nexport { type SegmentedGaugeProps } from './SegmentedGauge'\nexport { type SegmentedGaugeTrackProps } from './SegmentedGaugeTrack'\nexport { type SegmentedGaugeSegmentProps } from './SegmentedGaugeSegment'\nexport { type SegmentedGaugeLabelProps } from './SegmentedGaugeLabel'\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/segmented-gauge/SegmentedGaugeContext.tsx","../../src/segmented-gauge/SegmentedGaugeLabel.tsx","../../src/segmented-gauge/SegmentedGaugeSegment.tsx","../../src/segmented-gauge/SegmentedGaugeTrack.tsx","../../src/segmented-gauge/SegmentedGauge.tsx","../../src/segmented-gauge/index.ts"],"sourcesContent":["import { createContext, useContext } from 'react'\n\nexport interface SegmentedGaugeContextValue {\n value?: number\n min: number\n max: number\n segments: number\n currentIndex: number\n size: 'sm' | 'md'\n intent: 'main' | 'support' | 'accent' | 'success' | 'alert' | 'danger' | 'info' | 'neutral'\n customColor?: string\n labelId: string\n gaugeId: string\n}\n\nexport const SegmentedGaugeContext = createContext<SegmentedGaugeContextValue | null>(null)\n\nexport const useSegmentedGaugeContext = () => {\n const context = useContext(SegmentedGaugeContext)\n\n if (!context) {\n throw new Error('useSegmentedGaugeContext must be used within a SegmentedGauge provider')\n }\n\n return context\n}\n","import { cx } from 'class-variance-authority'\nimport { ComponentProps, Ref } from 'react'\n\nimport { useSegmentedGaugeContext } from './SegmentedGaugeContext'\n\nexport interface SegmentedGaugeLabelProps extends ComponentProps<'span'> {\n ref?: Ref<HTMLSpanElement>\n id?: string\n}\n\n/** The text label for the gauge. Renders a <span> element. */\nexport const SegmentedGaugeLabel = ({\n className,\n children,\n ref,\n id,\n ...props\n}: SegmentedGaugeLabelProps) => {\n const { labelId } = useSegmentedGaugeContext()\n\n return (\n <span\n data-spark-component=\"segmented-gauge-label\"\n data-testid=\"segmented-gauge-label\"\n ref={ref}\n id={id || labelId}\n className={cx('default:text-on-surface default:text-body-2', className)}\n {...props}\n >\n {children}\n </span>\n )\n}\n\nSegmentedGaugeLabel.displayName = 'SegmentedGauge.Label'\n","import { cx } from 'class-variance-authority'\nimport { ComponentProps, Ref, useMemo } from 'react'\n\nimport { useSegmentedGaugeContext } from './SegmentedGaugeContext'\n\nexport interface SegmentedGaugeSegmentProps extends ComponentProps<'div'> {\n /**\n * Index of this segment (0-based)\n */\n index?: number\n ref?: Ref<HTMLDivElement>\n}\n\n/**\n * An individual segment in the gauge. Renders a <div> element.\n */\n\nexport const SegmentedGaugeSegment = ({\n index = 0,\n className,\n children,\n ref,\n ...props\n}: SegmentedGaugeSegmentProps) => {\n const { size, intent, customColor, currentIndex } = useSegmentedGaugeContext()\n\n // Calculate isActive and isCurrent from context and index\n const isActive = currentIndex !== -1 && index <= currentIndex\n const isCurrent = currentIndex !== -1 && index === currentIndex\n\n const gaugeColor = useMemo(() => {\n // If customColor is provided, use it\n if (customColor) {\n return customColor\n }\n\n // Handle predefined intents\n switch (intent) {\n case 'main':\n return 'var(--color-main)'\n case 'support':\n return 'var(--color-support)'\n case 'accent':\n return 'var(--color-accent)'\n case 'success':\n return 'var(--color-success)'\n case 'alert':\n return 'var(--color-alert)'\n case 'danger':\n return 'var(--color-error)'\n case 'info':\n return 'var(--color-info)'\n case 'neutral':\n return 'var(--color-neutral)'\n default:\n return 'var(--color-neutral)'\n }\n }, [intent, customColor])\n\n const segmentClasses = cx(\n 'border-outline relative rounded-full',\n {\n 'h-sz-8 w-sz-24': size === 'sm',\n 'h-sz-12 w-sz-36': size === 'md',\n 'default:bg-[var(--gauge-color)]': isActive,\n 'border-sm bg-surface': !isActive,\n },\n className\n )\n\n const indicatorClasses = cx(\n 'absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2',\n 'default:bg-surface default:rounded-full',\n 'border-[var(--gauge-color)]',\n {\n 'size-sz-12 border-md': size === 'sm',\n 'size-sz-20 border-lg': size === 'md',\n }\n )\n\n return (\n <div\n data-spark-component=\"segmented-gauge-segment\"\n data-testid=\"segmented-gauge-segment\"\n data-active={isActive}\n data-current={isCurrent}\n ref={ref}\n style={\n {\n '--gauge-color': gaugeColor,\n } as React.CSSProperties\n }\n className={segmentClasses}\n {...props}\n >\n {children}\n {isCurrent && <div className={indicatorClasses} aria-hidden=\"true\" />}\n </div>\n )\n}\n\nSegmentedGaugeSegment.displayName = 'SegmentedGauge.Segment'\n","import { cx } from 'class-variance-authority'\nimport { ComponentProps, Ref } from 'react'\n\nexport interface SegmentedGaugeTrackProps extends ComponentProps<'div'> {\n ref?: Ref<HTMLDivElement>\n}\n\n/** The container track for the gauge segments. Renders a <div> element. */\nexport const SegmentedGaugeTrack = ({\n className,\n children,\n ref,\n ...props\n}: SegmentedGaugeTrackProps) => {\n return (\n <div\n data-spark-component=\"segmented-gauge-track\"\n ref={ref}\n className={cx('gap-sm relative flex rounded-full', className)}\n {...props}\n >\n {children}\n </div>\n )\n}\n\nSegmentedGaugeTrack.displayName = 'SegmentedGauge.Track'\n","import { cx } from 'class-variance-authority'\nimport { Ref, useId, useMemo } from 'react'\n\nimport { SegmentedGaugeContext } from './SegmentedGaugeContext'\nimport { SegmentedGaugeLabel } from './SegmentedGaugeLabel'\nimport { SegmentedGaugeSegment } from './SegmentedGaugeSegment'\nimport { SegmentedGaugeTrack } from './SegmentedGaugeTrack'\n\nconst calculateCurrentIndex = (\n value: number | undefined,\n min: number,\n max: number,\n segments: number\n) => {\n // If value is undefined or null, no segment is active\n if (value == null) {\n return -1\n }\n const normalizedValue = Math.max(min, Math.min(max, value))\n const range = max - min\n const segmentSize = range / (segments - 1)\n const rawIndex = (normalizedValue - min) / segmentSize\n\n // Clamp the index to valid range\n return Math.max(0, Math.min(segments - 1, Math.round(rawIndex)))\n}\n\nexport interface SegmentedGaugeProps {\n /**\n * The current value of the gauge\n */\n value?: number\n /**\n * Minimum value of the gauge (aria-valuemin)\n */\n min: number\n /**\n * Maximum value of the gauge (aria-valuemax)\n */\n max: number\n /**\n * Description text for the gauge (aria-describedby)\n */\n description?: string\n /**\n * Size of the gauge\n */\n size?: 'sm' | 'md'\n /**\n * Intent of the gauge - predefined color intent\n */\n intent?: 'main' | 'support' | 'accent' | 'success' | 'alert' | 'danger' | 'info' | 'neutral'\n /**\n * Custom color for the gauge (hex, CSS variable, etc.)\n */\n customColor?: string\n /**\n * ID of the gauge element\n */\n id?: string\n /**\n * Accessible label for the gauge (required if no id is provided)\n */\n 'aria-label'?: string\n /**\n * Textual representation of the current value (aria-valuetext)\n * By default, percentage is used (e.g. \"33%\")\n */\n 'aria-valuetext'?: string\n /**\n * Additional CSS classes\n */\n className?: string\n /**\n * Ref to the root element\n */\n ref?: Ref<HTMLDivElement>\n /**\n * Children render prop for custom rendering\n */\n children?: (props: {\n segments: {\n isActive: boolean\n isCurrent: boolean\n }[]\n currentIndex: number\n }) => React.ReactNode\n}\n\nexport const SegmentedGauge = ({\n value,\n min,\n max,\n description,\n size = 'md',\n intent = 'neutral',\n customColor,\n id,\n 'aria-label': ariaLabel,\n className,\n ref,\n children,\n ...props\n}: SegmentedGaugeProps) => {\n // Calculate segments from min and max\n const segments = max - min + 1\n const currentIndex = useMemo(\n () => calculateCurrentIndex(value, min, max, segments),\n [value, min, max, segments]\n )\n\n // Generate unique IDs\n const internalLabelId = useId()\n const generatedId = useId()\n // Use provided id or generated one for the gauge element\n const gaugeId = id || generatedId\n\n const segmentsData = useMemo(() => {\n return Array.from({ length: segments }, (_, index) => ({\n isActive: currentIndex !== -1 && index <= currentIndex,\n isCurrent: currentIndex !== -1 && index === currentIndex,\n }))\n }, [segments, currentIndex])\n\n const contextValue = useMemo(\n () => ({\n value,\n min,\n max,\n segments,\n currentIndex,\n size,\n intent,\n customColor,\n labelId: internalLabelId,\n gaugeId,\n }),\n [value, min, max, segments, currentIndex, size, intent, customColor, internalLabelId, gaugeId]\n )\n\n /**\n * A `meter` role MUST have a value. If the value is not available, the component uses a `status` role instead.\n */\n const roleProps =\n value != null\n ? {\n role: 'meter',\n 'aria-valuenow': value,\n 'aria-valuemin': min,\n 'aria-valuemax': max,\n }\n : {\n role: 'status',\n }\n\n return (\n <SegmentedGaugeContext value={contextValue}>\n <div\n id={gaugeId}\n data-spark-component=\"segmented-gauge\"\n ref={ref}\n className={cx('gap-md flex flex-wrap items-center', className)}\n {...roleProps}\n aria-labelledby={id ? `${gaugeId}-label` : undefined}\n aria-label={!id ? ariaLabel : undefined}\n aria-describedby={internalLabelId}\n {...props}\n >\n {children ? (\n children({\n segments: segmentsData,\n currentIndex,\n })\n ) : (\n <>\n <SegmentedGaugeTrack>\n {segmentsData.map((_, index) => (\n <SegmentedGaugeSegment key={index} index={index} />\n ))}\n </SegmentedGaugeTrack>\n\n {description && (\n <SegmentedGaugeLabel id={internalLabelId}>{description}</SegmentedGaugeLabel>\n )}\n </>\n )}\n </div>\n </SegmentedGaugeContext>\n )\n}\n\nSegmentedGauge.displayName = 'SegmentedGauge'\n","import { SegmentedGauge as Root } from './SegmentedGauge'\nimport { SegmentedGaugeLabel } from './SegmentedGaugeLabel'\nimport { SegmentedGaugeSegment } from './SegmentedGaugeSegment'\nimport { SegmentedGaugeTrack } from './SegmentedGaugeTrack'\n\n/**\n * A visual indicator that displays a value using discrete segments within a defined range.\n */\nexport const SegmentedGauge: typeof Root & {\n Track: typeof SegmentedGaugeTrack\n Segment: typeof SegmentedGaugeSegment\n Label: typeof SegmentedGaugeLabel\n} = Object.assign(Root, {\n Track: SegmentedGaugeTrack,\n Segment: SegmentedGaugeSegment,\n Label: SegmentedGaugeLabel,\n})\n\nSegmentedGauge.displayName = 'SegmentedGauge'\nSegmentedGaugeTrack.displayName = 'SegmentedGauge.Track'\nSegmentedGaugeSegment.displayName = 'SegmentedGauge.Segment'\nSegmentedGaugeLabel.displayName = 'SegmentedGauge.Label'\n\nexport { type SegmentedGaugeProps } from './SegmentedGauge'\nexport { type SegmentedGaugeTrackProps } from './SegmentedGaugeTrack'\nexport { type SegmentedGaugeSegmentProps } from './SegmentedGaugeSegment'\nexport { type SegmentedGaugeLabelProps } from './SegmentedGaugeLabel'\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/segmented-gauge/SegmentedGaugeContext.tsx","../../src/segmented-gauge/SegmentedGaugeLabel.tsx","../../src/segmented-gauge/SegmentedGaugeSegment.tsx","../../src/segmented-gauge/SegmentedGaugeTrack.tsx","../../src/segmented-gauge/SegmentedGauge.tsx","../../src/segmented-gauge/index.ts"],"sourcesContent":["import { createContext, useContext } from 'react'\n\nexport interface SegmentedGaugeContextValue {\n value?: number\n min: number\n max: number\n segments: number\n currentIndex: number\n size: 'sm' | 'md'\n intent: 'main' | 'support' | 'accent' | 'success' | 'alert' | 'danger' | 'info' | 'neutral'\n customColor?: string\n labelId: string\n gaugeId: string\n}\n\nexport const SegmentedGaugeContext = createContext<SegmentedGaugeContextValue | null>(null)\n\nexport const useSegmentedGaugeContext = () => {\n const context = useContext(SegmentedGaugeContext)\n\n if (!context) {\n throw new Error('useSegmentedGaugeContext must be used within a SegmentedGauge provider')\n }\n\n return context\n}\n","import { cx } from 'class-variance-authority'\nimport { ComponentProps, Ref } from 'react'\n\nimport { useSegmentedGaugeContext } from './SegmentedGaugeContext'\n\nexport interface SegmentedGaugeLabelProps extends ComponentProps<'span'> {\n ref?: Ref<HTMLSpanElement>\n id?: string\n}\n\n/** The text label for the gauge. Renders a <span> element. */\nexport const SegmentedGaugeLabel = ({\n className,\n children,\n ref,\n id,\n ...props\n}: SegmentedGaugeLabelProps) => {\n const { labelId } = useSegmentedGaugeContext()\n\n return (\n <span\n data-spark-component=\"segmented-gauge-label\"\n data-testid=\"segmented-gauge-label\"\n ref={ref}\n id={id || labelId}\n className={cx('default:text-on-surface default:text-body-2', className)}\n {...props}\n >\n {children}\n </span>\n )\n}\n\nSegmentedGaugeLabel.displayName = 'SegmentedGauge.Label'\n","import { cx } from 'class-variance-authority'\nimport { ComponentProps, Ref, useMemo } from 'react'\n\nimport { useSegmentedGaugeContext } from './SegmentedGaugeContext'\n\nexport interface SegmentedGaugeSegmentProps extends ComponentProps<'div'> {\n /**\n * Index of this segment (0-based)\n */\n index?: number\n ref?: Ref<HTMLDivElement>\n}\n\n/**\n * An individual segment in the gauge. Renders a <div> element.\n */\n\nexport const SegmentedGaugeSegment = ({\n index = 0,\n className,\n children,\n ref,\n ...props\n}: SegmentedGaugeSegmentProps) => {\n const { size, intent, customColor, currentIndex } = useSegmentedGaugeContext()\n\n // Calculate isActive and isCurrent from context and index\n const isActive = currentIndex !== -1 && index <= currentIndex\n const isCurrent = currentIndex !== -1 && index === currentIndex\n\n const gaugeColor = useMemo(() => {\n // If customColor is provided, use it\n if (customColor) {\n return customColor\n }\n\n // Handle predefined intents\n switch (intent) {\n case 'main':\n return 'var(--color-main)'\n case 'support':\n return 'var(--color-support)'\n case 'accent':\n return 'var(--color-accent)'\n case 'success':\n return 'var(--color-success)'\n case 'alert':\n return 'var(--color-alert)'\n case 'danger':\n return 'var(--color-error)'\n case 'info':\n return 'var(--color-info)'\n case 'neutral':\n return 'var(--color-neutral)'\n default:\n return 'var(--color-neutral)'\n }\n }, [intent, customColor])\n\n const segmentClasses = cx(\n 'border-outline relative rounded-full',\n {\n 'h-sz-8 w-sz-24': size === 'sm',\n 'h-sz-12 w-sz-36': size === 'md',\n 'default:bg-[var(--gauge-color)]': isActive,\n 'border-sm bg-surface': !isActive,\n },\n className\n )\n\n const indicatorClasses = cx(\n 'absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2',\n 'default:bg-surface default:rounded-full',\n 'border-[var(--gauge-color)]',\n {\n 'size-sz-12 border-md': size === 'sm',\n 'size-sz-20 border-lg': size === 'md',\n }\n )\n\n return (\n <div\n data-spark-component=\"segmented-gauge-segment\"\n data-testid=\"segmented-gauge-segment\"\n data-active={isActive}\n data-current={isCurrent}\n ref={ref}\n style={\n {\n '--gauge-color': gaugeColor,\n } as React.CSSProperties\n }\n className={segmentClasses}\n {...props}\n >\n {children}\n {isCurrent && <div className={indicatorClasses} aria-hidden=\"true\" />}\n </div>\n )\n}\n\nSegmentedGaugeSegment.displayName = 'SegmentedGauge.Segment'\n","import { cx } from 'class-variance-authority'\nimport { ComponentProps, Ref } from 'react'\n\nexport interface SegmentedGaugeTrackProps extends ComponentProps<'div'> {\n ref?: Ref<HTMLDivElement>\n}\n\n/** The container track for the gauge segments. Renders a <div> element. */\nexport const SegmentedGaugeTrack = ({\n className,\n children,\n ref,\n ...props\n}: SegmentedGaugeTrackProps) => {\n return (\n <div\n data-spark-component=\"segmented-gauge-track\"\n ref={ref}\n className={cx('gap-sm relative flex rounded-full', className)}\n {...props}\n >\n {children}\n </div>\n )\n}\n\nSegmentedGaugeTrack.displayName = 'SegmentedGauge.Track'\n","import { cx } from 'class-variance-authority'\nimport { Ref, useId, useMemo } from 'react'\n\nimport { SegmentedGaugeContext } from './SegmentedGaugeContext'\nimport { SegmentedGaugeLabel } from './SegmentedGaugeLabel'\nimport { SegmentedGaugeSegment } from './SegmentedGaugeSegment'\nimport { SegmentedGaugeTrack } from './SegmentedGaugeTrack'\n\nconst calculateCurrentIndex = (\n value: number | undefined,\n min: number,\n max: number,\n segments: number\n) => {\n // If value is undefined or null, no segment is active\n if (value == null) {\n return -1\n }\n const normalizedValue = Math.max(min, Math.min(max, value))\n const range = max - min\n const segmentSize = range / (segments - 1)\n const rawIndex = (normalizedValue - min) / segmentSize\n\n // Clamp the index to valid range\n return Math.max(0, Math.min(segments - 1, Math.round(rawIndex)))\n}\n\nexport interface SegmentedGaugeProps {\n /**\n * The current value of the gauge\n */\n value?: number\n /**\n * Minimum value of the gauge (aria-valuemin)\n */\n min: number\n /**\n * Maximum value of the gauge (aria-valuemax)\n */\n max: number\n /**\n * Description text for the gauge (aria-describedby)\n */\n description?: string\n /**\n * Size of the gauge\n */\n size?: 'sm' | 'md'\n /**\n * Intent of the gauge - predefined color intent\n */\n intent?: 'main' | 'support' | 'accent' | 'success' | 'alert' | 'danger' | 'info' | 'neutral'\n /**\n * Custom color for the gauge (hex, CSS variable, etc.)\n */\n customColor?: string\n /**\n * ID of the gauge element\n */\n id?: string\n /**\n * Accessible label for the gauge (required if no id is provided)\n */\n 'aria-label'?: string\n /**\n * Textual representation of the current value (aria-valuetext)\n * By default, percentage is used (e.g. \"33%\")\n */\n 'aria-valuetext'?: string\n /**\n * Additional CSS classes\n */\n className?: string\n /**\n * Ref to the root element\n */\n ref?: Ref<HTMLDivElement>\n /**\n * Children render prop for custom rendering\n */\n children?: (props: {\n segments: {\n isActive: boolean\n isCurrent: boolean\n }[]\n currentIndex: number\n }) => React.ReactNode\n}\n\nexport const SegmentedGauge = ({\n value,\n min,\n max,\n description,\n size = 'md',\n intent = 'neutral',\n customColor,\n id,\n 'aria-label': ariaLabel,\n className,\n ref,\n children,\n ...props\n}: SegmentedGaugeProps) => {\n // Calculate segments from min and max\n const segments = max - min + 1\n const currentIndex = useMemo(\n () => calculateCurrentIndex(value, min, max, segments),\n [value, min, max, segments]\n )\n\n // Generate unique IDs\n const internalLabelId = useId()\n const generatedId = useId()\n // Use provided id or generated one for the gauge element\n const gaugeId = id || generatedId\n\n const segmentsData = useMemo(() => {\n return Array.from({ length: segments }, (_, index) => ({\n isActive: currentIndex !== -1 && index <= currentIndex,\n isCurrent: currentIndex !== -1 && index === currentIndex,\n }))\n }, [segments, currentIndex])\n\n const contextValue = useMemo(\n () => ({\n value,\n min,\n max,\n segments,\n currentIndex,\n size,\n intent,\n customColor,\n labelId: internalLabelId,\n gaugeId,\n }),\n [value, min, max, segments, currentIndex, size, intent, customColor, internalLabelId, gaugeId]\n )\n\n /**\n * A `meter` role MUST have a value. If the value is not available, the component uses a `status` role instead.\n */\n const roleProps =\n value != null\n ? {\n role: 'meter',\n 'aria-valuenow': value,\n 'aria-valuemin': min,\n 'aria-valuemax': max,\n }\n : {\n role: 'status',\n }\n\n return (\n <SegmentedGaugeContext value={contextValue}>\n <div\n id={gaugeId}\n data-spark-component=\"segmented-gauge\"\n ref={ref}\n className={cx('gap-md flex flex-wrap items-center', className)}\n {...roleProps}\n aria-labelledby={id ? `${gaugeId}-label` : undefined}\n aria-label={!id ? ariaLabel : undefined}\n aria-describedby={internalLabelId}\n {...props}\n >\n {children ? (\n children({\n segments: segmentsData,\n currentIndex,\n })\n ) : (\n <>\n <SegmentedGaugeTrack>\n {segmentsData.map((_, index) => (\n <SegmentedGaugeSegment key={index} index={index} />\n ))}\n </SegmentedGaugeTrack>\n\n {description && (\n <SegmentedGaugeLabel id={internalLabelId}>{description}</SegmentedGaugeLabel>\n )}\n </>\n )}\n </div>\n </SegmentedGaugeContext>\n )\n}\n\nSegmentedGauge.displayName = 'SegmentedGauge'\n","import { SegmentedGauge as Root } from './SegmentedGauge'\nimport { SegmentedGaugeLabel } from './SegmentedGaugeLabel'\nimport { SegmentedGaugeSegment } from './SegmentedGaugeSegment'\nimport { SegmentedGaugeTrack } from './SegmentedGaugeTrack'\n\n/**\n * A visual indicator that displays a value using discrete segments within a defined range.\n */\nexport const SegmentedGauge: typeof Root & {\n Track: typeof SegmentedGaugeTrack\n Segment: typeof SegmentedGaugeSegment\n Label: typeof SegmentedGaugeLabel\n} = Object.assign(Root, {\n Track: SegmentedGaugeTrack,\n Segment: SegmentedGaugeSegment,\n Label: SegmentedGaugeLabel,\n})\n\nSegmentedGauge.displayName = 'SegmentedGauge'\nSegmentedGaugeTrack.displayName = 'SegmentedGauge.Track'\nSegmentedGaugeSegment.displayName = 'SegmentedGauge.Segment'\nSegmentedGaugeLabel.displayName = 'SegmentedGauge.Label'\n\nexport { type SegmentedGaugeProps } from './SegmentedGauge'\nexport { type SegmentedGaugeTrackProps } from './SegmentedGaugeTrack'\nexport { type SegmentedGaugeSegmentProps } from './SegmentedGaugeSegment'\nexport { type SegmentedGaugeLabelProps } from './SegmentedGaugeLabel'\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../chunk-C91j1N6u.js`);const e=require(`../icon-CRPcdgYp.js`);let t=require(`class-variance-authority`),n=require(`react`),r=require(`react/jsx-runtime`),i=require(`@spark-ui/icons/ArrowHorizontalDown`),a=require(`@spark-ui/components/form-field`),o=require(`@spark-ui/hooks/use-combined-state`);var s=e=>t=>n.Children.toArray(e).filter(n.isValidElement).find(e=>c(e)?.includes(t)),c=e=>e?e.type.displayName:``,l=(e,t=[])=>(n.Children.forEach(e,e=>{if((0,n.isValidElement)(e)){if(c(e)===`Select.Item`||c(e)===`Select.Placeholder`){let n=e.props;t.push({value:n.value,disabled:!!n.disabled,text:n.children})}e.props.children&&l(e.props.children,t)}}),t),u=e=>{let t=new Map;return l(e).forEach(e=>{t.set(e.value,e)}),t},d=(0,n.createContext)(null),f=`:select`,p=({children:e,defaultValue:t,value:i,onValueChange:s,disabled:c=!1,readOnly:l=!1,state:p,itemsComponent:m,name:h,required:g})=>{let[_,v]=(0,o.useCombinedState)(i,t,s),[y,b]=(0,n.useState)(void 0),[x,S]=(0,n.useState)(u(m)),[C,w]=(0,n.useState)(),T=x.entries().next()?.value?.[1],E=typeof _==`string`?x.get(_):T,D=i!=null,O=(0,a.useFormFieldControl)(),k=O.state||p,A=`${f}-field-${(0,n.useId)()}`,j=O.id||A,M=O.labelId,N=O.disabled??c,P=O.readOnly??l,F=O.name??h,I=!!(O.isRequired??g);return(0,n.useEffect)(()=>{let e=u(m),t=[...x.values()],n=[...e.values()];(t.length!==n.length||t.some((e,t)=>{let r=e.value!==n[t]?.value,i=e.text!==n[t]?.text;return r||i}))&&S(e)},[e]),(0,r.jsx)(d,{value:{disabled:N,readOnly:P,itemsMap:x,state:k,itemsComponent:m,selectedItem:E,setValue:v,isControlled:D,onValueChange:s,ariaLabel:C,setAriaLabel:w,fieldId:j,fieldLabelId:M,name:F,required:I,placeholder:y,setPlaceholder:b},children:e})},m=()=>{let e=(0,n.useContext)(d);if(!e)throw Error(`useSelectContext must be used within a Select provider`);return e},h=({children:e,...t})=>{let n=s(e),i=n(`Trigger`),a=n(`Items`);return(0,r.jsx)(p,{...t,itemsComponent:a,children:i})};h.displayName=`Select`;var g=(0,n.createContext)(null),_=({children:e})=>{let[t,i]=(0,n.useState)(``);return(0,r.jsx)(g,{value:{groupLabel:t,setGroupLabel:i},children:e})},v=()=>{let e=(0,n.useContext)(g);if(!e)throw Error(`useSelectGroupContext must be used within a SelectGroup provider`);return e},y=({children:e,ref:t,...n})=>(0,r.jsx)(_,{children:(0,r.jsx)(b,{ref:t,...n,children:e})}),b=({children:e,className:n,ref:i})=>{let{groupLabel:a}=v();return(0,r.jsx)(`optgroup`,{"data-spark-component":`select-group`,ref:i,className:(0,t.cx)(n),label:a,children:e})};y.displayName=`Select.Group`;var x=({disabled:e=!1,value:t,children:n,ref:i})=>(0,r.jsx)(`option`,{"data-spark-component":`select-item`,ref:i,value:t,disabled:e,children:n},t);x.displayName=`Select.Item`;var S=(0,t.cva)([`absolute left-0 top-0 size-full rounded-lg opacity-0`,`min-h-sz-44`,`ring-1 outline-hidden ring-inset focus:ring-2`],{variants:{state:{undefined:`ring-outline focus:ring-outline-high`,error:`ring-error`,alert:`ring-alert`,success:`ring-success`},disabled:{true:`cursor-not-allowed`},readOnly:{true:`cursor-default`}},compoundVariants:[{disabled:!1,state:void 0,class:`hover:ring-outline-high`}]}),C=({children:e,className:t,ref:n,...i})=>{let{state:a,disabled:o,readOnly:s,ariaLabel:c,fieldLabelId:l,isControlled:u,onValueChange:d,selectedItem:f,setValue:p,name:h,required:g,fieldId:_}=m(),v=e=>{u?(e.preventDefault(),d?.(e.target.value)):p(e.target.value)};return(0,r.jsx)(`select`,{"data-spark-component":`select-items`,ref:n,disabled:o||s,name:h,required:g,"aria-labelledby":l,...c&&{"aria-label":c},className:S({className:t,state:a,disabled:o,readOnly:s}),value:f?.value,onChange:v,id:_,...i,children:e})};C.displayName=`Select.Items`;var w=({children:e})=>{let{setGroupLabel:t}=v();return(0,n.useEffect)(()=>{t(e)},[e]),null};w.displayName=`Select.Label`;var T=({children:t})=>(0,r.jsx)(e.t,{size:`sm`,className:`shrink-0`,children:t});T.displayName=`Select.LeadingIcon`;var E=({disabled:e=!1,children:t,ref:i})=>{let{setPlaceholder:a}=m();return(0,n.useEffect)(()=>{a(t)},[t]),(0,r.jsx)(`option`,{"data-spark-component":`select-placeholder`,ref:i,value:``,disabled:e,children:t},`placeholder`)};E.displayName=`Select.Placeholder`;var D=(0,t.cva)([`relative flex w-full items-center justify-between`,`min-h-sz-44 rounded-input px-lg`,`text-body-1`,`ring-1 outline-hidden ring-inset focus-within:ring-focus`],{variants:{state:{undefined:`ring-outline`,error:`ring-error`,alert:`ring-alert`,success:`ring-success`},disabled:{false:`focus-within:ring-2`},readOnly:{true:``}},compoundVariants:[{readOnly:!1,disabled:!1,class:`bg-surface text-on-surface`},{readOnly:!0,class:`bg-on-surface/dim-5 text-on-surface cursor-default`},{disabled:!0,class:[`bg-on-surface/dim-5 text-on-surface/dim-3`,`cursor-not-allowed`]},{disabled:!1,state:void 0,class:`default:hover:ring-outline-high`}]}),O=({"aria-label":t,children:a,className:o,ref:s})=>{let{disabled:c,readOnly:l,state:u,setAriaLabel:d,itemsComponent:f}=m();return(0,n.useEffect)(()=>{t&&d(t)},[t]),(0,r.jsxs)(`div`,{"data-spark-component":`select-trigger`,ref:s,className:D({className:o,state:u,disabled:c,readOnly:l}),children:[(0,r.jsx)(`span`,{className:`gap-md flex items-center justify-start`,children:a}),(0,r.jsx)(e.t,{className:`ml-md shrink-0`,size:`sm`,children:(0,r.jsx)(i.ArrowHorizontalDown,{})}),f]})};O.displayName=`Select.Trigger`;var k=({children:e,className:n,placeholder:i,ref:a})=>{let{selectedItem:o,placeholder:s,disabled:c}=m(),l=o?.value==null,u=i||s;return(0,r.jsx)(`span`,{role:`presentation`,"data-spark-component":`select-value`,ref:a,className:(0,t.cx)(`flex shrink items-center text-left`,n),children:(0,r.jsx)(`span`,{className:(0,t.cx)(`line-clamp-1 flex-1 overflow-hidden break-all text-ellipsis`,l&&!c&&`text-on-surface/dim-1`),children:l?u:e||o?.text})})};k.displayName=`Select.Value`;var A=Object.assign(h,{Group:y,Item:x,Items:C,Placeholder:E,Label:w,Trigger:O,Value:k,LeadingIcon:T});A.displayName=`Select`,y.displayName=`Select.Group`,C.displayName=`Select.Items`,x.displayName=`Select.Item`,E.displayName=`Select.Placeholder`,w.displayName=`Select.Label`,O.displayName=`Select.Trigger`,k.displayName=`Select.Value`,T.displayName=`Select.LeadingIcon`,exports.Select=A,exports.SelectProvider=p,exports.useSelectContext=m;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../icon-DY4l9nsc.js");let t=require("class-variance-authority"),n=require("react"),r=require("react/jsx-runtime"),i=require("@spark-ui/icons/ArrowHorizontalDown"),a=require("@spark-ui/components/form-field"),o=require("@spark-ui/hooks/use-combined-state");var s=e=>t=>n.Children.toArray(e).filter(n.isValidElement).find(e=>c(e)?.includes(t)),c=e=>e?e.type.displayName:``,l=(e,t=[])=>(n.Children.forEach(e,e=>{if((0,n.isValidElement)(e)){if(c(e)===`Select.Item`||c(e)===`Select.Placeholder`){let n=e.props;t.push({value:n.value,disabled:!!n.disabled,text:n.children})}e.props.children&&l(e.props.children,t)}}),t),u=e=>{let t=new Map;return l(e).forEach(e=>{t.set(e.value,e)}),t},d=(0,n.createContext)(null),f=`:select`,p=({children:e,defaultValue:t,value:i,onValueChange:s,disabled:c=!1,readOnly:l=!1,state:p,itemsComponent:m,name:h,required:g})=>{let[_,v]=(0,o.useCombinedState)(i,t,s),[y,b]=(0,n.useState)(void 0),[x,S]=(0,n.useState)(u(m)),[C,w]=(0,n.useState)(),T=x.entries().next()?.value?.[1],E=typeof _==`string`?x.get(_):T,D=i!=null,O=(0,a.useFormFieldControl)(),k=O.state||p,A=`${f}-field-${(0,n.useId)()}`,j=O.id||A,M=O.labelId,N=O.disabled??c,P=O.readOnly??l,F=O.name??h,I=!!(O.isRequired??g);return(0,n.useEffect)(()=>{let e=u(m),t=[...x.values()],n=[...e.values()];(t.length!==n.length||t.some((e,t)=>{let r=e.value!==n[t]?.value,i=e.text!==n[t]?.text;return r||i}))&&S(e)},[e]),(0,r.jsx)(d,{value:{disabled:N,readOnly:P,itemsMap:x,state:k,itemsComponent:m,selectedItem:E,setValue:v,isControlled:D,onValueChange:s,ariaLabel:C,setAriaLabel:w,fieldId:j,fieldLabelId:M,name:F,required:I,placeholder:y,setPlaceholder:b},children:e})},m=()=>{let e=(0,n.useContext)(d);if(!e)throw Error(`useSelectContext must be used within a Select provider`);return e},h=({children:e,...t})=>{let n=s(e),i=n(`Trigger`),a=n(`Items`);return(0,r.jsx)(p,{...t,itemsComponent:a,children:i})};h.displayName=`Select`;var g=(0,n.createContext)(null),_=({children:e})=>{let[t,i]=(0,n.useState)(``);return(0,r.jsx)(g,{value:{groupLabel:t,setGroupLabel:i},children:e})},v=()=>{let e=(0,n.useContext)(g);if(!e)throw Error(`useSelectGroupContext must be used within a SelectGroup provider`);return e},y=({children:e,ref:t,...n})=>(0,r.jsx)(_,{children:(0,r.jsx)(b,{ref:t,...n,children:e})}),b=({children:e,className:n,ref:i})=>{let{groupLabel:a}=v();return(0,r.jsx)(`optgroup`,{"data-spark-component":`select-group`,ref:i,className:(0,t.cx)(n),label:a,children:e})};y.displayName=`Select.Group`;var x=({disabled:e=!1,value:t,children:n,ref:i})=>(0,r.jsx)(`option`,{"data-spark-component":`select-item`,ref:i,value:t,disabled:e,children:n},t);x.displayName=`Select.Item`;var S=(0,t.cva)([`absolute left-0 top-0 size-full rounded-lg opacity-0`,`min-h-sz-44`,`ring-1 outline-hidden ring-inset focus:ring-2`],{variants:{state:{undefined:`ring-outline focus:ring-outline-high`,error:`ring-error`,alert:`ring-alert`,success:`ring-success`},disabled:{true:`cursor-not-allowed`},readOnly:{true:`cursor-default`}},compoundVariants:[{disabled:!1,state:void 0,class:`hover:ring-outline-high`}]}),C=({children:e,className:t,ref:n,...i})=>{let{state:a,disabled:o,readOnly:s,ariaLabel:c,fieldLabelId:l,isControlled:u,onValueChange:d,selectedItem:f,setValue:p,name:h,required:g,fieldId:_}=m(),v=e=>{u?(e.preventDefault(),d?.(e.target.value)):p(e.target.value)};return(0,r.jsx)(`select`,{"data-spark-component":`select-items`,ref:n,disabled:o||s,name:h,required:g,"aria-labelledby":l,...c&&{"aria-label":c},className:S({className:t,state:a,disabled:o,readOnly:s}),value:f?.value,onChange:v,id:_,...i,children:e})};C.displayName=`Select.Items`;var w=({children:e})=>{let{setGroupLabel:t}=v();return(0,n.useEffect)(()=>{t(e)},[e]),null};w.displayName=`Select.Label`;var T=({children:t})=>(0,r.jsx)(e.t,{size:`sm`,className:`shrink-0`,children:t});T.displayName=`Select.LeadingIcon`;var E=({disabled:e=!1,children:t,ref:i})=>{let{setPlaceholder:a}=m();return(0,n.useEffect)(()=>{a(t)},[t]),(0,r.jsx)(`option`,{"data-spark-component":`select-placeholder`,ref:i,value:``,disabled:e,children:t},`placeholder`)};E.displayName=`Select.Placeholder`;var D=(0,t.cva)([`relative flex w-full items-center justify-between`,`min-h-sz-44 rounded-input px-lg`,`text-body-1`,`ring-1 outline-hidden ring-inset focus-within:ring-focus`],{variants:{state:{undefined:`ring-outline`,error:`ring-error`,alert:`ring-alert`,success:`ring-success`},disabled:{false:`focus-within:ring-2`},readOnly:{true:``}},compoundVariants:[{readOnly:!1,disabled:!1,class:`bg-surface text-on-surface`},{readOnly:!0,class:`bg-on-surface/dim-5 text-on-surface cursor-default`},{disabled:!0,class:[`bg-on-surface/dim-5 text-on-surface/dim-3`,`cursor-not-allowed`]},{disabled:!1,state:void 0,class:`default:hover:ring-outline-high`}]}),O=({"aria-label":t,children:a,className:o,ref:s})=>{let{disabled:c,readOnly:l,state:u,setAriaLabel:d,itemsComponent:f}=m();return(0,n.useEffect)(()=>{t&&d(t)},[t]),(0,r.jsxs)(`div`,{"data-spark-component":`select-trigger`,ref:s,className:D({className:o,state:u,disabled:c,readOnly:l}),children:[(0,r.jsx)(`span`,{className:`gap-md flex items-center justify-start`,children:a}),(0,r.jsx)(e.t,{className:`ml-md shrink-0`,size:`sm`,children:(0,r.jsx)(i.ArrowHorizontalDown,{})}),f]})};O.displayName=`Select.Trigger`;var k=({children:e,className:n,placeholder:i,ref:a})=>{let{selectedItem:o,placeholder:s,disabled:c}=m(),l=o?.value==null,u=i||s;return(0,r.jsx)(`span`,{role:`presentation`,"data-spark-component":`select-value`,ref:a,className:(0,t.cx)(`flex shrink items-center text-left`,n),children:(0,r.jsx)(`span`,{className:(0,t.cx)(`line-clamp-1 flex-1 overflow-hidden break-all text-ellipsis`,l&&!c&&`text-on-surface/dim-1`),children:l?u:e||o?.text})})};k.displayName=`Select.Value`;var A=Object.assign(h,{Group:y,Item:x,Items:C,Placeholder:E,Label:w,Trigger:O,Value:k,LeadingIcon:T});A.displayName=`Select`,y.displayName=`Select.Group`,C.displayName=`Select.Items`,x.displayName=`Select.Item`,E.displayName=`Select.Placeholder`,w.displayName=`Select.Label`,O.displayName=`Select.Trigger`,k.displayName=`Select.Value`,T.displayName=`Select.LeadingIcon`,exports.Select=A,exports.SelectProvider=p,exports.useSelectContext=m;
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/select/utils.ts","../../src/select/SelectContext.tsx","../../src/select/Select.tsx","../../src/select/SelectItemsGroupContext.tsx","../../src/select/SelectGroup.tsx","../../src/select/SelectItem.tsx","../../src/select/SelectItems.tsx","../../src/select/SelectLabel.tsx","../../src/select/SelectLeadingIcon.tsx","../../src/select/SelectPlaceholder.tsx","../../src/select/SelectTrigger.styles.tsx","../../src/select/SelectTrigger.tsx","../../src/select/SelectValue.tsx","../../src/select/index.ts"],"sourcesContent":["import { Children, type FC, isValidElement, type ReactElement, type ReactNode } from 'react'\n\nimport { type ItemProps } from './SelectItem'\nimport { type ItemsMap, type SelectItem } from './types'\n\nexport const findElement = (children: ReactNode) => (name: string) => {\n const validChildren = Children.toArray(children).filter(isValidElement)\n\n return validChildren.find(child => {\n return getElementDisplayName(child)?.includes(name)\n })\n}\n\nconst getElementDisplayName = (element?: ReactElement) => {\n return element ? (element.type as FC & { displayName?: string }).displayName : ''\n}\n\nconst getOrderedItems = (children: ReactNode, result: SelectItem[] = []): SelectItem[] => {\n Children.forEach(children, child => {\n if (!isValidElement(child)) return\n\n if (\n getElementDisplayName(child) === 'Select.Item' ||\n getElementDisplayName(child) === 'Select.Placeholder'\n ) {\n const childProps = child.props as ItemProps\n result.push({\n value: childProps.value,\n disabled: !!childProps.disabled,\n text: childProps.children,\n })\n }\n\n if ((child.props as { children: ReactNode }).children) {\n getOrderedItems((child.props as { children: ReactNode }).children, result)\n }\n })\n\n return result\n}\n\nexport const getItemsFromChildren = (children: ReactNode): ItemsMap => {\n const newMap: ItemsMap = new Map()\n\n getOrderedItems(children).forEach(itemData => {\n newMap.set(itemData.value, itemData)\n })\n\n return newMap\n}\n","import { useFormFieldControl } from '@spark-ui/components/form-field'\nimport { useCombinedState } from '@spark-ui/hooks/use-combined-state'\nimport {\n createContext,\n Dispatch,\n PropsWithChildren,\n ReactElement,\n SetStateAction,\n useContext,\n useEffect,\n useId,\n useState,\n} from 'react'\n\nimport { type ItemsMap, SelectItem } from './types'\nimport { getItemsFromChildren } from './utils'\n\nexport interface SelectContextState {\n itemsMap: ItemsMap\n disabled: boolean\n readOnly: boolean\n state?: 'error' | 'alert' | 'success'\n itemsComponent: ReactElement | undefined\n selectedItem: SelectItem | undefined\n setValue: (value: string) => void\n isControlled: boolean\n onValueChange?: (value: string) => void\n ariaLabel: string | undefined\n setAriaLabel: Dispatch<SetStateAction<string | undefined>>\n fieldId: string\n fieldLabelId: string | undefined\n name: string | undefined\n required: boolean\n placeholder: string | undefined\n setPlaceholder: Dispatch<SetStateAction<string | undefined>>\n}\n\nexport type SelectContextProps = PropsWithChildren<{\n /**\n * Use `state` prop to assign a specific state to the select, choosing from: `error`, `alert` and `success`. By doing so, the outline styles will be updated.\n */\n state?: 'error' | 'alert' | 'success'\n /**\n * When true, prevents the user from interacting with the select.\n */\n disabled?: boolean\n /**\n * Sets the select as interactive or not.\n */\n readOnly?: boolean\n /**\n * The value of the select when initially rendered. Use when you do not need to control the state of the select.\n */\n defaultValue?: string\n /**\n * The controlled value of the select. Should be used in conjunction with `onValueChange`.\n */\n value?: string\n /**\n * Event handler called when the value changes.\n */\n onValueChange?: (value: string) => void\n\n itemsComponent: ReactElement | undefined\n /**\n * This attribute is used to specify the name of the control.\n * If wrapped with a FormField with a name, will be inherited from it.\n */\n name?: string\n /**\n * A Boolean attribute indicating that an option with a non-empty string value must be selected.\n */\n required?: boolean\n}>\n\nconst SelectContext = createContext<SelectContextState | null>(null)\n\nconst ID_PREFIX = ':select'\n\nexport const SelectProvider = ({\n children,\n defaultValue,\n value: valueProp,\n onValueChange,\n disabled: disabledProp = false,\n readOnly: readOnlyProp = false,\n state: stateProp,\n itemsComponent,\n name: nameProp,\n required: requiredProp,\n}: SelectContextProps) => {\n const [value, setValue] = useCombinedState(valueProp, defaultValue, onValueChange)\n const [placeholder, setPlaceholder] = useState<string | undefined>(undefined)\n const [itemsMap, setItemsMap] = useState<ItemsMap>(getItemsFromChildren(itemsComponent))\n const [ariaLabel, setAriaLabel] = useState<string>()\n\n // Computed state\n const firstItem = itemsMap.entries().next()?.value?.[1]\n const selectedItem = typeof value === 'string' ? itemsMap.get(value) : firstItem\n const isControlled = valueProp != null\n\n // Derivated from FormField context\n const field = useFormFieldControl()\n const state = field.state || stateProp\n\n const internalFieldID = `${ID_PREFIX}-field-${useId()}`\n const fieldId = field.id || internalFieldID\n const fieldLabelId = field.labelId\n const disabled = field.disabled ?? disabledProp\n const readOnly = field.readOnly ?? readOnlyProp\n const name = field.name ?? nameProp\n const required = !!(field.isRequired ?? requiredProp)\n\n /**\n * Indices in a Map are set when an element is added to the Map.\n * If for some reason, in the Select:\n * - items order changes\n * - items are added\n * - items are removed\n *\n * The Map must be rebuilt from the new children in order to preserve logical indices.\n *\n */\n useEffect(() => {\n const newMap = getItemsFromChildren(itemsComponent)\n\n const previousItems = [...itemsMap.values()]\n const newItems = [...newMap.values()]\n\n const hasItemsChanges =\n previousItems.length !== newItems.length ||\n previousItems.some((item, index) => {\n const hasUpdatedValue = item.value !== newItems[index]?.value\n const hasUpdatedText = item.text !== newItems[index]?.text\n\n return hasUpdatedValue || hasUpdatedText\n })\n\n if (hasItemsChanges) {\n setItemsMap(newMap)\n }\n }, [children])\n\n return (\n <SelectContext\n value={{\n disabled,\n readOnly,\n itemsMap,\n state,\n itemsComponent,\n selectedItem,\n setValue,\n isControlled,\n onValueChange,\n ariaLabel,\n setAriaLabel,\n fieldId,\n fieldLabelId,\n name,\n required,\n placeholder,\n setPlaceholder,\n }}\n >\n {children}\n </SelectContext>\n )\n}\n\nexport const useSelectContext = () => {\n const context = useContext(SelectContext)\n\n if (!context) {\n throw Error('useSelectContext must be used within a Select provider')\n }\n\n return context\n}\n","import { type SelectContextProps, SelectProvider } from './SelectContext'\nimport { findElement } from './utils'\n\nexport type SelectProps = Omit<SelectContextProps, 'itemsComponent'>\n\nexport const Select = ({ children, ...props }: SelectProps) => {\n const finder = findElement(children)\n const trigger = finder('Trigger')\n const items = finder('Items')\n\n return (\n <SelectProvider {...props} itemsComponent={items}>\n {trigger}\n </SelectProvider>\n )\n}\n\nSelect.displayName = 'Select'\n","import { createContext, type PropsWithChildren, useContext, useState } from 'react'\n\nexport interface SelectContextState {\n groupLabel: string\n setGroupLabel: (label: string) => void\n}\n\ntype SelectContextProps = PropsWithChildren\n\nconst SelectGroupContext = createContext<SelectContextState | null>(null)\n\nexport const SelectGroupProvider = ({ children }: SelectContextProps) => {\n const [groupLabel, setGroupLabel] = useState('')\n\n return <SelectGroupContext value={{ groupLabel, setGroupLabel }}>{children}</SelectGroupContext>\n}\n\nexport const useSelectGroupContext = () => {\n const context = useContext(SelectGroupContext)\n\n if (!context) {\n throw Error('useSelectGroupContext must be used within a SelectGroup provider')\n }\n\n return context\n}\n","import { cx } from 'class-variance-authority'\nimport { ReactNode, type Ref } from 'react'\n\nimport { SelectGroupProvider, useSelectGroupContext } from './SelectItemsGroupContext'\n\ninterface GroupProps {\n children: ReactNode\n className?: string\n ref?: Ref<HTMLOptGroupElement>\n}\n\n/**\n * A group of related select items. Renders a <div> element.\n */\n\nexport const Group = ({ children, ref: forwardedRef, ...props }: GroupProps) => {\n return (\n <SelectGroupProvider>\n <GroupContent ref={forwardedRef} {...props}>\n {children}\n </GroupContent>\n </SelectGroupProvider>\n )\n}\n\nconst GroupContent = ({ children, className, ref: forwardedRef }: GroupProps) => {\n const { groupLabel } = useSelectGroupContext()\n\n return (\n <optgroup\n data-spark-component=\"select-group\"\n ref={forwardedRef}\n className={cx(className)}\n label={groupLabel}\n >\n {children}\n </optgroup>\n )\n}\n\nGroup.displayName = 'Select.Group'\n","import { type Ref } from 'react'\n\nexport interface ItemProps {\n disabled?: boolean\n value: string\n children: string\n ref?: Ref<HTMLOptionElement>\n}\n\n/**\n * A selectable item in the select list. Renders a <div> element.\n */\n\nexport const Item = ({ disabled = false, value, children, ref: forwardedRef }: ItemProps) => {\n return (\n <option\n data-spark-component=\"select-item\"\n ref={forwardedRef}\n key={value}\n value={value}\n disabled={disabled}\n // label\n >\n {children}\n </option>\n )\n}\n\nItem.displayName = 'Select.Item'\n","import { cva } from 'class-variance-authority'\nimport { ChangeEvent, ComponentPropsWithRef, PropsWithChildren } from 'react'\n\nimport { useSelectContext } from './SelectContext'\n\nexport const styles = cva(\n [\n 'absolute left-0 top-0 size-full rounded-lg opacity-0',\n 'min-h-sz-44',\n // outline styles\n 'ring-1 outline-hidden ring-inset focus:ring-2',\n ],\n {\n variants: {\n state: {\n undefined: 'ring-outline focus:ring-outline-high',\n error: 'ring-error',\n alert: 'ring-alert',\n success: 'ring-success',\n },\n disabled: {\n true: 'cursor-not-allowed',\n },\n readOnly: {\n true: 'cursor-default',\n },\n },\n compoundVariants: [\n {\n disabled: false,\n state: undefined,\n class: 'hover:ring-outline-high',\n },\n ],\n }\n)\n\n/**\n * Container for the select items list. Renders a <div> element.\n */\n\nexport const Items = ({\n children,\n className,\n ref,\n ...rest\n}: PropsWithChildren<ComponentPropsWithRef<'select'>>) => {\n const {\n state,\n disabled,\n readOnly,\n ariaLabel,\n fieldLabelId,\n isControlled,\n onValueChange,\n selectedItem,\n setValue,\n name,\n required,\n fieldId,\n } = useSelectContext()\n\n const handleChange = (event: ChangeEvent<HTMLSelectElement>) => {\n if (isControlled) {\n event.preventDefault()\n onValueChange?.(event.target.value)\n } else {\n setValue(event.target.value)\n }\n }\n\n return (\n <select\n data-spark-component=\"select-items\"\n ref={ref}\n disabled={disabled || readOnly}\n name={name}\n required={required}\n aria-labelledby={fieldLabelId}\n {...(ariaLabel && { 'aria-label': ariaLabel })}\n className={styles({ className, state, disabled, readOnly })}\n value={selectedItem?.value}\n onChange={handleChange}\n id={fieldId}\n {...rest}\n >\n {children}\n </select>\n )\n}\n\nItems.displayName = 'Select.Items'\n","import { useEffect } from 'react'\n\nimport { useSelectGroupContext } from './SelectItemsGroupContext'\n\ninterface LabelProps {\n children: string\n}\n\n/**\n * A label for a group of select items. Renders a <div> element.\n */\n\nexport const Label = ({ children }: LabelProps) => {\n const { setGroupLabel } = useSelectGroupContext()\n\n useEffect(() => {\n setGroupLabel(children)\n }, [children])\n\n return null\n}\n\nLabel.displayName = 'Select.Label'\n","import { ReactElement } from 'react'\n\nimport { Icon } from '../icon'\n\n/**\n * An icon displayed at the start of the select trigger. Renders a <span> element.\n */\n\nexport const LeadingIcon = ({ children }: { children: ReactElement }) => {\n return (\n <Icon size={'sm'} className=\"shrink-0\">\n {children}\n </Icon>\n )\n}\n\nLeadingIcon.displayName = 'Select.LeadingIcon'\n","import { type Ref, useEffect } from 'react'\n\nimport { useSelectContext } from './SelectContext'\n\nexport interface PlaceholderProps {\n disabled?: boolean\n children: string\n ref?: Ref<HTMLOptionElement>\n}\n\n/**\n * Placeholder text shown when no item is selected. Renders a <span> element.\n */\n\nexport const Placeholder = ({\n disabled = false,\n children,\n ref: forwardedRef,\n}: PlaceholderProps) => {\n const { setPlaceholder } = useSelectContext()\n\n useEffect(() => {\n setPlaceholder(children)\n }, [children])\n\n return (\n <option\n data-spark-component=\"select-placeholder\"\n ref={forwardedRef}\n key=\"placeholder\"\n value=\"\"\n disabled={disabled}\n >\n {children}\n </option>\n )\n}\n\nPlaceholder.displayName = 'Select.Placeholder'\n","import { cva } from 'class-variance-authority'\n\nexport const styles = cva(\n [\n 'relative flex w-full items-center justify-between',\n 'min-h-sz-44 rounded-input px-lg',\n 'text-body-1',\n // outline styles\n 'ring-1 outline-hidden ring-inset focus-within:ring-focus',\n ],\n {\n variants: {\n state: {\n undefined: 'ring-outline',\n error: 'ring-error',\n alert: 'ring-alert',\n success: 'ring-success',\n },\n disabled: {\n false: 'focus-within:ring-2',\n },\n readOnly: {\n true: '',\n },\n },\n compoundVariants: [\n {\n readOnly: false,\n disabled: false,\n class: 'bg-surface text-on-surface',\n },\n {\n readOnly: true,\n class: 'bg-on-surface/dim-5 text-on-surface cursor-default',\n },\n {\n disabled: true,\n class: ['bg-on-surface/dim-5 text-on-surface/dim-3', 'cursor-not-allowed'],\n },\n {\n disabled: false,\n state: undefined,\n class: 'default:hover:ring-outline-high',\n },\n ],\n }\n)\n","import { ArrowHorizontalDown } from '@spark-ui/icons/ArrowHorizontalDown'\nimport { ReactNode, type Ref, useEffect } from 'react'\n\nimport { Icon } from '../icon'\nimport { useSelectContext } from './SelectContext'\nimport { styles } from './SelectTrigger.styles'\n\ninterface TriggerProps {\n 'aria-label'?: string\n children: ReactNode\n className?: string\n ref?: Ref<HTMLDivElement>\n}\n\n/**\n * This trigger acts as a fake button for the `select` tag.\n * It is not interactive.\n */\n/**\n * The trigger button for the select dropdown. Renders a <button> element.\n */\n\nexport const Trigger = ({\n 'aria-label': ariaLabel,\n children,\n className,\n ref: forwardedRef,\n}: TriggerProps) => {\n const { disabled, readOnly, state, setAriaLabel, itemsComponent } = useSelectContext()\n\n useEffect(() => {\n if (ariaLabel) {\n setAriaLabel(ariaLabel)\n }\n }, [ariaLabel])\n\n return (\n <div\n data-spark-component=\"select-trigger\"\n ref={forwardedRef}\n className={styles({ className, state, disabled, readOnly })}\n >\n <span className=\"gap-md flex items-center justify-start\">{children}</span>\n\n <Icon className=\"ml-md shrink-0\" size=\"sm\">\n <ArrowHorizontalDown />\n </Icon>\n\n {itemsComponent}\n </div>\n )\n}\n\nTrigger.displayName = 'Select.Trigger'\n","import { cx } from 'class-variance-authority'\nimport { ReactNode, type Ref } from 'react'\n\nimport { useSelectContext } from './SelectContext'\n\nexport interface ValueProps {\n children?: ReactNode\n className?: string\n /**\n * Optional placeholder value for the trigger.\n * If not specified, the value inside `Select.Placeholder` item will be used.\n */\n placeholder?: string\n ref?: Ref<HTMLSpanElement>\n}\n\n/**\n * Displays the selected value in the select trigger. Renders a <span> element.\n */\n\nexport const Value = ({\n children,\n className,\n placeholder: customPlaceholder,\n ref: forwardedRef,\n}: ValueProps) => {\n const { selectedItem, placeholder, disabled } = useSelectContext()\n\n const isPlaceholderSelected = selectedItem?.value == null\n const valuePlaceholder = customPlaceholder || placeholder\n\n return (\n <span\n role=\"presentation\"\n data-spark-component=\"select-value\"\n ref={forwardedRef}\n className={cx('flex shrink items-center text-left', className)}\n >\n <span\n className={cx(\n 'line-clamp-1 flex-1 overflow-hidden break-all text-ellipsis',\n isPlaceholderSelected && !disabled && 'text-on-surface/dim-1'\n )}\n >\n {isPlaceholderSelected ? valuePlaceholder : children || selectedItem?.text}\n </span>\n </span>\n )\n}\n\nValue.displayName = 'Select.Value'\n","import { Select as Root } from './Select'\nimport { SelectProvider, useSelectContext } from './SelectContext'\nimport { Group } from './SelectGroup'\nimport { Item } from './SelectItem'\nimport { Items } from './SelectItems'\nimport { Label } from './SelectLabel'\nimport { LeadingIcon } from './SelectLeadingIcon'\nimport { Placeholder } from './SelectPlaceholder'\nimport { Trigger } from './SelectTrigger'\nimport { Value } from './SelectValue'\n\nexport { useSelectContext, SelectProvider }\n\n/**\n * A dropdown component that allows users to choose one option from a list of choices.\n */\nexport const Select: typeof Root & {\n Group: typeof Group\n Item: typeof Item\n Items: typeof Items\n Placeholder: typeof Placeholder\n Label: typeof Label\n Trigger: typeof Trigger\n Value: typeof Value\n LeadingIcon: typeof LeadingIcon\n} = Object.assign(Root, {\n Group,\n Item,\n Items,\n Placeholder,\n Label,\n Trigger,\n Value,\n LeadingIcon,\n})\n\nSelect.displayName = 'Select'\nGroup.displayName = 'Select.Group'\nItems.displayName = 'Select.Items'\nItem.displayName = 'Select.Item'\nPlaceholder.displayName = 'Select.Placeholder'\nLabel.displayName = 'Select.Label'\nTrigger.displayName = 'Select.Trigger'\nValue.displayName = 'Select.Value'\nLeadingIcon.displayName = 'Select.LeadingIcon'\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/select/utils.ts","../../src/select/SelectContext.tsx","../../src/select/Select.tsx","../../src/select/SelectItemsGroupContext.tsx","../../src/select/SelectGroup.tsx","../../src/select/SelectItem.tsx","../../src/select/SelectItems.tsx","../../src/select/SelectLabel.tsx","../../src/select/SelectLeadingIcon.tsx","../../src/select/SelectPlaceholder.tsx","../../src/select/SelectTrigger.styles.tsx","../../src/select/SelectTrigger.tsx","../../src/select/SelectValue.tsx","../../src/select/index.ts"],"sourcesContent":["import { Children, type FC, isValidElement, type ReactElement, type ReactNode } from 'react'\n\nimport { type ItemProps } from './SelectItem'\nimport { type ItemsMap, type SelectItem } from './types'\n\nexport const findElement = (children: ReactNode) => (name: string) => {\n const validChildren = Children.toArray(children).filter(isValidElement)\n\n return validChildren.find(child => {\n return getElementDisplayName(child)?.includes(name)\n })\n}\n\nconst getElementDisplayName = (element?: ReactElement) => {\n return element ? (element.type as FC & { displayName?: string }).displayName : ''\n}\n\nconst getOrderedItems = (children: ReactNode, result: SelectItem[] = []): SelectItem[] => {\n Children.forEach(children, child => {\n if (!isValidElement(child)) return\n\n if (\n getElementDisplayName(child) === 'Select.Item' ||\n getElementDisplayName(child) === 'Select.Placeholder'\n ) {\n const childProps = child.props as ItemProps\n result.push({\n value: childProps.value,\n disabled: !!childProps.disabled,\n text: childProps.children,\n })\n }\n\n if ((child.props as { children: ReactNode }).children) {\n getOrderedItems((child.props as { children: ReactNode }).children, result)\n }\n })\n\n return result\n}\n\nexport const getItemsFromChildren = (children: ReactNode): ItemsMap => {\n const newMap: ItemsMap = new Map()\n\n getOrderedItems(children).forEach(itemData => {\n newMap.set(itemData.value, itemData)\n })\n\n return newMap\n}\n","import { useFormFieldControl } from '@spark-ui/components/form-field'\nimport { useCombinedState } from '@spark-ui/hooks/use-combined-state'\nimport {\n createContext,\n Dispatch,\n PropsWithChildren,\n ReactElement,\n SetStateAction,\n useContext,\n useEffect,\n useId,\n useState,\n} from 'react'\n\nimport { type ItemsMap, SelectItem } from './types'\nimport { getItemsFromChildren } from './utils'\n\nexport interface SelectContextState {\n itemsMap: ItemsMap\n disabled: boolean\n readOnly: boolean\n state?: 'error' | 'alert' | 'success'\n itemsComponent: ReactElement | undefined\n selectedItem: SelectItem | undefined\n setValue: (value: string) => void\n isControlled: boolean\n onValueChange?: (value: string) => void\n ariaLabel: string | undefined\n setAriaLabel: Dispatch<SetStateAction<string | undefined>>\n fieldId: string\n fieldLabelId: string | undefined\n name: string | undefined\n required: boolean\n placeholder: string | undefined\n setPlaceholder: Dispatch<SetStateAction<string | undefined>>\n}\n\nexport type SelectContextProps = PropsWithChildren<{\n /**\n * Use `state` prop to assign a specific state to the select, choosing from: `error`, `alert` and `success`. By doing so, the outline styles will be updated.\n */\n state?: 'error' | 'alert' | 'success'\n /**\n * When true, prevents the user from interacting with the select.\n */\n disabled?: boolean\n /**\n * Sets the select as interactive or not.\n */\n readOnly?: boolean\n /**\n * The value of the select when initially rendered. Use when you do not need to control the state of the select.\n */\n defaultValue?: string\n /**\n * The controlled value of the select. Should be used in conjunction with `onValueChange`.\n */\n value?: string\n /**\n * Event handler called when the value changes.\n */\n onValueChange?: (value: string) => void\n\n itemsComponent: ReactElement | undefined\n /**\n * This attribute is used to specify the name of the control.\n * If wrapped with a FormField with a name, will be inherited from it.\n */\n name?: string\n /**\n * A Boolean attribute indicating that an option with a non-empty string value must be selected.\n */\n required?: boolean\n}>\n\nconst SelectContext = createContext<SelectContextState | null>(null)\n\nconst ID_PREFIX = ':select'\n\nexport const SelectProvider = ({\n children,\n defaultValue,\n value: valueProp,\n onValueChange,\n disabled: disabledProp = false,\n readOnly: readOnlyProp = false,\n state: stateProp,\n itemsComponent,\n name: nameProp,\n required: requiredProp,\n}: SelectContextProps) => {\n const [value, setValue] = useCombinedState(valueProp, defaultValue, onValueChange)\n const [placeholder, setPlaceholder] = useState<string | undefined>(undefined)\n const [itemsMap, setItemsMap] = useState<ItemsMap>(getItemsFromChildren(itemsComponent))\n const [ariaLabel, setAriaLabel] = useState<string>()\n\n // Computed state\n const firstItem = itemsMap.entries().next()?.value?.[1]\n const selectedItem = typeof value === 'string' ? itemsMap.get(value) : firstItem\n const isControlled = valueProp != null\n\n // Derivated from FormField context\n const field = useFormFieldControl()\n const state = field.state || stateProp\n\n const internalFieldID = `${ID_PREFIX}-field-${useId()}`\n const fieldId = field.id || internalFieldID\n const fieldLabelId = field.labelId\n const disabled = field.disabled ?? disabledProp\n const readOnly = field.readOnly ?? readOnlyProp\n const name = field.name ?? nameProp\n const required = !!(field.isRequired ?? requiredProp)\n\n /**\n * Indices in a Map are set when an element is added to the Map.\n * If for some reason, in the Select:\n * - items order changes\n * - items are added\n * - items are removed\n *\n * The Map must be rebuilt from the new children in order to preserve logical indices.\n *\n */\n useEffect(() => {\n const newMap = getItemsFromChildren(itemsComponent)\n\n const previousItems = [...itemsMap.values()]\n const newItems = [...newMap.values()]\n\n const hasItemsChanges =\n previousItems.length !== newItems.length ||\n previousItems.some((item, index) => {\n const hasUpdatedValue = item.value !== newItems[index]?.value\n const hasUpdatedText = item.text !== newItems[index]?.text\n\n return hasUpdatedValue || hasUpdatedText\n })\n\n if (hasItemsChanges) {\n setItemsMap(newMap)\n }\n }, [children])\n\n return (\n <SelectContext\n value={{\n disabled,\n readOnly,\n itemsMap,\n state,\n itemsComponent,\n selectedItem,\n setValue,\n isControlled,\n onValueChange,\n ariaLabel,\n setAriaLabel,\n fieldId,\n fieldLabelId,\n name,\n required,\n placeholder,\n setPlaceholder,\n }}\n >\n {children}\n </SelectContext>\n )\n}\n\nexport const useSelectContext = () => {\n const context = useContext(SelectContext)\n\n if (!context) {\n throw Error('useSelectContext must be used within a Select provider')\n }\n\n return context\n}\n","import { type SelectContextProps, SelectProvider } from './SelectContext'\nimport { findElement } from './utils'\n\nexport type SelectProps = Omit<SelectContextProps, 'itemsComponent'>\n\nexport const Select = ({ children, ...props }: SelectProps) => {\n const finder = findElement(children)\n const trigger = finder('Trigger')\n const items = finder('Items')\n\n return (\n <SelectProvider {...props} itemsComponent={items}>\n {trigger}\n </SelectProvider>\n )\n}\n\nSelect.displayName = 'Select'\n","import { createContext, type PropsWithChildren, useContext, useState } from 'react'\n\nexport interface SelectContextState {\n groupLabel: string\n setGroupLabel: (label: string) => void\n}\n\ntype SelectContextProps = PropsWithChildren\n\nconst SelectGroupContext = createContext<SelectContextState | null>(null)\n\nexport const SelectGroupProvider = ({ children }: SelectContextProps) => {\n const [groupLabel, setGroupLabel] = useState('')\n\n return <SelectGroupContext value={{ groupLabel, setGroupLabel }}>{children}</SelectGroupContext>\n}\n\nexport const useSelectGroupContext = () => {\n const context = useContext(SelectGroupContext)\n\n if (!context) {\n throw Error('useSelectGroupContext must be used within a SelectGroup provider')\n }\n\n return context\n}\n","import { cx } from 'class-variance-authority'\nimport { ReactNode, type Ref } from 'react'\n\nimport { SelectGroupProvider, useSelectGroupContext } from './SelectItemsGroupContext'\n\ninterface GroupProps {\n children: ReactNode\n className?: string\n ref?: Ref<HTMLOptGroupElement>\n}\n\n/**\n * A group of related select items. Renders a <div> element.\n */\n\nexport const Group = ({ children, ref: forwardedRef, ...props }: GroupProps) => {\n return (\n <SelectGroupProvider>\n <GroupContent ref={forwardedRef} {...props}>\n {children}\n </GroupContent>\n </SelectGroupProvider>\n )\n}\n\nconst GroupContent = ({ children, className, ref: forwardedRef }: GroupProps) => {\n const { groupLabel } = useSelectGroupContext()\n\n return (\n <optgroup\n data-spark-component=\"select-group\"\n ref={forwardedRef}\n className={cx(className)}\n label={groupLabel}\n >\n {children}\n </optgroup>\n )\n}\n\nGroup.displayName = 'Select.Group'\n","import { type Ref } from 'react'\n\nexport interface ItemProps {\n disabled?: boolean\n value: string\n children: string\n ref?: Ref<HTMLOptionElement>\n}\n\n/**\n * A selectable item in the select list. Renders a <div> element.\n */\n\nexport const Item = ({ disabled = false, value, children, ref: forwardedRef }: ItemProps) => {\n return (\n <option\n data-spark-component=\"select-item\"\n ref={forwardedRef}\n key={value}\n value={value}\n disabled={disabled}\n // label\n >\n {children}\n </option>\n )\n}\n\nItem.displayName = 'Select.Item'\n","import { cva } from 'class-variance-authority'\nimport { ChangeEvent, ComponentPropsWithRef, PropsWithChildren } from 'react'\n\nimport { useSelectContext } from './SelectContext'\n\nexport const styles = cva(\n [\n 'absolute left-0 top-0 size-full rounded-lg opacity-0',\n 'min-h-sz-44',\n // outline styles\n 'ring-1 outline-hidden ring-inset focus:ring-2',\n ],\n {\n variants: {\n state: {\n undefined: 'ring-outline focus:ring-outline-high',\n error: 'ring-error',\n alert: 'ring-alert',\n success: 'ring-success',\n },\n disabled: {\n true: 'cursor-not-allowed',\n },\n readOnly: {\n true: 'cursor-default',\n },\n },\n compoundVariants: [\n {\n disabled: false,\n state: undefined,\n class: 'hover:ring-outline-high',\n },\n ],\n }\n)\n\n/**\n * Container for the select items list. Renders a <div> element.\n */\n\nexport const Items = ({\n children,\n className,\n ref,\n ...rest\n}: PropsWithChildren<ComponentPropsWithRef<'select'>>) => {\n const {\n state,\n disabled,\n readOnly,\n ariaLabel,\n fieldLabelId,\n isControlled,\n onValueChange,\n selectedItem,\n setValue,\n name,\n required,\n fieldId,\n } = useSelectContext()\n\n const handleChange = (event: ChangeEvent<HTMLSelectElement>) => {\n if (isControlled) {\n event.preventDefault()\n onValueChange?.(event.target.value)\n } else {\n setValue(event.target.value)\n }\n }\n\n return (\n <select\n data-spark-component=\"select-items\"\n ref={ref}\n disabled={disabled || readOnly}\n name={name}\n required={required}\n aria-labelledby={fieldLabelId}\n {...(ariaLabel && { 'aria-label': ariaLabel })}\n className={styles({ className, state, disabled, readOnly })}\n value={selectedItem?.value}\n onChange={handleChange}\n id={fieldId}\n {...rest}\n >\n {children}\n </select>\n )\n}\n\nItems.displayName = 'Select.Items'\n","import { useEffect } from 'react'\n\nimport { useSelectGroupContext } from './SelectItemsGroupContext'\n\ninterface LabelProps {\n children: string\n}\n\n/**\n * A label for a group of select items. Renders a <div> element.\n */\n\nexport const Label = ({ children }: LabelProps) => {\n const { setGroupLabel } = useSelectGroupContext()\n\n useEffect(() => {\n setGroupLabel(children)\n }, [children])\n\n return null\n}\n\nLabel.displayName = 'Select.Label'\n","import { ReactElement } from 'react'\n\nimport { Icon } from '../icon'\n\n/**\n * An icon displayed at the start of the select trigger. Renders a <span> element.\n */\n\nexport const LeadingIcon = ({ children }: { children: ReactElement }) => {\n return (\n <Icon size={'sm'} className=\"shrink-0\">\n {children}\n </Icon>\n )\n}\n\nLeadingIcon.displayName = 'Select.LeadingIcon'\n","import { type Ref, useEffect } from 'react'\n\nimport { useSelectContext } from './SelectContext'\n\nexport interface PlaceholderProps {\n disabled?: boolean\n children: string\n ref?: Ref<HTMLOptionElement>\n}\n\n/**\n * Placeholder text shown when no item is selected. Renders a <span> element.\n */\n\nexport const Placeholder = ({\n disabled = false,\n children,\n ref: forwardedRef,\n}: PlaceholderProps) => {\n const { setPlaceholder } = useSelectContext()\n\n useEffect(() => {\n setPlaceholder(children)\n }, [children])\n\n return (\n <option\n data-spark-component=\"select-placeholder\"\n ref={forwardedRef}\n key=\"placeholder\"\n value=\"\"\n disabled={disabled}\n >\n {children}\n </option>\n )\n}\n\nPlaceholder.displayName = 'Select.Placeholder'\n","import { cva } from 'class-variance-authority'\n\nexport const styles = cva(\n [\n 'relative flex w-full items-center justify-between',\n 'min-h-sz-44 rounded-input px-lg',\n 'text-body-1',\n // outline styles\n 'ring-1 outline-hidden ring-inset focus-within:ring-focus',\n ],\n {\n variants: {\n state: {\n undefined: 'ring-outline',\n error: 'ring-error',\n alert: 'ring-alert',\n success: 'ring-success',\n },\n disabled: {\n false: 'focus-within:ring-2',\n },\n readOnly: {\n true: '',\n },\n },\n compoundVariants: [\n {\n readOnly: false,\n disabled: false,\n class: 'bg-surface text-on-surface',\n },\n {\n readOnly: true,\n class: 'bg-on-surface/dim-5 text-on-surface cursor-default',\n },\n {\n disabled: true,\n class: ['bg-on-surface/dim-5 text-on-surface/dim-3', 'cursor-not-allowed'],\n },\n {\n disabled: false,\n state: undefined,\n class: 'default:hover:ring-outline-high',\n },\n ],\n }\n)\n","import { ArrowHorizontalDown } from '@spark-ui/icons/ArrowHorizontalDown'\nimport { ReactNode, type Ref, useEffect } from 'react'\n\nimport { Icon } from '../icon'\nimport { useSelectContext } from './SelectContext'\nimport { styles } from './SelectTrigger.styles'\n\ninterface TriggerProps {\n 'aria-label'?: string\n children: ReactNode\n className?: string\n ref?: Ref<HTMLDivElement>\n}\n\n/**\n * This trigger acts as a fake button for the `select` tag.\n * It is not interactive.\n */\n/**\n * The trigger button for the select dropdown. Renders a <button> element.\n */\n\nexport const Trigger = ({\n 'aria-label': ariaLabel,\n children,\n className,\n ref: forwardedRef,\n}: TriggerProps) => {\n const { disabled, readOnly, state, setAriaLabel, itemsComponent } = useSelectContext()\n\n useEffect(() => {\n if (ariaLabel) {\n setAriaLabel(ariaLabel)\n }\n }, [ariaLabel])\n\n return (\n <div\n data-spark-component=\"select-trigger\"\n ref={forwardedRef}\n className={styles({ className, state, disabled, readOnly })}\n >\n <span className=\"gap-md flex items-center justify-start\">{children}</span>\n\n <Icon className=\"ml-md shrink-0\" size=\"sm\">\n <ArrowHorizontalDown />\n </Icon>\n\n {itemsComponent}\n </div>\n )\n}\n\nTrigger.displayName = 'Select.Trigger'\n","import { cx } from 'class-variance-authority'\nimport { ReactNode, type Ref } from 'react'\n\nimport { useSelectContext } from './SelectContext'\n\nexport interface ValueProps {\n children?: ReactNode\n className?: string\n /**\n * Optional placeholder value for the trigger.\n * If not specified, the value inside `Select.Placeholder` item will be used.\n */\n placeholder?: string\n ref?: Ref<HTMLSpanElement>\n}\n\n/**\n * Displays the selected value in the select trigger. Renders a <span> element.\n */\n\nexport const Value = ({\n children,\n className,\n placeholder: customPlaceholder,\n ref: forwardedRef,\n}: ValueProps) => {\n const { selectedItem, placeholder, disabled } = useSelectContext()\n\n const isPlaceholderSelected = selectedItem?.value == null\n const valuePlaceholder = customPlaceholder || placeholder\n\n return (\n <span\n role=\"presentation\"\n data-spark-component=\"select-value\"\n ref={forwardedRef}\n className={cx('flex shrink items-center text-left', className)}\n >\n <span\n className={cx(\n 'line-clamp-1 flex-1 overflow-hidden break-all text-ellipsis',\n isPlaceholderSelected && !disabled && 'text-on-surface/dim-1'\n )}\n >\n {isPlaceholderSelected ? valuePlaceholder : children || selectedItem?.text}\n </span>\n </span>\n )\n}\n\nValue.displayName = 'Select.Value'\n","import { Select as Root } from './Select'\nimport { SelectProvider, useSelectContext } from './SelectContext'\nimport { Group } from './SelectGroup'\nimport { Item } from './SelectItem'\nimport { Items } from './SelectItems'\nimport { Label } from './SelectLabel'\nimport { LeadingIcon } from './SelectLeadingIcon'\nimport { Placeholder } from './SelectPlaceholder'\nimport { Trigger } from './SelectTrigger'\nimport { Value } from './SelectValue'\n\nexport { useSelectContext, SelectProvider }\n\n/**\n * A dropdown component that allows users to choose one option from a list of choices.\n */\nexport const Select: typeof Root & {\n Group: typeof Group\n Item: typeof Item\n Items: typeof Items\n Placeholder: typeof Placeholder\n Label: typeof Label\n Trigger: typeof Trigger\n Value: typeof Value\n LeadingIcon: typeof LeadingIcon\n} = Object.assign(Root, {\n Group,\n Item,\n Items,\n Placeholder,\n Label,\n Trigger,\n Value,\n LeadingIcon,\n})\n\nSelect.displayName = 'Select'\nGroup.displayName = 'Select.Group'\nItems.displayName = 'Select.Items'\nItem.displayName = 'Select.Item'\nPlaceholder.displayName = 'Select.Placeholder'\nLabel.displayName = 'Select.Label'\nTrigger.displayName = 'Select.Trigger'\nValue.displayName = 'Select.Value'\nLeadingIcon.displayName = 'Select.LeadingIcon'\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { t as e } from "../icon-D05Uqh8_.mjs";
1
+ import { t as e } from "../icon-Bs-39J-H.mjs";
2
2
  import { cva as t, cx as n } from "class-variance-authority";
3
3
  import { Children as r, createContext as i, isValidElement as a, useContext as o, useEffect as s, useId as c, useState as l } from "react";
4
4
  import { jsx as u, jsxs as d } from "react/jsx-runtime";