@sixthshift/design-system 0.4.0 → 0.6.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 (417) hide show
  1. package/README.md +134 -87
  2. package/dist/components/Badge/Badge.d.ts +2 -2
  3. package/dist/components/Badge/Badge.js +2 -2
  4. package/dist/components/Button/Button.d.ts +1 -1
  5. package/dist/components/Button/Button.js +1 -1
  6. package/dist/components/Calendar/Calendar.d.ts.map +1 -1
  7. package/dist/components/Calendar/Calendar.js +1 -0
  8. package/dist/components/Calendar/Calendar.js.map +1 -1
  9. package/dist/components/Calendar/CalendarView.d.ts +0 -11
  10. package/dist/components/Calendar/CalendarView.d.ts.map +1 -1
  11. package/dist/components/Calendar/CalendarView.js +1 -0
  12. package/dist/components/Calendar/CalendarView.js.map +1 -1
  13. package/dist/components/Calendar/calendar.hooks.d.ts.map +1 -1
  14. package/dist/components/Calendar/calendar.hooks.js +1 -0
  15. package/dist/components/Calendar/calendar.hooks.js.map +1 -1
  16. package/dist/components/Card/Card.d.ts +50 -4
  17. package/dist/components/Card/Card.d.ts.map +1 -1
  18. package/dist/components/Card/Card.js +89 -5
  19. package/dist/components/Card/Card.js.map +1 -1
  20. package/dist/components/Card/index.d.ts +1 -1
  21. package/dist/components/Card/index.d.ts.map +1 -1
  22. package/dist/components/Card/index.js +1 -1
  23. package/dist/components/Card/index.js.map +1 -1
  24. package/dist/components/Checkbox/Checkbox.d.ts.map +1 -1
  25. package/dist/components/Checkbox/Checkbox.js +1 -0
  26. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  27. package/dist/components/CheckboxGroup/CheckboxGroup.d.ts.map +1 -1
  28. package/dist/components/CheckboxGroup/CheckboxGroup.js +1 -0
  29. package/dist/components/CheckboxGroup/CheckboxGroup.js.map +1 -1
  30. package/dist/components/Code/Editor/Editor.d.ts.map +1 -1
  31. package/dist/components/Code/Editor/Editor.js +1 -0
  32. package/dist/components/Code/Editor/Editor.js.map +1 -1
  33. package/dist/components/Code/Workspace/StatusBar.d.ts.map +1 -1
  34. package/dist/components/Code/Workspace/StatusBar.js +1 -0
  35. package/dist/components/Code/Workspace/StatusBar.js.map +1 -1
  36. package/dist/components/Code/Workspace/Toolbar.d.ts.map +1 -1
  37. package/dist/components/Code/Workspace/Toolbar.js +1 -0
  38. package/dist/components/Code/Workspace/Toolbar.js.map +1 -1
  39. package/dist/components/Code/Workspace/Workspace.d.ts.map +1 -1
  40. package/dist/components/Code/Workspace/Workspace.js +1 -0
  41. package/dist/components/Code/Workspace/Workspace.js.map +1 -1
  42. package/dist/components/DatePicker/DatePicker.d.ts.map +1 -1
  43. package/dist/components/DatePicker/DatePicker.js +5 -0
  44. package/dist/components/DatePicker/DatePicker.js.map +1 -1
  45. package/dist/components/DatePicker/DateSegments.d.ts.map +1 -1
  46. package/dist/components/DatePicker/DateSegments.js +1 -0
  47. package/dist/components/DatePicker/DateSegments.js.map +1 -1
  48. package/dist/components/DatePicker/datepicker.hooks.d.ts.map +1 -1
  49. package/dist/components/DatePicker/datepicker.hooks.js +1 -0
  50. package/dist/components/DatePicker/datepicker.hooks.js.map +1 -1
  51. package/dist/components/DateRangePicker/DateRangePicker.d.ts.map +1 -1
  52. package/dist/components/DateRangePicker/DateRangePicker.js +1 -0
  53. package/dist/components/DateRangePicker/DateRangePicker.js.map +1 -1
  54. package/dist/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
  55. package/dist/components/DateTimePicker/DateTimePicker.js +5 -0
  56. package/dist/components/DateTimePicker/DateTimePicker.js.map +1 -1
  57. package/dist/components/DateTimePicker/DateTimeSegments.d.ts.map +1 -1
  58. package/dist/components/DateTimePicker/DateTimeSegments.js +1 -0
  59. package/dist/components/DateTimePicker/DateTimeSegments.js.map +1 -1
  60. package/dist/components/DateTimeRangePicker/DateTimeRangePicker.d.ts.map +1 -1
  61. package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js +5 -0
  62. package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js.map +1 -1
  63. package/dist/components/FormField/FormField.d.ts +31 -10
  64. package/dist/components/FormField/FormField.d.ts.map +1 -1
  65. package/dist/components/FormField/FormField.js +46 -17
  66. package/dist/components/FormField/FormField.js.map +1 -1
  67. package/dist/components/FormField/index.d.ts +2 -2
  68. package/dist/components/FormField/index.d.ts.map +1 -1
  69. package/dist/components/FormField/index.js +1 -1
  70. package/dist/components/FormField/index.js.map +1 -1
  71. package/dist/components/HoverCard/HoverCard.d.ts.map +1 -1
  72. package/dist/components/HoverCard/HoverCard.js +1 -0
  73. package/dist/components/HoverCard/HoverCard.js.map +1 -1
  74. package/dist/components/HoverCard/components/HoverCardContent.d.ts.map +1 -1
  75. package/dist/components/HoverCard/components/HoverCardContent.js +1 -0
  76. package/dist/components/HoverCard/components/HoverCardContent.js.map +1 -1
  77. package/dist/components/HoverCard/components/HoverCardContext.d.ts.map +1 -1
  78. package/dist/components/HoverCard/components/HoverCardContext.js +1 -0
  79. package/dist/components/HoverCard/components/HoverCardContext.js.map +1 -1
  80. package/dist/components/HoverCard/components/HoverCardTrigger.d.ts.map +1 -1
  81. package/dist/components/HoverCard/components/HoverCardTrigger.js +1 -0
  82. package/dist/components/HoverCard/components/HoverCardTrigger.js.map +1 -1
  83. package/dist/components/Message/Message.d.ts +1 -1
  84. package/dist/components/Message/Message.js +1 -1
  85. package/dist/components/Modal/Modal.d.ts +14 -9
  86. package/dist/components/Modal/Modal.d.ts.map +1 -1
  87. package/dist/components/Modal/Modal.js +42 -20
  88. package/dist/components/Modal/Modal.js.map +1 -1
  89. package/dist/components/Modal/components/ModalContext.d.ts.map +1 -1
  90. package/dist/components/Modal/components/ModalContext.js +1 -0
  91. package/dist/components/Modal/components/ModalContext.js.map +1 -1
  92. package/dist/components/Modal/components/ModalHeader.d.ts.map +1 -1
  93. package/dist/components/Modal/components/ModalHeader.js +1 -0
  94. package/dist/components/Modal/components/ModalHeader.js.map +1 -1
  95. package/dist/components/Popover/Popover.d.ts.map +1 -1
  96. package/dist/components/Popover/Popover.js +5 -0
  97. package/dist/components/Popover/Popover.js.map +1 -1
  98. package/dist/components/Popover/components/PopoverBody.d.ts.map +1 -1
  99. package/dist/components/Popover/components/PopoverBody.js +1 -0
  100. package/dist/components/Popover/components/PopoverBody.js.map +1 -1
  101. package/dist/components/Popover/components/PopoverClose.d.ts.map +1 -1
  102. package/dist/components/Popover/components/PopoverClose.js +1 -0
  103. package/dist/components/Popover/components/PopoverClose.js.map +1 -1
  104. package/dist/components/Popover/components/PopoverContext.d.ts.map +1 -1
  105. package/dist/components/Popover/components/PopoverContext.js +1 -0
  106. package/dist/components/Popover/components/PopoverContext.js.map +1 -1
  107. package/dist/components/Popover/components/PopoverTrigger.d.ts.map +1 -1
  108. package/dist/components/Popover/components/PopoverTrigger.js +1 -0
  109. package/dist/components/Popover/components/PopoverTrigger.js.map +1 -1
  110. package/dist/components/RadioButton/RadioButton.d.ts.map +1 -1
  111. package/dist/components/RadioButton/RadioButton.js +1 -0
  112. package/dist/components/RadioButton/RadioButton.js.map +1 -1
  113. package/dist/components/RadioButtonGroup/RadioButtonGroup.d.ts.map +1 -1
  114. package/dist/components/RadioButtonGroup/RadioButtonGroup.js +1 -0
  115. package/dist/components/RadioButtonGroup/RadioButtonGroup.js.map +1 -1
  116. package/dist/components/SearchInput/SearchInput.d.ts +10 -7
  117. package/dist/components/SearchInput/SearchInput.d.ts.map +1 -1
  118. package/dist/components/SearchInput/SearchInput.js +11 -6
  119. package/dist/components/SearchInput/SearchInput.js.map +1 -1
  120. package/dist/components/Select/Select.d.ts +6 -0
  121. package/dist/components/Select/Select.d.ts.map +1 -1
  122. package/dist/components/Select/Select.js +9 -3
  123. package/dist/components/Select/Select.js.map +1 -1
  124. package/dist/components/Select/SelectDropdown.d.ts.map +1 -1
  125. package/dist/components/Select/SelectDropdown.js +1 -0
  126. package/dist/components/Select/SelectDropdown.js.map +1 -1
  127. package/dist/components/Select/SelectTrigger.d.ts.map +1 -1
  128. package/dist/components/Select/SelectTrigger.js +5 -1
  129. package/dist/components/Select/SelectTrigger.js.map +1 -1
  130. package/dist/components/Select/useSelectKeyboard.d.ts.map +1 -1
  131. package/dist/components/Select/useSelectKeyboard.js +1 -0
  132. package/dist/components/Select/useSelectKeyboard.js.map +1 -1
  133. package/dist/components/Sheet/Sheet.d.ts.map +1 -1
  134. package/dist/components/Sheet/Sheet.js +23 -9
  135. package/dist/components/Sheet/Sheet.js.map +1 -1
  136. package/dist/components/Sheet/components/SheetContext.d.ts.map +1 -1
  137. package/dist/components/Sheet/components/SheetContext.js +1 -0
  138. package/dist/components/Sheet/components/SheetContext.js.map +1 -1
  139. package/dist/components/Sheet/components/SheetHeader.d.ts.map +1 -1
  140. package/dist/components/Sheet/components/SheetHeader.js +1 -0
  141. package/dist/components/Sheet/components/SheetHeader.js.map +1 -1
  142. package/dist/components/Spinner/Spinner.d.ts +2 -2
  143. package/dist/components/Spinner/Spinner.js +2 -2
  144. package/dist/components/Switch/Switch.d.ts.map +1 -1
  145. package/dist/components/Switch/Switch.js +1 -0
  146. package/dist/components/Switch/Switch.js.map +1 -1
  147. package/dist/components/Tabs/Tabs.d.ts.map +1 -1
  148. package/dist/components/Tabs/Tabs.js +1 -0
  149. package/dist/components/Tabs/Tabs.js.map +1 -1
  150. package/dist/components/Tabs/components/TabsContext.d.ts.map +1 -1
  151. package/dist/components/Tabs/components/TabsContext.js +1 -0
  152. package/dist/components/Tabs/components/TabsContext.js.map +1 -1
  153. package/dist/components/Tabs/components/TabsList.d.ts.map +1 -1
  154. package/dist/components/Tabs/components/TabsList.js +1 -0
  155. package/dist/components/Tabs/components/TabsList.js.map +1 -1
  156. package/dist/components/Tabs/components/TabsPanels.d.ts.map +1 -1
  157. package/dist/components/Tabs/components/TabsPanels.js +1 -0
  158. package/dist/components/Tabs/components/TabsPanels.js.map +1 -1
  159. package/dist/components/Tabs/components/TabsTrigger.d.ts.map +1 -1
  160. package/dist/components/Tabs/components/TabsTrigger.js +2 -1
  161. package/dist/components/Tabs/components/TabsTrigger.js.map +1 -1
  162. package/dist/components/TagChip/TagChip.d.ts.map +1 -1
  163. package/dist/components/TagChip/TagChip.js +1 -0
  164. package/dist/components/TagChip/TagChip.js.map +1 -1
  165. package/dist/components/TagInput/TagInput.d.ts +8 -2
  166. package/dist/components/TagInput/TagInput.d.ts.map +1 -1
  167. package/dist/components/TagInput/TagInput.js +8 -5
  168. package/dist/components/TagInput/TagInput.js.map +1 -1
  169. package/dist/components/Textarea/Textarea.d.ts +21 -5
  170. package/dist/components/Textarea/Textarea.d.ts.map +1 -1
  171. package/dist/components/Textarea/Textarea.js +31 -5
  172. package/dist/components/Textarea/Textarea.js.map +1 -1
  173. package/dist/components/TimePicker/PeriodSelector.d.ts.map +1 -1
  174. package/dist/components/TimePicker/PeriodSelector.js +1 -0
  175. package/dist/components/TimePicker/PeriodSelector.js.map +1 -1
  176. package/dist/components/TimePicker/TimeColumn.d.ts.map +1 -1
  177. package/dist/components/TimePicker/TimeColumn.js +1 -0
  178. package/dist/components/TimePicker/TimeColumn.js.map +1 -1
  179. package/dist/components/TimePicker/TimePicker.d.ts.map +1 -1
  180. package/dist/components/TimePicker/TimePicker.js +5 -0
  181. package/dist/components/TimePicker/TimePicker.js.map +1 -1
  182. package/dist/components/TimePicker/TimeSegments.d.ts.map +1 -1
  183. package/dist/components/TimePicker/TimeSegments.js +1 -0
  184. package/dist/components/TimePicker/TimeSegments.js.map +1 -1
  185. package/dist/components/Toast/Toast.d.ts +16 -2
  186. package/dist/components/Toast/Toast.d.ts.map +1 -1
  187. package/dist/components/Toast/Toast.js +16 -6
  188. package/dist/components/Toast/Toast.js.map +1 -1
  189. package/dist/components/Toggle/Toggle.d.ts +1 -1
  190. package/dist/components/Toggle/Toggle.d.ts.map +1 -1
  191. package/dist/components/Toggle/Toggle.js +2 -1
  192. package/dist/components/Toggle/Toggle.js.map +1 -1
  193. package/dist/components/ToggleGroup/ToggleGroupMultiple.d.ts +2 -0
  194. package/dist/components/ToggleGroup/ToggleGroupMultiple.d.ts.map +1 -1
  195. package/dist/components/ToggleGroup/ToggleGroupMultiple.js +4 -3
  196. package/dist/components/ToggleGroup/ToggleGroupMultiple.js.map +1 -1
  197. package/dist/components/ToggleGroup/ToggleGroupSingle.d.ts +2 -0
  198. package/dist/components/ToggleGroup/ToggleGroupSingle.d.ts.map +1 -1
  199. package/dist/components/ToggleGroup/ToggleGroupSingle.js +4 -3
  200. package/dist/components/ToggleGroup/ToggleGroupSingle.js.map +1 -1
  201. package/dist/components/ToggleGroup/toggleGroup.types.d.ts +7 -0
  202. package/dist/components/ToggleGroup/toggleGroup.types.d.ts.map +1 -1
  203. package/dist/components/Tooltip/Tooltip.d.ts.map +1 -1
  204. package/dist/components/Tooltip/Tooltip.js +1 -0
  205. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  206. package/dist/components/Tooltip/components/TooltipBody.d.ts.map +1 -1
  207. package/dist/components/Tooltip/components/TooltipBody.js +1 -0
  208. package/dist/components/Tooltip/components/TooltipBody.js.map +1 -1
  209. package/dist/components/Tooltip/components/TooltipContext.d.ts.map +1 -1
  210. package/dist/components/Tooltip/components/TooltipContext.js +1 -0
  211. package/dist/components/Tooltip/components/TooltipContext.js.map +1 -1
  212. package/dist/components/Tooltip/components/TooltipTrigger.d.ts.map +1 -1
  213. package/dist/components/Tooltip/components/TooltipTrigger.js +1 -0
  214. package/dist/components/Tooltip/components/TooltipTrigger.js.map +1 -1
  215. package/dist/hooks/useCollapsible.d.ts.map +1 -1
  216. package/dist/hooks/useCollapsible.js +1 -0
  217. package/dist/hooks/useCollapsible.js.map +1 -1
  218. package/dist/hooks/useControllableState.d.ts.map +1 -1
  219. package/dist/hooks/useControllableState.js +1 -0
  220. package/dist/hooks/useControllableState.js.map +1 -1
  221. package/dist/hooks/useDebouncedCallback.d.ts.map +1 -1
  222. package/dist/hooks/useDebouncedCallback.js +1 -0
  223. package/dist/hooks/useDebouncedCallback.js.map +1 -1
  224. package/dist/hooks/useDebouncedState.d.ts.map +1 -1
  225. package/dist/hooks/useDebouncedState.js +1 -0
  226. package/dist/hooks/useDebouncedState.js.map +1 -1
  227. package/dist/hooks/useDeviceFormFactor.d.ts.map +1 -1
  228. package/dist/hooks/useDeviceFormFactor.js +1 -0
  229. package/dist/hooks/useDeviceFormFactor.js.map +1 -1
  230. package/dist/hooks/useLocalStorage.d.ts.map +1 -1
  231. package/dist/hooks/useLocalStorage.js +1 -0
  232. package/dist/hooks/useLocalStorage.js.map +1 -1
  233. package/dist/hooks/useMergedFloatingRef.d.ts.map +1 -1
  234. package/dist/hooks/useMergedFloatingRef.js +1 -0
  235. package/dist/hooks/useMergedFloatingRef.js.map +1 -1
  236. package/dist/hooks/usePresence.d.ts.map +1 -1
  237. package/dist/hooks/usePresence.js +1 -0
  238. package/dist/hooks/usePresence.js.map +1 -1
  239. package/dist/hooks/useStack.d.ts.map +1 -1
  240. package/dist/hooks/useStack.js +34 -10
  241. package/dist/hooks/useStack.js.map +1 -1
  242. package/dist/hooks/useTheme.d.ts.map +1 -1
  243. package/dist/hooks/useTheme.js +1 -0
  244. package/dist/hooks/useTheme.js.map +1 -1
  245. package/dist/internal/PickerField.d.ts.map +1 -1
  246. package/dist/internal/PickerField.js +1 -0
  247. package/dist/internal/PickerField.js.map +1 -1
  248. package/dist/internal/SegmentedField.d.ts.map +1 -1
  249. package/dist/internal/SegmentedField.js +1 -0
  250. package/dist/internal/SegmentedField.js.map +1 -1
  251. package/dist/internal/escapeLayers.d.ts +12 -0
  252. package/dist/internal/escapeLayers.d.ts.map +1 -0
  253. package/dist/internal/escapeLayers.js +48 -0
  254. package/dist/internal/escapeLayers.js.map +1 -0
  255. package/dist/lib/ErrorBoundary.d.ts.map +1 -1
  256. package/dist/lib/ErrorBoundary.js +1 -0
  257. package/dist/lib/ErrorBoundary.js.map +1 -1
  258. package/dist/lib/contexts/ComponentsContext/ComponentsContext.d.ts.map +1 -1
  259. package/dist/lib/contexts/ComponentsContext/ComponentsContext.js +1 -0
  260. package/dist/lib/contexts/ComponentsContext/ComponentsContext.js.map +1 -1
  261. package/dist/lib/contexts/OverlayContext/OverlayContext.d.ts +9 -4
  262. package/dist/lib/contexts/OverlayContext/OverlayContext.d.ts.map +1 -1
  263. package/dist/lib/contexts/OverlayContext/OverlayContext.js +34 -10
  264. package/dist/lib/contexts/OverlayContext/OverlayContext.js.map +1 -1
  265. package/dist/lib/contexts/OverlayContext/ToastStack.d.ts +20 -0
  266. package/dist/lib/contexts/OverlayContext/ToastStack.d.ts.map +1 -0
  267. package/dist/lib/contexts/OverlayContext/ToastStack.js +45 -0
  268. package/dist/lib/contexts/OverlayContext/ToastStack.js.map +1 -0
  269. package/dist/lib/contexts/OverlayContext/hooks/useModal.d.ts.map +1 -1
  270. package/dist/lib/contexts/OverlayContext/hooks/useModal.js +1 -0
  271. package/dist/lib/contexts/OverlayContext/hooks/useModal.js.map +1 -1
  272. package/dist/lib/contexts/OverlayContext/hooks/useToast.d.ts +27 -10
  273. package/dist/lib/contexts/OverlayContext/hooks/useToast.d.ts.map +1 -1
  274. package/dist/lib/contexts/OverlayContext/hooks/useToast.js +31 -49
  275. package/dist/lib/contexts/OverlayContext/hooks/useToast.js.map +1 -1
  276. package/dist/lib/contexts/OverlayContext/index.d.ts +2 -0
  277. package/dist/lib/contexts/OverlayContext/index.d.ts.map +1 -1
  278. package/dist/lib/contexts/OverlayContext/index.js +2 -0
  279. package/dist/lib/contexts/OverlayContext/index.js.map +1 -1
  280. package/dist/lib/contexts/OverlayContext/toastStore.d.ts +68 -0
  281. package/dist/lib/contexts/OverlayContext/toastStore.d.ts.map +1 -0
  282. package/dist/lib/contexts/OverlayContext/toastStore.js +69 -0
  283. package/dist/lib/contexts/OverlayContext/toastStore.js.map +1 -0
  284. package/dist/lib/withErrorBoundary.d.ts.map +1 -1
  285. package/dist/lib/withErrorBoundary.js +1 -0
  286. package/dist/lib/withErrorBoundary.js.map +1 -1
  287. package/dist/lib/withSuspenseAndErrorBoundary.d.ts.map +1 -1
  288. package/dist/lib/withSuspenseAndErrorBoundary.js +1 -0
  289. package/dist/lib/withSuspenseAndErrorBoundary.js.map +1 -1
  290. package/dist/{theme/schema.d.ts → theming/vocabulary.d.ts} +4 -4
  291. package/dist/theming/vocabulary.d.ts.map +1 -0
  292. package/dist/{theme/schema.js → theming/vocabulary.js} +4 -4
  293. package/dist/theming/vocabulary.js.map +1 -0
  294. package/package.json +22 -9
  295. package/src/components/Badge/Badge.tsx +2 -2
  296. package/src/components/Button/Button.tsx +1 -1
  297. package/src/{theme/recipes/button.css → components/Button/button.recipe.css} +2 -2
  298. package/src/components/Calendar/Calendar.tsx +2 -0
  299. package/src/components/Calendar/CalendarView.tsx +2 -0
  300. package/src/components/Calendar/calendar.hooks.ts +2 -0
  301. package/src/components/Card/Card.tsx +105 -17
  302. package/src/{theme/recipes/card.css → components/Card/card.recipe.css} +6 -1
  303. package/src/components/Card/index.ts +1 -1
  304. package/src/components/Checkbox/Checkbox.tsx +2 -0
  305. package/src/components/CheckboxGroup/CheckboxGroup.tsx +2 -0
  306. package/src/components/Code/Editor/Editor.tsx +2 -0
  307. package/src/components/Code/Workspace/StatusBar.tsx +2 -0
  308. package/src/components/Code/Workspace/Toolbar.tsx +2 -0
  309. package/src/components/Code/Workspace/Workspace.tsx +2 -0
  310. package/src/components/DatePicker/DatePicker.tsx +7 -0
  311. package/src/components/DatePicker/DateSegments.tsx +2 -0
  312. package/src/components/DatePicker/datepicker.hooks.ts +2 -0
  313. package/src/components/DateRangePicker/DateRangePicker.tsx +2 -0
  314. package/src/components/DateTimePicker/DateTimePicker.tsx +7 -1
  315. package/src/components/DateTimePicker/DateTimeSegments.tsx +2 -0
  316. package/src/components/DateTimeRangePicker/DateTimeRangePicker.tsx +7 -1
  317. package/src/components/FormField/FormField.tsx +94 -42
  318. package/src/components/FormField/index.ts +2 -2
  319. package/src/components/HoverCard/HoverCard.tsx +2 -0
  320. package/src/components/HoverCard/components/HoverCardContent.tsx +2 -0
  321. package/src/components/HoverCard/components/HoverCardContext.tsx +2 -0
  322. package/src/components/HoverCard/components/HoverCardTrigger.tsx +2 -0
  323. package/src/components/Message/Message.tsx +1 -1
  324. package/src/components/Modal/Modal.tsx +77 -51
  325. package/src/components/Modal/components/ModalContext.tsx +2 -0
  326. package/src/components/Modal/components/ModalHeader.tsx +2 -0
  327. package/src/components/Popover/Popover.tsx +7 -0
  328. package/src/components/Popover/components/PopoverBody.tsx +2 -0
  329. package/src/components/Popover/components/PopoverClose.tsx +2 -0
  330. package/src/components/Popover/components/PopoverContext.tsx +2 -0
  331. package/src/components/Popover/components/PopoverTrigger.tsx +2 -0
  332. package/src/components/RadioButton/RadioButton.tsx +2 -0
  333. package/src/components/RadioButtonGroup/RadioButtonGroup.tsx +2 -0
  334. package/src/components/SearchInput/SearchInput.tsx +15 -8
  335. package/src/components/Select/Select.tsx +24 -1
  336. package/src/components/Select/SelectDropdown.tsx +2 -0
  337. package/src/components/Select/SelectTrigger.tsx +5 -0
  338. package/src/components/Select/useSelectKeyboard.ts +2 -0
  339. package/src/components/Sheet/Sheet.tsx +49 -34
  340. package/src/components/Sheet/components/SheetContext.tsx +2 -0
  341. package/src/components/Sheet/components/SheetHeader.tsx +2 -0
  342. package/src/components/Spinner/Spinner.tsx +2 -2
  343. package/src/components/Switch/Switch.tsx +2 -0
  344. package/src/components/Tabs/Tabs.tsx +2 -0
  345. package/src/components/Tabs/components/TabsContext.tsx +2 -0
  346. package/src/components/Tabs/components/TabsList.tsx +2 -0
  347. package/src/components/Tabs/components/TabsPanels.tsx +2 -0
  348. package/src/components/Tabs/components/TabsTrigger.tsx +3 -1
  349. package/src/{theme/recipes/tabs.css → components/Tabs/tabs.recipe.css} +1 -1
  350. package/src/components/TagChip/TagChip.tsx +2 -0
  351. package/src/components/TagInput/TagInput.tsx +15 -6
  352. package/src/components/Textarea/Textarea.tsx +42 -6
  353. package/src/{theme/recipes/textarea.css → components/Textarea/textarea.recipe.css} +1 -1
  354. package/src/components/TimePicker/PeriodSelector.tsx +2 -0
  355. package/src/components/TimePicker/TimeColumn.tsx +2 -0
  356. package/src/components/TimePicker/TimePicker.tsx +7 -0
  357. package/src/components/TimePicker/TimeSegments.tsx +2 -0
  358. package/src/components/Toast/Toast.tsx +22 -6
  359. package/src/components/Toggle/Toggle.tsx +3 -1
  360. package/src/components/ToggleGroup/ToggleGroupMultiple.tsx +6 -0
  361. package/src/components/ToggleGroup/ToggleGroupSingle.tsx +6 -0
  362. package/src/components/ToggleGroup/toggleGroup.types.ts +7 -0
  363. package/src/components/Tooltip/Tooltip.tsx +2 -0
  364. package/src/components/Tooltip/components/TooltipBody.tsx +2 -0
  365. package/src/components/Tooltip/components/TooltipContext.tsx +2 -0
  366. package/src/components/Tooltip/components/TooltipTrigger.tsx +2 -0
  367. package/src/hooks/useCollapsible.ts +2 -0
  368. package/src/hooks/useControllableState.ts +2 -0
  369. package/src/hooks/useDebouncedCallback.ts +2 -0
  370. package/src/hooks/useDebouncedState.ts +2 -0
  371. package/src/hooks/useDeviceFormFactor.ts +2 -0
  372. package/src/hooks/useLocalStorage.ts +2 -0
  373. package/src/hooks/useMergedFloatingRef.ts +2 -0
  374. package/src/hooks/usePresence.ts +2 -0
  375. package/src/hooks/useStack.ts +35 -12
  376. package/src/hooks/useTheme.ts +2 -0
  377. package/src/internal/PickerField.tsx +2 -0
  378. package/src/internal/SegmentedField.tsx +2 -0
  379. package/src/internal/escapeLayers.ts +53 -0
  380. package/src/lib/ErrorBoundary.tsx +2 -0
  381. package/src/lib/contexts/ComponentsContext/ComponentsContext.tsx +2 -0
  382. package/src/lib/contexts/OverlayContext/OverlayContext.tsx +53 -26
  383. package/src/lib/contexts/OverlayContext/ToastStack.tsx +71 -0
  384. package/src/lib/contexts/OverlayContext/hooks/useModal.tsx +2 -0
  385. package/src/lib/contexts/OverlayContext/hooks/useToast.ts +30 -73
  386. package/src/lib/contexts/OverlayContext/index.ts +2 -0
  387. package/src/lib/contexts/OverlayContext/toastStore.ts +138 -0
  388. package/src/lib/withErrorBoundary.tsx +2 -0
  389. package/src/lib/withSuspenseAndErrorBoundary.tsx +2 -0
  390. package/src/theme/linen/index.css +11 -0
  391. package/src/theme/linen/palette.css +87 -0
  392. package/src/theme/linen/theme.css +371 -0
  393. package/src/theming/tailwind.css +356 -0
  394. package/src/{theme/schema.ts → theming/vocabulary.ts} +3 -3
  395. package/dist/theme/index.d.ts +0 -9
  396. package/dist/theme/index.d.ts.map +0 -1
  397. package/dist/theme/index.js +0 -8
  398. package/dist/theme/index.js.map +0 -1
  399. package/dist/theme/schema.d.ts.map +0 -1
  400. package/dist/theme/schema.js.map +0 -1
  401. package/src/theme/index.ts +0 -39
  402. package/src/theme/recipes/index.css +0 -28
  403. package/src/theme/tokens.css +0 -824
  404. /package/src/{theme/recipes/badge.css → components/Badge/badge.recipe.css} +0 -0
  405. /package/src/{theme/recipes/checkbox.css → components/Checkbox/checkbox.recipe.css} +0 -0
  406. /package/src/{theme/recipes/field.css → components/Field/field.recipe.css} +0 -0
  407. /package/src/{theme/recipes/input.css → components/Input/input.recipe.css} +0 -0
  408. /package/src/{theme/recipes/message.css → components/Message/message.recipe.css} +0 -0
  409. /package/src/{theme/recipes/modal.css → components/Modal/modal.recipe.css} +0 -0
  410. /package/src/{theme/recipes/radio-button.css → components/RadioButton/radio-button.recipe.css} +0 -0
  411. /package/src/{theme/recipes/search-input.css → components/SearchInput/search-input.recipe.css} +0 -0
  412. /package/src/{theme/recipes/select.css → components/Select/select.recipe.css} +0 -0
  413. /package/src/{theme/recipes/sheet.css → components/Sheet/sheet.recipe.css} +0 -0
  414. /package/src/{theme/recipes/spinner.css → components/Spinner/spinner.recipe.css} +0 -0
  415. /package/src/{theme/recipes/switch.css → components/Switch/switch.recipe.css} +0 -0
  416. /package/src/{theme/recipes/tag-chip.css → components/TagChip/tag-chip.recipe.css} +0 -0
  417. /package/src/{theme/recipes/toggle.css → components/Toggle/toggle.recipe.css} +0 -0
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { autoUpdate, FloatingPortal, flip, offset, shift, useClick, useDismiss, useFloating, useInteractions } from "@floating-ui/react";
2
4
  import { useControllableState } from "@sixthshift/design-system/hooks";
3
5
  import { cn } from "@sixthshift/design-system/utils";
@@ -16,8 +18,8 @@ import {
16
18
  toISOInstant,
17
19
  toISOInstantRange,
18
20
  } from "../../date-time";
19
-
20
21
  import { PickerField } from "../../internal";
22
+ import { useEscapeLayer } from "../../internal/escapeLayers";
21
23
  import { Button } from "../Button";
22
24
  import { CalendarView } from "../Calendar/CalendarView";
23
25
  import { isDateDisabled } from "../Calendar/calendar.hooks";
@@ -412,6 +414,10 @@ export const DateTimeRangePicker = React.forwardRef<HTMLDivElement, DateTimeRang
412
414
 
413
415
  const click = useClick(context);
414
416
  const dismiss = useDismiss(context);
417
+
418
+ // While open, an Escape press closes this picker's popover and must not
419
+ // also close a Modal/Sheet it is rendered inside.
420
+ useEscapeLayer(open);
415
421
  const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss]);
416
422
 
417
423
  // IDs for accessibility
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { Label } from "@sixthshift/design-system/label";
2
4
  import { Message } from "@sixthshift/design-system/message";
3
5
  import { Muted } from "@sixthshift/design-system/muted";
@@ -10,61 +12,111 @@ export type FormFieldFeedback = {
10
12
  };
11
13
 
12
14
  export type FormFieldProps = React.HTMLAttributes<HTMLDivElement> & {
13
- label: string;
14
- description?: string;
15
+ label: React.ReactNode;
16
+ description?: React.ReactNode;
15
17
  feedback?: FormFieldFeedback;
16
18
  required?: boolean;
17
19
  };
18
20
 
21
+ /** What FormField knows about the control it wraps — see `useFormField`. */
22
+ export type FormFieldContextValue = {
23
+ /** The id the control should carry, and the label's `htmlFor` target. */
24
+ id: string;
25
+ /** Space-separated ids of the description/feedback texts, for `aria-describedby`. `undefined` when there are none. */
26
+ describedBy: string | undefined;
27
+ /** True when `feedback.intent` is `"danger"` — the control should set `aria-invalid`. */
28
+ invalid: boolean;
29
+ /** Mirrors the `required` prop, for `aria-required`/`required` on the control. */
30
+ required: boolean;
31
+ };
32
+
33
+ const FormFieldContext = React.createContext<FormFieldContextValue | null>(null);
34
+
35
+ /**
36
+ * The wiring `FormField` provides to whatever control it wraps: `id`,
37
+ * `describedBy`, `invalid`, `required`. Direct element children get these
38
+ * cloned on automatically; a control nested deeper — inside a wrapper div, a
39
+ * render prop, a custom composite — can call this instead and apply them
40
+ * itself. Returns `null` outside a `FormField`.
41
+ */
42
+ export const useFormField = () => React.useContext(FormFieldContext);
43
+
19
44
  /**
20
45
  * Wraps a form control with a label, optional description, and optional
21
46
  * validation feedback — the composition point for `Input`, `Textarea`,
22
47
  * `Select` and similar controls inside a form.
23
48
  *
24
- * Generates an `id` (or uses the one passed in) and clones it, plus
25
- * `aria-describedby` (pointing at the description and/or feedback text) and
26
- * `aria-invalid` (when `feedback.intent` is `"danger"`), onto its child —
27
- * so the wrapped control does not need those wired by hand. It expects a
28
- * single child control: the same id and aria attributes get cloned onto
29
- * every child if more than one is passed.
49
+ * Generates an `id` (or uses the one passed in) and wires it, plus
50
+ * `aria-describedby` (pointing at the description and/or feedback text),
51
+ * `aria-invalid` (when `feedback.intent` is `"danger"`) and `aria-required`
52
+ * (when `required`), onto the first element child. A child's own attributes
53
+ * win: an explicit `id` is kept (and becomes the label's `htmlFor` target),
54
+ * an existing `aria-describedby` is merged with FormField's rather than
55
+ * replaced. Controls that aren't the first element child can self-wire via
56
+ * `useFormField`.
30
57
  *
31
58
  * Distinct from `Field`, which displays a read-only label/value pair rather
32
59
  * than wrapping an interactive control.
33
60
  */
34
- const FormField = React.forwardRef<HTMLDivElement, FormFieldProps>(({ className, label, description, feedback, required, children, id, ...props }, ref) => {
35
- const generatedId = React.useId();
36
- const fieldId = id ?? generatedId;
37
- const descriptionId = description ? `${fieldId}-description` : undefined;
38
- const feedbackId = feedback ? `${fieldId}-feedback` : undefined;
39
-
40
- return (
41
- <div ref={ref} className={cn("flex flex-col gap-1.5", className)} {...props}>
42
- <Label htmlFor={fieldId}>
43
- {label}
44
- {required && <span className="ml-0.5 text-fg-danger">*</span>}
45
- </Label>
46
-
47
- {description && <Muted id={descriptionId}>{description}</Muted>}
48
-
49
- {React.Children.map(children, (child) => {
50
- if (React.isValidElement(child)) {
51
- return React.cloneElement(child, {
52
- id: fieldId,
53
- "aria-describedby": cn(descriptionId, feedbackId) || undefined,
54
- "aria-invalid": feedback?.intent === "danger" ? true : undefined,
55
- } as React.HTMLAttributes<HTMLElement>);
56
- }
57
- return child;
58
- })}
59
-
60
- {feedback && (
61
- <Message id={feedbackId} intent={feedback.intent} size="sm">
62
- {feedback.message}
63
- </Message>
64
- )}
65
- </div>
66
- );
67
- });
61
+ const FormField = React.forwardRef<HTMLDivElement, FormFieldProps>(
62
+ ({ className, label, description, feedback, required = false, children, id, ...props }, ref) => {
63
+ const generatedId = React.useId();
64
+ const fieldId = id ?? generatedId;
65
+
66
+ // The label must point at the id the control actually carries: when the
67
+ // (first element) child brings its own id, that id wins over ours.
68
+ const childArray = React.Children.toArray(children);
69
+ const firstElementIndex = childArray.findIndex((child) => React.isValidElement(child));
70
+ const firstElement = firstElementIndex === -1 ? null : (childArray[firstElementIndex] as React.ReactElement<React.HTMLAttributes<HTMLElement>>);
71
+ const controlId = firstElement?.props.id ?? fieldId;
72
+
73
+ const descriptionId = description ? `${fieldId}-description` : undefined;
74
+ const feedbackId = feedback ? `${fieldId}-feedback` : undefined;
75
+ const invalid = feedback?.intent === "danger";
76
+
77
+ const contextValue = React.useMemo<FormFieldContextValue>(
78
+ () => ({
79
+ id: controlId,
80
+ describedBy: cn(descriptionId, feedbackId) || undefined,
81
+ invalid,
82
+ required,
83
+ }),
84
+ [controlId, descriptionId, feedbackId, invalid, required]
85
+ );
86
+
87
+ const wiredChildren = childArray.map((child, index) => {
88
+ if (index !== firstElementIndex || !React.isValidElement(child)) return child;
89
+ const childProps = child.props as React.HTMLAttributes<HTMLElement>;
90
+ return React.cloneElement(child, {
91
+ id: controlId,
92
+ // The child's own describedby (a tooltip, an external hint) is kept and
93
+ // FormField's ids are appended, rather than overwritten.
94
+ "aria-describedby": cn(childProps["aria-describedby"], descriptionId, feedbackId) || undefined,
95
+ "aria-invalid": childProps["aria-invalid"] ?? (invalid ? true : undefined),
96
+ "aria-required": childProps["aria-required"] ?? (required ? true : undefined),
97
+ } as React.HTMLAttributes<HTMLElement>);
98
+ });
99
+
100
+ return (
101
+ <div ref={ref} className={cn("flex flex-col gap-1.5", className)} {...props}>
102
+ <Label htmlFor={controlId}>
103
+ {label}
104
+ {required && <span className="ml-0.5 text-fg-danger">*</span>}
105
+ </Label>
106
+
107
+ {description && <Muted id={descriptionId}>{description}</Muted>}
108
+
109
+ <FormFieldContext.Provider value={contextValue}>{wiredChildren}</FormFieldContext.Provider>
110
+
111
+ {feedback && (
112
+ <Message id={feedbackId} intent={feedback.intent} size="sm">
113
+ {feedback.message}
114
+ </Message>
115
+ )}
116
+ </div>
117
+ );
118
+ }
119
+ );
68
120
  FormField.displayName = "FormField";
69
121
 
70
122
  export { FormField };
@@ -1,2 +1,2 @@
1
- export type { FormFieldFeedback, FormFieldProps } from "./FormField";
2
- export { FormField } from "./FormField";
1
+ export type { FormFieldContextValue, FormFieldFeedback, FormFieldProps } from "./FormField";
2
+ export { FormField, useFormField } from "./FormField";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { autoUpdate, flip, offset, type Placement, safePolygon, shift, useDismiss, useFloating, useFocus, useHover, useInteractions } from "@floating-ui/react";
2
4
  import { useControllableState } from "@sixthshift/design-system/hooks";
3
5
  import type * as React from "react";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { FloatingPortal } from "@floating-ui/react";
2
4
  import { cn } from "@sixthshift/design-system/utils";
3
5
  import * as React from "react";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { createContext, useContext } from "react";
2
4
 
3
5
  export type HoverCardContextValue = {
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import * as React from "react";
2
4
  import { Slot } from "../../../internal/Slot";
3
5
  import type { WritableRefObject } from "../../../internal/types";
@@ -10,7 +10,7 @@ import { MessageTitle } from "./components/MessageTitle";
10
10
  * Geometry only, plus the three `--message-*` component tokens.
11
11
  *
12
12
  * Every colour reads a `--message-*` component token whose value is decided by
13
- * src/theme/recipes/message.css. That file is the mapping from `intent` to a
13
+ * src/components/Message/message.recipe.css. That file is the mapping from `intent` to a
14
14
  * semantic token — the layer that used to be the `intent` entry in `variants`
15
15
  * here, compiled into class-name literals and unreachable from outside.
16
16
  * `size` stays here: it is pure geometry (padding, gap, font-size), not
@@ -1,7 +1,10 @@
1
- import { FloatingFocusManager, FloatingOverlay, useDismiss, useFloating, useInteractions, useRole } from "@floating-ui/react";
1
+ "use client";
2
+
3
+ import { FloatingFocusManager, FloatingOverlay, FloatingPortal, useDismiss, useFloating, useInteractions, useRole } from "@floating-ui/react";
2
4
  import { useMergedFloatingRef, usePresence } from "@sixthshift/design-system/hooks";
3
5
  import { cn } from "@sixthshift/design-system/utils";
4
6
  import { forwardRef, type HTMLAttributes, type ReactNode, useCallback, useContext, useEffect, useId, useMemo, useState } from "react";
7
+ import { hasOpenEscapeLayer, useEscapeDialog } from "../../internal/escapeLayers";
5
8
  import { ModalBody, ModalContext, ModalFooter, ModalHeader } from "./components";
6
9
 
7
10
  // =============================================================================
@@ -21,9 +24,11 @@ export type ModalProps = Pick<HTMLAttributes<HTMLDivElement>, "className" | "sty
21
24
  /** Children - use Modal.Header, Modal.Body, Modal.Footer */
22
25
  children: ReactNode;
23
26
  /**
24
- * When true (default), clicking outside closes the modal.
27
+ * When true (default), clicking outside — and, for a directly-mounted
28
+ * modal, pressing Escape — closes the modal.
25
29
  * When false, the modal can only be closed programmatically.
26
- * Note: Escape key is handled by OverlayContext for proper stack ordering.
30
+ * Note: for modals opened via `useModal()`, Escape is handled by
31
+ * OverlayContext for proper stack ordering.
27
32
  */
28
33
  dismissable?: boolean;
29
34
  /** When true, shows a close (X) button in the modal header */
@@ -66,6 +71,9 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
66
71
  ) => {
67
72
  // Prefer explicit prop over context (context is set by useModal's overlay system)
68
73
  const contextValue = useContext(ModalContext);
74
+ // A context means useModal's stack owns this modal — and with it, Escape
75
+ // ordering. Without one the modal is mounted directly and handles its own.
76
+ const isStackManaged = contextValue != null;
69
77
  // Context provides a parameterless onClose for programmatic modals; adapt to the boolean shape.
70
78
  const handleDismiss = useCallback(() => {
71
79
  if (onOpenChange) onOpenChange(false);
@@ -84,8 +92,12 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
84
92
 
85
93
  const { refs, context } = useFloating({
86
94
  open: true,
87
- onOpenChange: (nextOpen) => {
88
- if (!nextOpen) handleClose();
95
+ onOpenChange: (nextOpen, _event, reason) => {
96
+ if (nextOpen) return;
97
+ // A transient overlay (Select dropdown, Popover, picker) open above
98
+ // this modal consumes the Escape itself — don't also close the modal.
99
+ if (reason === "escape-key" && hasOpenEscapeLayer()) return;
100
+ handleClose();
89
101
  },
90
102
  });
91
103
 
@@ -93,8 +105,10 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
93
105
 
94
106
  const dismiss = useDismiss(context, {
95
107
  enabled: dismissable,
96
- // OverlayContext handles escape key for proper stack ordering
97
- escapeKey: false,
108
+ // Stack-managed modals leave Escape to OverlayContext, which closes
109
+ // topmost-first. A directly-mounted modal has no stack above it and
110
+ // handles its own Escape here.
111
+ escapeKey: !isStackManaged,
98
112
  outsidePress: true,
99
113
  outsidePressEvent: "mousedown",
100
114
  });
@@ -119,52 +133,63 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
119
133
 
120
134
  const modalContextValue = useMemo(() => ({ onClose: handleClose, closable, titleId, registerTitle }), [closable, handleClose, titleId, registerTitle]);
121
135
 
136
+ // While mounted (exit animation included), a non-modal Sheet underneath
137
+ // must leave Escape to this dialog.
138
+ useEscapeDialog(isMounted);
139
+
122
140
  if (!isMounted) return null;
123
141
 
124
142
  const isEntering = state === "entering";
125
143
  const isExiting = state === "exiting";
126
144
 
127
145
  return (
128
- <FloatingFocusManager context={context} modal>
129
- <FloatingOverlay
130
- lockScroll
131
- className={cn("modal-overlay fixed inset-0 z-modal bg-(--modal-overlay-bg)", isEntering && "animate-fade-in", isExiting && "animate-fade-out")}
132
- >
133
- <div
134
- ref={mergedRef}
135
- role="dialog"
136
- aria-modal="true"
137
- aria-label={ariaLabel}
138
- aria-labelledby={labelledBy}
139
- aria-describedby={ariaDescribedBy}
140
- tabIndex={-1}
141
- data-state={state}
142
- className={cn(
143
- "modal fixed flex flex-col overflow-hidden outline-hidden",
144
- "border-(color:--modal-border) rounded-xl border bg-(--modal-bg)",
145
- // Size
146
- sizeClasses[size],
147
- // Cap height so ModalBody scrolls instead of overflowing the viewport
148
- "max-h-[95%]",
149
- // Mobile: slide up from bottom, full width
150
- "max-sm:inset-x-0 max-sm:bottom-0 max-sm:rounded-b-none",
151
- isEntering && "max-sm:animate-slide-up-in",
152
- isExiting && "max-sm:animate-slide-up-out",
153
- // Desktop: horizontal center + vertical alignment
154
- "sm:left-1/2 sm:-translate-x-1/2",
155
- align === "center" && "sm:top-1/2 sm:-translate-y-1/2",
156
- align === "top" && "sm:top-[15vh]",
157
- isEntering && "sm:animate-fade-in",
158
- isExiting && "sm:animate-fade-out",
159
- className
160
- )}
161
- style={style}
162
- {...getFloatingProps({})}
146
+ // Self-portalled: `position: fixed` resolves against the nearest
147
+ // transformed/filtered ancestor, so a modal rendered in place inside a
148
+ // `transform` container would be positioned (and clipped) by it. Nested
149
+ // FloatingPortals attach to the parent portal's node, so a stack-managed
150
+ // modal stays inside the `OverlayProvider`'s custom `modal` root.
151
+ <FloatingPortal>
152
+ <FloatingFocusManager context={context} modal>
153
+ <FloatingOverlay
154
+ lockScroll
155
+ className={cn("modal-overlay fixed inset-0 z-modal bg-(--modal-overlay-bg)", isEntering && "animate-fade-in", isExiting && "animate-fade-out")}
163
156
  >
164
- <ModalContext.Provider value={modalContextValue}>{children}</ModalContext.Provider>
165
- </div>
166
- </FloatingOverlay>
167
- </FloatingFocusManager>
157
+ <div
158
+ ref={mergedRef}
159
+ role="dialog"
160
+ aria-modal="true"
161
+ aria-label={ariaLabel}
162
+ aria-labelledby={labelledBy}
163
+ aria-describedby={ariaDescribedBy}
164
+ tabIndex={-1}
165
+ data-state={state}
166
+ className={cn(
167
+ "modal fixed flex flex-col overflow-hidden outline-hidden",
168
+ "border-(color:--modal-border) rounded-xl border bg-(--modal-bg)",
169
+ // Size
170
+ sizeClasses[size],
171
+ // Cap height so ModalBody scrolls instead of overflowing the viewport
172
+ "max-h-[95%]",
173
+ // Mobile: slide up from bottom, full width
174
+ "max-sm:inset-x-0 max-sm:bottom-0 max-sm:rounded-b-none",
175
+ isEntering && "max-sm:animate-slide-up-in",
176
+ isExiting && "max-sm:animate-slide-up-out",
177
+ // Desktop: horizontal center + vertical alignment
178
+ "sm:left-1/2 sm:-translate-x-1/2",
179
+ align === "center" && "sm:top-1/2 sm:-translate-y-1/2",
180
+ align === "top" && "sm:top-[15vh]",
181
+ isEntering && "sm:animate-fade-in",
182
+ isExiting && "sm:animate-fade-out",
183
+ className
184
+ )}
185
+ style={style}
186
+ {...getFloatingProps({})}
187
+ >
188
+ <ModalContext.Provider value={modalContextValue}>{children}</ModalContext.Provider>
189
+ </div>
190
+ </FloatingOverlay>
191
+ </FloatingFocusManager>
192
+ </FloatingPortal>
168
193
  );
169
194
  }
170
195
  );
@@ -192,11 +217,12 @@ ModalRoot.displayName = "Modal";
192
217
  * Modal is mount-driven — a parent (or `useModal`'s stack) decides whether it
193
218
  * renders at all, so there's no `open` prop. `onOpenChange` still exists,
194
219
  * firing only with `false` on dismissal, so every overlay in this library
195
- * shares one open/close shape. Escape is *not* wired inside Modal itself:
196
- * `OverlayProvider` owns it globally so a stack of modals closes
197
- * topmost-first. That means Escape only closes a Modal opened through
198
- * `useModal()` — one a parent mounts directly from its own boolean state
199
- * won't respond to Escape unless the app wires that separately.
220
+ * shares one open/close shape. Escape closes the modal: a directly-mounted
221
+ * Modal handles it itself (via `useDismiss`, so `dismissable={false}` turns
222
+ * it off along with outside-press), while one opened through `useModal()`
223
+ * defers to `OverlayProvider`, which closes a stack of modals topmost-first.
224
+ * Either way, an Escape pressed while a transient overlay (Select dropdown,
225
+ * Popover, picker) is open above the modal closes only that overlay.
200
226
  *
201
227
  * Focus moves into the modal on mount and is trapped there
202
228
  * (`FloatingFocusManager modal`) until it unmounts, then returns to whatever
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { createContext, type PropsWithChildren, useContext, useMemo } from "react";
2
4
 
3
5
  /**
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { cn } from "@sixthshift/design-system/utils";
2
4
  import { X } from "lucide-react";
3
5
  import * as React from "react";
@@ -1,7 +1,10 @@
1
+ "use client";
2
+
1
3
  import { autoUpdate, flip, offset, type Placement, shift, useClick, useDismiss, useFloating, useInteractions } from "@floating-ui/react";
2
4
  import { useControllableState } from "@sixthshift/design-system/hooks";
3
5
  import type * as React from "react";
4
6
  import { useId } from "react";
7
+ import { useEscapeLayer } from "../../internal/escapeLayers";
5
8
  import { PopoverBody } from "./components/PopoverBody";
6
9
  import { PopoverClose } from "./components/PopoverClose";
7
10
  import { PopoverContext } from "./components/PopoverContext";
@@ -42,6 +45,10 @@ const PopoverRoot = ({ open: controlledOpen, onOpenChange, defaultOpen = false,
42
45
  const dismiss = useDismiss(context);
43
46
  const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss]);
44
47
 
48
+ // While open, an Escape press dismisses this popover and must not also
49
+ // close a Modal/Sheet underneath it.
50
+ useEscapeLayer(open);
51
+
45
52
  const id = useId();
46
53
  const contentId = `popover-content-${id}`;
47
54
  // `Popover.Body` is a `role="dialog"`, and a dialog with no accessible name is
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { FloatingPortal } from "@floating-ui/react";
2
4
  import { cn } from "@sixthshift/design-system/utils";
3
5
  import * as React from "react";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import * as React from "react";
2
4
  import { Slot } from "../../../internal/Slot";
3
5
  import { usePopoverContext } from "./PopoverContext";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { createContext, useContext } from "react";
2
4
 
3
5
  export type PopoverContextValue = {
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import * as React from "react";
2
4
  import { Slot } from "../../../internal/Slot";
3
5
  import { usePopoverContext } from "./PopoverContext";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { Label } from "@sixthshift/design-system/label";
2
4
  import { cn } from "@sixthshift/design-system/utils";
3
5
  import * as React from "react";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { useControllableState } from "@sixthshift/design-system/hooks";
2
4
  import { RadioButton } from "@sixthshift/design-system/radio-button";
3
5
  import { cn } from "@sixthshift/design-system/utils";
@@ -1,11 +1,14 @@
1
+ "use client";
2
+
1
3
  import { Input } from "@sixthshift/design-system/input";
2
4
  import { cn } from "@sixthshift/design-system/utils";
3
5
  import { Search, X } from "lucide-react";
4
6
  import * as React from "react";
5
7
 
6
- export type SearchInputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" | "onChange"> & {
8
+ export type SearchInputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, "type"> & {
7
9
  value: string;
8
- onChange: (value: string) => void;
10
+ /** Called with the input's string value on every change. The native `onChange(event)` also fires, unchanged. */
11
+ onValueChange: (value: string) => void;
9
12
  onClear?: () => void;
10
13
  /** Accessible name for the clear button — it is icon-only. */
11
14
  clearLabel?: string;
@@ -17,17 +20,18 @@ export type SearchInputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>,
17
20
  *
18
21
  * Wraps `Input`, using its `iconLeft`/`iconRight` slots, rather than
19
22
  * reimplementing the field. Always controlled — `value` is required and
20
- * there is no `defaultValue`/uncontrolled mode. `onChange` receives the
21
- * string value directly, not the change event. Clearing calls `onClear` if
22
- * provided, otherwise calls `onChange("")`.
23
+ * there is no `defaultValue`/uncontrolled mode. `onValueChange` receives the
24
+ * string value directly; the native `onChange` keeps its event signature and
25
+ * fires too if passed. Clearing calls `onClear` if provided, otherwise calls
26
+ * `onValueChange("")` — note a programmatic clear dispatches no native event.
23
27
  */
24
28
  const SearchInput = React.forwardRef<HTMLInputElement, SearchInputProps>(
25
- ({ className, value, onChange, onClear, clearLabel = "Clear search", ...props }, ref) => {
29
+ ({ className, value, onValueChange, onChange, onClear, clearLabel = "Clear search", ...props }, ref) => {
26
30
  const handleClear = () => {
27
31
  if (onClear) {
28
32
  onClear();
29
33
  } else {
30
- onChange("");
34
+ onValueChange("");
31
35
  }
32
36
  };
33
37
 
@@ -36,7 +40,10 @@ const SearchInput = React.forwardRef<HTMLInputElement, SearchInputProps>(
36
40
  ref={ref}
37
41
  type="text"
38
42
  value={value}
39
- onChange={(e) => onChange(e.target.value)}
43
+ onChange={(e) => {
44
+ onValueChange(e.target.value);
45
+ onChange?.(e);
46
+ }}
40
47
  iconLeft={<Search />}
41
48
  iconRight={
42
49
  value ? (
@@ -1,6 +1,9 @@
1
+ "use client";
2
+
1
3
  import { autoUpdate, flip, offset, shift, size, useFloating } from "@floating-ui/react";
2
4
  import { useControllableState } from "@sixthshift/design-system/hooks";
3
5
  import { forwardRef, type HTMLAttributes, type ReactElement, type Ref, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
6
+ import { useEscapeLayer } from "../../internal/escapeLayers";
4
7
  import { SelectDropdown, selectOptionId } from "./SelectDropdown";
5
8
  import { SelectTriggerButton, SelectTriggerSearch } from "./SelectTrigger";
6
9
  import { useSelectKeyboard } from "./useSelectKeyboard";
@@ -19,6 +22,12 @@ type SelectBaseProps<T extends string = string> = Omit<HTMLAttributes<HTMLElemen
19
22
  placeholder?: string;
20
23
  searchable?: boolean;
21
24
  clearable?: boolean;
25
+ /** Input name for native form submission. Selected value(s) are mirrored into hidden `<input>`s, `${name}[]` in multiple mode. */
26
+ name?: string;
27
+ /** The `form` attribute for the hidden input(s), for a Select rendered outside its `<form>`. */
28
+ form?: string;
29
+ /** Marks the combobox `aria-required`. Constraint validation is the caller's job — the mirrored inputs are hidden. */
30
+ required?: boolean;
22
31
  };
23
32
 
24
33
  type SelectSingleProps<T extends string = string> = SelectBaseProps<T> & {
@@ -54,6 +63,9 @@ const SelectRoot = forwardRef(function SelectRoot<T extends string = string>(pro
54
63
  placeholder = "Select...",
55
64
  searchable = false,
56
65
  clearable = false,
66
+ name,
67
+ form,
68
+ required = false,
57
69
  ...restProps
58
70
  } = props;
59
71
 
@@ -244,6 +256,10 @@ const SelectRoot = forwardRef(function SelectRoot<T extends string = string>(pro
244
256
  }
245
257
  }, [highlightedIndex, open]);
246
258
 
259
+ // While open, an Escape press closes the dropdown and must not also close
260
+ // a Modal/Sheet this Select is rendered inside.
261
+ useEscapeLayer(open);
262
+
247
263
  // Close on outside click
248
264
  useEffect(() => {
249
265
  if (!open) return;
@@ -266,7 +282,8 @@ const SelectRoot = forwardRef(function SelectRoot<T extends string = string>(pro
266
282
  const activeOptionId = open && highlightedIndex >= 0 && highlightedIndex < filteredOptions.length ? selectOptionId(listboxId, highlightedIndex) : undefined;
267
283
 
268
284
  // Strip mode from DOM props
269
- const { mode: _, value: _v, defaultValue: _dv, onValueChange: _oc, ...htmlProps } = restProps as Record<string, unknown>;
285
+ const { mode: _, value: _v, defaultValue: _dv, onValueChange: _oc, ...strippedProps } = restProps as Record<string, unknown>;
286
+ const htmlProps = required ? { "aria-required": true, ...strippedProps } : strippedProps;
270
287
 
271
288
  // `role="listbox"` requires an accessible name of its own, and the trigger
272
289
  // can't supply one: its label is the current *value*, and in searchable mode
@@ -311,6 +328,12 @@ const SelectRoot = forwardRef(function SelectRoot<T extends string = string>(pro
311
328
  />
312
329
  )}
313
330
 
331
+ {/* Hidden inputs mirror the selection for native form submission — same
332
+ contract as DatePicker: rendered inline (not portalled) so they sit
333
+ inside the surrounding <form> unless `form` points elsewhere. */}
334
+ {name && !isMultiple && singleValue && <input type="hidden" name={name} value={singleValue} {...(form ? { form } : {})} />}
335
+ {name && isMultiple && multiValue.map((v) => <input key={v} type="hidden" name={`${name}[]`} value={v} {...(form ? { form } : {})} />)}
336
+
314
337
  {open && !collapsed && (
315
338
  <SelectDropdown
316
339
  setFloating={refs.setFloating}
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { FloatingPortal } from "@floating-ui/react";
2
4
  import { cn } from "@sixthshift/design-system/utils";
3
5
  import { Check } from "lucide-react";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { cn } from "@sixthshift/design-system/utils";
2
4
  import { ChevronDown, X } from "lucide-react";
3
5
  import type { HTMLAttributes, Ref } from "react";
@@ -55,6 +57,9 @@ export const SelectTriggerButton = ({
55
57
  className
56
58
  )}
57
59
  title={collapsed ? displayLabel : undefined}
60
+ // The APG select-only combobox: a button-behaving element announced as
61
+ // a combobox, which also admits aria-required — a plain button doesn't.
62
+ role="combobox"
58
63
  aria-haspopup="listbox"
59
64
  aria-expanded={open}
60
65
  // The listbox unmounts when closed, so only reference it while it exists.