@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,152 @@
1
+ import {type ReactNode, useEffect, useLayoutEffect, useRef} from 'react'
2
+
3
+ /** Typed characters within this many ms build one typeahead prefix. */
4
+ const TYPEAHEAD_RESET_MS = 500
5
+
6
+ interface TreeKeyboardInput {
7
+ rootRef: React.RefObject<HTMLUListElement | null>
8
+ focusValue: string | null
9
+ setFocusValue: (value: string | null) => void
10
+ setKeyboardFocus: (on: boolean) => void
11
+ open: ReadonlyArray<string>
12
+ children: ReactNode
13
+ onSelect: (value: string) => void
14
+ setExpanded: (value: string, open: boolean) => void
15
+ }
16
+
17
+ /**
18
+ * Web: the tree's roving tab stop, arrow keys, Home/End, Enter and typeahead
19
+ * (WAI-ARIA tree pattern), read from the rendered DOM. Native has none of
20
+ * them (plan N0, Decision 6): useTreeKeyboard.native.ts.
21
+ */
22
+ export function useTreeKeyboard({
23
+ rootRef,
24
+ focusValue,
25
+ setFocusValue,
26
+ setKeyboardFocus,
27
+ open,
28
+ children,
29
+ onSelect,
30
+ setExpanded,
31
+ }: TreeKeyboardInput) {
32
+ // Exactly one item is tabbable: the focus holder, else the selection, else
33
+ // the first item — re-checked when an item appears, closes or goes away.
34
+ useLayoutEffect(() => {
35
+ const root = rootRef.current
36
+ if (!root) return
37
+ const tabbable = root.querySelector('[role="treeitem"][tabindex="0"]')
38
+ if (tabbable) return
39
+ const first = root.querySelector<HTMLElement>('[role="treeitem"]')
40
+ const next = first?.dataset.treeValue ?? null
41
+ if (next !== focusValue) setFocusValue(next)
42
+ }, [rootRef, focusValue, setFocusValue, open, children])
43
+
44
+ // Keyboard: DOM order over the rendered items IS the visible order (a
45
+ // closed branch renders no children), so navigation reads it directly.
46
+ const typeahead = useRef({text: '', at: 0})
47
+ useEffect(() => {
48
+ const root = rootRef.current
49
+ if (!root) return
50
+ const rootEl = root
51
+ const items = () => Array.from(rootEl.querySelectorAll<HTMLElement>('[role="treeitem"]'))
52
+ const focusItem = (el: HTMLElement | null | undefined) => {
53
+ if (!el) return
54
+ el.focus()
55
+ }
56
+ function onKeyDown(e: KeyboardEvent) {
57
+ const current = (e.target as HTMLElement).closest<HTMLElement>('[role="treeitem"]')
58
+ if (!current || !rootEl.contains(current)) return
59
+ const list = items()
60
+ const i = list.indexOf(current)
61
+ const val = current.dataset.treeValue ?? ''
62
+ const isOpen = current.getAttribute('aria-expanded')
63
+ switch (e.key) {
64
+ case 'ArrowDown':
65
+ e.preventDefault()
66
+ focusItem(list[i + 1])
67
+ return
68
+ case 'ArrowUp':
69
+ e.preventDefault()
70
+ focusItem(list[i - 1])
71
+ return
72
+ case 'ArrowRight':
73
+ e.preventDefault()
74
+ if (isOpen === 'false') setExpanded(val, true)
75
+ else if (isOpen === 'true') focusItem(list[i + 1])
76
+ return
77
+ case 'ArrowLeft': {
78
+ e.preventDefault()
79
+ if (isOpen === 'true') {
80
+ setExpanded(val, false)
81
+ return
82
+ }
83
+ const parent = current.parentElement?.closest<HTMLElement>('[role="treeitem"]')
84
+ if (parent && rootEl.contains(parent)) focusItem(parent)
85
+ return
86
+ }
87
+ case 'Home':
88
+ e.preventDefault()
89
+ focusItem(list[0])
90
+ return
91
+ case 'End':
92
+ e.preventDefault()
93
+ focusItem(list[list.length - 1])
94
+ return
95
+ case 'Enter':
96
+ case ' ': {
97
+ e.preventDefault()
98
+ // an item with href: follow its link (the click selects it and
99
+ // runs onNavigate, or the browser navigates)
100
+ const link = current.querySelector<HTMLElement>(':scope > [data-tree-link]')
101
+ if (link) link.click()
102
+ else onSelect(val)
103
+ return
104
+ }
105
+ }
106
+ // typeahead: a printable character, no modifier
107
+ if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
108
+ const now = Date.now()
109
+ const t = typeahead.current
110
+ t.text = now - t.at > TYPEAHEAD_RESET_MS ? e.key : t.text + e.key
111
+ t.at = now
112
+ // the same letter typed again cycles through the items it starts;
113
+ // a real prefix ("fi") narrows, and may match the current item
114
+ const typed = t.text.toLocaleLowerCase()
115
+ const repeat = [...typed].every((c) => c === typed[0])
116
+ const needle = repeat ? typed[0] : typed
117
+ const start = repeat ? i + 1 : i
118
+ for (let k = 0; k < list.length; k++) {
119
+ const el = list[(start + k) % list.length]
120
+ if ((el.dataset.treeText ?? '').toLocaleLowerCase().startsWith(needle)) {
121
+ focusItem(el)
122
+ break
123
+ }
124
+ }
125
+ }
126
+ }
127
+ // the roving tab stop follows focus, however it moved (keys or pointer)
128
+ function onFocusIn(e: FocusEvent) {
129
+ const item = (e.target as HTMLElement).closest<HTMLElement>('[role="treeitem"]')
130
+ const v = item?.dataset.treeValue
131
+ if (v) setFocusValue(v)
132
+ setKeyboardFocus(item?.matches(':focus-visible') ?? false)
133
+ }
134
+ function onFocusOut(e: FocusEvent) {
135
+ if (!rootEl.contains(e.relatedTarget as Node | null)) setKeyboardFocus(false)
136
+ }
137
+ const onPointerDown = () => setKeyboardFocus(false)
138
+ const onKey = () => setKeyboardFocus(true)
139
+ root.addEventListener('keydown', onKeyDown)
140
+ root.addEventListener('keydown', onKey)
141
+ root.addEventListener('focusin', onFocusIn)
142
+ root.addEventListener('focusout', onFocusOut)
143
+ root.addEventListener('pointerdown', onPointerDown)
144
+ return () => {
145
+ root.removeEventListener('keydown', onKeyDown)
146
+ root.removeEventListener('keydown', onKey)
147
+ root.removeEventListener('focusin', onFocusIn)
148
+ root.removeEventListener('focusout', onFocusOut)
149
+ root.removeEventListener('pointerdown', onPointerDown)
150
+ }
151
+ }, [rootRef, onSelect, setExpanded, setFocusValue, setKeyboardFocus])
152
+ }
@@ -0,0 +1,39 @@
1
+ import type {ComponentMeta} from '../component-meta'
2
+
3
+ export const meta: ComponentMeta = {
4
+ description:
5
+ 'A windowed list on @tanstack/react-virtual: only the rows in view (plus overscan) are mounted, and rows are measured so they may differ in height. role="list" with listitem rows carrying aria-setsize and aria-posinset. anchor="end" opens at the last item and stays pinned to the bottom while new items arrive and the reader is at the bottom (a transcript, a log). It fills its parent, or stops at maxHeight. Web only: on native, host FlashList or Legend List behind your own .native.tsx leaf.',
6
+ whenToUse: [
7
+ 'Hundreds to tens of thousands of rows: sessions, a chat transcript, log lines',
8
+ 'A list that grows at the bottom while the reader follows it (anchor="end")',
9
+ ],
10
+ whenNotToUse: [
11
+ 'Tabular data with columns and sorting — use VirtualTable',
12
+ 'A few dozen rows — use List; windowing only adds cost there',
13
+ 'Selectable options in a picker — use Listbox',
14
+ ],
15
+ anatomy: {
16
+ required: [],
17
+ optional: [],
18
+ },
19
+ relatedTo: [
20
+ {
21
+ component: 'VirtualTable',
22
+ kind: 'contrast',
23
+ relationship: 'VirtualTable windows table rows with columns; VirtualList windows any row',
24
+ },
25
+ {
26
+ component: 'List',
27
+ kind: 'composition',
28
+ relationship: 'Render a List.Item-like row in renderItem for the list look',
29
+ },
30
+ ],
31
+ example: `<VirtualList
32
+ aria-label="Transcript"
33
+ items={messages}
34
+ getKey={(m) => m.id}
35
+ estimateSize="controlLg"
36
+ anchor="end"
37
+ renderItem={(m) => <Text>{m.text}</Text>}
38
+ />`,
39
+ }
@@ -0,0 +1,95 @@
1
+ import type {Meta, StoryObj} from '@storybook/react'
2
+ import {expect, waitFor} from 'storybook/test'
3
+ import type {SizeToken} from '@duro-app/tokens/keys'
4
+ import {VirtualList} from './VirtualList'
5
+ import {Text} from '../Text/Text'
6
+
7
+ const meta: Meta = {
8
+ title: 'Components/VirtualList',
9
+ }
10
+
11
+ export default meta
12
+ type Story = StoryObj
13
+
14
+ const ITEMS = Array.from({length: 10_000}, (_, i) => ({id: i, text: `Message ${i + 1}`}))
15
+
16
+ function Rows({anchor}: {anchor?: 'start' | 'end'}) {
17
+ return (
18
+ <VirtualList
19
+ aria-label="Transcript"
20
+ items={ITEMS}
21
+ getKey={(m) => m.id}
22
+ estimateSize="controlLg"
23
+ anchor={anchor}
24
+ maxHeight="previewMaxH"
25
+ renderItem={(m) => <Text>{m.text}</Text>}
26
+ />
27
+ )
28
+ }
29
+
30
+ interface PlaygroundArgs {
31
+ count: number
32
+ anchor: 'start' | 'end'
33
+ overscan: number
34
+ estimateSize: SizeToken
35
+ }
36
+
37
+ export const Playground: StoryObj<PlaygroundArgs> = {
38
+ args: {count: 10_000, anchor: 'start', overscan: 5, estimateSize: 'controlLg'},
39
+ argTypes: {
40
+ count: {
41
+ control: {type: 'number', min: 0, max: 10_000, step: 500},
42
+ description: 'How many items',
43
+ },
44
+ anchor: {
45
+ control: 'inline-radio',
46
+ options: ['start', 'end'],
47
+ description: "'end' opens at the last item and follows new ones",
48
+ },
49
+ overscan: {
50
+ control: {type: 'number', min: 0, max: 50},
51
+ description: 'Rows rendered past each edge',
52
+ },
53
+ estimateSize: {
54
+ control: 'select',
55
+ options: ['controlSm', 'controlLg', 'touchTarget', 'iconXl'],
56
+ description: "Each row's starting height estimate (a size token)",
57
+ },
58
+ },
59
+ render: ({count, anchor, overscan, estimateSize}) => (
60
+ <VirtualList
61
+ aria-label="Transcript"
62
+ items={ITEMS.slice(0, count)}
63
+ getKey={(m) => m.id}
64
+ estimateSize={estimateSize}
65
+ anchor={anchor}
66
+ overscan={overscan}
67
+ maxHeight="previewMaxH"
68
+ renderItem={(m) => <Text>{m.text}</Text>}
69
+ />
70
+ ),
71
+ }
72
+
73
+ export const TenThousand: Story = {
74
+ render: () => <Rows />,
75
+ play: async ({canvas}) => {
76
+ const list = canvas.getByRole('list', {name: 'Transcript'})
77
+ await waitFor(() => expect(canvas.getAllByRole('listitem').length).toBeGreaterThan(0))
78
+ const rows = canvas.getAllByRole('listitem')
79
+ await expect(rows.length).toBeLessThan(50)
80
+ await expect(rows[0]).toHaveAttribute('aria-setsize', '10000')
81
+ await expect(rows[0]).toHaveAttribute('aria-posinset', '1')
82
+ await expect(list.scrollHeight).toBeGreaterThan(list.clientHeight * 100)
83
+ },
84
+ }
85
+
86
+ export const AnchoredToTheEnd: Story = {
87
+ render: () => <Rows anchor="end" />,
88
+ play: async ({canvas}) => {
89
+ await waitFor(() => expect(canvas.getByText('Message 10000')).toBeInTheDocument())
90
+ const rows = canvas.getAllByRole('listitem')
91
+ await expect(rows.length).toBeLessThan(50)
92
+ const last = rows.find((r) => r.getAttribute('aria-posinset') === '10000')
93
+ await expect(last).toBeDefined()
94
+ },
95
+ }
@@ -0,0 +1,103 @@
1
+ import {type ReactNode, useEffect, useLayoutEffect, useRef} from 'react'
2
+ import {html} from 'react-strict-dom'
3
+ import {useVirtualizer} from '@tanstack/react-virtual'
4
+ import {SIZES_PX, type SizeToken} from '@duro-app/tokens/keys'
5
+ import {resolveLength, type Length} from '../../shared/length'
6
+ import {styles} from './styles.css'
7
+
8
+ export interface VirtualListProps<T> {
9
+ items: readonly T[]
10
+ /** Each row's starting height estimate, a size token; rows are then measured. */
11
+ estimateSize: SizeToken
12
+ renderItem: (item: T, index: number) => ReactNode
13
+ /** A stable key per item (an id), so rows keep their measurement as items change. */
14
+ getKey: (item: T, index: number) => string | number
15
+ /** Rows rendered beyond each edge of the view. Default 5 */
16
+ overscan?: number
17
+ /**
18
+ * `end` starts at the last item and stays pinned to the bottom while new
19
+ * items arrive, as long as the reader is at the bottom (a transcript, a log).
20
+ * Default `start`.
21
+ */
22
+ anchor?: 'start' | 'end'
23
+ /** Cap the list's height; without it, it fills its parent. */
24
+ maxHeight?: Length
25
+ /** The list's accessible name. */
26
+ 'aria-label': string
27
+ }
28
+
29
+ /** Within this many px of the end counts as "at the bottom" for `anchor="end"`. */
30
+ const BOTTOM_SLOP = SIZES_PX.touchTarget
31
+
32
+ /**
33
+ * A windowed list: only the rows in view (plus `overscan`) are mounted, so a
34
+ * list of tens of thousands stays fast. Rows are measured as they render, so
35
+ * they may differ in height. It is a `role="list"`; each row is a listitem
36
+ * with `aria-setsize` and `aria-posinset`, so assistive tech knows the full
37
+ * length while most rows are not in the DOM. Web only: on native, host
38
+ * FlashList or Legend List behind your own `.native.tsx` leaf.
39
+ */
40
+ export function VirtualList<T>({
41
+ items,
42
+ estimateSize,
43
+ renderItem,
44
+ getKey,
45
+ overscan = 5,
46
+ anchor = 'start',
47
+ maxHeight,
48
+ 'aria-label': ariaLabel,
49
+ }: VirtualListProps<T>) {
50
+ const scrollRef = useRef<HTMLDivElement>(null)
51
+ const virtualizer = useVirtualizer({
52
+ count: items.length,
53
+ getScrollElement: () => scrollRef.current,
54
+ estimateSize: () => SIZES_PX[estimateSize],
55
+ getItemKey: (index) => getKey(items[index], index),
56
+ overscan,
57
+ })
58
+
59
+ // `end`: open at the last item, and follow new items while at the bottom.
60
+ const atBottom = useRef(true)
61
+ useEffect(() => {
62
+ const el = scrollRef.current
63
+ if (!el || anchor !== 'end') return
64
+ const onScroll = () => {
65
+ atBottom.current = el.scrollHeight - el.scrollTop - el.clientHeight <= BOTTOM_SLOP
66
+ }
67
+ el.addEventListener('scroll', onScroll, {passive: true})
68
+ return () => el.removeEventListener('scroll', onScroll)
69
+ }, [anchor])
70
+ useLayoutEffect(() => {
71
+ if (anchor !== 'end' || items.length === 0 || !atBottom.current) return
72
+ virtualizer.scrollToIndex(items.length - 1, {align: 'end'})
73
+ }, [anchor, items.length, virtualizer])
74
+
75
+ const rows = virtualizer.getVirtualItems()
76
+ return (
77
+ <html.div
78
+ ref={scrollRef}
79
+ role="list"
80
+ aria-label={ariaLabel}
81
+ style={[
82
+ styles.scroller,
83
+ maxHeight !== undefined && styles.scrollMax(resolveLength(maxHeight)),
84
+ ]}
85
+ >
86
+ <html.div style={styles.total(virtualizer.getTotalSize())}>
87
+ {rows.map((row) => (
88
+ <html.div
89
+ key={row.key}
90
+ ref={virtualizer.measureElement}
91
+ data-index={row.index}
92
+ role="listitem"
93
+ aria-setsize={items.length}
94
+ aria-posinset={row.index + 1}
95
+ style={styles.row(row.start)}
96
+ >
97
+ {renderItem(items[row.index], row.index)}
98
+ </html.div>
99
+ ))}
100
+ </html.div>
101
+ </html.div>
102
+ )
103
+ }
@@ -0,0 +1,31 @@
1
+ import {css} from 'react-strict-dom'
2
+
3
+ export const styles = css.create({
4
+ // The scroll container: it fills its parent (a pane, a flex column) by
5
+ // default, or stops at `maxHeight`.
6
+ scroller: {
7
+ position: 'relative',
8
+ width: '100%',
9
+ height: '100%',
10
+ minHeight: 0,
11
+ overflowY: 'auto',
12
+ overscrollBehavior: 'contain',
13
+ },
14
+ scrollMax: (maxHeight: number | string) => ({
15
+ height: 'auto',
16
+ maxHeight,
17
+ }),
18
+ // The full virtual height, so the scrollbar is the list's real length.
19
+ total: (height: number) => ({
20
+ position: 'relative',
21
+ width: '100%',
22
+ height,
23
+ }),
24
+ row: (y: number) => ({
25
+ position: 'absolute',
26
+ top: 0,
27
+ left: 0,
28
+ width: '100%',
29
+ transform: `translateY(${y}px)`,
30
+ }),
31
+ })
@@ -9,10 +9,14 @@ export const styles = css.create({
9
9
  wrap: {
10
10
  position: 'relative',
11
11
  width: '100%',
12
+ // Native has no border-box reset (the web's global-reset.css); declare it.
13
+ boxSizing: 'border-box',
12
14
  },
13
15
  scroll: {
14
16
  position: 'relative',
15
17
  width: '100%',
18
+ // Native has no border-box reset (the web's global-reset.css); declare it.
19
+ boxSizing: 'border-box',
16
20
  borderWidth: borders.hairline,
17
21
  borderStyle: 'solid',
18
22
  borderColor: colors.border,
@@ -66,6 +70,8 @@ export const styles = css.create({
66
70
  bodyTotal: (height: number) => ({
67
71
  position: 'relative',
68
72
  width: '100%',
73
+ // Native has no border-box reset (the web's global-reset.css); declare it.
74
+ boxSizing: 'border-box',
69
75
  height,
70
76
  }),
71
77
  row: {
@@ -73,6 +79,8 @@ export const styles = css.create({
73
79
  flexDirection: 'row',
74
80
  alignItems: 'center',
75
81
  width: '100%',
82
+ // Native has no border-box reset (the web's global-reset.css); declare it.
83
+ boxSizing: 'border-box',
76
84
  borderBottomWidth: borders.hairline,
77
85
  borderBottomStyle: 'solid',
78
86
  borderBottomColor: colors.border,
@@ -113,6 +121,8 @@ export const styles = css.create({
113
121
  stackScroll: {
114
122
  position: 'relative',
115
123
  width: '100%',
124
+ // Native has no border-box reset (the web's global-reset.css); declare it.
125
+ boxSizing: 'border-box',
116
126
  backgroundColor: 'transparent',
117
127
  },
118
128
  stackBody: {
@@ -125,6 +135,8 @@ export const styles = css.create({
125
135
  flexDirection: 'column',
126
136
  gap: spacing.xs,
127
137
  width: '100%',
138
+ // Native has no border-box reset (the web's global-reset.css); declare it.
139
+ boxSizing: 'border-box',
128
140
  padding: spacing.sm,
129
141
  borderWidth: borders.hairline,
130
142
  borderStyle: 'solid',
@@ -0,0 +1,30 @@
1
+ import {useEffect, useState, type RefObject} from 'react'
2
+ import {useWindowDimensions} from 'react-native'
3
+ import {breakpointsPx, type Breakpoint} from '@duro-app/tokens/tokens/breakpoints.css'
4
+
5
+ interface Measurable {
6
+ measure?: (cb: (x: number, y: number, width: number) => void) => void
7
+ }
8
+
9
+ /**
10
+ * Native: whether the view behind `ref` is narrower than a Duro breakpoint.
11
+ * There is no ResizeObserver; the view is measured after layout and again
12
+ * whenever the window changes size (a rotation, a split screen). `false`
13
+ * until the first measurement, as on the web.
14
+ */
15
+ export function useContainerBelow(ref: RefObject<unknown>, breakpoint: Breakpoint): boolean {
16
+ const [below, setBelow] = useState(false)
17
+ const limit = breakpointsPx[breakpoint]
18
+ const {width: windowWidth} = useWindowDimensions()
19
+
20
+ useEffect(() => {
21
+ const frame = requestAnimationFrame(() => {
22
+ ;(ref.current as Measurable | null)?.measure?.((_x, _y, width) => {
23
+ if (width > 0) setBelow(width < limit)
24
+ })
25
+ })
26
+ return () => cancelAnimationFrame(frame)
27
+ }, [ref, limit, windowWidth])
28
+
29
+ return below
30
+ }
@@ -7,7 +7,8 @@
7
7
  * TagGroup, ThemeProvider's portal) that use web-only DOM APIs — and Combobox
8
8
  * imports `react-dom`, so pulling the full barrel crashes on Metro. This barrel
9
9
  * re-exports ONLY the components whose source is RN-safe (pure react-strict-dom
10
- * `html` + `css`, audited 2026-06-02). The rest get RN variants in a later pass.
10
+ * `html` + `css`) or that have a `.native` variant; `pnpm typecheck:native` and
11
+ * `scripts/check-native-graph.mjs` hold the line (plan N0).
11
12
  *
12
13
  * Tokens resolve fine on native already: @duro-app/tokens's subpath exports
13
14
  * (`./tokens/*.css`, `./themes/*.css`) point straight at their `.css.ts`
@@ -29,7 +30,63 @@ export {Textarea, type TextareaVariant} from './components/Textarea/Textarea'
29
30
  export {Field} from './components/Field/Field'
30
31
  export {InputGroup} from './components/InputGroup/InputGroup'
31
32
 
33
+ // Status and feedback (5.12: Icon draws with react-native-svg on native)
34
+ export {Alert, type AlertVariant} from './components/Alert/Alert'
35
+ export {Callout, type CalloutVariant} from './components/Callout/Callout'
36
+ export {EmptyState} from './components/EmptyState/EmptyState'
37
+ export {Icon, type IconName} from './components/Icon'
38
+ export {Meter, type MeterProps, type MeterTone} from './components/Meter/Meter'
39
+ export {Spinner, type SpinnerSize} from './components/Spinner/Spinner'
40
+ export {
41
+ StatusIcon,
42
+ type StatusIconName,
43
+ type StatusIconVariant,
44
+ } from './components/StatusIcon/StatusIcon'
45
+
46
+ // Choice and collection
47
+ export {List, type ListSelectionMode} from './components/List/List'
48
+ export {RadioGroup} from './components/RadioGroup/RadioGroup'
49
+ export {Toggle, type ToggleSize} from './components/Toggle/Toggle'
50
+ export {Tag, type TagVariant} from './components/Tag/Tag'
51
+ export {SideNav} from './components/SideNav/SideNav'
52
+ export {Tabs} from './components/Tabs/Tabs'
53
+ export {Tree} from './components/Tree/Tree'
54
+ // Always stacked on native: a phone is below every collapseBelow.
55
+ export {SplitPane} from './components/SplitPane/SplitPane'
56
+ // Always the phone shape on native: the bar on top, the rail in a left Drawer.
57
+ export {
58
+ AppShell,
59
+ type AppShellCollapse,
60
+ type AppShellRailState,
61
+ } from './components/AppShell/AppShell'
62
+
63
+ // Overlays (5.12: rendered in an OverlayModal on native)
64
+ export {ConfirmDialog} from './components/ConfirmDialog/ConfirmDialog'
65
+ export {Dialog, type DialogSize} from './components/Dialog/Dialog'
66
+ // 5.13: the panel follows the finger on the UI thread (Gesture Handler + Reanimated)
67
+ export {
68
+ Drawer,
69
+ type DrawerAnchor,
70
+ type DrawerSize,
71
+ type SnapPoint,
72
+ } from './components/Drawer/Drawer'
73
+ export {Menu, type MenuTriggerVariant} from './components/Menu/Menu'
74
+ export {Popover, type PopoverHandle} from './components/Popover/Popover'
75
+ // Touch has no hover: a Tooltip's text is its trigger's accessibility hint.
76
+ export {Tooltip} from './components/Tooltip/Tooltip'
77
+ // Drawn through the portal host, above every open overlay and the keyboard.
78
+ export {
79
+ ToastProvider,
80
+ useToast,
81
+ type ToastVariant,
82
+ type ToastOptions,
83
+ type ToastAction,
84
+ type ToastContextValue,
85
+ type ToastProviderProps,
86
+ } from './components/Toast'
87
+
32
88
  // Layout primitives
89
+ export {PageShell, type PageShellMaxWidth, type PageShellPadding} from './components/PageShell'
33
90
  export {Stack, type SpacingKey} from './components/Stack/Stack'
34
91
  export {Inline} from './components/Inline/Inline'
35
92
  export {Cluster} from './components/Cluster/Cluster'
@@ -41,5 +98,8 @@ export type {GridTrack} from './shared/length'
41
98
  export {
42
99
  ThemeProvider,
43
100
  usePortalMount,
101
+ useResolvedColors,
44
102
  type ThemeName,
45
103
  } from './components/ThemeProvider/ThemeProvider'
104
+ // Where toasts draw on native: mount one inside a natively presented route.
105
+ export {PortalHost} from './shared/portal/PortalHost'
package/src/index.ts CHANGED
@@ -49,7 +49,12 @@ export {
49
49
  } from './components/DragDrop/DragDrop'
50
50
  export {Dialog, type DialogSize} from './components/Dialog/Dialog'
51
51
  export {DetailPanel, type DetailPanelSize} from './components/DetailPanel/DetailPanel'
52
- export {Drawer, type DrawerAnchor, type DrawerSize} from './components/Drawer/Drawer'
52
+ export {
53
+ Drawer,
54
+ type DrawerAnchor,
55
+ type DrawerSize,
56
+ type SnapPoint,
57
+ } from './components/Drawer/Drawer'
53
58
  export {EmptyState} from './components/EmptyState/EmptyState'
54
59
  export {Heading, type HeadingVariant} from './components/Heading/Heading'
55
60
  export {List, type ListSelectionMode} from './components/List/List'
@@ -115,6 +120,8 @@ export {
115
120
  type TimelineRowProps,
116
121
  } from './components/Timeline/Timeline'
117
122
  export {Tree} from './components/Tree/Tree'
123
+ export {SplitPane} from './components/SplitPane/SplitPane'
124
+ export {VirtualList, type VirtualListProps} from './components/VirtualList/VirtualList'
118
125
  export {Textarea, type TextareaVariant} from './components/Textarea/Textarea'
119
126
  export {
120
127
  ThemeProvider,
@@ -0,0 +1,10 @@
1
+ // The native graph has no DOM lib. A few spots on it still name DOM members:
2
+ // ConfirmDialog reads `event.target.value` from an Input change (RSD's native
3
+ // change event carries it too), and Grid reads `process.env.NODE_ENV`
4
+ // (inlined by Metro). Declare exactly those members rather than pulling in
5
+ // lib.dom.
6
+ interface HTMLInputElement {
7
+ value: string
8
+ }
9
+
10
+ declare const process: {env: {NODE_ENV?: string}}
@@ -0,0 +1,3 @@
1
+ // Empty on purpose: tsconfig.native.json maps "react-dom" here, so a react-dom
2
+ // import on the native graph has no exports to resolve and fails to typecheck.
3
+ export {}