@duro-app/ui 5.11.0 → 5.13.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 (331) hide show
  1. package/dist/Table-Bms2ftt3.js +1694 -0
  2. package/dist/Table-Bms2ftt3.js.map +1 -0
  3. package/dist/compiler-runtime-BuugS4D0.js.map +1 -1
  4. package/dist/components/Alert/Alert.d.ts.map +1 -1
  5. package/dist/components/AppShell/native.css.d.ts +53 -0
  6. package/dist/components/AppShell/native.css.d.ts.map +1 -0
  7. package/dist/components/Button/Button.d.ts +2 -1
  8. package/dist/components/Button/Button.d.ts.map +1 -1
  9. package/dist/components/Button/styles.css.d.ts +1 -0
  10. package/dist/components/Button/styles.css.d.ts.map +1 -1
  11. package/dist/components/Callout/Callout.d.ts.map +1 -1
  12. package/dist/components/Callout/styles.css.d.ts +3 -0
  13. package/dist/components/Callout/styles.css.d.ts.map +1 -1
  14. package/dist/components/Checkbox/Checkbox.d.ts.map +1 -1
  15. package/dist/components/ConfirmDialog/ConfirmDialog.d.ts.map +1 -1
  16. package/dist/components/DetailPanel/DetailPanel.d.ts.map +1 -1
  17. package/dist/components/Dialog/Dialog.d.ts +2 -17
  18. package/dist/components/Dialog/Dialog.d.ts.map +1 -1
  19. package/dist/components/Dialog/Dialog.stories.d.ts +4 -0
  20. package/dist/components/Dialog/Dialog.stories.d.ts.map +1 -1
  21. package/dist/components/Dialog/native.css.d.ts +82 -0
  22. package/dist/components/Dialog/native.css.d.ts.map +1 -0
  23. package/dist/components/Dialog/parts.css.d.ts +82 -0
  24. package/dist/components/Dialog/parts.css.d.ts.map +1 -0
  25. package/dist/components/Dialog/parts.d.ts +35 -0
  26. package/dist/components/Dialog/parts.d.ts.map +1 -0
  27. package/dist/components/Dialog/styles.css.d.ts +1 -74
  28. package/dist/components/Dialog/styles.css.d.ts.map +1 -1
  29. package/dist/components/Drawer/Drawer.d.ts +7 -35
  30. package/dist/components/Drawer/Drawer.d.ts.map +1 -1
  31. package/dist/components/Drawer/Drawer.meta.d.ts.map +1 -1
  32. package/dist/components/Drawer/DrawerScroll.stories.d.ts.map +1 -1
  33. package/dist/components/Drawer/DrawerSnap.stories.d.ts +20 -0
  34. package/dist/components/Drawer/DrawerSnap.stories.d.ts.map +1 -0
  35. package/dist/components/Drawer/native.css.d.ts +55 -0
  36. package/dist/components/Drawer/native.css.d.ts.map +1 -0
  37. package/dist/components/Drawer/parts.css.d.ts +96 -0
  38. package/dist/components/Drawer/parts.css.d.ts.map +1 -0
  39. package/dist/components/Drawer/parts.d.ts +56 -0
  40. package/dist/components/Drawer/parts.d.ts.map +1 -0
  41. package/dist/components/Drawer/sheetPhysics.d.ts +56 -0
  42. package/dist/components/Drawer/sheetPhysics.d.ts.map +1 -0
  43. package/dist/components/Drawer/styles.css.d.ts +15 -58
  44. package/dist/components/Drawer/styles.css.d.ts.map +1 -1
  45. package/dist/components/Drawer/touchInput.d.ts +35 -0
  46. package/dist/components/Drawer/touchInput.d.ts.map +1 -0
  47. package/dist/components/Drawer/useDrawerRoot.d.ts +33 -0
  48. package/dist/components/Drawer/useDrawerRoot.d.ts.map +1 -0
  49. package/dist/components/Drawer/useSheetHeights.d.ts +7 -0
  50. package/dist/components/Drawer/useSheetHeights.d.ts.map +1 -0
  51. package/dist/components/Drawer/useSwipeDismiss.d.ts +13 -2
  52. package/dist/components/Drawer/useSwipeDismiss.d.ts.map +1 -1
  53. package/dist/components/Grid/styles.css.d.ts +1 -0
  54. package/dist/components/Grid/styles.css.d.ts.map +1 -1
  55. package/dist/components/Icon/Icon.d.ts +3 -9
  56. package/dist/components/Icon/Icon.d.ts.map +1 -1
  57. package/dist/components/Icon/geometry.d.ts +60 -0
  58. package/dist/components/Icon/geometry.d.ts.map +1 -0
  59. package/dist/components/Icon/types.d.ts +15 -0
  60. package/dist/components/Icon/types.d.ts.map +1 -0
  61. package/dist/components/Input/Input.d.ts +5 -1
  62. package/dist/components/Input/Input.d.ts.map +1 -1
  63. package/dist/components/Input/styles.css.d.ts +1 -0
  64. package/dist/components/Input/styles.css.d.ts.map +1 -1
  65. package/dist/components/LinkButton/styles.css.d.ts +1 -0
  66. package/dist/components/LinkButton/styles.css.d.ts.map +1 -1
  67. package/dist/components/Menu/Menu.d.ts +7 -1
  68. package/dist/components/Menu/Menu.d.ts.map +1 -1
  69. package/dist/components/Menu/native.css.d.ts +14 -0
  70. package/dist/components/Menu/native.css.d.ts.map +1 -0
  71. package/dist/components/Menu/useMenuOpen.d.ts +16 -0
  72. package/dist/components/Menu/useMenuOpen.d.ts.map +1 -0
  73. package/dist/components/Menu/useMenuRoot.d.ts +3 -6
  74. package/dist/components/Menu/useMenuRoot.d.ts.map +1 -1
  75. package/dist/components/Meter/styles.css.d.ts +1 -0
  76. package/dist/components/Meter/styles.css.d.ts.map +1 -1
  77. package/dist/components/PageShell/styles.css.d.ts +2 -0
  78. package/dist/components/PageShell/styles.css.d.ts.map +1 -1
  79. package/dist/components/Popover/native.css.d.ts +11 -0
  80. package/dist/components/Popover/native.css.d.ts.map +1 -0
  81. package/dist/components/RadioGroup/RadioGroup.d.ts +1 -17
  82. package/dist/components/RadioGroup/RadioGroup.d.ts.map +1 -1
  83. package/dist/components/RadioGroup/RadioGroup.stories.d.ts +3 -0
  84. package/dist/components/RadioGroup/RadioGroup.stories.d.ts.map +1 -1
  85. package/dist/components/RadioGroup/group.d.ts +31 -0
  86. package/dist/components/RadioGroup/group.d.ts.map +1 -0
  87. package/dist/components/RadioGroup/styles.css.d.ts +10 -0
  88. package/dist/components/RadioGroup/styles.css.d.ts.map +1 -1
  89. package/dist/components/ScrollArea/styles.css.d.ts +1 -0
  90. package/dist/components/ScrollArea/styles.css.d.ts.map +1 -1
  91. package/dist/components/SideNav/Chevron.d.ts +4 -0
  92. package/dist/components/SideNav/Chevron.d.ts.map +1 -0
  93. package/dist/components/SideNav/SideNav.d.ts.map +1 -1
  94. package/dist/components/SideNav/styles.css.d.ts +6 -0
  95. package/dist/components/SideNav/styles.css.d.ts.map +1 -1
  96. package/dist/components/SideNav/types.d.ts +6 -0
  97. package/dist/components/SideNav/types.d.ts.map +1 -0
  98. package/dist/components/Spinner/Spinner.d.ts +2 -6
  99. package/dist/components/Spinner/Spinner.d.ts.map +1 -1
  100. package/dist/components/Spinner/Spinner.types.d.ts +6 -0
  101. package/dist/components/Spinner/Spinner.types.d.ts.map +1 -0
  102. package/dist/components/Spinner/ring.css.d.ts +39 -0
  103. package/dist/components/Spinner/ring.css.d.ts.map +1 -0
  104. package/dist/components/Spinner/styles.css.d.ts +1 -28
  105. package/dist/components/Spinner/styles.css.d.ts.map +1 -1
  106. package/dist/components/SplitPane/SplitPane.d.ts +38 -0
  107. package/dist/components/SplitPane/SplitPane.d.ts.map +1 -0
  108. package/dist/components/SplitPane/SplitPane.meta.d.ts +3 -0
  109. package/dist/components/SplitPane/SplitPane.meta.d.ts.map +1 -0
  110. package/dist/components/SplitPane/SplitPane.stories.d.ts +17 -0
  111. package/dist/components/SplitPane/SplitPane.stories.d.ts.map +1 -0
  112. package/dist/components/SplitPane/styles.css.d.ts +78 -0
  113. package/dist/components/SplitPane/styles.css.d.ts.map +1 -0
  114. package/dist/components/StatusIcon/StatusIcon.d.ts.map +1 -1
  115. package/dist/components/StatusIcon/styles.css.d.ts +3 -0
  116. package/dist/components/StatusIcon/styles.css.d.ts.map +1 -1
  117. package/dist/components/Switch/Switch.d.ts +2 -18
  118. package/dist/components/Switch/Switch.d.ts.map +1 -1
  119. package/dist/components/Switch/Switch.types.d.ts +18 -0
  120. package/dist/components/Switch/Switch.types.d.ts.map +1 -0
  121. package/dist/components/TableOfContents/styles.css.d.ts +1 -0
  122. package/dist/components/TableOfContents/styles.css.d.ts.map +1 -1
  123. package/dist/components/Tabs/TabLayout.d.ts +7 -0
  124. package/dist/components/Tabs/TabLayout.d.ts.map +1 -0
  125. package/dist/components/Tabs/Tabs.d.ts.map +1 -1
  126. package/dist/components/Tabs/indicator.d.ts +28 -0
  127. package/dist/components/Tabs/indicator.d.ts.map +1 -0
  128. package/dist/components/Tabs/styles.css.d.ts +4 -0
  129. package/dist/components/Tabs/styles.css.d.ts.map +1 -1
  130. package/dist/components/Tabs/useTabIndicator.d.ts +4 -0
  131. package/dist/components/Tabs/useTabIndicator.d.ts.map +1 -0
  132. package/dist/components/Tabs/useTabKeys.d.ts +13 -0
  133. package/dist/components/Tabs/useTabKeys.d.ts.map +1 -0
  134. package/dist/components/Tag/RemoveGlyph.d.ts +4 -0
  135. package/dist/components/Tag/RemoveGlyph.d.ts.map +1 -0
  136. package/dist/components/Tag/Tag.d.ts +2 -24
  137. package/dist/components/Tag/Tag.d.ts.map +1 -1
  138. package/dist/components/Tag/styles.css.d.ts +8 -0
  139. package/dist/components/Tag/styles.css.d.ts.map +1 -1
  140. package/dist/components/Tag/types.d.ts +39 -0
  141. package/dist/components/Tag/types.d.ts.map +1 -0
  142. package/dist/components/Textarea/styles.css.d.ts +1 -0
  143. package/dist/components/Textarea/styles.css.d.ts.map +1 -1
  144. package/dist/components/Toast/ToastProvider.d.ts +2 -21
  145. package/dist/components/Toast/ToastProvider.d.ts.map +1 -1
  146. package/dist/components/Toast/native.css.d.ts +81 -0
  147. package/dist/components/Toast/native.css.d.ts.map +1 -0
  148. package/dist/components/Toast/store.d.ts +47 -0
  149. package/dist/components/Toast/store.d.ts.map +1 -0
  150. package/dist/components/Toggle/Toggle.d.ts.map +1 -1
  151. package/dist/components/Toggle/styles.css.d.ts +11 -2
  152. package/dist/components/Toggle/styles.css.d.ts.map +1 -1
  153. package/dist/components/Tooltip/TooltipHint.d.ts +9 -0
  154. package/dist/components/Tooltip/TooltipHint.d.ts.map +1 -0
  155. package/dist/components/Tree/Tree.d.ts.map +1 -1
  156. package/dist/components/Tree/styles.css.d.ts +6 -0
  157. package/dist/components/Tree/styles.css.d.ts.map +1 -1
  158. package/dist/components/Tree/useTreeKeyboard.d.ts +19 -0
  159. package/dist/components/Tree/useTreeKeyboard.d.ts.map +1 -0
  160. package/dist/components/VirtualList/VirtualList.d.ts +33 -0
  161. package/dist/components/VirtualList/VirtualList.d.ts.map +1 -0
  162. package/dist/components/VirtualList/VirtualList.meta.d.ts +3 -0
  163. package/dist/components/VirtualList/VirtualList.meta.d.ts.map +1 -0
  164. package/dist/components/VirtualList/VirtualList.stories.d.ts +15 -0
  165. package/dist/components/VirtualList/VirtualList.stories.d.ts.map +1 -0
  166. package/dist/components/VirtualList/styles.css.d.ts +28 -0
  167. package/dist/components/VirtualList/styles.css.d.ts.map +1 -0
  168. package/dist/components/VirtualTable/styles.css.d.ts +6 -0
  169. package/dist/components/VirtualTable/styles.css.d.ts.map +1 -1
  170. package/dist/index.css +1 -1
  171. package/dist/index.d.ts +3 -1
  172. package/dist/index.d.ts.map +1 -1
  173. package/dist/index.js +6202 -4794
  174. package/dist/index.js.map +1 -1
  175. package/dist/shared/ButtonHost.d.ts +11 -0
  176. package/dist/shared/ButtonHost.d.ts.map +1 -0
  177. package/dist/shared/portal/hostRegistry.d.ts +33 -0
  178. package/dist/shared/portal/hostRegistry.d.ts.map +1 -0
  179. package/dist/shared/pressableChild.d.ts +14 -0
  180. package/dist/shared/pressableChild.d.ts.map +1 -0
  181. package/dist/shared/useOverlayOpenState.d.ts +40 -0
  182. package/dist/shared/useOverlayOpenState.d.ts.map +1 -0
  183. package/dist/table.js +141 -135
  184. package/dist/table.js.map +1 -1
  185. package/package.json +34 -4
  186. package/src/components/Alert/Alert.tsx +14 -2
  187. package/src/components/AppShell/AppShell.meta.ts +1 -1
  188. package/src/components/AppShell/AppShell.native.tsx +174 -0
  189. package/src/components/AppShell/native.css.ts +56 -0
  190. package/src/components/Button/Button.tsx +6 -5
  191. package/src/components/Button/styles.css.ts +2 -0
  192. package/src/components/Callout/Callout.tsx +17 -3
  193. package/src/components/Callout/styles.css.ts +5 -0
  194. package/src/components/Checkbox/Checkbox.native.tsx +67 -0
  195. package/src/components/Checkbox/Checkbox.tsx +1 -50
  196. package/src/components/ConfirmDialog/ConfirmDialog.tsx +3 -0
  197. package/src/components/DetailPanel/DetailPanel.tsx +9 -45
  198. package/src/components/Dialog/Dialog.native.tsx +296 -0
  199. package/src/components/Dialog/Dialog.stories.tsx +71 -0
  200. package/src/components/Dialog/Dialog.tsx +41 -130
  201. package/src/components/Dialog/native.css.ts +78 -0
  202. package/src/components/Dialog/parts.css.ts +101 -0
  203. package/src/components/Dialog/parts.tsx +61 -0
  204. package/src/components/Dialog/styles.css.ts +2 -89
  205. package/src/components/Drawer/Drawer.meta.ts +2 -1
  206. package/src/components/Drawer/Drawer.native.tsx +469 -0
  207. package/src/components/Drawer/Drawer.tsx +63 -179
  208. package/src/components/Drawer/DrawerScroll.stories.tsx +4 -54
  209. package/src/components/Drawer/DrawerSnap.stories.tsx +205 -0
  210. package/src/components/Drawer/native.css.ts +58 -0
  211. package/src/components/Drawer/parts.css.ts +125 -0
  212. package/src/components/Drawer/parts.tsx +110 -0
  213. package/src/components/Drawer/sheetPhysics.ts +103 -0
  214. package/src/components/Drawer/styles.css.ts +19 -74
  215. package/src/components/Drawer/touchInput.ts +81 -0
  216. package/src/components/Drawer/useDrawerRoot.ts +90 -0
  217. package/src/components/Drawer/useSheetHeights.ts +42 -0
  218. package/src/components/Drawer/useSwipeDismiss.ts +146 -23
  219. package/src/components/Grid/styles.css.ts +2 -0
  220. package/src/components/Icon/Icon.native.tsx +54 -0
  221. package/src/components/Icon/Icon.tsx +19 -430
  222. package/src/components/Icon/geometry.ts +456 -0
  223. package/src/components/Icon/types.ts +74 -0
  224. package/src/components/Input/Input.tsx +8 -0
  225. package/src/components/Input/styles.css.ts +2 -0
  226. package/src/components/LinkButton/styles.css.ts +2 -0
  227. package/src/components/Menu/Menu.meta.ts +1 -1
  228. package/src/components/Menu/Menu.native.tsx +268 -0
  229. package/src/components/Menu/Menu.tsx +11 -2
  230. package/src/components/Menu/native.css.ts +18 -0
  231. package/src/components/Menu/useMenuOpen.ts +33 -0
  232. package/src/components/Menu/useMenuRoot.ts +7 -26
  233. package/src/components/Meter/styles.css.ts +2 -0
  234. package/src/components/PageShell/styles.css.ts +4 -0
  235. package/src/components/Popover/Popover.meta.ts +1 -1
  236. package/src/components/Popover/Popover.native.tsx +205 -0
  237. package/src/components/Popover/native.css.ts +13 -0
  238. package/src/components/RadioGroup/RadioGroup.meta.ts +1 -1
  239. package/src/components/RadioGroup/RadioGroup.native.tsx +48 -0
  240. package/src/components/RadioGroup/RadioGroup.stories.tsx +64 -1
  241. package/src/components/RadioGroup/RadioGroup.tsx +8 -83
  242. package/src/components/RadioGroup/group.tsx +85 -0
  243. package/src/components/RadioGroup/styles.css.ts +13 -0
  244. package/src/components/ScrollArea/styles.css.ts +2 -0
  245. package/src/components/SideNav/Chevron.native.tsx +20 -0
  246. package/src/components/SideNav/Chevron.tsx +20 -0
  247. package/src/components/SideNav/SideNav.meta.ts +1 -1
  248. package/src/components/SideNav/SideNav.tsx +57 -41
  249. package/src/components/SideNav/styles.css.ts +8 -0
  250. package/src/components/SideNav/types.ts +6 -0
  251. package/src/components/Spinner/Spinner.native.tsx +37 -0
  252. package/src/components/Spinner/Spinner.tsx +6 -15
  253. package/src/components/Spinner/Spinner.types.ts +6 -0
  254. package/src/components/Spinner/ring.css.ts +45 -0
  255. package/src/components/Spinner/styles.css.ts +2 -32
  256. package/src/components/SplitPane/SplitPane.meta.ts +40 -0
  257. package/src/components/SplitPane/SplitPane.native.tsx +47 -0
  258. package/src/components/SplitPane/SplitPane.stories.tsx +148 -0
  259. package/src/components/SplitPane/SplitPane.tsx +218 -0
  260. package/src/components/SplitPane/styles.css.ts +81 -0
  261. package/src/components/StatusIcon/StatusIcon.tsx +13 -2
  262. package/src/components/StatusIcon/styles.css.ts +5 -0
  263. package/src/components/Switch/Switch.native.tsx +45 -0
  264. package/src/components/Switch/Switch.tsx +22 -44
  265. package/src/components/Switch/Switch.types.ts +18 -0
  266. package/src/components/TableOfContents/styles.css.ts +2 -0
  267. package/src/components/Tabs/TabLayout.native.tsx +9 -0
  268. package/src/components/Tabs/TabLayout.tsx +6 -0
  269. package/src/components/Tabs/Tabs.meta.ts +1 -1
  270. package/src/components/Tabs/Tabs.tsx +38 -136
  271. package/src/components/Tabs/indicator.ts +36 -0
  272. package/src/components/Tabs/styles.css.ts +7 -0
  273. package/src/components/Tabs/useTabIndicator.native.ts +35 -0
  274. package/src/components/Tabs/useTabIndicator.ts +49 -0
  275. package/src/components/Tabs/useTabKeys.native.ts +2 -0
  276. package/src/components/Tabs/useTabKeys.ts +99 -0
  277. package/src/components/Tag/RemoveGlyph.native.tsx +17 -0
  278. package/src/components/Tag/RemoveGlyph.tsx +10 -0
  279. package/src/components/Tag/Tag.meta.ts +1 -1
  280. package/src/components/Tag/Tag.native.tsx +70 -0
  281. package/src/components/Tag/Tag.tsx +9 -37
  282. package/src/components/Tag/styles.css.ts +9 -0
  283. package/src/components/Tag/types.ts +43 -0
  284. package/src/components/Textarea/styles.css.ts +2 -0
  285. package/src/components/ThemeProvider/ThemeProvider.native.tsx +70 -10
  286. package/src/components/Toast/Toast.meta.ts +1 -1
  287. package/src/components/Toast/ToastProvider.native.tsx +226 -0
  288. package/src/components/Toast/ToastProvider.tsx +22 -84
  289. package/src/components/Toast/native.css.ts +78 -0
  290. package/src/components/Toast/store.ts +96 -0
  291. package/src/components/Toggle/Toggle.tsx +5 -3
  292. package/src/components/Toggle/styles.css.ts +14 -2
  293. package/src/components/Tooltip/Tooltip.meta.ts +1 -1
  294. package/src/components/Tooltip/Tooltip.native.tsx +36 -0
  295. package/src/components/Tooltip/TooltipHint.ts +13 -0
  296. package/src/components/Tree/Tree.meta.ts +1 -1
  297. package/src/components/Tree/Tree.tsx +23 -135
  298. package/src/components/Tree/styles.css.ts +6 -0
  299. package/src/components/Tree/useTreeKeyboard.native.ts +5 -0
  300. package/src/components/Tree/useTreeKeyboard.ts +152 -0
  301. package/src/components/VirtualList/VirtualList.meta.ts +39 -0
  302. package/src/components/VirtualList/VirtualList.stories.tsx +95 -0
  303. package/src/components/VirtualList/VirtualList.tsx +103 -0
  304. package/src/components/VirtualList/styles.css.ts +31 -0
  305. package/src/components/VirtualTable/styles.css.ts +12 -0
  306. package/src/hooks/useContainerBelow.native.ts +30 -0
  307. package/src/index.native.ts +61 -1
  308. package/src/index.ts +8 -1
  309. package/src/native-guard/globals.d.ts +10 -0
  310. package/src/native-guard/react-dom.d.ts +3 -0
  311. package/src/shared/ButtonHost.native.tsx +70 -0
  312. package/src/shared/ButtonHost.tsx +12 -0
  313. package/src/shared/accessibilityFocus.native.ts +23 -0
  314. package/src/shared/portal/AnchoredOverlay.native.tsx +117 -0
  315. package/src/shared/portal/OverlayModal.native.tsx +51 -0
  316. package/src/shared/portal/PortalHost.native.tsx +102 -0
  317. package/src/shared/portal/hostRegistry.ts +76 -0
  318. package/src/shared/portal/themeScope.native.ts +18 -0
  319. package/src/shared/pressableChild.ts +33 -0
  320. package/src/shared/safeArea.native.ts +22 -0
  321. package/src/shared/useKeyboardHeight.native.ts +18 -0
  322. package/src/shared/useOverlayOpenState.ts +128 -0
  323. package/src/shared/useScreenReaderEnabled.native.ts +19 -0
  324. package/dist/Table-ColfpgKV.js +0 -1552
  325. package/dist/Table-ColfpgKV.js.map +0 -1
  326. package/dist/components/ThemeProvider/ThemeProvider.native.d.ts +0 -10
  327. package/dist/components/ThemeProvider/ThemeProvider.native.d.ts.map +0 -1
  328. package/dist/index.native.d.ts +0 -33
  329. package/dist/index.native.d.ts.map +0 -1
  330. package/dist/platform.native.d.ts +0 -2
  331. package/dist/platform.native.d.ts.map +0 -1
@@ -0,0 +1,58 @@
1
+ import {css} from 'react-strict-dom'
2
+ import {colors} from '@duro-app/tokens/tokens/colors.css'
3
+ import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
4
+ import {shadows} from '@duro-app/tokens/tokens/shadows.css'
5
+ import {borders} from '@duro-app/tokens/tokens/borders.css'
6
+
7
+ /**
8
+ * Native Drawer surfaces. Motion is Reanimated's (plan N0, Decision 4): these
9
+ * styles draw the panel and backdrop, the animated views around them move them.
10
+ */
11
+ export const nativeStyles = css.create({
12
+ fill: {
13
+ position: 'absolute',
14
+ top: 0,
15
+ left: 0,
16
+ right: 0,
17
+ bottom: 0,
18
+ },
19
+ scrim: {
20
+ backgroundColor: colors.scrim,
21
+ },
22
+ panel: {
23
+ flexGrow: 1,
24
+ backgroundColor: colors.bgCard,
25
+ boxShadow: shadows.lg,
26
+ display: 'flex',
27
+ flexDirection: 'column',
28
+ overflow: 'hidden',
29
+ },
30
+ panelBottom: {
31
+ borderTopWidth: borders.hairline,
32
+ borderTopStyle: 'solid',
33
+ borderTopColor: colors.border,
34
+ borderTopLeftRadius: radii.lg,
35
+ borderTopRightRadius: radii.lg,
36
+ },
37
+ panelRight: {
38
+ borderLeftWidth: borders.hairline,
39
+ borderLeftStyle: 'solid',
40
+ borderLeftColor: colors.border,
41
+ },
42
+ panelLeft: {
43
+ borderRightWidth: borders.hairline,
44
+ borderRightStyle: 'solid',
45
+ borderRightColor: colors.border,
46
+ },
47
+ // The safe-area insets (and a snap sheet's overshoot skirt), as padding.
48
+ insets: (top: number, bottom: number) => ({paddingTop: top, paddingBottom: bottom}),
49
+ // The Body's padding inside its ScrollView (no flex: it sizes to content).
50
+ bodyContent: {
51
+ paddingLeft: spacing.lg,
52
+ paddingRight: spacing.lg,
53
+ paddingBottom: spacing.lg,
54
+ },
55
+ // A snap sheet resting below its largest detent: its lower part is off
56
+ // screen, so the list scrolls that far further to show its end.
57
+ hiddenBelow: (height: number) => ({paddingBottom: height}),
58
+ })
@@ -0,0 +1,125 @@
1
+ import {css} from 'react-strict-dom'
2
+ import {colors} from '@duro-app/tokens/tokens/colors.css'
3
+ import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
4
+ import {typography} from '@duro-app/tokens/tokens/typography.css'
5
+ import {duration} from '@duro-app/tokens/tokens/motion.css'
6
+ import {sizes} from '@duro-app/tokens/tokens/sizes.css'
7
+ import {borders} from '@duro-app/tokens/tokens/borders.css'
8
+
9
+ /** Styles of the Drawer parts both platforms draw (no animations: native has none). */
10
+ export const partStyles = css.create({
11
+ // --- Grabber (snap sheets) ---
12
+ grabberHit: {
13
+ display: 'flex',
14
+ alignItems: 'center',
15
+ justifyContent: 'center',
16
+ alignSelf: 'stretch',
17
+ flexShrink: 0,
18
+ paddingTop: spacing.sm,
19
+ paddingBottom: spacing.sm,
20
+ borderWidth: 0,
21
+ backgroundColor: 'transparent',
22
+ },
23
+ grabber: {
24
+ display: 'block',
25
+ width: sizes.sheetGrabberW,
26
+ height: sizes.sheetGrabberH,
27
+ borderRadius: radii.full,
28
+ backgroundColor: colors.border,
29
+ },
30
+
31
+ // --- Header ---
32
+ header: {
33
+ // Header, Description and Footer keep their size; only the Body yields.
34
+ flexShrink: 0,
35
+ display: 'flex',
36
+ alignItems: 'flex-start',
37
+ justifyContent: 'space-between',
38
+ paddingTop: spacing.lg,
39
+ paddingLeft: spacing.lg,
40
+ paddingRight: spacing.lg,
41
+ paddingBottom: spacing.md,
42
+ gap: spacing.md,
43
+ },
44
+
45
+ // Under a snap sheet's grabber the header needs no top padding of its own.
46
+ headerUnderGrabber: {
47
+ paddingTop: 0,
48
+ },
49
+
50
+ // Title centred, the other children at the end (Header titleAlign="center").
51
+ headerCentered: {
52
+ alignItems: 'center',
53
+ },
54
+
55
+ headerSide: {
56
+ display: 'flex',
57
+ flexGrow: 1,
58
+ flexBasis: 0,
59
+ },
60
+
61
+ headerEnd: {
62
+ justifyContent: 'flex-end',
63
+ },
64
+
65
+ // --- Title ---
66
+ title: {
67
+ fontSize: typography.fontSizeLg,
68
+ fontWeight: typography.fontWeightSemibold,
69
+ lineHeight: typography.lineHeight,
70
+ color: colors.text,
71
+ margin: 0,
72
+ },
73
+
74
+ // --- Description ---
75
+ description: {
76
+ fontSize: typography.fontSizeSm,
77
+ lineHeight: typography.lineHeight,
78
+ color: colors.textMuted,
79
+ margin: 0,
80
+ paddingLeft: spacing.lg,
81
+ paddingRight: spacing.lg,
82
+ paddingBottom: spacing.md,
83
+ },
84
+
85
+ // --- Footer ---
86
+ footer: {
87
+ display: 'flex',
88
+ alignItems: 'center',
89
+ justifyContent: 'flex-end',
90
+ gap: spacing.sm,
91
+ paddingTop: spacing.md,
92
+ paddingLeft: spacing.lg,
93
+ paddingRight: spacing.lg,
94
+ paddingBottom: spacing.lg,
95
+ borderTopWidth: borders.hairline,
96
+ borderTopStyle: 'solid',
97
+ borderTopColor: colors.border,
98
+ },
99
+
100
+ // --- Close button ---
101
+ closeButton: {
102
+ display: 'flex',
103
+ alignItems: 'center',
104
+ justifyContent: 'center',
105
+ width: sizes.iconButton,
106
+ height: sizes.iconButton,
107
+ borderRadius: radii.sm,
108
+ borderWidth: 0,
109
+ backgroundColor: {
110
+ default: 'transparent',
111
+ ':hover': colors.bgCardHover,
112
+ },
113
+ color: colors.textMuted,
114
+ cursor: 'pointer',
115
+ padding: 0,
116
+ flexShrink: 0,
117
+ transitionProperty: 'background-color',
118
+ transitionDuration: duration.fast,
119
+ },
120
+
121
+ // --- Inline wrapper ---
122
+ inlineWrapper: {
123
+ display: 'inline-flex',
124
+ },
125
+ })
@@ -0,0 +1,110 @@
1
+ import {
2
+ Children,
3
+ createContext,
4
+ isValidElement,
5
+ type ReactNode,
6
+ type RefObject,
7
+ useContext,
8
+ } from 'react'
9
+ import {html} from 'react-strict-dom'
10
+ import type {DurationToken} from '@duro-app/tokens/keys'
11
+ import {partStyles} from './parts.css'
12
+ import type {SnapPoint} from './sheetPhysics'
13
+
14
+ export type DrawerAnchor = 'right' | 'left' | 'bottom'
15
+ export type DrawerSize = 'sm' | 'md' | 'lg'
16
+ export type {SnapPoint}
17
+
18
+ export interface DrawerContextValue {
19
+ /** Rendered: open, or playing the exit animation. */
20
+ open: boolean
21
+ closing: boolean
22
+ closeDuration: DurationToken
23
+ anchor: DrawerAnchor
24
+ dismissable: boolean
25
+ swipeDismiss: boolean
26
+ requestOpen: () => void
27
+ requestClose: () => void
28
+ /** Close immediately without the exit animation (a swipe has played its own) */
29
+ requestCloseImmediate: () => void
30
+ titleId: string
31
+ descriptionId: string
32
+ /** The panel's host: a DOM element on the web, a view on native */
33
+ panelRef: RefObject<unknown>
34
+ /** A bottom sheet's detents, ascending; empty when the drawer has none */
35
+ snapPoints: readonly SnapPoint[]
36
+ /** Index into snapPoints the sheet rests at */
37
+ snapIndex: number
38
+ setSnapIndex: (index: number) => void
39
+ }
40
+
41
+ export const DrawerContext = createContext<DrawerContextValue | null>(null)
42
+
43
+ export function useDrawer() {
44
+ const ctx = useContext(DrawerContext)
45
+ if (!ctx) throw new Error('Drawer compound components must be used within Drawer.Root')
46
+ return ctx
47
+ }
48
+
49
+ export interface GrabberLabels {
50
+ /** The grabber's name when a press grows the sheet */
51
+ expand: string
52
+ /** The grabber's name when a press shrinks it */
53
+ collapse: string
54
+ }
55
+
56
+ export const DEFAULT_GRABBER_LABELS: GrabberLabels = {
57
+ expand: 'Expand sheet',
58
+ collapse: 'Shrink sheet',
59
+ }
60
+
61
+ // The parts below are pure react-strict-dom, drawn the same on both platforms.
62
+
63
+ interface HeaderProps {
64
+ children: ReactNode
65
+ /**
66
+ * 'center' puts the Title in the middle and the other children at the end,
67
+ * as a phone sheet does (a text close button on the right). Default: 'start'
68
+ */
69
+ titleAlign?: 'start' | 'center'
70
+ }
71
+
72
+ export function Header({children, titleAlign = 'start'}: HeaderProps) {
73
+ const underGrabber = useDrawer().snapPoints.length > 0 && partStyles.headerUnderGrabber
74
+ if (titleAlign === 'start') {
75
+ return <html.div style={[partStyles.header, underGrabber]}>{children}</html.div>
76
+ }
77
+ const items = Children.toArray(children)
78
+ const isTitle = (c: ReactNode) => isValidElement(c) && c.type === Title
79
+ return (
80
+ <html.div style={[partStyles.header, underGrabber, partStyles.headerCentered]}>
81
+ <html.div style={partStyles.headerSide} />
82
+ {items.filter(isTitle)}
83
+ <html.div style={[partStyles.headerSide, partStyles.headerEnd]}>
84
+ {items.filter((c) => !isTitle(c))}
85
+ </html.div>
86
+ </html.div>
87
+ )
88
+ }
89
+
90
+ export function Title({children}: {children: ReactNode}) {
91
+ const {titleId} = useDrawer()
92
+ return (
93
+ <html.h2 id={titleId} style={partStyles.title}>
94
+ {children}
95
+ </html.h2>
96
+ )
97
+ }
98
+
99
+ export function Description({children}: {children: ReactNode}) {
100
+ const {descriptionId} = useDrawer()
101
+ return (
102
+ <html.p id={descriptionId} style={partStyles.description}>
103
+ {children}
104
+ </html.p>
105
+ )
106
+ }
107
+
108
+ export function Footer({children}: {children: ReactNode}) {
109
+ return <html.div style={partStyles.footer}>{children}</html.div>
110
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * The drawer's drag rules, shared by the web (pointer events) and native
3
+ * (Gesture Handler on the UI thread) so both settle a drag the same way.
4
+ * Pure numbers: no DOM, no React Native. The functions the native drag
5
+ * calls on the UI thread are worklets ('worklet'); on the web the directive
6
+ * is an inert string.
7
+ */
8
+
9
+ import {DURATION_MS, SPACING_PX} from '@duro-app/tokens/keys'
10
+
11
+ /** A bottom-sheet detent (plan N0, Decision 5). */
12
+ export type SnapPoint = 'medium' | 'large'
13
+
14
+ /** Fraction of the panel (or of the lowest detent) a drag must cover to dismiss */
15
+ export const DISMISS_THRESHOLD = 0.3
16
+
17
+ /** Release speed (px/ms) that dismisses, or moves one detent, whatever the distance */
18
+ export const VELOCITY_THRESHOLD = 0.5
19
+
20
+ /** Duration of the slide-out once a drag dismisses (ms) */
21
+ export const DISMISS_DURATION = DURATION_MS.brisk
22
+
23
+ /** Duration of the settle onto a detent, or the snap back (ms) */
24
+ export const SNAP_DURATION = DURATION_MS.base
25
+
26
+ /** Movement (px) before a press becomes a drag; under it a tap stays a tap */
27
+ export const DRAG_SLOP = 8
28
+
29
+ /** Movement (px) across the drag's axis that hands the gesture to a scroll instead */
30
+ export const CROSS_SLOP = 16
31
+
32
+ /** 'medium' is half the window */
33
+ export const MEDIUM_FRACTION = 0.5
34
+
35
+ /** How far (px) a snap sheet may be pulled past its largest detent: it resists, never further */
36
+ export const SNAP_MAX_OVERSHOOT = SPACING_PX.lg
37
+
38
+ /** Height of one detent: 'medium' half the window, 'large' the window less its top inset. */
39
+ export function detentHeight(point: SnapPoint, windowHeight: number, topInset: number): number {
40
+ 'worklet'
41
+ return point === 'medium' ? windowHeight * MEDIUM_FRACTION : windowHeight - topInset
42
+ }
43
+
44
+ /** The detents in ascending height, each once. */
45
+ export function sortSnapPoints(points: readonly SnapPoint[]): SnapPoint[] {
46
+ return (['medium', 'large'] as const).filter((p) => points.includes(p))
47
+ }
48
+
49
+ /**
50
+ * Where a released drag settles: a detent index into `heights` (ascending),
51
+ * or 'dismiss'.
52
+ *
53
+ * - A fling (|velocity| over VELOCITY_THRESHOLD) moves one detent in its
54
+ * direction from the detent it started at; down from the lowest dismisses.
55
+ * - Otherwise the nearest detent wins, unless the sheet is below the lowest
56
+ * by more than DISMISS_THRESHOLD of it, which dismisses.
57
+ *
58
+ * `velocity` is px/ms, positive downward (closing).
59
+ */
60
+ export function settleDetent({
61
+ heights,
62
+ from,
63
+ height,
64
+ velocity,
65
+ }: {
66
+ heights: readonly number[]
67
+ from: number
68
+ height: number
69
+ velocity: number
70
+ }): number | 'dismiss' {
71
+ 'worklet'
72
+ const last = heights.length - 1
73
+ if (velocity > VELOCITY_THRESHOLD) return from === 0 ? 'dismiss' : from - 1
74
+ if (velocity < -VELOCITY_THRESHOLD) return Math.min(from + 1, last)
75
+ if (height < heights[0] * (1 - DISMISS_THRESHOLD)) return 'dismiss'
76
+ let nearest = 0
77
+ for (let i = 1; i <= last; i++) {
78
+ if (Math.abs(heights[i] - height) < Math.abs(heights[nearest] - height)) nearest = i
79
+ }
80
+ return nearest
81
+ }
82
+
83
+ /** The grabber's tap: the next detent up, wrapping from the largest to the smallest. */
84
+ export function cycleDetent(index: number, count: number): number {
85
+ 'worklet'
86
+ return count === 0 ? 0 : (index + 1) % count
87
+ }
88
+
89
+ /**
90
+ * Past the largest detent the sheet resists: the overshoot grows ever more
91
+ * slowly and never exceeds `max`.
92
+ */
93
+ export function rubberBand(overshoot: number, max: number): number {
94
+ 'worklet'
95
+ if (overshoot <= 0) return 0
96
+ return (overshoot / (overshoot + max * 4)) * max
97
+ }
98
+
99
+ /** Whether a side drawer's release dismisses it. `movement` is toward its edge. */
100
+ export function shouldDismissEdge(movement: number, size: number, velocity: number): boolean {
101
+ 'worklet'
102
+ return movement / size > DISMISS_THRESHOLD || velocity > VELOCITY_THRESHOLD
103
+ }
@@ -1,7 +1,6 @@
1
1
  import {css} from 'react-strict-dom'
2
2
  import {colors} from '@duro-app/tokens/tokens/colors.css'
3
3
  import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
4
- import {typography} from '@duro-app/tokens/tokens/typography.css'
5
4
  import {shadows} from '@duro-app/tokens/tokens/shadows.css'
6
5
  import {duration, easing} from '@duro-app/tokens/tokens/motion.css'
7
6
  import {sizes} from '@duro-app/tokens/tokens/sizes.css'
@@ -79,6 +78,8 @@ export const styles = css.create({
79
78
  // Panel sizing per anchor
80
79
  panelHorizontal: {
81
80
  width: '100%',
81
+ // Native has no border-box reset (the web's global-reset.css); declare it.
82
+ boxSizing: 'border-box',
82
83
  height: '100%',
83
84
  borderLeftWidth: borders.hairline,
84
85
  borderLeftStyle: 'solid',
@@ -86,6 +87,8 @@ export const styles = css.create({
86
87
  },
87
88
  panelVertical: {
88
89
  width: '100%',
90
+ // Native has no border-box reset (the web's global-reset.css); declare it.
91
+ boxSizing: 'border-box',
89
92
  borderTopWidth: borders.hairline,
90
93
  borderTopStyle: 'solid',
91
94
  borderTopColor: colors.border,
@@ -93,6 +96,21 @@ export const styles = css.create({
93
96
  borderTopRightRadius: radii.lg,
94
97
  maxHeight: '85vh',
95
98
  },
99
+ // A snap sheet: as tall as its detent, which replaces the 85vh cap.
100
+ panelSnap: {
101
+ maxHeight: 'none',
102
+ transitionProperty: 'height',
103
+ transitionDuration: duration.base,
104
+ transitionTimingFunction: easing.easeOut,
105
+ },
106
+ sheetHeight: (height: number) => ({height}),
107
+
108
+ // The grabber's web-only additions: a grab cursor, and the browser leaves the drag to us.
109
+ grabberHitWeb: {
110
+ cursor: 'grab',
111
+ touchAction: 'none',
112
+ },
113
+
96
114
  panelLeftBorder: {
97
115
  borderLeftWidth: 0,
98
116
  borderRightWidth: borders.hairline,
@@ -176,38 +194,6 @@ export const styles = css.create({
176
194
  closeDurationBase: {animationDuration: duration.base},
177
195
  closeDurationSlow: {animationDuration: duration.slow},
178
196
 
179
- // --- Header ---
180
- header: {
181
- display: 'flex',
182
- alignItems: 'flex-start',
183
- justifyContent: 'space-between',
184
- paddingTop: spacing.lg,
185
- paddingLeft: spacing.lg,
186
- paddingRight: spacing.lg,
187
- paddingBottom: spacing.md,
188
- gap: spacing.md,
189
- },
190
-
191
- // --- Title ---
192
- title: {
193
- fontSize: typography.fontSizeLg,
194
- fontWeight: typography.fontWeightSemibold,
195
- lineHeight: typography.lineHeight,
196
- color: colors.text,
197
- margin: 0,
198
- },
199
-
200
- // --- Description ---
201
- description: {
202
- fontSize: typography.fontSizeSm,
203
- lineHeight: typography.lineHeight,
204
- color: colors.textMuted,
205
- margin: 0,
206
- paddingLeft: spacing.lg,
207
- paddingRight: spacing.lg,
208
- paddingBottom: spacing.md,
209
- },
210
-
211
197
  // --- Body ---
212
198
  body: {
213
199
  paddingLeft: spacing.lg,
@@ -216,45 +202,4 @@ export const styles = css.create({
216
202
  overflowY: 'auto',
217
203
  flex: 1,
218
204
  },
219
-
220
- // --- Footer ---
221
- footer: {
222
- display: 'flex',
223
- alignItems: 'center',
224
- justifyContent: 'flex-end',
225
- gap: spacing.sm,
226
- paddingTop: spacing.md,
227
- paddingLeft: spacing.lg,
228
- paddingRight: spacing.lg,
229
- paddingBottom: spacing.lg,
230
- borderTopWidth: borders.hairline,
231
- borderTopStyle: 'solid',
232
- borderTopColor: colors.border,
233
- },
234
-
235
- // --- Close button ---
236
- closeButton: {
237
- display: 'flex',
238
- alignItems: 'center',
239
- justifyContent: 'center',
240
- width: sizes.iconButton,
241
- height: sizes.iconButton,
242
- borderRadius: radii.sm,
243
- borderWidth: 0,
244
- backgroundColor: {
245
- default: 'transparent',
246
- ':hover': colors.bgCardHover,
247
- },
248
- color: colors.textMuted,
249
- cursor: 'pointer',
250
- padding: 0,
251
- flexShrink: 0,
252
- transitionProperty: 'background-color',
253
- transitionDuration: duration.fast,
254
- },
255
-
256
- // --- Inline wrapper ---
257
- inlineWrapper: {
258
- display: 'inline-flex',
259
- },
260
205
  })
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Real touch input and viewport control for Storybook interaction tests
3
+ * (the Drawer's, SplitPane's), through
4
+ * Chrome DevTools Protocol: synthetic pointer events neither scroll nor go
5
+ * through touch adjustment the way a finger does.
6
+ */
7
+
8
+ export type Point = {x: number; y: number}
9
+
10
+ /**
11
+ * A CDP command. `vitest/browser` loads only under the test runner, so it is
12
+ * imported here, at call time: a static import breaks the built Storybook.
13
+ * `CDPSession` is empty until the provider's typings load; `send` exists at runtime.
14
+ */
15
+ export async function cdpSend(method: string, params: object) {
16
+ const {cdp} = await import('vitest/browser')
17
+ const session = cdp() as unknown as {send: (m: string, p: object) => Promise<unknown>}
18
+ return session.send(method, params)
19
+ }
20
+
21
+ /** Set the page's viewport (test runner only, like `cdpSend`). */
22
+ export async function setViewport(width: number, height: number) {
23
+ const {page} = await import('vitest/browser')
24
+ await page.viewport(width, height)
25
+ }
26
+
27
+ export const pause = (ms: number) => new Promise((r) => setTimeout(r, ms))
28
+
29
+ /**
30
+ * CDP input lands in top-level page coordinates; the story runs in an iframe
31
+ * the test runner offsets and may scale to fit. Map a point up every frame.
32
+ */
33
+ export function toPage(p: Point): Point {
34
+ let {x, y} = p
35
+ let win: Window = window
36
+ while (win.frameElement && win.parent !== win) {
37
+ const rect = win.frameElement.getBoundingClientRect()
38
+ const scale = rect.width / win.innerWidth
39
+ x = rect.left + x * scale
40
+ y = rect.top + y * scale
41
+ win = win.parent
42
+ }
43
+ return {x, y}
44
+ }
45
+
46
+ /**
47
+ * A page-coordinate touch point with a 1px radius: Chrome's touch adjustment
48
+ * snaps a fat default touch onto the nearest control, which would turn every
49
+ * drag in the list into a drag on a toggle.
50
+ */
51
+ const touchAt = (p: Point) => ({...toPage(p), radiusX: 1, radiusY: 1})
52
+
53
+ /**
54
+ * A finger drag from one point to another, in `steps` moves 16 ms apart.
55
+ * Each move is a round trip to the browser; few of them keep a test well
56
+ * inside its timeout on a loaded machine.
57
+ */
58
+ export async function touchDrag(from: Point, to: Point, steps = 6) {
59
+ const send = (type: string, touchPoints: Point[]) =>
60
+ cdpSend('Input.dispatchTouchEvent', {type, touchPoints: touchPoints.map(touchAt)})
61
+ await send('touchStart', [from])
62
+ for (let i = 1; i <= steps; i++) {
63
+ await send('touchMove', [
64
+ {x: from.x + ((to.x - from.x) * i) / steps, y: from.y + ((to.y - from.y) * i) / steps},
65
+ ])
66
+ await pause(16)
67
+ }
68
+ await send('touchEnd', [])
69
+ }
70
+
71
+ /** A finger tap. */
72
+ export async function touchTap(at: Point) {
73
+ await cdpSend('Input.dispatchTouchEvent', {type: 'touchStart', touchPoints: [touchAt(at)]})
74
+ await cdpSend('Input.dispatchTouchEvent', {type: 'touchEnd', touchPoints: []})
75
+ }
76
+
77
+ /** The centre of an element, in its own document's coordinates. */
78
+ export function centre(el: Element): Point {
79
+ const r = el.getBoundingClientRect()
80
+ return {x: r.left + r.width / 2, y: r.top + r.height / 2}
81
+ }
@@ -0,0 +1,90 @@
1
+ import {type ReactNode, useEffect, useId, useMemo, useRef, useState} from 'react'
2
+ import type {DurationToken} from '@duro-app/tokens/keys'
3
+ import {useOverlayOpenState} from '../../shared/useOverlayOpenState'
4
+ import type {DrawerAnchor, DrawerContextValue} from './parts'
5
+ import {sortSnapPoints, type SnapPoint} from './sheetPhysics'
6
+
7
+ export interface DrawerRootProps {
8
+ children: ReactNode
9
+ /** Controlled open state */
10
+ open?: boolean
11
+ /** Default open state (uncontrolled) */
12
+ defaultOpen?: boolean
13
+ /** Called when drawer open state changes */
14
+ onOpenChange?: (open: boolean) => void
15
+ /** Which edge the drawer slides from. Default: 'right' */
16
+ anchor?: DrawerAnchor
17
+ /** Whether clicking backdrop or pressing Escape (Android's back button on native) closes the drawer. Default: true */
18
+ dismissable?: boolean
19
+ /** Whether swipe gesture dismisses the drawer. Default: true */
20
+ swipeDismiss?: boolean
21
+ /** Motion token for the close animation. Default: 'fast' (150ms) */
22
+ closeAnimationDuration?: DurationToken
23
+ /**
24
+ * Bottom anchor only: the heights the sheet rests at. 'medium' is half the
25
+ * window, 'large' the window less its top safe-area inset. A drag moves
26
+ * between them and dismisses below the lowest; the grabber steps through
27
+ * them on a tap. Without it, a bottom drawer is as tall as its content.
28
+ */
29
+ snapPoints?: readonly SnapPoint[]
30
+ /** The detent the sheet opens at. Default: the lowest of snapPoints */
31
+ defaultSnapPoint?: SnapPoint
32
+ }
33
+
34
+ /** The state both platforms' Drawer.Root share: open and closing, ids, and the detent. */
35
+ export function useDrawerRoot({
36
+ open: controlledOpen,
37
+ defaultOpen = false,
38
+ onOpenChange,
39
+ anchor = 'right',
40
+ dismissable = true,
41
+ swipeDismiss = true,
42
+ closeAnimationDuration = 'fast',
43
+ snapPoints: snapPointsProp,
44
+ defaultSnapPoint,
45
+ }: Omit<DrawerRootProps, 'children'>): DrawerContextValue {
46
+ const {
47
+ present,
48
+ closing,
49
+ requestOpen,
50
+ requestClose,
51
+ closeNow: requestCloseImmediate,
52
+ } = useOverlayOpenState({
53
+ open: controlledOpen,
54
+ defaultOpen,
55
+ onOpenChange,
56
+ exitDuration: closeAnimationDuration,
57
+ closeNotice: 'after-exit',
58
+ })
59
+ const panelRef = useRef<unknown>(null)
60
+ const titleId = useId()
61
+ const descriptionId = useId()
62
+
63
+ // Keyed by content: a new array literal each render is the same detents.
64
+ const snapKey = anchor === 'bottom' && snapPointsProp ? sortSnapPoints(snapPointsProp).join() : ''
65
+ const snapPoints = useMemo(() => (snapKey ? (snapKey.split(',') as SnapPoint[]) : []), [snapKey])
66
+ const openIndex = Math.max(0, defaultSnapPoint ? snapPoints.indexOf(defaultSnapPoint) : 0)
67
+ const [snapIndex, setSnapIndex] = useState(openIndex)
68
+ // Each opening starts at the default detent.
69
+ useEffect(() => {
70
+ if (!present) setSnapIndex(openIndex)
71
+ }, [present, openIndex])
72
+
73
+ return {
74
+ open: present,
75
+ closing,
76
+ closeDuration: closeAnimationDuration,
77
+ anchor,
78
+ dismissable,
79
+ swipeDismiss,
80
+ requestOpen,
81
+ requestClose,
82
+ requestCloseImmediate,
83
+ titleId,
84
+ descriptionId,
85
+ panelRef,
86
+ snapPoints,
87
+ snapIndex,
88
+ setSnapIndex,
89
+ }
90
+ }