@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
@@ -1,10 +1,8 @@
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
- import {sizes} from '@duro-app/tokens/tokens/sizes.css'
8
6
  import {borders} from '@duro-app/tokens/tokens/borders.css'
9
7
  import {layers} from '@duro-app/tokens/tokens/layers.css'
10
8
 
@@ -60,6 +58,8 @@ export const styles = css.create({
60
58
  borderRadius: radii.lg,
61
59
  boxShadow: shadows.lg,
62
60
  width: '100%',
61
+ // Native has no border-box reset (the web's global-reset.css); declare it.
62
+ boxSizing: 'border-box',
63
63
  maxHeight: '85vh',
64
64
  display: 'flex',
65
65
  flexDirection: 'column',
@@ -115,93 +115,6 @@ export const styles = css.create({
115
115
  closeDurationBase: {animationDuration: duration.base},
116
116
  closeDurationSlow: {animationDuration: duration.slow},
117
117
 
118
- // --- Sizes ---
119
- sm: {maxWidth: sizes.dialogSm},
120
- md: {maxWidth: sizes.dialogMd},
121
- lg: {maxWidth: sizes.dialogLg},
122
-
123
- // --- Header ---
124
- header: {
125
- display: 'flex',
126
- alignItems: 'flex-start',
127
- justifyContent: 'space-between',
128
- paddingTop: spacing.lg,
129
- paddingLeft: spacing.lg,
130
- paddingRight: spacing.lg,
131
- paddingBottom: spacing.md,
132
- gap: spacing.md,
133
- },
134
-
135
- // --- Title ---
136
- title: {
137
- fontSize: typography.fontSizeLg,
138
- fontWeight: typography.fontWeightSemibold,
139
- lineHeight: typography.lineHeight,
140
- color: colors.text,
141
- margin: 0,
142
- },
143
-
144
- // --- Description ---
145
- description: {
146
- fontSize: typography.fontSizeSm,
147
- lineHeight: typography.lineHeight,
148
- color: colors.textMuted,
149
- margin: 0,
150
- paddingLeft: spacing.lg,
151
- paddingRight: spacing.lg,
152
- paddingBottom: spacing.md,
153
- },
154
-
155
- // --- Body ---
156
- body: {
157
- paddingLeft: spacing.lg,
158
- paddingRight: spacing.lg,
159
- paddingBottom: spacing.lg,
160
- overflowY: 'auto',
161
- flex: 1,
162
- },
163
-
164
- // --- Footer ---
165
- footer: {
166
- display: 'flex',
167
- alignItems: 'center',
168
- justifyContent: 'flex-end',
169
- gap: spacing.sm,
170
- paddingTop: spacing.md,
171
- paddingLeft: spacing.lg,
172
- paddingRight: spacing.lg,
173
- paddingBottom: spacing.lg,
174
- borderTopWidth: borders.hairline,
175
- borderTopStyle: 'solid',
176
- borderTopColor: colors.border,
177
- },
178
-
179
- // --- Close button ---
180
- closeButton: {
181
- display: 'flex',
182
- alignItems: 'center',
183
- justifyContent: 'center',
184
- width: sizes.iconButton,
185
- height: sizes.iconButton,
186
- borderRadius: radii.sm,
187
- borderWidth: 0,
188
- backgroundColor: {
189
- default: 'transparent',
190
- ':hover': colors.bgCardHover,
191
- },
192
- color: colors.textMuted,
193
- cursor: 'pointer',
194
- padding: 0,
195
- flexShrink: 0,
196
- transitionProperty: 'background-color',
197
- transitionDuration: duration.fast,
198
- },
199
-
200
- // --- Inline wrapper (for Trigger / Close children) ---
201
- inlineWrapper: {
202
- display: 'inline-flex',
203
- },
204
-
205
118
  // --- Reduced motion ---
206
119
  reducedMotion: {
207
120
  animationDuration: duration.minimal,
@@ -2,10 +2,11 @@ import type {ComponentMeta} from '../component-meta'
2
2
 
3
3
  export const meta: ComponentMeta = {
4
4
  description:
5
- 'Modal sliding panel from a screen edge (right, left, or bottom). Slides in with backdrop overlay. Supports swipe-to-dismiss gestures on mobile, backdrop click dismiss, and Escape key. Swipe yields to an inner Duro ScrollArea: a drag there scrolls it, and dismisses only toward the edge from the scroll start (a native overflow:auto child is not covered — use ScrollArea). Use for secondary workflows, settings panels, or mobile-friendly detail views.',
5
+ 'Modal sliding panel from a screen edge (right, left, or bottom). Slides in with backdrop overlay. Supports swipe-to-dismiss gestures on mobile, backdrop click dismiss, and Escape key. Swipe yields to an inner Duro ScrollArea: a drag there scrolls it, and dismisses only toward the edge from the scroll start (a native overflow:auto child is not covered — use ScrollArea). A bottom drawer with snapPoints (medium and large) is a phone sheet: it rests at its detents, a grabber steps through them on a tap, a drag moves between them and dismisses below the lowest; its Body is the scroll the drag defers to. Header titleAlign="center" centres the title with a text close at the end. Use for secondary workflows, settings panels, or mobile-friendly detail views.',
6
6
  whenToUse: [
7
7
  'Settings or configuration panels that slide in from the side',
8
8
  'Mobile-friendly detail views (bottom sheet pattern)',
9
+ 'Phone pickers and lists that need more room on demand (snapPoints)',
9
10
  'Secondary workflows that need more space than a dialog',
10
11
  'Filters, navigation drawers, or form panels',
11
12
  ],
@@ -0,0 +1,469 @@
1
+ import {
2
+ createContext,
3
+ type ReactNode,
4
+ type RefObject,
5
+ useContext,
6
+ useEffect,
7
+ useMemo,
8
+ useRef,
9
+ } from 'react'
10
+ import {
11
+ type AccessibilityActionEvent,
12
+ type LayoutChangeEvent,
13
+ Pressable,
14
+ StyleSheet,
15
+ useWindowDimensions,
16
+ type View,
17
+ } from 'react-native'
18
+ import {Gesture, GestureDetector, type GestureType} from 'react-native-gesture-handler'
19
+ import Animated, {
20
+ type AnimatedRef,
21
+ Easing,
22
+ scrollTo,
23
+ useAnimatedRef,
24
+ useAnimatedScrollHandler,
25
+ useAnimatedStyle,
26
+ useSharedValue,
27
+ withTiming,
28
+ } from 'react-native-reanimated'
29
+ import {scheduleOnRN} from 'react-native-worklets'
30
+ import {html} from 'react-strict-dom'
31
+ import {DURATION_MS, SIZES_PX} from '@duro-app/tokens/keys'
32
+ import {ModalContext} from '../../shared/ModalContext'
33
+ import {OverlayModal} from '../../shared/portal/OverlayModal'
34
+ import {focusForAccessibility} from '../../shared/accessibilityFocus'
35
+ import {useSafeAreaInsets} from '../../shared/safeArea'
36
+ import {withPress} from '../../shared/pressableChild'
37
+ import {ControlContextBoundary} from '../Toolbar/ControlContextBoundary'
38
+ import {Icon} from '../Icon/Icon'
39
+ import {nativeStyles} from './native.css'
40
+ import {partStyles} from './parts.css'
41
+ import {
42
+ DEFAULT_GRABBER_LABELS,
43
+ Description,
44
+ DrawerContext,
45
+ Footer,
46
+ Header,
47
+ Title,
48
+ useDrawer,
49
+ type DrawerSize,
50
+ type GrabberLabels,
51
+ } from './parts'
52
+ import {
53
+ CROSS_SLOP,
54
+ DISMISS_DURATION,
55
+ DRAG_SLOP,
56
+ SNAP_DURATION,
57
+ SNAP_MAX_OVERSHOOT,
58
+ cycleDetent,
59
+ detentHeight,
60
+ rubberBand,
61
+ settleDetent,
62
+ shouldDismissEdge,
63
+ } from './sheetPhysics'
64
+ import {useDrawerRoot, type DrawerRootProps} from './useDrawerRoot'
65
+
66
+ export type {DrawerAnchor, DrawerSize, SnapPoint} from './parts'
67
+
68
+ /** Native-only wiring: where accessibility focus returns on close. */
69
+ const TriggerRefContext = createContext<RefObject<unknown> | null>(null)
70
+
71
+ /** What Drawer.Body needs from its sheet: the scroll the drag defers to. */
72
+ interface SheetScroll {
73
+ scrollRef: AnimatedRef<Animated.ScrollView>
74
+ scrollGesture: GestureType
75
+ onScroll: ReturnType<typeof useAnimatedScrollHandler>
76
+ /** px of a snap sheet below the screen's bottom edge at its detent */
77
+ hiddenBelow: number
78
+ }
79
+
80
+ const SheetScrollContext = createContext<SheetScroll | null>(null)
81
+
82
+ /**
83
+ * Native Drawer (plan N0, Slice B): the same compound API, in an
84
+ * OverlayModal. The panel follows the finger on the UI thread (Gesture
85
+ * Handler + Reanimated); a bottom drawer with `snapPoints` rests at its
86
+ * detents and dismisses below the lowest. The backdrop press and Android's
87
+ * back button close it; the screen reader's focus moves into the panel on
88
+ * open and back to the trigger on close.
89
+ */
90
+ function Root({children, ...props}: DrawerRootProps) {
91
+ const ctx = useDrawerRoot(props)
92
+ const triggerRef = useRef<unknown>(null)
93
+
94
+ // Back to the trigger once the drawer is gone.
95
+ const wasOpen = useRef(ctx.open)
96
+ useEffect(() => {
97
+ if (wasOpen.current && !ctx.open) focusForAccessibility(triggerRef.current, 'Drawer')
98
+ wasOpen.current = ctx.open
99
+ }, [ctx.open])
100
+
101
+ return (
102
+ <DrawerContext.Provider value={ctx}>
103
+ <TriggerRefContext.Provider value={triggerRef}>{children}</TriggerRefContext.Provider>
104
+ </DrawerContext.Provider>
105
+ )
106
+ }
107
+
108
+ /** Opens the drawer from its one child (a Button), whose host gets focus back on close. */
109
+ function Trigger({children}: {children: ReactNode}) {
110
+ const {requestOpen} = useDrawer()
111
+ const triggerRef = useContext(TriggerRefContext)
112
+ return withPress(children, requestOpen, triggerRef ?? undefined)
113
+ }
114
+
115
+ interface PortalProps {
116
+ children: ReactNode
117
+ size?: DrawerSize
118
+ /** The grabber's accessible names, by what a press does next. Snap sheets only */
119
+ grabberLabels?: GrabberLabels
120
+ }
121
+
122
+ const PANEL_WIDTH = {sm: SIZES_PX.panelSm, md: SIZES_PX.panelMd, lg: SIZES_PX.panelLg} as const
123
+
124
+ /** A content-height bottom drawer is capped at 85% of the window, as the web caps it at 85vh. */
125
+ const CONTENT_SHEET_CAP = 0.85
126
+
127
+ const easeOut = Easing.out(Easing.cubic)
128
+ const easeIn = Easing.in(Easing.cubic)
129
+
130
+ function Portal({children, size = 'md', grabberLabels = DEFAULT_GRABBER_LABELS}: PortalProps) {
131
+ const {
132
+ open,
133
+ closing,
134
+ closeDuration,
135
+ anchor,
136
+ dismissable,
137
+ swipeDismiss,
138
+ requestClose,
139
+ requestCloseImmediate,
140
+ titleId,
141
+ panelRef,
142
+ snapPoints,
143
+ snapIndex,
144
+ setSnapIndex,
145
+ } = useDrawer()
146
+ const window = useWindowDimensions()
147
+ const insets = useSafeAreaInsets()
148
+ const bottom = anchor === 'bottom'
149
+ const snapping = snapPoints.length > 0
150
+
151
+ const heights = useMemo(
152
+ () => snapPoints.map((p) => detentHeight(p, window.height, insets.top)),
153
+ [snapPoints, window.height, insets.top],
154
+ )
155
+ const high = heights.length > 0 ? heights[heights.length - 1] : 0
156
+ // Where the panel rests at its detent: its offset below its own full height.
157
+ const rest = snapping ? high - heights[snapIndex] : 0
158
+ // Off screen: the whole panel past its edge (a snap sheet's overshoot skirt included).
159
+ const hidden = snapping ? high + SNAP_MAX_OVERSHOOT : bottom ? window.height : window.width
160
+
161
+ // The panel's displacement toward its edge, in px: 0 shows it whole; a
162
+ // snap sheet rests at restOf(detent); negative only while pulled past its
163
+ // largest detent.
164
+ const offset = useSharedValue(hidden)
165
+ const start = useSharedValue(0)
166
+ const extent = useSharedValue(bottom ? window.height : window.width)
167
+ const index = useSharedValue(snapIndex)
168
+ const owns = useSharedValue(false)
169
+ const inBody = useSharedValue(false)
170
+ const scrollY = useSharedValue(0)
171
+ const scrollRef = useAnimatedRef<Animated.ScrollView>()
172
+
173
+ // Enter, exit, and a detent chosen from React (the grabber): animate there.
174
+ useEffect(() => {
175
+ if (!open) {
176
+ offset.value = hidden
177
+ return
178
+ }
179
+ if (closing) {
180
+ offset.value = withTiming(hidden, {duration: DURATION_MS[closeDuration], easing: easeIn})
181
+ return
182
+ }
183
+ index.value = snapIndex
184
+ offset.value = withTiming(rest, {duration: DURATION_MS.base, easing: easeOut})
185
+ // `rest` and `hidden` follow the window: a rotation re-seats the panel too.
186
+ }, [open, closing, closeDuration, snapIndex, rest, hidden, index, offset])
187
+
188
+ const heightsShared = useSharedValue(heights)
189
+ useEffect(() => {
190
+ heightsShared.value = heights
191
+ }, [heights, heightsShared])
192
+
193
+ const scrollGesture = useMemo(
194
+ () =>
195
+ Gesture.Native().onBegin(() => {
196
+ inBody.value = true
197
+ }),
198
+ [inBody],
199
+ )
200
+
201
+ const onScroll = useAnimatedScrollHandler((e) => {
202
+ scrollY.value = e.contentOffset.y
203
+ })
204
+
205
+ const horizontalSign = anchor === 'left' ? -1 : 1
206
+ const canDismiss = swipeDismiss && dismissable
207
+ const pan = useMemo(() => {
208
+ const toEdge = (x: number, y: number) => {
209
+ 'worklet'
210
+ return bottom ? y : x * horizontalSign
211
+ }
212
+ const settleTo = (target: number) => {
213
+ 'worklet'
214
+ offset.value = withTiming(target, {duration: SNAP_DURATION, easing: easeOut})
215
+ }
216
+ const dismiss = () => {
217
+ 'worklet'
218
+ offset.value = withTiming(hidden, {duration: DISMISS_DURATION, easing: easeIn}, (done) => {
219
+ if (done) scheduleOnRN(requestCloseImmediate)
220
+ })
221
+ }
222
+ const gesture = Gesture.Pan()
223
+ .enabled(swipeDismiss || snapping)
224
+ .onStart((e) => {
225
+ start.value = offset.value
226
+ const t = toEdge(e.translationX, e.translationY)
227
+ // From the content, the sheet takes the drag only with the list at its
228
+ // top: toward the edge, or upward while a larger detent remains.
229
+ owns.value =
230
+ !bottom || !inBody.value
231
+ ? true
232
+ : scrollY.value <= 0 &&
233
+ (t > 0 || (snapping && index.value < heightsShared.value.length - 1))
234
+ })
235
+ .onUpdate((e) => {
236
+ if (!owns.value) return
237
+ if (inBody.value) scrollTo(scrollRef, 0, 0, false)
238
+ const next = start.value + toEdge(e.translationX, e.translationY)
239
+ if (snapping) offset.value = next < 0 ? -rubberBand(-next, SNAP_MAX_OVERSHOOT) : next
240
+ else offset.value = Math.max(0, next)
241
+ })
242
+ .onEnd((e) => {
243
+ if (!owns.value) return
244
+ const velocity = toEdge(e.velocityX, e.velocityY) / 1000
245
+ if (snapping) {
246
+ const hs = heightsShared.value
247
+ const top = hs[hs.length - 1]
248
+ const settled = settleDetent({
249
+ heights: hs,
250
+ from: index.value,
251
+ height: top - offset.value,
252
+ velocity,
253
+ })
254
+ if (settled === 'dismiss') {
255
+ if (canDismiss) dismiss()
256
+ else settleTo(top - hs[0])
257
+ return
258
+ }
259
+ settleTo(top - hs[settled])
260
+ if (settled !== index.value) {
261
+ index.value = settled
262
+ scheduleOnRN(setSnapIndex, settled)
263
+ }
264
+ return
265
+ }
266
+ if (canDismiss && shouldDismissEdge(offset.value, extent.value, velocity)) dismiss()
267
+ else settleTo(0)
268
+ })
269
+ .onFinalize(() => {
270
+ inBody.value = false
271
+ owns.value = false
272
+ })
273
+ .simultaneousWithExternalGesture(scrollGesture)
274
+ return bottom
275
+ ? gesture.activeOffsetY([-DRAG_SLOP, DRAG_SLOP]).failOffsetX([-CROSS_SLOP, CROSS_SLOP])
276
+ : gesture.activeOffsetX([-DRAG_SLOP, DRAG_SLOP]).failOffsetY([-CROSS_SLOP, CROSS_SLOP])
277
+ }, [
278
+ bottom,
279
+ horizontalSign,
280
+ snapping,
281
+ swipeDismiss,
282
+ canDismiss,
283
+ hidden,
284
+ requestCloseImmediate,
285
+ setSnapIndex,
286
+ scrollGesture,
287
+ offset,
288
+ start,
289
+ extent,
290
+ index,
291
+ owns,
292
+ inBody,
293
+ scrollY,
294
+ scrollRef,
295
+ heightsShared,
296
+ ])
297
+
298
+ const panelMotion = useAnimatedStyle(() =>
299
+ bottom
300
+ ? {transform: [{translateY: offset.value}]}
301
+ : {transform: [{translateX: offset.value * horizontalSign}]},
302
+ )
303
+ const backdropMotion = useAnimatedStyle(() => {
304
+ if (snapping) {
305
+ const hs = heightsShared.value
306
+ const visible = hs[hs.length - 1] - offset.value
307
+ return {opacity: Math.min(1, Math.max(0, visible / hs[0]))}
308
+ }
309
+ return {opacity: Math.min(1, Math.max(0, 1 - offset.value / extent.value))}
310
+ })
311
+
312
+ const onPanelLayout = (e: LayoutChangeEvent) => {
313
+ const {width, height} = e.nativeEvent.layout
314
+ extent.value = bottom ? height : width
315
+ }
316
+
317
+ const placement = bottom
318
+ ? snapping
319
+ ? [layout.bottom, {height: high + SNAP_MAX_OVERSHOOT, bottom: -SNAP_MAX_OVERSHOOT}]
320
+ : [layout.bottom, {maxHeight: window.height * CONTENT_SHEET_CAP}]
321
+ : [layout.side, anchor === 'left' ? layout.left : layout.right, {maxWidth: PANEL_WIDTH[size]}]
322
+ const padTop = bottom ? 0 : insets.top
323
+ const padBottom = insets.bottom + (snapping ? SNAP_MAX_OVERSHOOT : 0)
324
+
325
+ const nextIndex = cycleDetent(snapIndex, snapPoints.length)
326
+ const onGrabberAction = (e: AccessibilityActionEvent) => {
327
+ if (e.nativeEvent.actionName === 'increment') {
328
+ setSnapIndex(Math.min(snapIndex + 1, snapPoints.length - 1))
329
+ } else if (e.nativeEvent.actionName === 'decrement') {
330
+ setSnapIndex(Math.max(snapIndex - 1, 0))
331
+ }
332
+ }
333
+
334
+ const sheetScroll: SheetScroll = {
335
+ scrollRef,
336
+ scrollGesture,
337
+ onScroll,
338
+ hiddenBelow: snapping ? high - heights[snapIndex] : 0,
339
+ }
340
+
341
+ return (
342
+ <OverlayModal
343
+ visible={open}
344
+ onRequestClose={() => {
345
+ if (dismissable) requestClose()
346
+ }}
347
+ onShow={() => focusForAccessibility(panelRef.current, 'Drawer')}
348
+ >
349
+ <Animated.View style={[StyleSheet.absoluteFill, backdropMotion]}>
350
+ <Pressable
351
+ style={StyleSheet.absoluteFill}
352
+ onPress={dismissable ? requestClose : undefined}
353
+ accessible={false}
354
+ importantForAccessibility="no"
355
+ >
356
+ <html.div aria-hidden style={[nativeStyles.fill, nativeStyles.scrim]} />
357
+ </Pressable>
358
+ </Animated.View>
359
+ <GestureDetector gesture={pan}>
360
+ <Animated.View
361
+ ref={panelRef as RefObject<View | null>}
362
+ accessibilityViewIsModal
363
+ onLayout={onPanelLayout}
364
+ style={[placement, panelMotion]}
365
+ >
366
+ <html.div
367
+ role="dialog"
368
+ aria-modal
369
+ aria-labelledby={titleId}
370
+ style={[
371
+ nativeStyles.panel,
372
+ bottom && nativeStyles.panelBottom,
373
+ anchor === 'right' && nativeStyles.panelRight,
374
+ anchor === 'left' && nativeStyles.panelLeft,
375
+ nativeStyles.insets(padTop, padBottom),
376
+ ]}
377
+ >
378
+ {snapping && (
379
+ <Pressable
380
+ accessibilityRole="adjustable"
381
+ accessibilityLabel={
382
+ nextIndex > snapIndex ? grabberLabels.expand : grabberLabels.collapse
383
+ }
384
+ accessibilityValue={{text: snapPoints[snapIndex]}}
385
+ accessibilityActions={[{name: 'increment'}, {name: 'decrement'}]}
386
+ onAccessibilityAction={onGrabberAction}
387
+ onPress={() => setSnapIndex(nextIndex)}
388
+ >
389
+ <html.div style={partStyles.grabberHit}>
390
+ <html.div style={partStyles.grabber} />
391
+ </html.div>
392
+ </Pressable>
393
+ )}
394
+ <ModalContext.Provider value={true}>
395
+ <ControlContextBoundary>
396
+ <SheetScrollContext.Provider value={sheetScroll}>
397
+ {children}
398
+ </SheetScrollContext.Provider>
399
+ </ControlContextBoundary>
400
+ </ModalContext.Provider>
401
+ </html.div>
402
+ </Animated.View>
403
+ </GestureDetector>
404
+ </OverlayModal>
405
+ )
406
+ }
407
+
408
+ // Layout-only RN styles: where the moving panel sits before it moves.
409
+ const layout = StyleSheet.create({
410
+ bottom: {position: 'absolute', left: 0, right: 0, bottom: 0},
411
+ side: {position: 'absolute', top: 0, bottom: 0, width: '100%'},
412
+ left: {left: 0},
413
+ right: {right: 0},
414
+ body: {flexGrow: 0, flexShrink: 1},
415
+ bodyFill: {flex: 1},
416
+ })
417
+
418
+ /**
419
+ * Native Body: a ScrollView, since a native div does not scroll its
420
+ * overflow. In a bottom sheet the drag defers to it: the list scrolls, and
421
+ * moves the sheet only from its top.
422
+ */
423
+ function Body({children}: {children: ReactNode}) {
424
+ const sheet = useContext(SheetScrollContext)
425
+ const {snapPoints} = useDrawer()
426
+ if (!sheet) return null
427
+ return (
428
+ <GestureDetector gesture={sheet.scrollGesture}>
429
+ <Animated.ScrollView
430
+ ref={sheet.scrollRef}
431
+ onScroll={sheet.onScroll}
432
+ scrollEventThrottle={16}
433
+ bounces={false}
434
+ style={snapPoints.length > 0 ? layout.bodyFill : layout.body}
435
+ >
436
+ <html.div style={[nativeStyles.bodyContent, nativeStyles.hiddenBelow(sheet.hiddenBelow)]}>
437
+ {children}
438
+ </html.div>
439
+ </Animated.ScrollView>
440
+ </GestureDetector>
441
+ )
442
+ }
443
+
444
+ interface CloseProps {
445
+ children?: ReactNode
446
+ 'aria-label'?: string
447
+ }
448
+
449
+ function Close({children, 'aria-label': ariaLabel = 'Close'}: CloseProps) {
450
+ const {requestClose} = useDrawer()
451
+ if (children) return withPress(children, requestClose)
452
+ return (
453
+ <html.button onClick={requestClose} aria-label={ariaLabel} style={partStyles.closeButton}>
454
+ <Icon name="x" size="sm" color="textMuted" />
455
+ </html.button>
456
+ )
457
+ }
458
+
459
+ export const Drawer = {
460
+ Root,
461
+ Trigger,
462
+ Portal,
463
+ Header,
464
+ Title,
465
+ Description,
466
+ Body,
467
+ Footer,
468
+ Close,
469
+ }