@sixthshift/design-system 0.5.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 (412) hide show
  1. package/README.md +132 -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 +2 -2
  17. package/dist/components/Card/Card.d.ts.map +1 -1
  18. package/dist/components/Card/Card.js +3 -2
  19. package/dist/components/Card/Card.js.map +1 -1
  20. package/dist/components/Checkbox/Checkbox.d.ts.map +1 -1
  21. package/dist/components/Checkbox/Checkbox.js +1 -0
  22. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  23. package/dist/components/CheckboxGroup/CheckboxGroup.d.ts.map +1 -1
  24. package/dist/components/CheckboxGroup/CheckboxGroup.js +1 -0
  25. package/dist/components/CheckboxGroup/CheckboxGroup.js.map +1 -1
  26. package/dist/components/Code/Editor/Editor.d.ts.map +1 -1
  27. package/dist/components/Code/Editor/Editor.js +1 -0
  28. package/dist/components/Code/Editor/Editor.js.map +1 -1
  29. package/dist/components/Code/Workspace/StatusBar.d.ts.map +1 -1
  30. package/dist/components/Code/Workspace/StatusBar.js +1 -0
  31. package/dist/components/Code/Workspace/StatusBar.js.map +1 -1
  32. package/dist/components/Code/Workspace/Toolbar.d.ts.map +1 -1
  33. package/dist/components/Code/Workspace/Toolbar.js +1 -0
  34. package/dist/components/Code/Workspace/Toolbar.js.map +1 -1
  35. package/dist/components/Code/Workspace/Workspace.d.ts.map +1 -1
  36. package/dist/components/Code/Workspace/Workspace.js +1 -0
  37. package/dist/components/Code/Workspace/Workspace.js.map +1 -1
  38. package/dist/components/DatePicker/DatePicker.d.ts.map +1 -1
  39. package/dist/components/DatePicker/DatePicker.js +5 -0
  40. package/dist/components/DatePicker/DatePicker.js.map +1 -1
  41. package/dist/components/DatePicker/DateSegments.d.ts.map +1 -1
  42. package/dist/components/DatePicker/DateSegments.js +1 -0
  43. package/dist/components/DatePicker/DateSegments.js.map +1 -1
  44. package/dist/components/DatePicker/datepicker.hooks.d.ts.map +1 -1
  45. package/dist/components/DatePicker/datepicker.hooks.js +1 -0
  46. package/dist/components/DatePicker/datepicker.hooks.js.map +1 -1
  47. package/dist/components/DateRangePicker/DateRangePicker.d.ts.map +1 -1
  48. package/dist/components/DateRangePicker/DateRangePicker.js +1 -0
  49. package/dist/components/DateRangePicker/DateRangePicker.js.map +1 -1
  50. package/dist/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
  51. package/dist/components/DateTimePicker/DateTimePicker.js +5 -0
  52. package/dist/components/DateTimePicker/DateTimePicker.js.map +1 -1
  53. package/dist/components/DateTimePicker/DateTimeSegments.d.ts.map +1 -1
  54. package/dist/components/DateTimePicker/DateTimeSegments.js +1 -0
  55. package/dist/components/DateTimePicker/DateTimeSegments.js.map +1 -1
  56. package/dist/components/DateTimeRangePicker/DateTimeRangePicker.d.ts.map +1 -1
  57. package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js +5 -0
  58. package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js.map +1 -1
  59. package/dist/components/FormField/FormField.d.ts +31 -10
  60. package/dist/components/FormField/FormField.d.ts.map +1 -1
  61. package/dist/components/FormField/FormField.js +46 -17
  62. package/dist/components/FormField/FormField.js.map +1 -1
  63. package/dist/components/FormField/index.d.ts +2 -2
  64. package/dist/components/FormField/index.d.ts.map +1 -1
  65. package/dist/components/FormField/index.js +1 -1
  66. package/dist/components/FormField/index.js.map +1 -1
  67. package/dist/components/HoverCard/HoverCard.d.ts.map +1 -1
  68. package/dist/components/HoverCard/HoverCard.js +1 -0
  69. package/dist/components/HoverCard/HoverCard.js.map +1 -1
  70. package/dist/components/HoverCard/components/HoverCardContent.d.ts.map +1 -1
  71. package/dist/components/HoverCard/components/HoverCardContent.js +1 -0
  72. package/dist/components/HoverCard/components/HoverCardContent.js.map +1 -1
  73. package/dist/components/HoverCard/components/HoverCardContext.d.ts.map +1 -1
  74. package/dist/components/HoverCard/components/HoverCardContext.js +1 -0
  75. package/dist/components/HoverCard/components/HoverCardContext.js.map +1 -1
  76. package/dist/components/HoverCard/components/HoverCardTrigger.d.ts.map +1 -1
  77. package/dist/components/HoverCard/components/HoverCardTrigger.js +1 -0
  78. package/dist/components/HoverCard/components/HoverCardTrigger.js.map +1 -1
  79. package/dist/components/Message/Message.d.ts +1 -1
  80. package/dist/components/Message/Message.js +1 -1
  81. package/dist/components/Modal/Modal.d.ts +14 -9
  82. package/dist/components/Modal/Modal.d.ts.map +1 -1
  83. package/dist/components/Modal/Modal.js +42 -20
  84. package/dist/components/Modal/Modal.js.map +1 -1
  85. package/dist/components/Modal/components/ModalContext.d.ts.map +1 -1
  86. package/dist/components/Modal/components/ModalContext.js +1 -0
  87. package/dist/components/Modal/components/ModalContext.js.map +1 -1
  88. package/dist/components/Modal/components/ModalHeader.d.ts.map +1 -1
  89. package/dist/components/Modal/components/ModalHeader.js +1 -0
  90. package/dist/components/Modal/components/ModalHeader.js.map +1 -1
  91. package/dist/components/Popover/Popover.d.ts.map +1 -1
  92. package/dist/components/Popover/Popover.js +5 -0
  93. package/dist/components/Popover/Popover.js.map +1 -1
  94. package/dist/components/Popover/components/PopoverBody.d.ts.map +1 -1
  95. package/dist/components/Popover/components/PopoverBody.js +1 -0
  96. package/dist/components/Popover/components/PopoverBody.js.map +1 -1
  97. package/dist/components/Popover/components/PopoverClose.d.ts.map +1 -1
  98. package/dist/components/Popover/components/PopoverClose.js +1 -0
  99. package/dist/components/Popover/components/PopoverClose.js.map +1 -1
  100. package/dist/components/Popover/components/PopoverContext.d.ts.map +1 -1
  101. package/dist/components/Popover/components/PopoverContext.js +1 -0
  102. package/dist/components/Popover/components/PopoverContext.js.map +1 -1
  103. package/dist/components/Popover/components/PopoverTrigger.d.ts.map +1 -1
  104. package/dist/components/Popover/components/PopoverTrigger.js +1 -0
  105. package/dist/components/Popover/components/PopoverTrigger.js.map +1 -1
  106. package/dist/components/RadioButton/RadioButton.d.ts.map +1 -1
  107. package/dist/components/RadioButton/RadioButton.js +1 -0
  108. package/dist/components/RadioButton/RadioButton.js.map +1 -1
  109. package/dist/components/RadioButtonGroup/RadioButtonGroup.d.ts.map +1 -1
  110. package/dist/components/RadioButtonGroup/RadioButtonGroup.js +1 -0
  111. package/dist/components/RadioButtonGroup/RadioButtonGroup.js.map +1 -1
  112. package/dist/components/SearchInput/SearchInput.d.ts +10 -7
  113. package/dist/components/SearchInput/SearchInput.d.ts.map +1 -1
  114. package/dist/components/SearchInput/SearchInput.js +11 -6
  115. package/dist/components/SearchInput/SearchInput.js.map +1 -1
  116. package/dist/components/Select/Select.d.ts +6 -0
  117. package/dist/components/Select/Select.d.ts.map +1 -1
  118. package/dist/components/Select/Select.js +9 -3
  119. package/dist/components/Select/Select.js.map +1 -1
  120. package/dist/components/Select/SelectDropdown.d.ts.map +1 -1
  121. package/dist/components/Select/SelectDropdown.js +1 -0
  122. package/dist/components/Select/SelectDropdown.js.map +1 -1
  123. package/dist/components/Select/SelectTrigger.d.ts.map +1 -1
  124. package/dist/components/Select/SelectTrigger.js +5 -1
  125. package/dist/components/Select/SelectTrigger.js.map +1 -1
  126. package/dist/components/Select/useSelectKeyboard.d.ts.map +1 -1
  127. package/dist/components/Select/useSelectKeyboard.js +1 -0
  128. package/dist/components/Select/useSelectKeyboard.js.map +1 -1
  129. package/dist/components/Sheet/Sheet.d.ts.map +1 -1
  130. package/dist/components/Sheet/Sheet.js +23 -9
  131. package/dist/components/Sheet/Sheet.js.map +1 -1
  132. package/dist/components/Sheet/components/SheetContext.d.ts.map +1 -1
  133. package/dist/components/Sheet/components/SheetContext.js +1 -0
  134. package/dist/components/Sheet/components/SheetContext.js.map +1 -1
  135. package/dist/components/Sheet/components/SheetHeader.d.ts.map +1 -1
  136. package/dist/components/Sheet/components/SheetHeader.js +1 -0
  137. package/dist/components/Sheet/components/SheetHeader.js.map +1 -1
  138. package/dist/components/Spinner/Spinner.d.ts +2 -2
  139. package/dist/components/Spinner/Spinner.js +2 -2
  140. package/dist/components/Switch/Switch.d.ts.map +1 -1
  141. package/dist/components/Switch/Switch.js +1 -0
  142. package/dist/components/Switch/Switch.js.map +1 -1
  143. package/dist/components/Tabs/Tabs.d.ts.map +1 -1
  144. package/dist/components/Tabs/Tabs.js +1 -0
  145. package/dist/components/Tabs/Tabs.js.map +1 -1
  146. package/dist/components/Tabs/components/TabsContext.d.ts.map +1 -1
  147. package/dist/components/Tabs/components/TabsContext.js +1 -0
  148. package/dist/components/Tabs/components/TabsContext.js.map +1 -1
  149. package/dist/components/Tabs/components/TabsList.d.ts.map +1 -1
  150. package/dist/components/Tabs/components/TabsList.js +1 -0
  151. package/dist/components/Tabs/components/TabsList.js.map +1 -1
  152. package/dist/components/Tabs/components/TabsPanels.d.ts.map +1 -1
  153. package/dist/components/Tabs/components/TabsPanels.js +1 -0
  154. package/dist/components/Tabs/components/TabsPanels.js.map +1 -1
  155. package/dist/components/Tabs/components/TabsTrigger.d.ts.map +1 -1
  156. package/dist/components/Tabs/components/TabsTrigger.js +2 -1
  157. package/dist/components/Tabs/components/TabsTrigger.js.map +1 -1
  158. package/dist/components/TagChip/TagChip.d.ts.map +1 -1
  159. package/dist/components/TagChip/TagChip.js +1 -0
  160. package/dist/components/TagChip/TagChip.js.map +1 -1
  161. package/dist/components/TagInput/TagInput.d.ts +8 -2
  162. package/dist/components/TagInput/TagInput.d.ts.map +1 -1
  163. package/dist/components/TagInput/TagInput.js +8 -5
  164. package/dist/components/TagInput/TagInput.js.map +1 -1
  165. package/dist/components/Textarea/Textarea.d.ts +21 -5
  166. package/dist/components/Textarea/Textarea.d.ts.map +1 -1
  167. package/dist/components/Textarea/Textarea.js +31 -5
  168. package/dist/components/Textarea/Textarea.js.map +1 -1
  169. package/dist/components/TimePicker/PeriodSelector.d.ts.map +1 -1
  170. package/dist/components/TimePicker/PeriodSelector.js +1 -0
  171. package/dist/components/TimePicker/PeriodSelector.js.map +1 -1
  172. package/dist/components/TimePicker/TimeColumn.d.ts.map +1 -1
  173. package/dist/components/TimePicker/TimeColumn.js +1 -0
  174. package/dist/components/TimePicker/TimeColumn.js.map +1 -1
  175. package/dist/components/TimePicker/TimePicker.d.ts.map +1 -1
  176. package/dist/components/TimePicker/TimePicker.js +5 -0
  177. package/dist/components/TimePicker/TimePicker.js.map +1 -1
  178. package/dist/components/TimePicker/TimeSegments.d.ts.map +1 -1
  179. package/dist/components/TimePicker/TimeSegments.js +1 -0
  180. package/dist/components/TimePicker/TimeSegments.js.map +1 -1
  181. package/dist/components/Toast/Toast.d.ts +16 -2
  182. package/dist/components/Toast/Toast.d.ts.map +1 -1
  183. package/dist/components/Toast/Toast.js +16 -6
  184. package/dist/components/Toast/Toast.js.map +1 -1
  185. package/dist/components/Toggle/Toggle.d.ts +1 -1
  186. package/dist/components/Toggle/Toggle.d.ts.map +1 -1
  187. package/dist/components/Toggle/Toggle.js +2 -1
  188. package/dist/components/Toggle/Toggle.js.map +1 -1
  189. package/dist/components/ToggleGroup/ToggleGroupMultiple.d.ts +2 -0
  190. package/dist/components/ToggleGroup/ToggleGroupMultiple.d.ts.map +1 -1
  191. package/dist/components/ToggleGroup/ToggleGroupMultiple.js +4 -3
  192. package/dist/components/ToggleGroup/ToggleGroupMultiple.js.map +1 -1
  193. package/dist/components/ToggleGroup/ToggleGroupSingle.d.ts +2 -0
  194. package/dist/components/ToggleGroup/ToggleGroupSingle.d.ts.map +1 -1
  195. package/dist/components/ToggleGroup/ToggleGroupSingle.js +4 -3
  196. package/dist/components/ToggleGroup/ToggleGroupSingle.js.map +1 -1
  197. package/dist/components/ToggleGroup/toggleGroup.types.d.ts +7 -0
  198. package/dist/components/ToggleGroup/toggleGroup.types.d.ts.map +1 -1
  199. package/dist/components/Tooltip/Tooltip.d.ts.map +1 -1
  200. package/dist/components/Tooltip/Tooltip.js +1 -0
  201. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  202. package/dist/components/Tooltip/components/TooltipBody.d.ts.map +1 -1
  203. package/dist/components/Tooltip/components/TooltipBody.js +1 -0
  204. package/dist/components/Tooltip/components/TooltipBody.js.map +1 -1
  205. package/dist/components/Tooltip/components/TooltipContext.d.ts.map +1 -1
  206. package/dist/components/Tooltip/components/TooltipContext.js +1 -0
  207. package/dist/components/Tooltip/components/TooltipContext.js.map +1 -1
  208. package/dist/components/Tooltip/components/TooltipTrigger.d.ts.map +1 -1
  209. package/dist/components/Tooltip/components/TooltipTrigger.js +1 -0
  210. package/dist/components/Tooltip/components/TooltipTrigger.js.map +1 -1
  211. package/dist/hooks/useCollapsible.d.ts.map +1 -1
  212. package/dist/hooks/useCollapsible.js +1 -0
  213. package/dist/hooks/useCollapsible.js.map +1 -1
  214. package/dist/hooks/useControllableState.d.ts.map +1 -1
  215. package/dist/hooks/useControllableState.js +1 -0
  216. package/dist/hooks/useControllableState.js.map +1 -1
  217. package/dist/hooks/useDebouncedCallback.d.ts.map +1 -1
  218. package/dist/hooks/useDebouncedCallback.js +1 -0
  219. package/dist/hooks/useDebouncedCallback.js.map +1 -1
  220. package/dist/hooks/useDebouncedState.d.ts.map +1 -1
  221. package/dist/hooks/useDebouncedState.js +1 -0
  222. package/dist/hooks/useDebouncedState.js.map +1 -1
  223. package/dist/hooks/useDeviceFormFactor.d.ts.map +1 -1
  224. package/dist/hooks/useDeviceFormFactor.js +1 -0
  225. package/dist/hooks/useDeviceFormFactor.js.map +1 -1
  226. package/dist/hooks/useLocalStorage.d.ts.map +1 -1
  227. package/dist/hooks/useLocalStorage.js +1 -0
  228. package/dist/hooks/useLocalStorage.js.map +1 -1
  229. package/dist/hooks/useMergedFloatingRef.d.ts.map +1 -1
  230. package/dist/hooks/useMergedFloatingRef.js +1 -0
  231. package/dist/hooks/useMergedFloatingRef.js.map +1 -1
  232. package/dist/hooks/usePresence.d.ts.map +1 -1
  233. package/dist/hooks/usePresence.js +1 -0
  234. package/dist/hooks/usePresence.js.map +1 -1
  235. package/dist/hooks/useStack.d.ts.map +1 -1
  236. package/dist/hooks/useStack.js +34 -10
  237. package/dist/hooks/useStack.js.map +1 -1
  238. package/dist/hooks/useTheme.d.ts.map +1 -1
  239. package/dist/hooks/useTheme.js +1 -0
  240. package/dist/hooks/useTheme.js.map +1 -1
  241. package/dist/internal/PickerField.d.ts.map +1 -1
  242. package/dist/internal/PickerField.js +1 -0
  243. package/dist/internal/PickerField.js.map +1 -1
  244. package/dist/internal/SegmentedField.d.ts.map +1 -1
  245. package/dist/internal/SegmentedField.js +1 -0
  246. package/dist/internal/SegmentedField.js.map +1 -1
  247. package/dist/internal/escapeLayers.d.ts +12 -0
  248. package/dist/internal/escapeLayers.d.ts.map +1 -0
  249. package/dist/internal/escapeLayers.js +48 -0
  250. package/dist/internal/escapeLayers.js.map +1 -0
  251. package/dist/lib/ErrorBoundary.d.ts.map +1 -1
  252. package/dist/lib/ErrorBoundary.js +1 -0
  253. package/dist/lib/ErrorBoundary.js.map +1 -1
  254. package/dist/lib/contexts/ComponentsContext/ComponentsContext.d.ts.map +1 -1
  255. package/dist/lib/contexts/ComponentsContext/ComponentsContext.js +1 -0
  256. package/dist/lib/contexts/ComponentsContext/ComponentsContext.js.map +1 -1
  257. package/dist/lib/contexts/OverlayContext/OverlayContext.d.ts +9 -4
  258. package/dist/lib/contexts/OverlayContext/OverlayContext.d.ts.map +1 -1
  259. package/dist/lib/contexts/OverlayContext/OverlayContext.js +25 -12
  260. package/dist/lib/contexts/OverlayContext/OverlayContext.js.map +1 -1
  261. package/dist/lib/contexts/OverlayContext/ToastStack.d.ts +20 -0
  262. package/dist/lib/contexts/OverlayContext/ToastStack.d.ts.map +1 -0
  263. package/dist/lib/contexts/OverlayContext/ToastStack.js +45 -0
  264. package/dist/lib/contexts/OverlayContext/ToastStack.js.map +1 -0
  265. package/dist/lib/contexts/OverlayContext/hooks/useModal.d.ts.map +1 -1
  266. package/dist/lib/contexts/OverlayContext/hooks/useModal.js +1 -0
  267. package/dist/lib/contexts/OverlayContext/hooks/useModal.js.map +1 -1
  268. package/dist/lib/contexts/OverlayContext/hooks/useToast.d.ts +27 -10
  269. package/dist/lib/contexts/OverlayContext/hooks/useToast.d.ts.map +1 -1
  270. package/dist/lib/contexts/OverlayContext/hooks/useToast.js +31 -49
  271. package/dist/lib/contexts/OverlayContext/hooks/useToast.js.map +1 -1
  272. package/dist/lib/contexts/OverlayContext/index.d.ts +2 -0
  273. package/dist/lib/contexts/OverlayContext/index.d.ts.map +1 -1
  274. package/dist/lib/contexts/OverlayContext/index.js +2 -0
  275. package/dist/lib/contexts/OverlayContext/index.js.map +1 -1
  276. package/dist/lib/contexts/OverlayContext/toastStore.d.ts +68 -0
  277. package/dist/lib/contexts/OverlayContext/toastStore.d.ts.map +1 -0
  278. package/dist/lib/contexts/OverlayContext/toastStore.js +69 -0
  279. package/dist/lib/contexts/OverlayContext/toastStore.js.map +1 -0
  280. package/dist/lib/withErrorBoundary.d.ts.map +1 -1
  281. package/dist/lib/withErrorBoundary.js +1 -0
  282. package/dist/lib/withErrorBoundary.js.map +1 -1
  283. package/dist/lib/withSuspenseAndErrorBoundary.d.ts.map +1 -1
  284. package/dist/lib/withSuspenseAndErrorBoundary.js +1 -0
  285. package/dist/lib/withSuspenseAndErrorBoundary.js.map +1 -1
  286. package/dist/{theme/schema.d.ts → theming/vocabulary.d.ts} +4 -4
  287. package/dist/theming/vocabulary.d.ts.map +1 -0
  288. package/dist/{theme/schema.js → theming/vocabulary.js} +4 -4
  289. package/dist/theming/vocabulary.js.map +1 -0
  290. package/package.json +18 -6
  291. package/src/components/Badge/Badge.tsx +2 -2
  292. package/src/components/Button/Button.tsx +1 -1
  293. package/src/{theme/recipes/button.css → components/Button/button.recipe.css} +2 -2
  294. package/src/components/Calendar/Calendar.tsx +2 -0
  295. package/src/components/Calendar/CalendarView.tsx +2 -0
  296. package/src/components/Calendar/calendar.hooks.ts +2 -0
  297. package/src/components/Card/Card.tsx +4 -2
  298. package/src/components/Checkbox/Checkbox.tsx +2 -0
  299. package/src/components/CheckboxGroup/CheckboxGroup.tsx +2 -0
  300. package/src/components/Code/Editor/Editor.tsx +2 -0
  301. package/src/components/Code/Workspace/StatusBar.tsx +2 -0
  302. package/src/components/Code/Workspace/Toolbar.tsx +2 -0
  303. package/src/components/Code/Workspace/Workspace.tsx +2 -0
  304. package/src/components/DatePicker/DatePicker.tsx +7 -0
  305. package/src/components/DatePicker/DateSegments.tsx +2 -0
  306. package/src/components/DatePicker/datepicker.hooks.ts +2 -0
  307. package/src/components/DateRangePicker/DateRangePicker.tsx +2 -0
  308. package/src/components/DateTimePicker/DateTimePicker.tsx +7 -1
  309. package/src/components/DateTimePicker/DateTimeSegments.tsx +2 -0
  310. package/src/components/DateTimeRangePicker/DateTimeRangePicker.tsx +7 -1
  311. package/src/components/FormField/FormField.tsx +94 -42
  312. package/src/components/FormField/index.ts +2 -2
  313. package/src/components/HoverCard/HoverCard.tsx +2 -0
  314. package/src/components/HoverCard/components/HoverCardContent.tsx +2 -0
  315. package/src/components/HoverCard/components/HoverCardContext.tsx +2 -0
  316. package/src/components/HoverCard/components/HoverCardTrigger.tsx +2 -0
  317. package/src/components/Message/Message.tsx +1 -1
  318. package/src/components/Modal/Modal.tsx +77 -51
  319. package/src/components/Modal/components/ModalContext.tsx +2 -0
  320. package/src/components/Modal/components/ModalHeader.tsx +2 -0
  321. package/src/components/Popover/Popover.tsx +7 -0
  322. package/src/components/Popover/components/PopoverBody.tsx +2 -0
  323. package/src/components/Popover/components/PopoverClose.tsx +2 -0
  324. package/src/components/Popover/components/PopoverContext.tsx +2 -0
  325. package/src/components/Popover/components/PopoverTrigger.tsx +2 -0
  326. package/src/components/RadioButton/RadioButton.tsx +2 -0
  327. package/src/components/RadioButtonGroup/RadioButtonGroup.tsx +2 -0
  328. package/src/components/SearchInput/SearchInput.tsx +15 -8
  329. package/src/components/Select/Select.tsx +24 -1
  330. package/src/components/Select/SelectDropdown.tsx +2 -0
  331. package/src/components/Select/SelectTrigger.tsx +5 -0
  332. package/src/components/Select/useSelectKeyboard.ts +2 -0
  333. package/src/components/Sheet/Sheet.tsx +49 -34
  334. package/src/components/Sheet/components/SheetContext.tsx +2 -0
  335. package/src/components/Sheet/components/SheetHeader.tsx +2 -0
  336. package/src/components/Spinner/Spinner.tsx +2 -2
  337. package/src/components/Switch/Switch.tsx +2 -0
  338. package/src/components/Tabs/Tabs.tsx +2 -0
  339. package/src/components/Tabs/components/TabsContext.tsx +2 -0
  340. package/src/components/Tabs/components/TabsList.tsx +2 -0
  341. package/src/components/Tabs/components/TabsPanels.tsx +2 -0
  342. package/src/components/Tabs/components/TabsTrigger.tsx +3 -1
  343. package/src/{theme/recipes/tabs.css → components/Tabs/tabs.recipe.css} +1 -1
  344. package/src/components/TagChip/TagChip.tsx +2 -0
  345. package/src/components/TagInput/TagInput.tsx +15 -6
  346. package/src/components/Textarea/Textarea.tsx +42 -6
  347. package/src/{theme/recipes/textarea.css → components/Textarea/textarea.recipe.css} +1 -1
  348. package/src/components/TimePicker/PeriodSelector.tsx +2 -0
  349. package/src/components/TimePicker/TimeColumn.tsx +2 -0
  350. package/src/components/TimePicker/TimePicker.tsx +7 -0
  351. package/src/components/TimePicker/TimeSegments.tsx +2 -0
  352. package/src/components/Toast/Toast.tsx +22 -6
  353. package/src/components/Toggle/Toggle.tsx +3 -1
  354. package/src/components/ToggleGroup/ToggleGroupMultiple.tsx +6 -0
  355. package/src/components/ToggleGroup/ToggleGroupSingle.tsx +6 -0
  356. package/src/components/ToggleGroup/toggleGroup.types.ts +7 -0
  357. package/src/components/Tooltip/Tooltip.tsx +2 -0
  358. package/src/components/Tooltip/components/TooltipBody.tsx +2 -0
  359. package/src/components/Tooltip/components/TooltipContext.tsx +2 -0
  360. package/src/components/Tooltip/components/TooltipTrigger.tsx +2 -0
  361. package/src/hooks/useCollapsible.ts +2 -0
  362. package/src/hooks/useControllableState.ts +2 -0
  363. package/src/hooks/useDebouncedCallback.ts +2 -0
  364. package/src/hooks/useDebouncedState.ts +2 -0
  365. package/src/hooks/useDeviceFormFactor.ts +2 -0
  366. package/src/hooks/useLocalStorage.ts +2 -0
  367. package/src/hooks/useMergedFloatingRef.ts +2 -0
  368. package/src/hooks/usePresence.ts +2 -0
  369. package/src/hooks/useStack.ts +35 -12
  370. package/src/hooks/useTheme.ts +2 -0
  371. package/src/internal/PickerField.tsx +2 -0
  372. package/src/internal/SegmentedField.tsx +2 -0
  373. package/src/internal/escapeLayers.ts +53 -0
  374. package/src/lib/ErrorBoundary.tsx +2 -0
  375. package/src/lib/contexts/ComponentsContext/ComponentsContext.tsx +2 -0
  376. package/src/lib/contexts/OverlayContext/OverlayContext.tsx +42 -26
  377. package/src/lib/contexts/OverlayContext/ToastStack.tsx +71 -0
  378. package/src/lib/contexts/OverlayContext/hooks/useModal.tsx +2 -0
  379. package/src/lib/contexts/OverlayContext/hooks/useToast.ts +30 -73
  380. package/src/lib/contexts/OverlayContext/index.ts +2 -0
  381. package/src/lib/contexts/OverlayContext/toastStore.ts +138 -0
  382. package/src/lib/withErrorBoundary.tsx +2 -0
  383. package/src/lib/withSuspenseAndErrorBoundary.tsx +2 -0
  384. package/src/theme/linen/index.css +11 -0
  385. package/src/theme/linen/palette.css +87 -0
  386. package/src/theme/linen/theme.css +371 -0
  387. package/src/theming/tailwind.css +356 -0
  388. package/src/{theme/schema.ts → theming/vocabulary.ts} +3 -3
  389. package/dist/theme/index.d.ts +0 -9
  390. package/dist/theme/index.d.ts.map +0 -1
  391. package/dist/theme/index.js +0 -8
  392. package/dist/theme/index.js.map +0 -1
  393. package/dist/theme/schema.d.ts.map +0 -1
  394. package/dist/theme/schema.js.map +0 -1
  395. package/src/theme/index.ts +0 -39
  396. package/src/theme/recipes/index.css +0 -28
  397. package/src/theme/tokens.css +0 -824
  398. /package/src/{theme/recipes/badge.css → components/Badge/badge.recipe.css} +0 -0
  399. /package/src/{theme/recipes/card.css → components/Card/card.recipe.css} +0 -0
  400. /package/src/{theme/recipes/checkbox.css → components/Checkbox/checkbox.recipe.css} +0 -0
  401. /package/src/{theme/recipes/field.css → components/Field/field.recipe.css} +0 -0
  402. /package/src/{theme/recipes/input.css → components/Input/input.recipe.css} +0 -0
  403. /package/src/{theme/recipes/message.css → components/Message/message.recipe.css} +0 -0
  404. /package/src/{theme/recipes/modal.css → components/Modal/modal.recipe.css} +0 -0
  405. /package/src/{theme/recipes/radio-button.css → components/RadioButton/radio-button.recipe.css} +0 -0
  406. /package/src/{theme/recipes/search-input.css → components/SearchInput/search-input.recipe.css} +0 -0
  407. /package/src/{theme/recipes/select.css → components/Select/select.recipe.css} +0 -0
  408. /package/src/{theme/recipes/sheet.css → components/Sheet/sheet.recipe.css} +0 -0
  409. /package/src/{theme/recipes/spinner.css → components/Spinner/spinner.recipe.css} +0 -0
  410. /package/src/{theme/recipes/switch.css → components/Switch/switch.recipe.css} +0 -0
  411. /package/src/{theme/recipes/tag-chip.css → components/TagChip/tag-chip.recipe.css} +0 -0
  412. /package/src/{theme/recipes/toggle.css → components/Toggle/toggle.recipe.css} +0 -0
@@ -8,7 +8,7 @@
8
8
  * or intent the library never shipped.
9
9
  *
10
10
  * The grammar is `--button-{context}[-{state}]` — the same `context` and
11
- * `state` words the semantic layer uses (src/theme/schema.ts). Note what is
11
+ * `state` words the semantic layer uses (src/theming/vocabulary.ts). Note what is
12
12
  * deliberately *absent*: intent and weight never appear in a token name. They
13
13
  * select, via the `data-` attributes below. Putting them in the name
14
14
  * (`--button-bg-danger`) would restore the combinatorial explosion this layer
@@ -47,7 +47,7 @@
47
47
  --button-fg: var(--fg-on-brand);
48
48
  }
49
49
 
50
- /* `neutral` means *no colour family* — the grey tiers. It used to resolve to
50
+ /* `neutral` means *no colour family* — the grey ramp. It used to resolve to
51
51
  the brand fill, which made `intent="neutral"` mean one thing on Button and
52
52
  another on Badge and Message, where it was already grey. Button
53
53
  was the outlier; this is the cell that ends that. A brand-coloured button
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import * as React from "react";
2
4
  import { useCallback, useMemo } from "react";
3
5
  import {
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  /**
2
4
  * CalendarView — the Temporal-typed calendar grid.
3
5
  *
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { useCallback, useMemo } from "react";
2
4
  import { addDays, formatDateMediumYear, isPlainDate, isSameDay, isSameMonth, startOfMonth, startOfWeek, Temporal, today } from "../../date-time";
3
5
  import type { DateRangeValue, DateSelectionState, DisabledDateMatcher, SelectionMode } from "./calendar.types";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { cn } from "@sixthshift/design-system/utils";
2
4
  import { cva, type VariantProps } from "class-variance-authority";
3
5
  import * as React from "react";
@@ -6,7 +8,7 @@ import * as React from "react";
6
8
  * Geometry only, plus the five `--card-*` component tokens.
7
9
  *
8
10
  * Every colour reads a `--card-*` token whose value is decided by
9
- * src/theme/recipes/card.css. `size` stays here: it is pure geometry
11
+ * src/components/Card/card.recipe.css. `size` stays here: it is pure geometry
10
12
  * (padding, header gap, corner radius), not colour, so it has no reason to
11
13
  * move — the same split Message.tsx documents.
12
14
  *
@@ -108,7 +110,7 @@ export type CardProps = Omit<React.HTMLAttributes<HTMLDivElement>, "title"> &
108
110
  * `role="button"`, `tabIndex={0}`, and both Enter and Space activate it, not
109
111
  * just a pointer click. Omit `onClick` for a purely presentational card.
110
112
  *
111
- * Colour reads `--card-*` component tokens (src/theme/recipes/card.css) —
113
+ * Colour reads `--card-*` component tokens (src/components/Card/card.recipe.css) —
112
114
  * the same seam Button and Badge use — so a consumer can re-point them
113
115
  * without a release.
114
116
  */
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { useControllableState } from "@sixthshift/design-system/hooks";
2
4
  import { Label } from "@sixthshift/design-system/label";
3
5
  import { cn } from "@sixthshift/design-system/utils";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { Checkbox } from "@sixthshift/design-system/checkbox";
2
4
  import { useControllableState } from "@sixthshift/design-system/hooks";
3
5
  import { cn } from "@sixthshift/design-system/utils";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import MonacoEditor from "@monaco-editor/react";
2
4
  import type { editor } from "monaco-editor";
3
5
  import type { HTMLAttributes } from "react";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import type { HTMLAttributes, ReactNode } from "react";
2
4
 
3
5
  export type StatusBarItem = {
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import type { HTMLAttributes, ReactNode } from "react";
2
4
  import { Button } from "../../Button";
3
5
 
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import type { HTMLAttributes, ReactNode } from "react";
2
4
  import { useState } from "react";
3
5
  import { Editor } from "../Editor";
@@ -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";
@@ -19,6 +21,7 @@ import {
19
21
  toISODateRange,
20
22
  } from "../../date-time";
21
23
  import { PickerField } from "../../internal";
24
+ import { useEscapeLayer } from "../../internal/escapeLayers";
22
25
  import { CalendarView } from "../Calendar/CalendarView";
23
26
  import { isDateDisabled } from "../Calendar/calendar.hooks";
24
27
  import type { PresetOption } from "../Calendar/calendar.types";
@@ -236,6 +239,10 @@ export const DatePicker = React.forwardRef<HTMLDivElement, DatePickerProps>((pro
236
239
 
237
240
  const click = useClick(context);
238
241
  const dismiss = useDismiss(context);
242
+
243
+ // While open, an Escape press closes this picker's popover and must not
244
+ // also close a Modal/Sheet it is rendered inside.
245
+ useEscapeLayer(open);
239
246
  const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss]);
240
247
 
241
248
  // IDs for accessibility
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { type Temporal, today, toISODate } from "../../date-time";
2
4
  import { SegmentedField, type SegmentSpec, type SegmentValues } from "../../internal";
3
5
  import type { DateSegmentOrder } from "./datepicker.types";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { useCallback } from "react";
2
4
  import { formatDateMediumYear, isPlainDate, Temporal } from "../../date-time";
3
5
  import type { DatePickerMode, DateRangeValue, DateSelectionState, DisabledDateMatcher } from "./datepicker.types";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import * as React from "react";
2
4
  import { useMemo } from "react";
3
5
  import { addDaysISO, startOfMonthISO, todayISO } from "../../date-time";
@@ -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";
@@ -13,8 +15,8 @@ import {
13
15
  today,
14
16
  toISOInstantOrUndefined,
15
17
  } from "../../date-time";
16
-
17
18
  import { PickerField } from "../../internal";
19
+ import { useEscapeLayer } from "../../internal/escapeLayers";
18
20
  import { Button } from "../Button";
19
21
  import { CalendarView } from "../Calendar/CalendarView";
20
22
  import { isDateDisabled } from "../Calendar/calendar.hooks";
@@ -251,6 +253,10 @@ export const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerPro
251
253
 
252
254
  const click = useClick(context);
253
255
  const dismiss = useDismiss(context);
256
+
257
+ // While open, an Escape press closes this picker's popover and must not
258
+ // also close a Modal/Sheet it is rendered inside.
259
+ useEscapeLayer(open);
254
260
  const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss]);
255
261
 
256
262
  // IDs for accessibility
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { Temporal } from "../../date-time";
2
4
  import { SegmentedField, type SegmentSpec, type SegmentValues } from "../../internal";
3
5
  import { dateFromSegments, dateSpec, dateToSegments, parseDatePasteIn } from "../DatePicker/DateSegments";
@@ -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";