@sixthshift/design-system 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (518) hide show
  1. package/README.md +116 -12
  2. package/dist/components/Badge/Badge.d.ts +64 -9
  3. package/dist/components/Badge/Badge.d.ts.map +1 -1
  4. package/dist/components/Badge/Badge.js +54 -46
  5. package/dist/components/Badge/Badge.js.map +1 -1
  6. package/dist/components/Badge/index.d.ts +1 -1
  7. package/dist/components/Badge/index.d.ts.map +1 -1
  8. package/dist/components/Badge/index.js +1 -1
  9. package/dist/components/Badge/index.js.map +1 -1
  10. package/dist/components/BarChart/BarChart.d.ts +12 -0
  11. package/dist/components/BarChart/BarChart.d.ts.map +1 -1
  12. package/dist/components/BarChart/BarChart.js +12 -0
  13. package/dist/components/BarChart/BarChart.js.map +1 -1
  14. package/dist/components/Button/Button.d.ts +85 -6
  15. package/dist/components/Button/Button.d.ts.map +1 -1
  16. package/dist/components/Button/Button.js +97 -106
  17. package/dist/components/Button/Button.js.map +1 -1
  18. package/dist/components/Button/index.d.ts +1 -1
  19. package/dist/components/Button/index.d.ts.map +1 -1
  20. package/dist/components/Button/index.js +1 -1
  21. package/dist/components/Button/index.js.map +1 -1
  22. package/dist/components/Calendar/Calendar.d.ts +18 -4
  23. package/dist/components/Calendar/Calendar.d.ts.map +1 -1
  24. package/dist/components/Calendar/Calendar.js +19 -5
  25. package/dist/components/Calendar/Calendar.js.map +1 -1
  26. package/dist/components/Calendar/CalendarView.d.ts.map +1 -1
  27. package/dist/components/Calendar/CalendarView.js +10 -2
  28. package/dist/components/Calendar/CalendarView.js.map +1 -1
  29. package/dist/components/Calendar/calendar.types.d.ts +9 -0
  30. package/dist/components/Calendar/calendar.types.d.ts.map +1 -1
  31. package/dist/components/Card/Card.d.ts +61 -2
  32. package/dist/components/Card/Card.d.ts.map +1 -1
  33. package/dist/components/Card/Card.js +99 -3
  34. package/dist/components/Card/Card.js.map +1 -1
  35. package/dist/components/Card/index.d.ts +1 -1
  36. package/dist/components/Card/index.d.ts.map +1 -1
  37. package/dist/components/Card/index.js +1 -1
  38. package/dist/components/Card/index.js.map +1 -1
  39. package/dist/components/Checkbox/Checkbox.d.ts +21 -0
  40. package/dist/components/Checkbox/Checkbox.d.ts.map +1 -1
  41. package/dist/components/Checkbox/Checkbox.js +22 -1
  42. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  43. package/dist/components/CheckboxGroup/CheckboxGroup.d.ts +22 -0
  44. package/dist/components/CheckboxGroup/CheckboxGroup.d.ts.map +1 -1
  45. package/dist/components/CheckboxGroup/CheckboxGroup.js +22 -0
  46. package/dist/components/CheckboxGroup/CheckboxGroup.js.map +1 -1
  47. package/dist/components/Code/Editor/Editor.js +2 -2
  48. package/dist/components/Code/Editor/Editor.js.map +1 -1
  49. package/dist/components/Code/Editor/themes.d.ts +2 -2
  50. package/dist/components/Code/Editor/themes.d.ts.map +1 -1
  51. package/dist/components/Code/Editor/themes.js +50 -32
  52. package/dist/components/Code/Editor/themes.js.map +1 -1
  53. package/dist/components/Code/ValidationStatus/ValidationStatus.d.ts.map +1 -1
  54. package/dist/components/Code/ValidationStatus/ValidationStatus.js.map +1 -1
  55. package/dist/components/DatePicker/DatePicker.d.ts +43 -0
  56. package/dist/components/DatePicker/DatePicker.d.ts.map +1 -1
  57. package/dist/components/DatePicker/DatePicker.js +166 -4
  58. package/dist/components/DatePicker/DatePicker.js.map +1 -1
  59. package/dist/components/DatePicker/DateSegments.d.ts +60 -0
  60. package/dist/components/DatePicker/DateSegments.d.ts.map +1 -0
  61. package/dist/components/DatePicker/DateSegments.js +80 -0
  62. package/dist/components/DatePicker/DateSegments.js.map +1 -0
  63. package/dist/components/DatePicker/datepicker.types.d.ts +19 -1
  64. package/dist/components/DatePicker/datepicker.types.d.ts.map +1 -1
  65. package/dist/components/DatePicker/index.d.ts +1 -1
  66. package/dist/components/DatePicker/index.d.ts.map +1 -1
  67. package/dist/components/DateRangePicker/DateRangePicker.d.ts +20 -2
  68. package/dist/components/DateRangePicker/DateRangePicker.d.ts.map +1 -1
  69. package/dist/components/DateRangePicker/DateRangePicker.js +22 -4
  70. package/dist/components/DateRangePicker/DateRangePicker.js.map +1 -1
  71. package/dist/components/DateRangePicker/daterangepicker.types.d.ts +6 -1
  72. package/dist/components/DateRangePicker/daterangepicker.types.d.ts.map +1 -1
  73. package/dist/components/DateTimePicker/DateTimePicker.d.ts +30 -2
  74. package/dist/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
  75. package/dist/components/DateTimePicker/DateTimePicker.js +105 -32
  76. package/dist/components/DateTimePicker/DateTimePicker.js.map +1 -1
  77. package/dist/components/DateTimePicker/DateTimeSegments.d.ts +41 -0
  78. package/dist/components/DateTimePicker/DateTimeSegments.d.ts.map +1 -0
  79. package/dist/components/DateTimePicker/DateTimeSegments.js +67 -0
  80. package/dist/components/DateTimePicker/DateTimeSegments.js.map +1 -0
  81. package/dist/components/DateTimePicker/datetimepicker.types.d.ts +6 -1
  82. package/dist/components/DateTimePicker/datetimepicker.types.d.ts.map +1 -1
  83. package/dist/components/DateTimeRangePicker/DateTimeRangePicker.d.ts +28 -2
  84. package/dist/components/DateTimeRangePicker/DateTimeRangePicker.d.ts.map +1 -1
  85. package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js +136 -58
  86. package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js.map +1 -1
  87. package/dist/components/DateTimeRangePicker/datetimerangepicker.types.d.ts +6 -1
  88. package/dist/components/DateTimeRangePicker/datetimerangepicker.types.d.ts.map +1 -1
  89. package/dist/components/Field/Field.d.ts +15 -17
  90. package/dist/components/Field/Field.d.ts.map +1 -1
  91. package/dist/components/Field/Field.js +18 -3
  92. package/dist/components/Field/Field.js.map +1 -1
  93. package/dist/components/FormField/FormField.d.ts +15 -0
  94. package/dist/components/FormField/FormField.d.ts.map +1 -1
  95. package/dist/components/FormField/FormField.js +15 -0
  96. package/dist/components/FormField/FormField.js.map +1 -1
  97. package/dist/components/HoverCard/HoverCard.d.ts +6 -0
  98. package/dist/components/HoverCard/HoverCard.d.ts.map +1 -1
  99. package/dist/components/HoverCard/HoverCard.js +6 -0
  100. package/dist/components/HoverCard/HoverCard.js.map +1 -1
  101. package/dist/components/Input/Input.d.ts +15 -0
  102. package/dist/components/Input/Input.d.ts.map +1 -1
  103. package/dist/components/Input/Input.js +18 -3
  104. package/dist/components/Input/Input.js.map +1 -1
  105. package/dist/components/LineChart/LineChart.d.ts +17 -0
  106. package/dist/components/LineChart/LineChart.d.ts.map +1 -1
  107. package/dist/components/LineChart/LineChart.js +17 -0
  108. package/dist/components/LineChart/LineChart.js.map +1 -1
  109. package/dist/components/Markdown/Markdown.d.ts +17 -0
  110. package/dist/components/Markdown/Markdown.d.ts.map +1 -1
  111. package/dist/components/Markdown/Markdown.js +17 -0
  112. package/dist/components/Markdown/Markdown.js.map +1 -1
  113. package/dist/components/Message/Message.d.ts +49 -4
  114. package/dist/components/Message/Message.d.ts.map +1 -1
  115. package/dist/components/Message/Message.js +53 -17
  116. package/dist/components/Message/Message.js.map +1 -1
  117. package/dist/components/Message/index.d.ts +1 -1
  118. package/dist/components/Message/index.d.ts.map +1 -1
  119. package/dist/components/Modal/Modal.d.ts +36 -0
  120. package/dist/components/Modal/Modal.d.ts.map +1 -1
  121. package/dist/components/Modal/Modal.js +37 -1
  122. package/dist/components/Modal/Modal.js.map +1 -1
  123. package/dist/components/Modal/components/ModalHeader.js +1 -1
  124. package/dist/components/Modal/components/ModalHeader.js.map +1 -1
  125. package/dist/components/Popover/Popover.d.ts +29 -0
  126. package/dist/components/Popover/Popover.d.ts.map +1 -1
  127. package/dist/components/Popover/Popover.js +29 -0
  128. package/dist/components/Popover/Popover.js.map +1 -1
  129. package/dist/components/ProgressBar/ProgressBar.d.ts +14 -0
  130. package/dist/components/ProgressBar/ProgressBar.d.ts.map +1 -1
  131. package/dist/components/ProgressBar/ProgressBar.js +14 -0
  132. package/dist/components/ProgressBar/ProgressBar.js.map +1 -1
  133. package/dist/components/RadioButton/RadioButton.d.ts +26 -0
  134. package/dist/components/RadioButton/RadioButton.d.ts.map +1 -1
  135. package/dist/components/RadioButton/RadioButton.js +35 -1
  136. package/dist/components/RadioButton/RadioButton.js.map +1 -1
  137. package/dist/components/RadioButtonGroup/RadioButtonGroup.d.ts +26 -0
  138. package/dist/components/RadioButtonGroup/RadioButtonGroup.d.ts.map +1 -1
  139. package/dist/components/RadioButtonGroup/RadioButtonGroup.js +33 -0
  140. package/dist/components/RadioButtonGroup/RadioButtonGroup.js.map +1 -1
  141. package/dist/components/SearchInput/SearchInput.d.ts +10 -0
  142. package/dist/components/SearchInput/SearchInput.d.ts.map +1 -1
  143. package/dist/components/SearchInput/SearchInput.js +12 -1
  144. package/dist/components/SearchInput/SearchInput.js.map +1 -1
  145. package/dist/components/Select/Select.d.ts +4 -5
  146. package/dist/components/Select/Select.d.ts.map +1 -1
  147. package/dist/components/Select/Select.js +4 -5
  148. package/dist/components/Select/Select.js.map +1 -1
  149. package/dist/components/Select/SelectDropdown.d.ts.map +1 -1
  150. package/dist/components/Select/SelectDropdown.js +4 -3
  151. package/dist/components/Select/SelectDropdown.js.map +1 -1
  152. package/dist/components/Select/SelectTrigger.js +2 -2
  153. package/dist/components/Select/SelectTrigger.js.map +1 -1
  154. package/dist/components/Separator/Separator.d.ts +10 -0
  155. package/dist/components/Separator/Separator.d.ts.map +1 -1
  156. package/dist/components/Separator/Separator.js +10 -0
  157. package/dist/components/Separator/Separator.js.map +1 -1
  158. package/dist/components/Sheet/Sheet.d.ts +6 -0
  159. package/dist/components/Sheet/Sheet.d.ts.map +1 -1
  160. package/dist/components/Sheet/Sheet.js +7 -1
  161. package/dist/components/Sheet/Sheet.js.map +1 -1
  162. package/dist/components/Sheet/components/SheetFooter.js +1 -1
  163. package/dist/components/Sheet/components/SheetFooter.js.map +1 -1
  164. package/dist/components/Sheet/components/SheetHeader.js +1 -1
  165. package/dist/components/Sheet/components/SheetHeader.js.map +1 -1
  166. package/dist/components/Sparkline/Sparkline.d.ts +11 -0
  167. package/dist/components/Sparkline/Sparkline.d.ts.map +1 -1
  168. package/dist/components/Sparkline/Sparkline.js +11 -0
  169. package/dist/components/Sparkline/Sparkline.js.map +1 -1
  170. package/dist/components/Spinner/Spinner.d.ts +27 -2
  171. package/dist/components/Spinner/Spinner.d.ts.map +1 -1
  172. package/dist/components/Spinner/Spinner.js +28 -3
  173. package/dist/components/Spinner/Spinner.js.map +1 -1
  174. package/dist/components/Switch/Switch.d.ts +18 -0
  175. package/dist/components/Switch/Switch.d.ts.map +1 -1
  176. package/dist/components/Switch/Switch.js +19 -1
  177. package/dist/components/Switch/Switch.js.map +1 -1
  178. package/dist/components/Tabs/Tabs.d.ts +6 -0
  179. package/dist/components/Tabs/Tabs.d.ts.map +1 -1
  180. package/dist/components/Tabs/Tabs.js +6 -0
  181. package/dist/components/Tabs/Tabs.js.map +1 -1
  182. package/dist/components/Tabs/components/TabsTrigger.d.ts.map +1 -1
  183. package/dist/components/Tabs/components/TabsTrigger.js +22 -6
  184. package/dist/components/Tabs/components/TabsTrigger.js.map +1 -1
  185. package/dist/components/TagChip/TagChip.d.ts +14 -12
  186. package/dist/components/TagChip/TagChip.d.ts.map +1 -1
  187. package/dist/components/TagChip/TagChip.js +14 -1
  188. package/dist/components/TagChip/TagChip.js.map +1 -1
  189. package/dist/components/TagInput/TagInput.d.ts +11 -8
  190. package/dist/components/TagInput/TagInput.d.ts.map +1 -1
  191. package/dist/components/TagInput/TagInput.js +11 -0
  192. package/dist/components/TagInput/TagInput.js.map +1 -1
  193. package/dist/components/Text/Text.d.ts +9 -0
  194. package/dist/components/Text/Text.d.ts.map +1 -1
  195. package/dist/components/Text/Text.js +9 -0
  196. package/dist/components/Text/Text.js.map +1 -1
  197. package/dist/components/TextInline/TextInline.d.ts +15 -4
  198. package/dist/components/TextInline/TextInline.d.ts.map +1 -1
  199. package/dist/components/TextInline/TextInline.js +11 -0
  200. package/dist/components/TextInline/TextInline.js.map +1 -1
  201. package/dist/components/Textarea/Textarea.d.ts +12 -0
  202. package/dist/components/Textarea/Textarea.d.ts.map +1 -1
  203. package/dist/components/Textarea/Textarea.js +13 -1
  204. package/dist/components/Textarea/Textarea.js.map +1 -1
  205. package/dist/components/TimePicker/TimeColumn.d.ts +10 -1
  206. package/dist/components/TimePicker/TimeColumn.d.ts.map +1 -1
  207. package/dist/components/TimePicker/TimeColumn.js +8 -1
  208. package/dist/components/TimePicker/TimeColumn.js.map +1 -1
  209. package/dist/components/TimePicker/TimePicker.d.ts +32 -0
  210. package/dist/components/TimePicker/TimePicker.d.ts.map +1 -1
  211. package/dist/components/TimePicker/TimePicker.js +93 -6
  212. package/dist/components/TimePicker/TimePicker.js.map +1 -1
  213. package/dist/components/TimePicker/TimeSegments.d.ts +50 -0
  214. package/dist/components/TimePicker/TimeSegments.d.ts.map +1 -0
  215. package/dist/components/TimePicker/TimeSegments.js +93 -0
  216. package/dist/components/TimePicker/TimeSegments.js.map +1 -0
  217. package/dist/components/TimePicker/timepicker.hooks.d.ts +0 -4
  218. package/dist/components/TimePicker/timepicker.hooks.d.ts.map +1 -1
  219. package/dist/components/TimePicker/timepicker.hooks.js +1 -23
  220. package/dist/components/TimePicker/timepicker.hooks.js.map +1 -1
  221. package/dist/components/TimePicker/timepicker.types.d.ts +0 -2
  222. package/dist/components/TimePicker/timepicker.types.d.ts.map +1 -1
  223. package/dist/components/Toast/Toast.d.ts +27 -0
  224. package/dist/components/Toast/Toast.d.ts.map +1 -1
  225. package/dist/components/Toast/Toast.js +28 -1
  226. package/dist/components/Toast/Toast.js.map +1 -1
  227. package/dist/components/Toggle/Toggle.d.ts +31 -12
  228. package/dist/components/Toggle/Toggle.d.ts.map +1 -1
  229. package/dist/components/Toggle/Toggle.js +19 -31
  230. package/dist/components/Toggle/Toggle.js.map +1 -1
  231. package/dist/components/Toggle/index.d.ts +1 -1
  232. package/dist/components/Toggle/index.d.ts.map +1 -1
  233. package/dist/components/Toggle/index.js +1 -1
  234. package/dist/components/Toggle/index.js.map +1 -1
  235. package/dist/components/ToggleGroup/ToggleGroup.d.ts +17 -0
  236. package/dist/components/ToggleGroup/ToggleGroup.d.ts.map +1 -1
  237. package/dist/components/ToggleGroup/ToggleGroup.js +17 -0
  238. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
  239. package/dist/components/ToggleGroup/ToggleGroupItem.d.ts +1 -1
  240. package/dist/components/ToggleGroup/ToggleGroupItem.d.ts.map +1 -1
  241. package/dist/components/ToggleGroup/ToggleGroupItem.js +7 -6
  242. package/dist/components/ToggleGroup/ToggleGroupItem.js.map +1 -1
  243. package/dist/components/ToggleGroup/ToggleGroupMultiple.d.ts +4 -3
  244. package/dist/components/ToggleGroup/ToggleGroupMultiple.d.ts.map +1 -1
  245. package/dist/components/ToggleGroup/ToggleGroupMultiple.js +2 -2
  246. package/dist/components/ToggleGroup/ToggleGroupMultiple.js.map +1 -1
  247. package/dist/components/ToggleGroup/ToggleGroupSingle.d.ts +4 -3
  248. package/dist/components/ToggleGroup/ToggleGroupSingle.d.ts.map +1 -1
  249. package/dist/components/ToggleGroup/ToggleGroupSingle.js +2 -2
  250. package/dist/components/ToggleGroup/ToggleGroupSingle.js.map +1 -1
  251. package/dist/components/ToggleGroup/toggleGroup.types.d.ts +12 -3
  252. package/dist/components/ToggleGroup/toggleGroup.types.d.ts.map +1 -1
  253. package/dist/components/Tooltip/Tooltip.d.ts +6 -0
  254. package/dist/components/Tooltip/Tooltip.d.ts.map +1 -1
  255. package/dist/components/Tooltip/Tooltip.js +6 -0
  256. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  257. package/dist/date-time/index.d.ts.map +1 -1
  258. package/dist/date-time/index.js +0 -1
  259. package/dist/date-time/index.js.map +1 -1
  260. package/dist/internal/PickerField.d.ts +45 -0
  261. package/dist/internal/PickerField.d.ts.map +1 -0
  262. package/dist/internal/PickerField.js +24 -0
  263. package/dist/internal/PickerField.js.map +1 -0
  264. package/dist/internal/SegmentedField.d.ts +98 -0
  265. package/dist/internal/SegmentedField.d.ts.map +1 -0
  266. package/dist/internal/SegmentedField.js +315 -0
  267. package/dist/internal/SegmentedField.js.map +1 -0
  268. package/dist/internal/index.d.ts +2 -0
  269. package/dist/internal/index.d.ts.map +1 -1
  270. package/dist/internal/index.js +2 -0
  271. package/dist/internal/index.js.map +1 -1
  272. package/dist/lib/contexts/OverlayContext/OverlayContext.d.ts +2 -2
  273. package/dist/lib/contexts/OverlayContext/OverlayContext.d.ts.map +1 -1
  274. package/dist/lib/contexts/OverlayContext/OverlayContext.js +17 -6
  275. package/dist/lib/contexts/OverlayContext/OverlayContext.js.map +1 -1
  276. package/dist/theme/index.d.ts +2 -2
  277. package/dist/theme/index.d.ts.map +1 -1
  278. package/dist/theme/index.js +1 -1
  279. package/dist/theme/index.js.map +1 -1
  280. package/dist/theme/schema.d.ts +0 -22
  281. package/dist/theme/schema.d.ts.map +1 -1
  282. package/dist/theme/schema.js +0 -9
  283. package/dist/theme/schema.js.map +1 -1
  284. package/package.json +7 -48
  285. package/src/components/Badge/Badge.tsx +85 -48
  286. package/src/components/Badge/index.ts +10 -1
  287. package/src/components/BarChart/BarChart.tsx +12 -0
  288. package/src/components/Button/Button.tsx +129 -106
  289. package/src/components/Button/index.ts +10 -1
  290. package/src/components/Calendar/Calendar.tsx +29 -16
  291. package/src/components/Calendar/CalendarView.tsx +10 -1
  292. package/src/components/Calendar/calendar.types.ts +10 -0
  293. package/src/components/Card/Card.tsx +114 -15
  294. package/src/components/Card/index.ts +1 -1
  295. package/src/components/Checkbox/Checkbox.tsx +22 -2
  296. package/src/components/CheckboxGroup/CheckboxGroup.tsx +22 -0
  297. package/src/components/Code/Editor/Editor.tsx +2 -2
  298. package/src/components/Code/Editor/themes.ts +50 -32
  299. package/src/components/Code/ValidationStatus/ValidationStatus.tsx +6 -3
  300. package/src/components/DatePicker/DatePicker.tsx +279 -28
  301. package/src/components/DatePicker/DateSegments.tsx +125 -0
  302. package/src/components/DatePicker/datepicker.types.ts +21 -1
  303. package/src/components/DatePicker/index.ts +8 -1
  304. package/src/components/DateRangePicker/DateRangePicker.tsx +22 -4
  305. package/src/components/DateRangePicker/daterangepicker.types.ts +7 -1
  306. package/src/components/DateTimePicker/DateTimePicker.tsx +143 -70
  307. package/src/components/DateTimePicker/DateTimeSegments.tsx +113 -0
  308. package/src/components/DateTimePicker/datetimepicker.types.ts +7 -1
  309. package/src/components/DateTimeRangePicker/DateTimeRangePicker.tsx +195 -104
  310. package/src/components/DateTimeRangePicker/datetimerangepicker.types.ts +7 -1
  311. package/src/components/Field/Field.tsx +20 -22
  312. package/src/components/FormField/FormField.tsx +15 -0
  313. package/src/components/HoverCard/HoverCard.tsx +6 -0
  314. package/src/components/Input/Input.tsx +19 -4
  315. package/src/components/LineChart/LineChart.tsx +17 -0
  316. package/src/components/Markdown/Markdown.tsx +17 -0
  317. package/src/components/Message/Message.tsx +73 -23
  318. package/src/components/Message/index.ts +1 -1
  319. package/src/components/Modal/Modal.tsx +42 -3
  320. package/src/components/Modal/components/ModalHeader.tsx +1 -1
  321. package/src/components/Popover/Popover.tsx +29 -0
  322. package/src/components/ProgressBar/ProgressBar.tsx +14 -0
  323. package/src/components/RadioButton/RadioButton.tsx +34 -3
  324. package/src/components/RadioButtonGroup/RadioButtonGroup.tsx +32 -0
  325. package/src/components/SearchInput/SearchInput.tsx +13 -2
  326. package/src/components/Select/Select.tsx +4 -5
  327. package/src/components/Select/SelectDropdown.tsx +13 -9
  328. package/src/components/Select/SelectTrigger.tsx +7 -7
  329. package/src/components/Separator/Separator.tsx +10 -0
  330. package/src/components/Sheet/Sheet.tsx +8 -2
  331. package/src/components/Sheet/components/SheetFooter.tsx +1 -1
  332. package/src/components/Sheet/components/SheetHeader.tsx +2 -2
  333. package/src/components/Sparkline/Sparkline.tsx +11 -0
  334. package/src/components/Spinner/Spinner.tsx +28 -3
  335. package/src/components/Switch/Switch.tsx +27 -4
  336. package/src/components/Tabs/Tabs.tsx +6 -0
  337. package/src/components/Tabs/components/TabsTrigger.tsx +22 -5
  338. package/src/components/TagChip/TagChip.tsx +18 -16
  339. package/src/components/TagInput/TagInput.tsx +11 -8
  340. package/src/components/Text/Text.tsx +9 -0
  341. package/src/components/TextInline/TextInline.tsx +11 -0
  342. package/src/components/Textarea/Textarea.tsx +13 -1
  343. package/src/components/TimePicker/TimeColumn.tsx +24 -1
  344. package/src/components/TimePicker/TimePicker.tsx +127 -39
  345. package/src/components/TimePicker/TimeSegments.tsx +131 -0
  346. package/src/components/TimePicker/timepicker.hooks.ts +1 -24
  347. package/src/components/TimePicker/timepicker.types.ts +0 -3
  348. package/src/components/Toast/Toast.tsx +28 -1
  349. package/src/components/Toggle/Toggle.tsx +34 -32
  350. package/src/components/Toggle/index.ts +1 -1
  351. package/src/components/ToggleGroup/ToggleGroup.tsx +17 -0
  352. package/src/components/ToggleGroup/ToggleGroupItem.tsx +13 -8
  353. package/src/components/ToggleGroup/ToggleGroupMultiple.tsx +3 -1
  354. package/src/components/ToggleGroup/ToggleGroupSingle.tsx +3 -1
  355. package/src/components/ToggleGroup/toggleGroup.types.ts +12 -3
  356. package/src/components/Tooltip/Tooltip.tsx +6 -0
  357. package/src/date-time/index.ts +0 -1
  358. package/src/internal/PickerField.tsx +90 -0
  359. package/src/internal/SegmentedField.tsx +445 -0
  360. package/src/internal/index.ts +2 -0
  361. package/src/lib/contexts/OverlayContext/OverlayContext.tsx +21 -10
  362. package/src/theme/index.ts +0 -6
  363. package/src/theme/recipes/badge.css +140 -0
  364. package/src/theme/recipes/button.css +167 -0
  365. package/src/theme/recipes/card.css +31 -0
  366. package/src/theme/recipes/checkbox.css +44 -0
  367. package/src/theme/recipes/field.css +14 -0
  368. package/src/theme/recipes/index.css +28 -0
  369. package/src/theme/recipes/input.css +29 -0
  370. package/src/theme/recipes/message.css +47 -0
  371. package/src/theme/recipes/modal.css +31 -0
  372. package/src/theme/recipes/radio-button.css +42 -0
  373. package/src/theme/recipes/search-input.css +16 -0
  374. package/src/theme/recipes/select.css +105 -0
  375. package/src/theme/recipes/sheet.css +34 -0
  376. package/src/theme/recipes/spinner.css +17 -0
  377. package/src/theme/recipes/switch.css +39 -0
  378. package/src/theme/recipes/tabs.css +41 -0
  379. package/src/theme/recipes/tag-chip.css +27 -0
  380. package/src/theme/recipes/textarea.css +20 -0
  381. package/src/theme/recipes/toggle.css +92 -0
  382. package/src/theme/schema.ts +0 -27
  383. package/src/theme/tokens.css +26 -9
  384. package/dist/components/Avatar/Avatar.d.ts +0 -6
  385. package/dist/components/Avatar/Avatar.d.ts.map +0 -1
  386. package/dist/components/Avatar/Avatar.js +0 -11
  387. package/dist/components/Avatar/Avatar.js.map +0 -1
  388. package/dist/components/Avatar/index.d.ts +0 -2
  389. package/dist/components/Avatar/index.d.ts.map +0 -1
  390. package/dist/components/Avatar/index.js +0 -2
  391. package/dist/components/Avatar/index.js.map +0 -1
  392. package/dist/components/Breadcrumb/Breadcrumb.d.ts +0 -12
  393. package/dist/components/Breadcrumb/Breadcrumb.d.ts.map +0 -1
  394. package/dist/components/Breadcrumb/Breadcrumb.js +0 -19
  395. package/dist/components/Breadcrumb/Breadcrumb.js.map +0 -1
  396. package/dist/components/Breadcrumb/index.d.ts +0 -2
  397. package/dist/components/Breadcrumb/index.d.ts.map +0 -1
  398. package/dist/components/Breadcrumb/index.js +0 -2
  399. package/dist/components/Breadcrumb/index.js.map +0 -1
  400. package/dist/components/ColorDot/ColorDot.d.ts +0 -19
  401. package/dist/components/ColorDot/ColorDot.d.ts.map +0 -1
  402. package/dist/components/ColorDot/ColorDot.js +0 -36
  403. package/dist/components/ColorDot/ColorDot.js.map +0 -1
  404. package/dist/components/ColorDot/index.d.ts +0 -2
  405. package/dist/components/ColorDot/index.d.ts.map +0 -1
  406. package/dist/components/ColorDot/index.js +0 -2
  407. package/dist/components/ColorDot/index.js.map +0 -1
  408. package/dist/components/EmptyState/EmptyState.d.ts +0 -26
  409. package/dist/components/EmptyState/EmptyState.d.ts.map +0 -1
  410. package/dist/components/EmptyState/EmptyState.js +0 -9
  411. package/dist/components/EmptyState/EmptyState.js.map +0 -1
  412. package/dist/components/EmptyState/index.d.ts +0 -2
  413. package/dist/components/EmptyState/index.d.ts.map +0 -1
  414. package/dist/components/EmptyState/index.js +0 -2
  415. package/dist/components/EmptyState/index.js.map +0 -1
  416. package/dist/components/HeatMap/HeatMapCalendar.d.ts +0 -11
  417. package/dist/components/HeatMap/HeatMapCalendar.d.ts.map +0 -1
  418. package/dist/components/HeatMap/HeatMapCalendar.js +0 -68
  419. package/dist/components/HeatMap/HeatMapCalendar.js.map +0 -1
  420. package/dist/components/HeatMap/HeatMapMatrix.d.ts +0 -12
  421. package/dist/components/HeatMap/HeatMapMatrix.d.ts.map +0 -1
  422. package/dist/components/HeatMap/HeatMapMatrix.js +0 -35
  423. package/dist/components/HeatMap/HeatMapMatrix.js.map +0 -1
  424. package/dist/components/HeatMap/heat-map.utils.d.ts +0 -10
  425. package/dist/components/HeatMap/heat-map.utils.d.ts.map +0 -1
  426. package/dist/components/HeatMap/heat-map.utils.js +0 -10
  427. package/dist/components/HeatMap/heat-map.utils.js.map +0 -1
  428. package/dist/components/HeatMap/index.d.ts +0 -4
  429. package/dist/components/HeatMap/index.d.ts.map +0 -1
  430. package/dist/components/HeatMap/index.js +0 -3
  431. package/dist/components/HeatMap/index.js.map +0 -1
  432. package/dist/components/MetricList/MetricList.d.ts +0 -9
  433. package/dist/components/MetricList/MetricList.d.ts.map +0 -1
  434. package/dist/components/MetricList/MetricList.js +0 -8
  435. package/dist/components/MetricList/MetricList.js.map +0 -1
  436. package/dist/components/MetricList/MetricRow.d.ts +0 -13
  437. package/dist/components/MetricList/MetricRow.d.ts.map +0 -1
  438. package/dist/components/MetricList/MetricRow.js +0 -13
  439. package/dist/components/MetricList/MetricRow.js.map +0 -1
  440. package/dist/components/MetricList/index.d.ts +0 -3
  441. package/dist/components/MetricList/index.d.ts.map +0 -1
  442. package/dist/components/MetricList/index.js +0 -3
  443. package/dist/components/MetricList/index.js.map +0 -1
  444. package/dist/components/NavBottom/NavBottom.d.ts +0 -16
  445. package/dist/components/NavBottom/NavBottom.d.ts.map +0 -1
  446. package/dist/components/NavBottom/NavBottom.js +0 -21
  447. package/dist/components/NavBottom/NavBottom.js.map +0 -1
  448. package/dist/components/NavBottom/index.d.ts +0 -3
  449. package/dist/components/NavBottom/index.d.ts.map +0 -1
  450. package/dist/components/NavBottom/index.js +0 -2
  451. package/dist/components/NavBottom/index.js.map +0 -1
  452. package/dist/components/NavSide/NavItem.d.ts +0 -9
  453. package/dist/components/NavSide/NavItem.d.ts.map +0 -1
  454. package/dist/components/NavSide/NavItem.js +0 -13
  455. package/dist/components/NavSide/NavItem.js.map +0 -1
  456. package/dist/components/NavSide/NavSide.d.ts +0 -16
  457. package/dist/components/NavSide/NavSide.d.ts.map +0 -1
  458. package/dist/components/NavSide/NavSide.js +0 -12
  459. package/dist/components/NavSide/NavSide.js.map +0 -1
  460. package/dist/components/NavSide/index.d.ts +0 -3
  461. package/dist/components/NavSide/index.d.ts.map +0 -1
  462. package/dist/components/NavSide/index.js +0 -2
  463. package/dist/components/NavSide/index.js.map +0 -1
  464. package/dist/components/NavSide/types.d.ts +0 -38
  465. package/dist/components/NavSide/types.d.ts.map +0 -1
  466. package/dist/components/NavSide/types.js +0 -2
  467. package/dist/components/NavSide/types.js.map +0 -1
  468. package/dist/components/Pagination/Pagination.d.ts +0 -35
  469. package/dist/components/Pagination/Pagination.d.ts.map +0 -1
  470. package/dist/components/Pagination/Pagination.js +0 -26
  471. package/dist/components/Pagination/Pagination.js.map +0 -1
  472. package/dist/components/Pagination/index.d.ts +0 -3
  473. package/dist/components/Pagination/index.d.ts.map +0 -1
  474. package/dist/components/Pagination/index.js +0 -2
  475. package/dist/components/Pagination/index.js.map +0 -1
  476. package/dist/components/Skeleton/Skeleton.d.ts +0 -4
  477. package/dist/components/Skeleton/Skeleton.d.ts.map +0 -1
  478. package/dist/components/Skeleton/Skeleton.js +0 -9
  479. package/dist/components/Skeleton/Skeleton.js.map +0 -1
  480. package/dist/components/Skeleton/index.d.ts +0 -2
  481. package/dist/components/Skeleton/index.d.ts.map +0 -1
  482. package/dist/components/Skeleton/index.js +0 -2
  483. package/dist/components/Skeleton/index.js.map +0 -1
  484. package/dist/components/StatsCard/StatsCard.d.ts +0 -17
  485. package/dist/components/StatsCard/StatsCard.d.ts.map +0 -1
  486. package/dist/components/StatsCard/StatsCard.js +0 -15
  487. package/dist/components/StatsCard/StatsCard.js.map +0 -1
  488. package/dist/components/StatsCard/index.d.ts +0 -2
  489. package/dist/components/StatsCard/index.d.ts.map +0 -1
  490. package/dist/components/StatsCard/index.js +0 -2
  491. package/dist/components/StatsCard/index.js.map +0 -1
  492. package/src/components/Avatar/Avatar.tsx +0 -19
  493. package/src/components/Avatar/index.ts +0 -1
  494. package/src/components/Breadcrumb/Breadcrumb.tsx +0 -47
  495. package/src/components/Breadcrumb/index.ts +0 -5
  496. package/src/components/ColorDot/ColorDot.tsx +0 -52
  497. package/src/components/ColorDot/index.ts +0 -1
  498. package/src/components/EmptyState/EmptyState.tsx +0 -30
  499. package/src/components/EmptyState/index.ts +0 -1
  500. package/src/components/HeatMap/HeatMapCalendar.tsx +0 -127
  501. package/src/components/HeatMap/HeatMapMatrix.tsx +0 -75
  502. package/src/components/HeatMap/heat-map.utils.ts +0 -15
  503. package/src/components/HeatMap/index.ts +0 -3
  504. package/src/components/MetricList/MetricList.tsx +0 -18
  505. package/src/components/MetricList/MetricRow.tsx +0 -30
  506. package/src/components/MetricList/index.ts +0 -2
  507. package/src/components/NavBottom/NavBottom.tsx +0 -55
  508. package/src/components/NavBottom/index.ts +0 -3
  509. package/src/components/NavSide/NavItem.tsx +0 -39
  510. package/src/components/NavSide/NavSide.tsx +0 -42
  511. package/src/components/NavSide/index.ts +0 -2
  512. package/src/components/NavSide/types.ts +0 -36
  513. package/src/components/Pagination/Pagination.tsx +0 -86
  514. package/src/components/Pagination/index.ts +0 -2
  515. package/src/components/Skeleton/Skeleton.tsx +0 -9
  516. package/src/components/Skeleton/index.ts +0 -1
  517. package/src/components/StatsCard/StatsCard.tsx +0 -41
  518. package/src/components/StatsCard/index.ts +0 -1
@@ -0,0 +1,445 @@
1
+ import { cn } from "@sixthshift/design-system/utils";
2
+ import * as React from "react";
3
+ import { useRef, useState } from "react";
4
+
5
+ /**
6
+ * One segment of a segmented field.
7
+ *
8
+ * `numeric` covers everything that is typed as digits — a month, an hour, a
9
+ * year. `choice` covers a segment with a fixed vocabulary, which in practice
10
+ * means AM/PM: it holds the index of an option, is typed by first letter, and
11
+ * announces the word rather than the number.
12
+ */
13
+ export type SegmentSpec =
14
+ | {
15
+ kind?: "numeric";
16
+ /** Key in the values record. */
17
+ name: string;
18
+ /** Accessible name, e.g. `"Month"`. */
19
+ label: string;
20
+ /** Shown when empty, e.g. `"mm"`. */
21
+ placeholder: string;
22
+ /** Width in digits: 2 for a month, 4 for a year. */
23
+ digits: number;
24
+ min: number;
25
+ max: number;
26
+ /** Rendered before this segment, e.g. `"/"` or `":"`. */
27
+ separatorBefore?: string | undefined;
28
+ /** Where an arrow key starts from when the segment is empty. */
29
+ stepFrom?: number | undefined;
30
+ }
31
+ | {
32
+ kind: "choice";
33
+ name: string;
34
+ label: string;
35
+ placeholder: string;
36
+ /** Ordered vocabulary; the segment's value is an index into it. */
37
+ options: string[];
38
+ separatorBefore?: string | undefined;
39
+ stepFrom?: number | undefined;
40
+ };
41
+
42
+ /** Segment values by name. `null` is "nothing typed yet". */
43
+ export type SegmentValues = Record<string, number | null>;
44
+
45
+ export type SegmentedFieldProps<T> = {
46
+ spec: SegmentSpec[];
47
+ /** The value the segments display. `undefined` leaves them empty. */
48
+ value: T | undefined;
49
+ /** Split a value into segments. */
50
+ toSegments: (value: T | undefined) => SegmentValues;
51
+ /** Build a value from a complete set of segments. May normalise (clamp a day). */
52
+ fromSegments: (values: Record<string, number>) => T;
53
+ /** Stable identity for a value — how an echo is told from a real change. */
54
+ keyOf: (value: T) => string;
55
+ /**
56
+ * Fires with a whole value, or `undefined` the moment the segments stop
57
+ * describing one. Never fires a partial value, and never mid-number.
58
+ */
59
+ onChange: (value: T | undefined) => void;
60
+ /** Read a pasted string into segments. Omit to ignore pastes. */
61
+ parsePaste?: ((text: string) => SegmentValues | null) | undefined;
62
+ /** Accessible name for the group. */
63
+ label: string;
64
+ isDisabled?: boolean | undefined;
65
+ isInvalid?: boolean | undefined;
66
+ /** `Alt+ArrowDown` — the combobox convention for "open the popup". */
67
+ onOpenRequest?: (() => void) | undefined;
68
+ /** `Enter` — the caller decides whether that commits, submits or nothing. */
69
+ onSubmit?: (() => void) | undefined;
70
+ id?: string | undefined;
71
+ className?: string | undefined;
72
+ };
73
+
74
+ type Limits = { min: number; max: number; digits: number };
75
+
76
+ function limitsOf(segment: SegmentSpec): Limits {
77
+ if (segment.kind === "choice") {
78
+ // `digits: 0` is what makes a digit roll straight past AM/PM instead of
79
+ // being swallowed by it.
80
+ return { min: 0, max: segment.options.length - 1, digits: 0 };
81
+ }
82
+ return { min: segment.min, max: segment.max, digits: segment.digits };
83
+ }
84
+
85
+ const pad = (value: number, digits: number) => String(value).padStart(digits, "0");
86
+
87
+ function displayOf(segment: SegmentSpec, current: number | null, typed: string): string {
88
+ if (typed !== "") return typed;
89
+ if (current === null) return segment.placeholder;
90
+ if (segment.kind === "choice") return segment.options[current] ?? segment.placeholder;
91
+ return pad(current, segment.digits);
92
+ }
93
+
94
+ /**
95
+ * A typeable field made of spinbutton segments — the shared keyboard model
96
+ * behind every picker's trigger.
97
+ *
98
+ * Segments rather than a text box and a parser: `08/09` cannot be read without
99
+ * knowing the order, and a parser that guesses is wrong for half the world. A
100
+ * labelled `Month` spinbutton cannot be misread, and it displays in exactly the
101
+ * format it accepts, so there is no reformat-on-blur surprise.
102
+ *
103
+ * The behaviour that makes it typeable rather than merely focusable:
104
+ *
105
+ * - **A digit that cannot extend the current segment rolls into the next one.**
106
+ * `152026` in an `mdy` field is January the 5th 2026. Restarting the segment
107
+ * instead — the obvious implementation — silently turns January into May and
108
+ * eats the day, which is what makes a segmented field feel broken.
109
+ * - **Separators advance.** `1/5/2026` typed literally works, because `/` is how
110
+ * a person says "done with this one".
111
+ * - **`Backspace` takes back one digit**, then clears the segment, then steps
112
+ * back to the previous one — the only keyboard route to a segment the digits
113
+ * have already advanced past.
114
+ * - **A click anywhere in the field focuses a segment**, the way clicking a text
115
+ * input anywhere gives it a caret.
116
+ * - **`onChange` is held while a number is still being typed.** A year arrives
117
+ * one digit at a time, and `2` is the start of 2026, not the year 2. Deferred
118
+ * edits flush on blur.
119
+ *
120
+ * Values are the caller's own type; `toSegments`/`fromSegments` are the codec
121
+ * and `keyOf` supplies identity, which Temporal objects do not have.
122
+ *
123
+ * Internal. Not exported from the package.
124
+ */
125
+ export function SegmentedField<T>({
126
+ spec,
127
+ value,
128
+ toSegments,
129
+ fromSegments,
130
+ keyOf,
131
+ onChange,
132
+ parsePaste,
133
+ label,
134
+ isDisabled = false,
135
+ isInvalid = false,
136
+ onOpenRequest,
137
+ onSubmit,
138
+ id,
139
+ className,
140
+ }: SegmentedFieldProps<T>) {
141
+ const names = spec.map((segment) => segment.name);
142
+ const bySpec = (name: string) => spec.find((segment) => segment.name === name)!;
143
+ // `!= null` rather than `!== null`: a key the codec forgot to set would be
144
+ // `undefined`, which a `!== null` test would count as filled — and hand
145
+ // `fromSegments` a NaN.
146
+ const isComplete = (values: SegmentValues) => names.every((name) => values[name] != null);
147
+
148
+ const [segments, setSegments] = useState<SegmentValues>(() => toSegments(value));
149
+
150
+ // Props win, except over the value this field just emitted. Comparing a caller
151
+ // supplied key rather than the value itself matters twice: Temporal has no
152
+ // value identity, and an echoed-back value must not restart the user's edit.
153
+ const valueKey = value === undefined ? "" : keyOf(value);
154
+ const syncedKey = useRef(valueKey);
155
+ if (valueKey !== syncedKey.current) {
156
+ syncedKey.current = valueKey;
157
+ setSegments(toSegments(value));
158
+ }
159
+
160
+ // The digits typed into the segment being filled. State rather than a ref
161
+ // because it is rendered: a year mid-typing shows `202`, not `0202`.
162
+ const [buffer, setBuffer] = useState<{ segment: string; text: string } | null>(null);
163
+ const nodes = useRef<Record<string, HTMLSpanElement | null>>({});
164
+ const hadValue = useRef(isComplete(toSegments(value)));
165
+
166
+ const focusSegment = (name: string | undefined) => {
167
+ if (name) nodes.current[name]?.focus();
168
+ };
169
+
170
+ const publish = (next: SegmentValues) => {
171
+ if (!isComplete(next)) {
172
+ if (hadValue.current) {
173
+ hadValue.current = false;
174
+ // Pre-empt the sync for the cleared value too. Without this, emptying
175
+ // one segment round-trips as `undefined` and wipes the others — the
176
+ // user cleared the day, not the whole date. A controlled caller that
177
+ // *rejects* the clear still wins: its value no longer matches this
178
+ // key, so the next render syncs back to it.
179
+ syncedKey.current = "";
180
+ onChange(undefined);
181
+ }
182
+ return;
183
+ }
184
+ const composed = fromSegments(next as Record<string, number>);
185
+ const key = keyOf(composed);
186
+
187
+ // `fromSegments` may normalise — February the 31st is the 28th — so the
188
+ // segments follow the value rather than the keystrokes. Otherwise the
189
+ // field reads 02/31 while the value says the 28th.
190
+ const normalised = toSegments(composed);
191
+ if (names.some((name) => normalised[name] !== next[name])) setSegments(normalised);
192
+
193
+ if (key === syncedKey.current) return;
194
+ hadValue.current = true;
195
+ syncedKey.current = key;
196
+ onChange(composed);
197
+ };
198
+
199
+ /**
200
+ * `defer` holds the announcement while a number is still being typed, so a
201
+ * controlled caller is not handed `0002-12-25`, `0020-12-25`, `0202-12-25` in
202
+ * a row — and anything validating against a bound does not flash mid-word.
203
+ */
204
+ const commit = (next: SegmentValues, defer = false) => {
205
+ setSegments(next);
206
+ if (!defer) publish(next);
207
+ };
208
+
209
+ const applyDigit = (name: string, digit: string) => {
210
+ let target = name;
211
+ let started = buffer?.segment === name ? buffer.text : "";
212
+
213
+ // At most one hop per digit in practice; bounded so a bad spec cannot spin.
214
+ for (let hop = 0; hop <= spec.length; hop++) {
215
+ const { max, digits } = limitsOf(bySpec(target));
216
+ const combined = started + digit;
217
+ const next = names[names.indexOf(target) + 1];
218
+
219
+ if (digits > 0 && Number(combined) <= max && combined.length <= digits) {
220
+ const numeric = Number(combined);
221
+ const full = combined.length >= digits || numeric * 10 > max;
222
+ setBuffer(full ? null : { segment: target, text: combined });
223
+ commit({ ...segments, [target]: numeric === 0 && limitsOf(bySpec(target)).min > 0 ? null : numeric }, !full);
224
+ focusSegment(full ? next : target);
225
+ return;
226
+ }
227
+
228
+ if (!next) {
229
+ if (digits === 0) return; // a choice segment takes no digits
230
+ setBuffer({ segment: target, text: digit });
231
+ commit({ ...segments, [target]: Number(digit) === 0 ? null : Number(digit) });
232
+ focusSegment(target);
233
+ return;
234
+ }
235
+
236
+ target = next;
237
+ started = "";
238
+ }
239
+ };
240
+
241
+ /** A letter picks a `choice` option by first letter — `a` for AM, `p` for PM. */
242
+ const applyLetter = (name: string, letter: string) => {
243
+ const segment = bySpec(name);
244
+ if (segment.kind !== "choice") return false;
245
+ const index = segment.options.findIndex((option) => option.toLowerCase().startsWith(letter.toLowerCase()));
246
+ if (index === -1) return false;
247
+ setBuffer(null);
248
+ commit({ ...segments, [name]: index });
249
+ focusSegment(names[names.indexOf(name) + 1]);
250
+ return true;
251
+ };
252
+
253
+ const step = (name: string, delta: number) => {
254
+ const segment = bySpec(name);
255
+ const { min, max } = limitsOf(segment);
256
+ const current = segments[name];
257
+ if (current === null || current === undefined) {
258
+ commit({ ...segments, [name]: segment.stepFrom ?? min });
259
+ return;
260
+ }
261
+ let next = current + delta;
262
+ // Cyclic fields wrap, which is what an arrow key means on a month or an
263
+ // hour. A year does not — 9999 to 1 is never the intent — and it says so
264
+ // by spanning more than two digits.
265
+ const cyclic = segment.kind === "choice" || limitsOf(segment).digits <= 2;
266
+ if (!cyclic) next = Math.min(Math.max(next, min), max);
267
+ else if (next > max) next = min;
268
+ else if (next < min) next = max;
269
+ commit({ ...segments, [name]: next });
270
+ };
271
+
272
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLSpanElement>, name: string) => {
273
+ if (isDisabled) return;
274
+
275
+ if (event.altKey && event.key === "ArrowDown") {
276
+ event.preventDefault();
277
+ onOpenRequest?.();
278
+ return;
279
+ }
280
+
281
+ const plain = !event.altKey && !event.ctrlKey && !event.metaKey;
282
+
283
+ if (plain && /^[0-9]$/.test(event.key)) {
284
+ event.preventDefault();
285
+ applyDigit(name, event.key);
286
+ return;
287
+ }
288
+
289
+ if (plain && /^[a-z]$/i.test(event.key) && applyLetter(name, event.key)) {
290
+ event.preventDefault();
291
+ return;
292
+ }
293
+
294
+ // Any plausible separator counts, whatever this field's own separators
295
+ // are: it means "done with this one", and without it a typed `/` is
296
+ // swallowed and the next digit lands on the wrong segment.
297
+ //
298
+ // Only with a number still in progress, though. A segment that already
299
+ // filled up has advanced on its own, and treating the separator as a second
300
+ // advance would skip the segment after it: typing `1/6/2026` would land 2026
301
+ // in the hour, because the `6` had already moved on.
302
+ if (plain && ["/", "-", ".", ",", ":", " "].includes(event.key)) {
303
+ event.preventDefault();
304
+ if (buffer?.segment !== name) return;
305
+ setBuffer(null);
306
+ focusSegment(names[names.indexOf(name) + 1]);
307
+ return;
308
+ }
309
+
310
+ switch (event.key) {
311
+ case "ArrowUp":
312
+ event.preventDefault();
313
+ setBuffer(null);
314
+ step(name, 1);
315
+ break;
316
+ case "ArrowDown":
317
+ event.preventDefault();
318
+ setBuffer(null);
319
+ step(name, -1);
320
+ break;
321
+ case "ArrowLeft":
322
+ event.preventDefault();
323
+ setBuffer(null);
324
+ focusSegment(names[names.indexOf(name) - 1]);
325
+ break;
326
+ case "ArrowRight":
327
+ event.preventDefault();
328
+ setBuffer(null);
329
+ focusSegment(names[names.indexOf(name) + 1]);
330
+ break;
331
+ case "Backspace": {
332
+ event.preventDefault();
333
+ const typed = buffer?.segment === name ? buffer.text : "";
334
+ if (typed.length > 1) {
335
+ const text = typed.slice(0, -1);
336
+ setBuffer({ segment: name, text });
337
+ commit({ ...segments, [name]: Number(text) === 0 ? null : Number(text) }, true);
338
+ break;
339
+ }
340
+ setBuffer(null);
341
+ if (typed.length === 0 && segments[name] === null) {
342
+ focusSegment(names[names.indexOf(name) - 1]);
343
+ break;
344
+ }
345
+ commit({ ...segments, [name]: null });
346
+ break;
347
+ }
348
+ case "Delete":
349
+ event.preventDefault();
350
+ setBuffer(null);
351
+ commit({ ...segments, [name]: null });
352
+ break;
353
+ case "Enter":
354
+ event.preventDefault();
355
+ onSubmit?.();
356
+ break;
357
+ default:
358
+ break;
359
+ }
360
+ };
361
+
362
+ const handlePaste = (event: React.ClipboardEvent<HTMLFieldSetElement>) => {
363
+ if (isDisabled || !parsePaste) return;
364
+ event.preventDefault();
365
+ const parsed = parsePaste(event.clipboardData.getData("text").trim());
366
+ if (!parsed) return;
367
+ setBuffer(null);
368
+ commit(parsed);
369
+ focusSegment(names[names.length - 1]);
370
+ };
371
+
372
+ return (
373
+ // A `fieldset` rather than a `div role="group"`: the segments are one
374
+ // related set of controls, which is the element's whole job. Tailwind's
375
+ // preflight already strips its default margin, padding and border.
376
+ <fieldset
377
+ id={id}
378
+ aria-label={label}
379
+ aria-invalid={isInvalid || undefined}
380
+ aria-disabled={isDisabled || undefined}
381
+ onMouseDown={(event) => {
382
+ // A click anywhere in the field focuses a segment, the way clicking a
383
+ // text input anywhere puts a caret in it. Without this, only the digits
384
+ // themselves are clickable and the rest of the box silently does
385
+ // nothing — which reads as "typing is broken", because nothing has focus.
386
+ if (isDisabled) return;
387
+ if ((event.target as HTMLElement).closest("[data-segment]")) return;
388
+ event.preventDefault();
389
+ focusSegment(names.find((name) => segments[name] === null) ?? names[0]);
390
+ }}
391
+ onPaste={handlePaste}
392
+ onBlur={(event) => {
393
+ // Only when focus leaves the field entirely — moving between segments is
394
+ // a blur too, and mid-edit is not the moment to announce anything.
395
+ if (event.currentTarget.contains(event.relatedTarget as Node | null)) return;
396
+ // A live buffer is exactly the condition under which a commit was
397
+ // deferred, so it is the only case with anything to flush.
398
+ if (!buffer) return;
399
+ setBuffer(null);
400
+ publish(segments);
401
+ }}
402
+ className={cn("flex flex-1 cursor-text items-center gap-0.5 text-sm", isDisabled && "opacity-50", className)}
403
+ >
404
+ {spec.map((segment) => {
405
+ const current = segments[segment.name] ?? null;
406
+ const { min, max } = limitsOf(segment);
407
+ const typed = buffer?.segment === segment.name ? buffer.text : "";
408
+ const text = displayOf(segment, current, typed);
409
+ return (
410
+ <React.Fragment key={segment.name}>
411
+ {segment.separatorBefore && (
412
+ <span aria-hidden="true" className="text-fg-subtle">
413
+ {segment.separatorBefore}
414
+ </span>
415
+ )}
416
+ <span
417
+ ref={(node) => {
418
+ nodes.current[segment.name] = node;
419
+ }}
420
+ role="spinbutton"
421
+ tabIndex={isDisabled ? -1 : 0}
422
+ aria-label={segment.label}
423
+ aria-valuemin={min}
424
+ aria-valuemax={max}
425
+ aria-valuenow={current ?? undefined}
426
+ aria-valuetext={current === null ? "Empty" : text}
427
+ aria-disabled={isDisabled || undefined}
428
+ data-segment={segment.name}
429
+ onKeyDown={(event) => handleKeyDown(event, segment.name)}
430
+ onFocus={() => {
431
+ if (buffer?.segment !== segment.name) setBuffer(null);
432
+ }}
433
+ className={cn(
434
+ "cursor-text rounded-sm px-0.5 tabular-nums outline-hidden focus:bg-bg-brand-subtle focus:text-fg-on-brand-subtle",
435
+ current === null && "text-fg-subtle"
436
+ )}
437
+ >
438
+ {text}
439
+ </span>
440
+ </React.Fragment>
441
+ );
442
+ })}
443
+ </fieldset>
444
+ );
445
+ }
@@ -1,2 +1,4 @@
1
+ export { PickerField, type PickerFieldProps } from "./PickerField";
2
+ export { SegmentedField, type SegmentedFieldProps, type SegmentSpec, type SegmentValues } from "./SegmentedField";
1
3
  export { Slot, type SlotProps } from "./Slot";
2
4
  export type { WritableRefObject } from "./types";
@@ -18,19 +18,30 @@ type OverlayStackItem = StackItem & {
18
18
  };
19
19
 
20
20
  type OverlayContextType = {
21
- modalRoot: FloatingPortalProps["root"];
21
+ modalRoot?: FloatingPortalProps["root"];
22
22
  modalStack: ReturnType<typeof useStack<OverlayStackItem>>;
23
- toastRoot: FloatingPortalProps["root"];
23
+ toastRoot?: FloatingPortalProps["root"];
24
24
  toastStack: ReturnType<typeof useStack<OverlayStackItem>>;
25
25
  };
26
26
 
27
+ /**
28
+ * `root` omitted when the caller gave none, rather than passed as `undefined`
29
+ * or `null`. The distinction is load-bearing: `FloatingPortal` bails out of
30
+ * creating its portal node entirely on an explicit `null` ("wait for the root
31
+ * to exist"), and only a missing `root` falls through to `document.body`.
32
+ */
33
+ const rootProp = (root: FloatingPortalProps["root"] | undefined) => (root === undefined ? {} : { root });
34
+
27
35
  const OverlayContext = createContext<OverlayContextType>(undefined as unknown as OverlayContextType);
28
36
  export const useOverlayContext = () => useContext(OverlayContext);
29
37
 
30
38
  export const OverlayProvider = ({ modal: modalRoot, toast: toastRoot, children }: PropsWithChildren<OverlayContextProps>) => {
31
- const resolvedModalRoot = modalRoot ?? document.body;
32
- const resolvedToastRoot = toastRoot ?? document.body;
33
-
39
+ // The roots pass through unresolved, deliberately. Defaulting them to
40
+ // `document.body` here read the DOM during render, which threw the whole
41
+ // tree under SSR — and it bought nothing: `FloatingPortal` already falls
42
+ // back to `document.body` when no `root` is given, on the client, where a
43
+ // body exists. Both portals below are behind a non-empty stack, and both
44
+ // stacks start empty, so nothing portals on the server either way.
34
45
  const modalStack = useStack<OverlayStackItem>([]);
35
46
  const toastStack = useStack<OverlayStackItem>([]);
36
47
 
@@ -55,12 +66,12 @@ export const OverlayProvider = ({ modal: modalRoot, toast: toastRoot, children }
55
66
  // Memoize context value to prevent unnecessary re-renders in consumers
56
67
  const contextValue = useMemo(
57
68
  () => ({
58
- modalRoot: resolvedModalRoot,
69
+ modalRoot,
59
70
  modalStack,
60
- toastRoot: resolvedToastRoot,
71
+ toastRoot,
61
72
  toastStack,
62
73
  }),
63
- [resolvedModalRoot, modalStack, resolvedToastRoot, toastStack]
74
+ [modalRoot, modalStack, toastRoot, toastStack]
64
75
  );
65
76
 
66
77
  return (
@@ -69,7 +80,7 @@ export const OverlayProvider = ({ modal: modalRoot, toast: toastRoot, children }
69
80
 
70
81
  {/* Modal stack - rendered in portal */}
71
82
  {modals.length > 0 && (
72
- <FloatingPortal root={resolvedModalRoot}>
83
+ <FloatingPortal {...rootProp(modalRoot)}>
73
84
  {modals.map(({ id, component: Component, data }) => (
74
85
  <Component key={id} data={data} />
75
86
  ))}
@@ -78,7 +89,7 @@ export const OverlayProvider = ({ modal: modalRoot, toast: toastRoot, children }
78
89
 
79
90
  {/* Toast stack - rendered in portal */}
80
91
  {toasts.length > 0 && (
81
- <FloatingPortal root={resolvedToastRoot}>
92
+ <FloatingPortal {...rootProp(toastRoot)}>
82
93
  <div className="pointer-events-none fixed bottom-6 left-1/2 z-toast flex -translate-x-1/2 flex-col-reverse items-center gap-3">
83
94
  {toasts.map(({ id, component: Component, onClose, ...props }) => (
84
95
  <div key={id} className="pointer-events-auto">
@@ -6,9 +6,7 @@
6
6
  */
7
7
 
8
8
  export type {
9
- BgKey,
10
9
  BgToken,
11
- BorderKey,
12
10
  BorderToken,
13
11
  ColorMode,
14
12
  ColorModeSchema,
@@ -16,17 +14,14 @@ export type {
16
14
  Context,
17
15
  CSSVarName,
18
16
  Feedback,
19
- FgKey,
20
17
  FgToken,
21
18
  HexValue,
22
19
  Hierarchy,
23
- HSLValue,
24
20
  Intent,
25
21
  Semantic,
26
22
  StandaloneToken,
27
23
  State,
28
24
  ThemeSchema,
29
- TokenKey,
30
25
  TokenName,
31
26
  Weight,
32
27
  } from "./schema";
@@ -36,7 +31,6 @@ export {
36
31
  cssVar,
37
32
  feedback,
38
33
  hierarchy,
39
- hsl,
40
34
  intents,
41
35
  semantics,
42
36
  standaloneTokens,
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Badge recipe — the component-token layer for Badge.
3
+ *
4
+ * This is the mapping from `(variant, intent)` to a semantic token. It used
5
+ * to live in `compoundVariants` inside Badge.tsx, where it was compiled into
6
+ * class-name literals and no consumer could reach it. Here it is data, so a
7
+ * consumer can re-point a cell, scope a change to a subtree, or add an intent
8
+ * the library never shipped.
9
+ *
10
+ * The grammar is `--badge-{context}`, context one of bg, fg, border, ring.
11
+ * Note what is deliberately *absent*: intent and variant never appear in a
12
+ * token name. They select, via the `data-` attributes below. Putting them in
13
+ * the name (`--badge-bg-danger`) would restore the combinatorial explosion
14
+ * this layer exists to remove, and would leave a consumer unable to add an
15
+ * intent, because the variable for it would not exist.
16
+ *
17
+ * `@layer components` is load-bearing, not tidiness. The cascade compares
18
+ * layers before specificity, and unlayered author CSS outranks any layered
19
+ * author CSS — so a consumer's `.badge { ... }` (0,1,0) beats the cell rules
20
+ * below (0,3,0) without an `!important`. The corollary is a trap worth
21
+ * knowing: an override written *inside* a layer is compared on specificity
22
+ * again, and silently loses. Consumer overrides go unlayered.
23
+ */
24
+
25
+ @layer components {
26
+ /* The floor. Applies when no cell below matches — an intent a consumer has
27
+ added but not yet styled renders as a plain, legible badge rather than
28
+ something broken. Transparent border/background plus normal foreground
29
+ is the resting default, not a new opinion. */
30
+ .badge {
31
+ --badge-bg: transparent;
32
+ --badge-fg: var(--fg-normal);
33
+ --badge-border: transparent;
34
+ --badge-ring: var(--border-brand);
35
+ }
36
+
37
+ /* solid — a filled surface, with text read on top of it. The `fg-on-*`
38
+ partner is the pairing check-contrast.ts verifies, so these cells inherit
39
+ that guarantee rather than restating it. Border stays transparent (the
40
+ floor already sets it); only bg/fg vary. */
41
+ .badge[data-variant="solid"][data-intent="neutral"] {
42
+ --badge-bg: var(--bg-strong);
43
+ --badge-fg: var(--fg-on-strong);
44
+ }
45
+
46
+ .badge[data-variant="solid"][data-intent="brand"] {
47
+ --badge-bg: var(--bg-brand);
48
+ --badge-fg: var(--fg-on-brand);
49
+ }
50
+
51
+ .badge[data-variant="solid"][data-intent="danger"] {
52
+ --badge-bg: var(--bg-danger);
53
+ --badge-fg: var(--fg-on-danger);
54
+ }
55
+
56
+ .badge[data-variant="solid"][data-intent="success"] {
57
+ --badge-bg: var(--bg-success);
58
+ --badge-fg: var(--fg-on-success);
59
+ }
60
+
61
+ .badge[data-variant="solid"][data-intent="warning"] {
62
+ --badge-bg: var(--bg-warning);
63
+ --badge-fg: var(--fg-on-warning);
64
+ }
65
+
66
+ .badge[data-variant="solid"][data-intent="muted"] {
67
+ --badge-bg: var(--bg-subtle);
68
+ --badge-fg: var(--fg-subtle);
69
+ }
70
+
71
+ /* soft — a subdued surface, text read on top of it. Border stays
72
+ transparent, same as solid. */
73
+ .badge[data-variant="soft"][data-intent="neutral"] {
74
+ --badge-bg: var(--bg-subtle);
75
+ --badge-fg: var(--fg-normal);
76
+ }
77
+
78
+ .badge[data-variant="soft"][data-intent="brand"] {
79
+ --badge-bg: var(--bg-brand-subtle);
80
+ --badge-fg: var(--fg-on-brand-subtle);
81
+ }
82
+
83
+ .badge[data-variant="soft"][data-intent="danger"] {
84
+ --badge-bg: var(--bg-danger-subtle);
85
+ --badge-fg: var(--fg-on-danger-subtle);
86
+ }
87
+
88
+ .badge[data-variant="soft"][data-intent="success"] {
89
+ --badge-bg: var(--bg-success-subtle);
90
+ --badge-fg: var(--fg-on-success-subtle);
91
+ }
92
+
93
+ .badge[data-variant="soft"][data-intent="warning"] {
94
+ --badge-bg: var(--bg-warning-subtle);
95
+ --badge-fg: var(--fg-on-warning-subtle);
96
+ }
97
+
98
+ .badge[data-variant="soft"][data-intent="muted"] {
99
+ --badge-bg: var(--bg-subtle);
100
+ --badge-fg: var(--fg-subtle);
101
+ }
102
+
103
+ /* outline — a resting surface (bg-normal, shared across every intent) with
104
+ the text and border colour carrying the intent. `neutral` and `muted`
105
+ both land on `border-normal`, but differ in foreground, so they stay
106
+ separate cells rather than merged into one selector list. */
107
+ .badge[data-variant="outline"] {
108
+ --badge-bg: var(--bg-normal);
109
+ }
110
+
111
+ .badge[data-variant="outline"][data-intent="neutral"] {
112
+ --badge-border: var(--border-normal);
113
+ --badge-fg: var(--fg-normal);
114
+ }
115
+
116
+ .badge[data-variant="outline"][data-intent="brand"] {
117
+ --badge-border: var(--border-brand);
118
+ --badge-fg: var(--fg-brand);
119
+ }
120
+
121
+ .badge[data-variant="outline"][data-intent="danger"] {
122
+ --badge-border: var(--border-danger);
123
+ --badge-fg: var(--fg-danger);
124
+ }
125
+
126
+ .badge[data-variant="outline"][data-intent="success"] {
127
+ --badge-border: var(--border-success);
128
+ --badge-fg: var(--fg-success);
129
+ }
130
+
131
+ .badge[data-variant="outline"][data-intent="warning"] {
132
+ --badge-border: var(--border-warning);
133
+ --badge-fg: var(--fg-warning);
134
+ }
135
+
136
+ .badge[data-variant="outline"][data-intent="muted"] {
137
+ --badge-border: var(--border-normal);
138
+ --badge-fg: var(--fg-subtle);
139
+ }
140
+ }