@multiplatform.one/components 7.8.0 → 7.11.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 (452) hide show
  1. package/README.md +98 -0
  2. package/dist/cjs/Accordion/index.cjs +3 -3
  3. package/dist/cjs/Accordion/index.native.js +4 -4
  4. package/dist/cjs/Accordion/index.native.js.map +1 -1
  5. package/dist/cjs/Chip/index.cjs +1 -1
  6. package/dist/cjs/Chip/index.native.js +1 -1
  7. package/dist/cjs/Chip/index.native.js.map +1 -1
  8. package/dist/cjs/ContextMenu/index.cjs +3 -3
  9. package/dist/cjs/ContextMenu/index.native.js +3 -3
  10. package/dist/cjs/ContextMenu/index.native.js.map +1 -1
  11. package/dist/cjs/DropdownMenu/index.cjs +3 -3
  12. package/dist/cjs/DropdownMenu/index.native.js +3 -3
  13. package/dist/cjs/DropdownMenu/index.native.js.map +1 -1
  14. package/dist/cjs/Meter/index.cjs +3 -2
  15. package/dist/cjs/Meter/index.native.js +3 -2
  16. package/dist/cjs/Meter/index.native.js.map +1 -1
  17. package/dist/cjs/Meter/meter.cjs +1 -2
  18. package/dist/cjs/Meter/meter.native.js +1 -2
  19. package/dist/cjs/Meter/meter.native.js.map +1 -1
  20. package/dist/cjs/Pagination.cjs +2 -2
  21. package/dist/cjs/Pagination.native.js +2 -2
  22. package/dist/cjs/Pagination.native.js.map +1 -1
  23. package/dist/cjs/ProgressSteps/index.cjs +3 -3
  24. package/dist/cjs/ProgressSteps/index.native.js +3 -3
  25. package/dist/cjs/ProgressSteps/index.native.js.map +1 -1
  26. package/dist/cjs/Quote/index.cjs +1 -2
  27. package/dist/cjs/Quote/index.native.js +1 -2
  28. package/dist/cjs/Quote/index.native.js.map +1 -1
  29. package/dist/cjs/SheetModal/index.cjs +22 -3
  30. package/dist/cjs/SheetModal/index.native.js +50 -10
  31. package/dist/cjs/SheetModal/index.native.js.map +1 -1
  32. package/dist/cjs/Tabs/index.cjs +34 -13
  33. package/dist/cjs/Tabs/index.native.js +38 -13
  34. package/dist/cjs/Tabs/index.native.js.map +1 -1
  35. package/dist/cjs/Toast/index.cjs +4 -5
  36. package/dist/cjs/Toast/index.native.js +4 -5
  37. package/dist/cjs/Toast/index.native.js.map +1 -1
  38. package/dist/cjs/TreeSelect/index.cjs +46 -7
  39. package/dist/cjs/TreeSelect/index.native.js +58 -7
  40. package/dist/cjs/TreeSelect/index.native.js.map +1 -1
  41. package/dist/cjs/charts/PieChart.cjs +1 -2
  42. package/dist/cjs/charts/PieChart.native.js +1 -2
  43. package/dist/cjs/charts/PieChart.native.js.map +1 -1
  44. package/dist/cjs/code/HighlightedCode/index.cjs +1 -1
  45. package/dist/cjs/code/HighlightedCode/index.native.js +1 -1
  46. package/dist/cjs/code/HighlightedCode/index.native.js.map +1 -1
  47. package/dist/cjs/componentColors.cjs +6 -4
  48. package/dist/cjs/componentColors.native.js +6 -4
  49. package/dist/cjs/componentColors.native.js.map +1 -1
  50. package/dist/cjs/images/Image/ImageFrame.cjs +4 -2
  51. package/dist/cjs/images/Image/ImageFrame.native.js +4 -2
  52. package/dist/cjs/images/Image/ImageFrame.native.js.map +1 -1
  53. package/dist/cjs/layouts/AsyncBoundary.cjs +1 -2
  54. package/dist/cjs/layouts/AsyncBoundary.native.js +1 -2
  55. package/dist/cjs/layouts/AsyncBoundary.native.js.map +1 -1
  56. package/dist/cjs/layouts/EmptyState.cjs +6 -2
  57. package/dist/cjs/layouts/EmptyState.native.js +10 -6
  58. package/dist/cjs/layouts/EmptyState.native.js.map +1 -1
  59. package/dist/cjs/layouts/attachments.cjs +4 -4
  60. package/dist/cjs/layouts/attachments.native.js +4 -4
  61. package/dist/cjs/layouts/attachments.native.js.map +1 -1
  62. package/dist/cjs/layouts/breadcrumbs.cjs +1 -2
  63. package/dist/cjs/layouts/breadcrumbs.native.js +1 -2
  64. package/dist/cjs/layouts/breadcrumbs.native.js.map +1 -1
  65. package/dist/cjs/layouts/page.cjs +2 -2
  66. package/dist/cjs/layouts/page.native.js +2 -2
  67. package/dist/cjs/layouts/page.native.js.map +1 -1
  68. package/dist/cjs/layouts/sidebar.cjs +2 -2
  69. package/dist/cjs/layouts/sidebar.native.js +2 -2
  70. package/dist/cjs/layouts/sidebar.native.js.map +1 -1
  71. package/dist/cjs/layouts/tags.cjs +6 -7
  72. package/dist/cjs/layouts/tags.native.js +6 -9
  73. package/dist/cjs/layouts/tags.native.js.map +1 -1
  74. package/dist/cjs/layouts/timeline.cjs +5 -5
  75. package/dist/cjs/layouts/timeline.native.js +5 -5
  76. package/dist/cjs/layouts/timeline.native.js.map +1 -1
  77. package/dist/cjs/surfaces.cjs +1 -0
  78. package/dist/cjs/surfaces.native.js +1 -0
  79. package/dist/cjs/surfaces.native.js.map +1 -1
  80. package/dist/cjs/views/Calendar.cjs +11 -8
  81. package/dist/cjs/views/Calendar.native.js +11 -8
  82. package/dist/cjs/views/Calendar.native.js.map +1 -1
  83. package/dist/cjs/views/Dashboard.cjs +6 -6
  84. package/dist/cjs/views/Dashboard.native.js +7 -7
  85. package/dist/cjs/views/Dashboard.native.js.map +1 -1
  86. package/dist/cjs/views/Gantt.cjs +3 -3
  87. package/dist/cjs/views/Gantt.native.js +3 -3
  88. package/dist/cjs/views/Gantt.native.js.map +1 -1
  89. package/dist/cjs/views/Kanban.cjs +2 -2
  90. package/dist/cjs/views/Kanban.native.js +2 -2
  91. package/dist/cjs/views/Kanban.native.js.map +1 -1
  92. package/dist/cjs/views/ReportBuilder.cjs +4 -4
  93. package/dist/cjs/views/ReportBuilder.native.js +4 -4
  94. package/dist/cjs/views/ReportBuilder.native.js.map +1 -1
  95. package/dist/cjs/views/TreeView.cjs +248 -19
  96. package/dist/cjs/views/TreeView.native.js +264 -20
  97. package/dist/cjs/views/TreeView.native.js.map +1 -1
  98. package/dist/cjs/views/treeViewReveal.cjs +55 -0
  99. package/dist/cjs/views/treeViewReveal.native.js +80 -0
  100. package/dist/cjs/views/treeViewReveal.native.js.map +1 -0
  101. package/dist/esm/Accordion/index.mjs +3 -3
  102. package/dist/esm/Accordion/index.mjs.map +1 -1
  103. package/dist/esm/Accordion/index.native.js +4 -4
  104. package/dist/esm/Accordion/index.native.js.map +1 -1
  105. package/dist/esm/Chip/index.mjs +1 -1
  106. package/dist/esm/Chip/index.mjs.map +1 -1
  107. package/dist/esm/Chip/index.native.js +1 -1
  108. package/dist/esm/Chip/index.native.js.map +1 -1
  109. package/dist/esm/ContextMenu/index.mjs +3 -3
  110. package/dist/esm/ContextMenu/index.mjs.map +1 -1
  111. package/dist/esm/ContextMenu/index.native.js +3 -3
  112. package/dist/esm/ContextMenu/index.native.js.map +1 -1
  113. package/dist/esm/DropdownMenu/index.mjs +3 -3
  114. package/dist/esm/DropdownMenu/index.mjs.map +1 -1
  115. package/dist/esm/DropdownMenu/index.native.js +3 -3
  116. package/dist/esm/DropdownMenu/index.native.js.map +1 -1
  117. package/dist/esm/Meter/index.mjs +3 -2
  118. package/dist/esm/Meter/index.mjs.map +1 -1
  119. package/dist/esm/Meter/index.native.js +3 -2
  120. package/dist/esm/Meter/index.native.js.map +1 -1
  121. package/dist/esm/Meter/meter.mjs +1 -2
  122. package/dist/esm/Meter/meter.mjs.map +1 -1
  123. package/dist/esm/Meter/meter.native.js +1 -2
  124. package/dist/esm/Meter/meter.native.js.map +1 -1
  125. package/dist/esm/Pagination.mjs +2 -2
  126. package/dist/esm/Pagination.mjs.map +1 -1
  127. package/dist/esm/Pagination.native.js +2 -2
  128. package/dist/esm/Pagination.native.js.map +1 -1
  129. package/dist/esm/ProgressSteps/index.mjs +3 -3
  130. package/dist/esm/ProgressSteps/index.mjs.map +1 -1
  131. package/dist/esm/ProgressSteps/index.native.js +3 -3
  132. package/dist/esm/ProgressSteps/index.native.js.map +1 -1
  133. package/dist/esm/Quote/index.mjs +1 -2
  134. package/dist/esm/Quote/index.mjs.map +1 -1
  135. package/dist/esm/Quote/index.native.js +1 -2
  136. package/dist/esm/Quote/index.native.js.map +1 -1
  137. package/dist/esm/SheetModal/index.mjs +22 -3
  138. package/dist/esm/SheetModal/index.mjs.map +1 -1
  139. package/dist/esm/SheetModal/index.native.js +51 -11
  140. package/dist/esm/SheetModal/index.native.js.map +1 -1
  141. package/dist/esm/Tabs/index.mjs +34 -13
  142. package/dist/esm/Tabs/index.mjs.map +1 -1
  143. package/dist/esm/Tabs/index.native.js +38 -13
  144. package/dist/esm/Tabs/index.native.js.map +1 -1
  145. package/dist/esm/Toast/index.mjs +4 -5
  146. package/dist/esm/Toast/index.mjs.map +1 -1
  147. package/dist/esm/Toast/index.native.js +4 -5
  148. package/dist/esm/Toast/index.native.js.map +1 -1
  149. package/dist/esm/TreeSelect/index.mjs +46 -7
  150. package/dist/esm/TreeSelect/index.mjs.map +1 -1
  151. package/dist/esm/TreeSelect/index.native.js +58 -7
  152. package/dist/esm/TreeSelect/index.native.js.map +1 -1
  153. package/dist/esm/charts/PieChart.mjs +1 -2
  154. package/dist/esm/charts/PieChart.mjs.map +1 -1
  155. package/dist/esm/charts/PieChart.native.js +1 -2
  156. package/dist/esm/charts/PieChart.native.js.map +1 -1
  157. package/dist/esm/code/HighlightedCode/index.mjs +1 -1
  158. package/dist/esm/code/HighlightedCode/index.mjs.map +1 -1
  159. package/dist/esm/code/HighlightedCode/index.native.js +1 -1
  160. package/dist/esm/code/HighlightedCode/index.native.js.map +1 -1
  161. package/dist/esm/componentColors.mjs +6 -4
  162. package/dist/esm/componentColors.mjs.map +1 -1
  163. package/dist/esm/componentColors.native.js +6 -4
  164. package/dist/esm/componentColors.native.js.map +1 -1
  165. package/dist/esm/images/Image/ImageFrame.mjs +4 -2
  166. package/dist/esm/images/Image/ImageFrame.mjs.map +1 -1
  167. package/dist/esm/images/Image/ImageFrame.native.js +4 -2
  168. package/dist/esm/images/Image/ImageFrame.native.js.map +1 -1
  169. package/dist/esm/layouts/AsyncBoundary.mjs +1 -2
  170. package/dist/esm/layouts/AsyncBoundary.mjs.map +1 -1
  171. package/dist/esm/layouts/AsyncBoundary.native.js +1 -2
  172. package/dist/esm/layouts/AsyncBoundary.native.js.map +1 -1
  173. package/dist/esm/layouts/EmptyState.mjs +7 -3
  174. package/dist/esm/layouts/EmptyState.mjs.map +1 -1
  175. package/dist/esm/layouts/EmptyState.native.js +11 -7
  176. package/dist/esm/layouts/EmptyState.native.js.map +1 -1
  177. package/dist/esm/layouts/attachments.mjs +4 -4
  178. package/dist/esm/layouts/attachments.mjs.map +1 -1
  179. package/dist/esm/layouts/attachments.native.js +4 -4
  180. package/dist/esm/layouts/attachments.native.js.map +1 -1
  181. package/dist/esm/layouts/breadcrumbs.mjs +1 -2
  182. package/dist/esm/layouts/breadcrumbs.mjs.map +1 -1
  183. package/dist/esm/layouts/breadcrumbs.native.js +1 -2
  184. package/dist/esm/layouts/breadcrumbs.native.js.map +1 -1
  185. package/dist/esm/layouts/page.mjs +2 -2
  186. package/dist/esm/layouts/page.mjs.map +1 -1
  187. package/dist/esm/layouts/page.native.js +2 -2
  188. package/dist/esm/layouts/page.native.js.map +1 -1
  189. package/dist/esm/layouts/sidebar.mjs +2 -2
  190. package/dist/esm/layouts/sidebar.mjs.map +1 -1
  191. package/dist/esm/layouts/sidebar.native.js +2 -2
  192. package/dist/esm/layouts/sidebar.native.js.map +1 -1
  193. package/dist/esm/layouts/tags.mjs +6 -7
  194. package/dist/esm/layouts/tags.mjs.map +1 -1
  195. package/dist/esm/layouts/tags.native.js +6 -9
  196. package/dist/esm/layouts/tags.native.js.map +1 -1
  197. package/dist/esm/layouts/timeline.mjs +5 -5
  198. package/dist/esm/layouts/timeline.mjs.map +1 -1
  199. package/dist/esm/layouts/timeline.native.js +5 -5
  200. package/dist/esm/layouts/timeline.native.js.map +1 -1
  201. package/dist/esm/surfaces.mjs +1 -0
  202. package/dist/esm/surfaces.mjs.map +1 -1
  203. package/dist/esm/surfaces.native.js +1 -0
  204. package/dist/esm/surfaces.native.js.map +1 -1
  205. package/dist/esm/views/Calendar.mjs +11 -8
  206. package/dist/esm/views/Calendar.mjs.map +1 -1
  207. package/dist/esm/views/Calendar.native.js +11 -8
  208. package/dist/esm/views/Calendar.native.js.map +1 -1
  209. package/dist/esm/views/Dashboard.mjs +6 -6
  210. package/dist/esm/views/Dashboard.mjs.map +1 -1
  211. package/dist/esm/views/Dashboard.native.js +7 -7
  212. package/dist/esm/views/Dashboard.native.js.map +1 -1
  213. package/dist/esm/views/Gantt.mjs +3 -3
  214. package/dist/esm/views/Gantt.mjs.map +1 -1
  215. package/dist/esm/views/Gantt.native.js +3 -3
  216. package/dist/esm/views/Gantt.native.js.map +1 -1
  217. package/dist/esm/views/Kanban.mjs +2 -2
  218. package/dist/esm/views/Kanban.mjs.map +1 -1
  219. package/dist/esm/views/Kanban.native.js +2 -2
  220. package/dist/esm/views/Kanban.native.js.map +1 -1
  221. package/dist/esm/views/ReportBuilder.mjs +4 -4
  222. package/dist/esm/views/ReportBuilder.mjs.map +1 -1
  223. package/dist/esm/views/ReportBuilder.native.js +4 -4
  224. package/dist/esm/views/ReportBuilder.native.js.map +1 -1
  225. package/dist/esm/views/TreeView.mjs +249 -20
  226. package/dist/esm/views/TreeView.mjs.map +1 -1
  227. package/dist/esm/views/TreeView.native.js +265 -21
  228. package/dist/esm/views/TreeView.native.js.map +1 -1
  229. package/dist/esm/views/treeViewReveal.mjs +29 -0
  230. package/dist/esm/views/treeViewReveal.mjs.map +1 -0
  231. package/dist/esm/views/treeViewReveal.native.js +51 -0
  232. package/dist/esm/views/treeViewReveal.native.js.map +1 -0
  233. package/dist/jsx/Accordion/index.mjs +3 -3
  234. package/dist/jsx/Accordion/index.mjs.map +1 -1
  235. package/dist/jsx/Accordion/index.native.js +4 -4
  236. package/dist/jsx/Accordion/index.native.js.map +1 -1
  237. package/dist/jsx/Chip/index.mjs +1 -1
  238. package/dist/jsx/Chip/index.mjs.map +1 -1
  239. package/dist/jsx/Chip/index.native.js +1 -1
  240. package/dist/jsx/Chip/index.native.js.map +1 -1
  241. package/dist/jsx/ContextMenu/index.mjs +3 -3
  242. package/dist/jsx/ContextMenu/index.mjs.map +1 -1
  243. package/dist/jsx/ContextMenu/index.native.js +3 -3
  244. package/dist/jsx/ContextMenu/index.native.js.map +1 -1
  245. package/dist/jsx/DropdownMenu/index.mjs +3 -3
  246. package/dist/jsx/DropdownMenu/index.mjs.map +1 -1
  247. package/dist/jsx/DropdownMenu/index.native.js +3 -3
  248. package/dist/jsx/DropdownMenu/index.native.js.map +1 -1
  249. package/dist/jsx/Meter/index.mjs +3 -2
  250. package/dist/jsx/Meter/index.mjs.map +1 -1
  251. package/dist/jsx/Meter/index.native.js +3 -2
  252. package/dist/jsx/Meter/index.native.js.map +1 -1
  253. package/dist/jsx/Meter/meter.mjs +1 -2
  254. package/dist/jsx/Meter/meter.mjs.map +1 -1
  255. package/dist/jsx/Meter/meter.native.js +1 -2
  256. package/dist/jsx/Meter/meter.native.js.map +1 -1
  257. package/dist/jsx/Pagination.mjs +2 -2
  258. package/dist/jsx/Pagination.mjs.map +1 -1
  259. package/dist/jsx/Pagination.native.js +2 -2
  260. package/dist/jsx/Pagination.native.js.map +1 -1
  261. package/dist/jsx/ProgressSteps/index.mjs +3 -3
  262. package/dist/jsx/ProgressSteps/index.mjs.map +1 -1
  263. package/dist/jsx/ProgressSteps/index.native.js +3 -3
  264. package/dist/jsx/ProgressSteps/index.native.js.map +1 -1
  265. package/dist/jsx/Quote/index.mjs +1 -2
  266. package/dist/jsx/Quote/index.mjs.map +1 -1
  267. package/dist/jsx/Quote/index.native.js +1 -2
  268. package/dist/jsx/Quote/index.native.js.map +1 -1
  269. package/dist/jsx/SheetModal/index.mjs +22 -3
  270. package/dist/jsx/SheetModal/index.mjs.map +1 -1
  271. package/dist/jsx/SheetModal/index.native.js +50 -10
  272. package/dist/jsx/SheetModal/index.native.js.map +1 -1
  273. package/dist/jsx/Tabs/index.mjs +34 -13
  274. package/dist/jsx/Tabs/index.mjs.map +1 -1
  275. package/dist/jsx/Tabs/index.native.js +38 -13
  276. package/dist/jsx/Tabs/index.native.js.map +1 -1
  277. package/dist/jsx/Toast/index.mjs +4 -5
  278. package/dist/jsx/Toast/index.mjs.map +1 -1
  279. package/dist/jsx/Toast/index.native.js +4 -5
  280. package/dist/jsx/Toast/index.native.js.map +1 -1
  281. package/dist/jsx/TreeSelect/index.mjs +46 -7
  282. package/dist/jsx/TreeSelect/index.mjs.map +1 -1
  283. package/dist/jsx/TreeSelect/index.native.js +58 -7
  284. package/dist/jsx/TreeSelect/index.native.js.map +1 -1
  285. package/dist/jsx/charts/PieChart.mjs +1 -2
  286. package/dist/jsx/charts/PieChart.mjs.map +1 -1
  287. package/dist/jsx/charts/PieChart.native.js +1 -2
  288. package/dist/jsx/charts/PieChart.native.js.map +1 -1
  289. package/dist/jsx/code/HighlightedCode/index.mjs +1 -1
  290. package/dist/jsx/code/HighlightedCode/index.mjs.map +1 -1
  291. package/dist/jsx/code/HighlightedCode/index.native.js +1 -1
  292. package/dist/jsx/code/HighlightedCode/index.native.js.map +1 -1
  293. package/dist/jsx/componentColors.mjs +6 -4
  294. package/dist/jsx/componentColors.mjs.map +1 -1
  295. package/dist/jsx/componentColors.native.js +6 -4
  296. package/dist/jsx/componentColors.native.js.map +1 -1
  297. package/dist/jsx/images/Image/ImageFrame.mjs +4 -2
  298. package/dist/jsx/images/Image/ImageFrame.mjs.map +1 -1
  299. package/dist/jsx/images/Image/ImageFrame.native.js +4 -2
  300. package/dist/jsx/images/Image/ImageFrame.native.js.map +1 -1
  301. package/dist/jsx/layouts/AsyncBoundary.mjs +1 -2
  302. package/dist/jsx/layouts/AsyncBoundary.mjs.map +1 -1
  303. package/dist/jsx/layouts/AsyncBoundary.native.js +1 -2
  304. package/dist/jsx/layouts/AsyncBoundary.native.js.map +1 -1
  305. package/dist/jsx/layouts/EmptyState.mjs +7 -3
  306. package/dist/jsx/layouts/EmptyState.mjs.map +1 -1
  307. package/dist/jsx/layouts/EmptyState.native.js +10 -6
  308. package/dist/jsx/layouts/EmptyState.native.js.map +1 -1
  309. package/dist/jsx/layouts/attachments.mjs +4 -4
  310. package/dist/jsx/layouts/attachments.mjs.map +1 -1
  311. package/dist/jsx/layouts/attachments.native.js +4 -4
  312. package/dist/jsx/layouts/attachments.native.js.map +1 -1
  313. package/dist/jsx/layouts/breadcrumbs.mjs +1 -2
  314. package/dist/jsx/layouts/breadcrumbs.mjs.map +1 -1
  315. package/dist/jsx/layouts/breadcrumbs.native.js +1 -2
  316. package/dist/jsx/layouts/breadcrumbs.native.js.map +1 -1
  317. package/dist/jsx/layouts/page.mjs +2 -2
  318. package/dist/jsx/layouts/page.mjs.map +1 -1
  319. package/dist/jsx/layouts/page.native.js +2 -2
  320. package/dist/jsx/layouts/page.native.js.map +1 -1
  321. package/dist/jsx/layouts/sidebar.mjs +2 -2
  322. package/dist/jsx/layouts/sidebar.mjs.map +1 -1
  323. package/dist/jsx/layouts/sidebar.native.js +2 -2
  324. package/dist/jsx/layouts/sidebar.native.js.map +1 -1
  325. package/dist/jsx/layouts/tags.mjs +6 -7
  326. package/dist/jsx/layouts/tags.mjs.map +1 -1
  327. package/dist/jsx/layouts/tags.native.js +6 -9
  328. package/dist/jsx/layouts/tags.native.js.map +1 -1
  329. package/dist/jsx/layouts/timeline.mjs +5 -5
  330. package/dist/jsx/layouts/timeline.mjs.map +1 -1
  331. package/dist/jsx/layouts/timeline.native.js +5 -5
  332. package/dist/jsx/layouts/timeline.native.js.map +1 -1
  333. package/dist/jsx/surfaces.mjs +1 -0
  334. package/dist/jsx/surfaces.mjs.map +1 -1
  335. package/dist/jsx/surfaces.native.js +1 -0
  336. package/dist/jsx/surfaces.native.js.map +1 -1
  337. package/dist/jsx/views/Calendar.mjs +11 -8
  338. package/dist/jsx/views/Calendar.mjs.map +1 -1
  339. package/dist/jsx/views/Calendar.native.js +11 -8
  340. package/dist/jsx/views/Calendar.native.js.map +1 -1
  341. package/dist/jsx/views/Dashboard.mjs +6 -6
  342. package/dist/jsx/views/Dashboard.mjs.map +1 -1
  343. package/dist/jsx/views/Dashboard.native.js +7 -7
  344. package/dist/jsx/views/Dashboard.native.js.map +1 -1
  345. package/dist/jsx/views/Gantt.mjs +3 -3
  346. package/dist/jsx/views/Gantt.mjs.map +1 -1
  347. package/dist/jsx/views/Gantt.native.js +3 -3
  348. package/dist/jsx/views/Gantt.native.js.map +1 -1
  349. package/dist/jsx/views/Kanban.mjs +2 -2
  350. package/dist/jsx/views/Kanban.mjs.map +1 -1
  351. package/dist/jsx/views/Kanban.native.js +2 -2
  352. package/dist/jsx/views/Kanban.native.js.map +1 -1
  353. package/dist/jsx/views/ReportBuilder.mjs +4 -4
  354. package/dist/jsx/views/ReportBuilder.mjs.map +1 -1
  355. package/dist/jsx/views/ReportBuilder.native.js +4 -4
  356. package/dist/jsx/views/ReportBuilder.native.js.map +1 -1
  357. package/dist/jsx/views/TreeView.mjs +249 -20
  358. package/dist/jsx/views/TreeView.mjs.map +1 -1
  359. package/dist/jsx/views/TreeView.native.js +264 -20
  360. package/dist/jsx/views/TreeView.native.js.map +1 -1
  361. package/dist/jsx/views/treeViewReveal.mjs +29 -0
  362. package/dist/jsx/views/treeViewReveal.mjs.map +1 -0
  363. package/dist/jsx/views/treeViewReveal.native.js +80 -0
  364. package/dist/jsx/views/treeViewReveal.native.js.map +1 -0
  365. package/package.json +11 -11
  366. package/src/Accordion/index.tsx +4 -6
  367. package/src/Chip/index.tsx +1 -1
  368. package/src/ContextMenu/index.tsx +3 -3
  369. package/src/DropdownMenu/index.tsx +3 -3
  370. package/src/Meter/Meter.spec.tsx +12 -4
  371. package/src/Meter/index.tsx +3 -2
  372. package/src/Meter/meter.ts +6 -1
  373. package/src/Pagination.tsx +2 -2
  374. package/src/ProgressSteps/index.tsx +3 -3
  375. package/src/Quote/index.tsx +1 -3
  376. package/src/SheetModal/SheetModal.fill.spec.tsx +147 -0
  377. package/src/SheetModal/SheetModal.ownedBody.spec.tsx +200 -0
  378. package/src/SheetModal/SheetModal.stories.spec.tsx +23 -2
  379. package/src/SheetModal/SheetModal.stories.tsx +275 -2
  380. package/src/SheetModal/index.native.tsx +50 -6
  381. package/src/SheetModal/index.tsx +40 -3
  382. package/src/Tabs/Tabs.lifecycle.spec.tsx +123 -0
  383. package/src/Tabs/Tabs.measurement.spec.tsx +186 -0
  384. package/src/Tabs/Tabs.stories.tsx +43 -0
  385. package/src/Tabs/index.tsx +44 -15
  386. package/src/Toast/Toast.spec.tsx +120 -48
  387. package/src/Toast/index.tsx +5 -3
  388. package/src/TreeSelect/index.tsx +61 -1
  389. package/src/charts/PieChart.tsx +1 -2
  390. package/src/code/HighlightedCode/index.tsx +1 -1
  391. package/src/componentColors.ts +6 -4
  392. package/src/images/Image/ImageFrame.tsx +2 -2
  393. package/src/layouts/AsyncBoundary.tsx +1 -2
  394. package/src/layouts/EmptyState.spec.tsx +58 -1
  395. package/src/layouts/EmptyState.tsx +11 -7
  396. package/src/layouts/KeyboardAvoidingWrapper/KeyboardAvoidingWrapper.stories.tsx +3 -3
  397. package/src/layouts/PaneScaffold.stories.tsx +3 -8
  398. package/src/layouts/SafeAreaWrapper/SafeAreaWrapper.stories.tsx +3 -3
  399. package/src/layouts/attachments.tsx +4 -4
  400. package/src/layouts/breadcrumbs.tsx +1 -2
  401. package/src/layouts/page.tsx +2 -2
  402. package/src/layouts/sidebar.tsx +2 -2
  403. package/src/layouts/tags-trigger.spec.tsx +32 -0
  404. package/src/layouts/tags.tsx +7 -9
  405. package/src/layouts/timeline.tsx +5 -5
  406. package/src/surfaces.spec.tsx +97 -3
  407. package/src/surfaces.tsx +4 -2
  408. package/src/textAccentSecondary.spec.tsx +187 -0
  409. package/src/views/Calendar.tsx +9 -8
  410. package/src/views/Dashboard.tsx +13 -8
  411. package/src/views/Gantt.tsx +3 -3
  412. package/src/views/Kanban.native.tsx +2 -2
  413. package/src/views/Kanban.tsx +2 -2
  414. package/src/views/ReportBuilder.tsx +4 -4
  415. package/src/views/TreeView.reveal.spec.tsx +174 -0
  416. package/src/views/TreeView.reveal.stories.tsx +249 -0
  417. package/src/views/TreeView.scroll-index.spec.tsx +246 -0
  418. package/src/views/TreeView.scroll.spec.tsx +857 -0
  419. package/src/views/TreeView.spec.tsx +1 -1
  420. package/src/views/TreeView.tsx +353 -29
  421. package/src/views/treeViewReveal.spec.ts +83 -0
  422. package/src/views/treeViewReveal.ts +45 -0
  423. package/src/views/views.spec.tsx +13 -12
  424. package/types/Accordion/index.d.ts.map +1 -1
  425. package/types/Meter/index.d.ts.map +1 -1
  426. package/types/Meter/meter.d.ts +5 -1
  427. package/types/Meter/meter.d.ts.map +1 -1
  428. package/types/Quote/index.d.ts.map +1 -1
  429. package/types/SheetModal/index.d.ts +8 -1
  430. package/types/SheetModal/index.d.ts.map +1 -1
  431. package/types/SheetModal/index.native.d.ts +1 -1
  432. package/types/SheetModal/index.native.d.ts.map +1 -1
  433. package/types/Tabs/index.d.ts.map +1 -1
  434. package/types/Toast/index.d.ts.map +1 -1
  435. package/types/TreeSelect/index.d.ts +9 -1
  436. package/types/TreeSelect/index.d.ts.map +1 -1
  437. package/types/charts/PieChart.d.ts.map +1 -1
  438. package/types/componentColors.d.ts +1 -4
  439. package/types/componentColors.d.ts.map +1 -1
  440. package/types/images/Image/ImageFrame.d.ts.map +1 -1
  441. package/types/layouts/AsyncBoundary.d.ts.map +1 -1
  442. package/types/layouts/EmptyState.d.ts.map +1 -1
  443. package/types/layouts/breadcrumbs.d.ts.map +1 -1
  444. package/types/layouts/tags.d.ts.map +1 -1
  445. package/types/surfaces.d.ts.map +1 -1
  446. package/types/views/Calendar.d.ts.map +1 -1
  447. package/types/views/Dashboard.d.ts +7 -3
  448. package/types/views/Dashboard.d.ts.map +1 -1
  449. package/types/views/TreeView.d.ts +19 -1
  450. package/types/views/TreeView.d.ts.map +1 -1
  451. package/types/views/treeViewReveal.d.ts +11 -0
  452. package/types/views/treeViewReveal.d.ts.map +1 -0
@@ -92,7 +92,7 @@ describe("TreeView knob wiring", () => {
92
92
  expect(tree.getAttribute("data-fill")).toBe("transparent");
93
93
  });
94
94
 
95
- it("row labels are T-VALUE primary ink, never componentColors.text.secondary", () => {
95
+ it("row labels are T-VALUE primary ink, never a secondary ramp step", () => {
96
96
  expect(treeViewSource).not.toMatch(/componentColors\.text\.(secondary|muted)/);
97
97
  expect(treeViewSource).toMatch(
98
98
  /color=\{isSelected && onAccentLabel \? onAccentLabel : "\$color"\}/,
@@ -35,7 +35,7 @@ import React, {
35
35
  type ReactNode,
36
36
  } from "react";
37
37
  import { useTranslation } from "../shared/i18n";
38
- import { Text, View, XStack, YStack, type YStackProps } from "tamagui";
38
+ import { Text, View, XStack, YStack, isWeb, type YStackProps } from "tamagui";
39
39
  import { autoScrollVelocity } from "./kanbanDnd";
40
40
  import {
41
41
  applyTreeMove,
@@ -54,6 +54,7 @@ import {
54
54
  type TreeDropTarget,
55
55
  } from "./treeViewDnd";
56
56
  import { diffVisibleRows, flattenVisibleIds, isEmptyDiff } from "./treeViewMotion";
57
+ import { treeRevealExpansion, treeRevealOffset } from "./treeViewReveal";
57
58
  import {
58
59
  TREE_LIFT_SHADOW,
59
60
  treeDefaultHeight,
@@ -133,8 +134,20 @@ export interface TreeViewProps<T = unknown> extends Omit<YStackProps, "children"
133
134
  overscan?: number;
134
135
  /** Height of the scrollable container in pixels */
135
136
  height?: number;
137
+ /** Initial vertical offset in layout pixels, restored once after usable list layout. */
138
+ initialScrollOffset?: number;
139
+ /** Observed, bounded vertical offset. Does not report unacknowledged scroll commands. */
140
+ onScrollOffsetChange?: (offset: number) => void;
136
141
  /** Message shown when there are no nodes */
137
142
  emptyMessage?: string;
143
+ /** Controlled expansion; takes precedence over initial expansion props. */
144
+ expandedIds?: string[];
145
+ /** Next complete expansion set from a user toggle or explicit reveal. */
146
+ onExpandedIdsChange?: (ids: string[]) => void;
147
+ /** Expand ancestors and reveal this row. Change requestId to reveal the same ID again. */
148
+ revealRequest?: { id: string; requestId: number };
149
+ /** Called once when the current reveal target is measured, mounted and visible. */
150
+ onRevealComplete?: (request: { id: string; requestId: number }) => void;
138
151
  /** Initially expanded node IDs (wins over expandByDefault when provided) */
139
152
  defaultExpandedIds?: string[];
140
153
  /** Expand every branch on initial load */
@@ -291,8 +304,14 @@ export function TreeView<T = unknown>({
291
304
  estimateSize: estimateSizeProp,
292
305
  overscan = 10,
293
306
  height: heightProp,
307
+ initialScrollOffset,
308
+ onScrollOffsetChange,
294
309
  emptyMessage: emptyMessageProp,
295
310
  defaultExpandedIds,
311
+ expandedIds: controlledExpandedIds,
312
+ onExpandedIdsChange,
313
+ revealRequest,
314
+ onRevealComplete,
296
315
  expandByDefault = false,
297
316
  display = "default",
298
317
  showExpansionArrows = true,
@@ -339,6 +358,31 @@ export function TreeView<T = unknown>({
339
358
  const treeId = useId();
340
359
  const containerRef = useRef<HTMLDivElement>(null);
341
360
  const flashListRef = useRef<FlashListRef<FlatNode<T>>>(null);
361
+ const initialScroll = useRef<
362
+ | {
363
+ offset: number;
364
+ attempted?: { target: number; width: number; height: number; contentHeight: number };
365
+ }
366
+ | undefined
367
+ >(
368
+ initialScrollOffset === undefined
369
+ ? undefined
370
+ : { offset: Number.isFinite(initialScrollOffset) ? Math.max(0, initialScrollOffset) : 0 },
371
+ );
372
+ const cancelInitialScroll = useCallback(() => {
373
+ initialScroll.current = undefined;
374
+ }, []);
375
+ const hasNodes = nodes.length > 0;
376
+ const listInstance = useMemo(
377
+ () => ({ hasNodes, attached: false, loaded: false, indexScrollRequested: false, offset: 0 }),
378
+ [hasNodes],
379
+ );
380
+ const scrollActive = useRef(true);
381
+ const lastReportedOffset = useRef<number | undefined>(undefined);
382
+ const offsetCallback = useRef(onScrollOffsetChange);
383
+ useLayoutEffect(() => {
384
+ offsetCallback.current = onScrollOffsetChange;
385
+ }, [onScrollOffsetChange]);
342
386
  const [focusedIndex, setFocusedIndex] = useState(-1);
343
387
  // LC-71: ring is a React flag, not a render-time wasKeyboardFocus() read —
344
388
  // Tab-away/Tab-back with the same activedescendant must still re-paint.
@@ -357,12 +401,25 @@ export function TreeView<T = unknown>({
357
401
  const handleContainerBlur = useCallback(() => {
358
402
  setKeyboardRing(false);
359
403
  }, []);
360
- const [expandedIds, setExpandedIds] = useState<Set<string>>(() => {
404
+ const [internalExpandedIds, setInternalExpandedIds] = useState<Set<string>>(() => {
361
405
  if (defaultExpandedIds) return new Set(defaultExpandedIds);
362
406
  if (expandByDefault) return collectBranchIds(nodes);
363
407
  return new Set();
364
408
  });
365
409
 
410
+ const expandedIds = useMemo(
411
+ () =>
412
+ controlledExpandedIds === undefined ? internalExpandedIds : new Set(controlledExpandedIds),
413
+ [controlledExpandedIds, internalExpandedIds],
414
+ );
415
+ const setExpandedIds = useCallback(
416
+ (next: ReadonlySet<string>) => {
417
+ if (controlledExpandedIds === undefined) setInternalExpandedIds(new Set(next));
418
+ onExpandedIdsChange?.([...next]);
419
+ },
420
+ [controlledExpandedIds, onExpandedIdsChange],
421
+ );
422
+
366
423
  // ── Expand/collapse motion (FLIP + enter fade; Axiom 4) ────────────────
367
424
  const [motion, setMotion] = useState<TreeMotion | null>(null);
368
425
  const rowHeights = useRef(new Map<string, number>());
@@ -373,30 +430,37 @@ export function TreeView<T = unknown>({
373
430
 
374
431
  const toggleNode = useCallback(
375
432
  (node: TreeNode<T>) => {
433
+ cancelInitialScroll();
376
434
  const next = new Set(expandedIds);
377
435
  if (next.has(node.id)) {
378
436
  next.delete(node.id);
379
437
  } else {
380
438
  next.add(node.id);
381
439
  }
382
- // Mount never animates and `animation: none` / reduced motion skips
383
- // the machine entirely — the toggle then renders in one jump.
384
- if (transition) {
385
- const diff = diffVisibleRows(
386
- flattenVisibleIds(nodes, expandedIds),
387
- flattenVisibleIds(nodes, next),
388
- getRowHeight,
389
- );
390
- setMotion(isEmptyDiff(diff) ? null : { phase: "armed", ...diff });
391
- } else {
392
- setMotion(null);
393
- }
394
440
  setExpandedIds(next);
395
441
  onNodeToggle?.(node);
396
442
  },
397
- [expandedIds, nodes, transition, getRowHeight, onNodeToggle],
443
+ [expandedIds, setExpandedIds, onNodeToggle, cancelInitialScroll],
398
444
  );
399
445
 
446
+ const previousExpandedIds = useRef(expandedIds);
447
+ useLayoutEffect(() => {
448
+ const previous = previousExpandedIds.current;
449
+ previousExpandedIds.current = expandedIds;
450
+ if (previous.size === expandedIds.size && [...previous].every((id) => expandedIds.has(id)))
451
+ return;
452
+ if (transition) {
453
+ const diff = diffVisibleRows(
454
+ flattenVisibleIds(nodes, previous),
455
+ flattenVisibleIds(nodes, expandedIds),
456
+ getRowHeight,
457
+ );
458
+ setMotion(isEmptyDiff(diff) ? null : { phase: "armed", ...diff });
459
+ } else {
460
+ setMotion(null);
461
+ }
462
+ }, [expandedIds, nodes, transition, getRowHeight]);
463
+
400
464
  // Release the FLIP one frame after the armed styles paint so the tween
401
465
  // runs from the previous layout to the new one.
402
466
  useLayoutEffect(() => {
@@ -432,13 +496,243 @@ export function TreeView<T = unknown>({
432
496
  return result;
433
497
  }, [nodes, expandedIds]);
434
498
 
499
+ const revealIntent = useRef<
500
+ | {
501
+ id: string;
502
+ requestId: number;
503
+ complete: boolean;
504
+ notifiedExpansion?: ReadonlySet<string>;
505
+ }
506
+ | undefined
507
+ >(undefined);
508
+ const scrollFrame = useRef<number | undefined>(undefined);
509
+ const scrollStep = useRef<() => void>(() => {});
510
+ const renderedRows = useRef(new Set<string>());
511
+ const scrollBounds = useCallback(() => {
512
+ const list = flashListRef.current;
513
+ if (!scrollActive.current || !listInstance.attached || !listInstance.loaded || !list) return;
514
+ if (isWeb) {
515
+ const rect = containerRef.current?.getBoundingClientRect();
516
+ if (!rect || !(rect.width > 0) || !(rect.height > 0)) return;
517
+ }
518
+ const viewport = list.getWindowSize();
519
+ const contentHeight = list.getChildContainerDimensions().height + list.getFirstItemOffset();
520
+ if (
521
+ !Number.isFinite(viewport.width) ||
522
+ viewport.width <= 0 ||
523
+ !Number.isFinite(viewport.height) ||
524
+ viewport.height <= 0 ||
525
+ !Number.isFinite(contentHeight) ||
526
+ contentHeight <= 0
527
+ )
528
+ return;
529
+ return {
530
+ list,
531
+ width: viewport.width,
532
+ height: viewport.height,
533
+ contentHeight,
534
+ maxOffset: Math.max(0, contentHeight - viewport.height),
535
+ };
536
+ }, [listInstance]);
537
+ const observeOffset = useCallback(
538
+ (offset: number, fromEvent = false) => {
539
+ if (!scrollActive.current || !listInstance.attached || !Number.isFinite(offset)) return;
540
+ if (
541
+ fromEvent &&
542
+ initialScroll.current?.attempted === undefined &&
543
+ offset !== listInstance.offset
544
+ ) {
545
+ cancelInitialScroll();
546
+ }
547
+ listInstance.offset = offset;
548
+ const bounds = scrollBounds();
549
+ if (!bounds) return;
550
+ const bounded = Math.max(0, Math.min(offset, bounds.maxOffset));
551
+ const initial = initialScroll.current;
552
+ if (initial) {
553
+ const target = Math.min(initial.offset, bounds.maxOffset);
554
+ if (initial.attempted ? Math.abs(bounded - target) >= 1 : bounded !== target) return;
555
+ initialScroll.current = undefined;
556
+ }
557
+ if (offsetCallback.current && lastReportedOffset.current !== bounded) {
558
+ lastReportedOffset.current = bounded;
559
+ offsetCallback.current(bounded);
560
+ }
561
+ },
562
+ [cancelInitialScroll, scrollBounds, listInstance],
563
+ );
564
+ const scheduleScroll = useCallback(() => {
565
+ if (!scrollActive.current || !listInstance.attached || scrollFrame.current !== undefined)
566
+ return;
567
+ if (
568
+ !initialScroll.current &&
569
+ (!revealIntent.current || revealIntent.current.complete) &&
570
+ !offsetCallback.current
571
+ )
572
+ return;
573
+ scrollFrame.current = requestAnimationFrame(() => {
574
+ scrollFrame.current = undefined;
575
+ if (listInstance.attached) scrollStep.current();
576
+ });
577
+ }, [listInstance]);
578
+ const attachList = useCallback(
579
+ (list: FlashListRef<FlatNode<T>> | null) => {
580
+ flashListRef.current = list;
581
+ listInstance.attached = list !== null;
582
+ if (!list) {
583
+ listInstance.loaded = false;
584
+ if (initialScroll.current) initialScroll.current.attempted = undefined;
585
+ if (scrollFrame.current !== undefined) cancelAnimationFrame(scrollFrame.current);
586
+ scrollFrame.current = undefined;
587
+ }
588
+ },
589
+ [listInstance],
590
+ );
591
+
592
+ useLayoutEffect(() => {
593
+ scrollActive.current = true;
594
+ return () => {
595
+ scrollActive.current = false;
596
+ revealIntent.current = undefined;
597
+ if (scrollFrame.current !== undefined) cancelAnimationFrame(scrollFrame.current);
598
+ scrollFrame.current = undefined;
599
+ };
600
+ }, []);
601
+
602
+ useLayoutEffect(() => {
603
+ if (!revealRequest) {
604
+ revealIntent.current = undefined;
605
+ } else if (
606
+ revealIntent.current?.id !== revealRequest.id ||
607
+ revealIntent.current?.requestId !== revealRequest.requestId
608
+ ) {
609
+ cancelInitialScroll();
610
+ revealIntent.current = { ...revealRequest, complete: false };
611
+ }
612
+ const step = () => {
613
+ const currentList = flashListRef.current;
614
+ if (currentList) {
615
+ // Index preparation changes FlashList's getter before transport completes.
616
+ // Once requested, only delivered events can advance this list's observation.
617
+ observeOffset(
618
+ listInstance.indexScrollRequested
619
+ ? listInstance.offset
620
+ : currentList.getAbsoluteLastScrollOffset(),
621
+ );
622
+ }
623
+ if (!scrollActive.current) return;
624
+ if (scrollStep.current !== step) {
625
+ scheduleScroll();
626
+ return;
627
+ }
628
+ const intent = revealIntent.current;
629
+ if (!intent || intent.complete) {
630
+ const initial = initialScroll.current;
631
+ const bounds = scrollBounds();
632
+ if (!initial || !bounds || flatNodes.length === 0) return;
633
+ const target = Math.min(initial.offset, bounds.maxOffset);
634
+ const attempted = initial.attempted;
635
+ if (
636
+ !attempted ||
637
+ attempted.target !== target ||
638
+ attempted.width !== bounds.width ||
639
+ attempted.height !== bounds.height ||
640
+ attempted.contentHeight !== bounds.contentHeight
641
+ ) {
642
+ initial.attempted = {
643
+ target,
644
+ width: bounds.width,
645
+ height: bounds.height,
646
+ contentHeight: bounds.contentHeight,
647
+ };
648
+ bounds.list.scrollToOffset({ offset: target, animated: false });
649
+ }
650
+ return;
651
+ }
652
+ const next = treeRevealExpansion(nodes, expandedIds, intent.id);
653
+ if (!next) return;
654
+ if (next !== expandedIds) {
655
+ const notified = intent.notifiedExpansion;
656
+ if (!notified || notified.size !== next.size || [...next].some((id) => !notified.has(id))) {
657
+ intent.notifiedExpansion = next;
658
+ setExpandedIds(next);
659
+ }
660
+ return;
661
+ }
662
+ if (motion) {
663
+ setMotion(null);
664
+ return;
665
+ }
666
+ const list = flashListRef.current;
667
+ const index = flatNodes.findIndex((item) => item.node.id === intent.id);
668
+ if (!list || index < 0) return;
669
+ if (isWeb) {
670
+ const rect = containerRef.current?.getBoundingClientRect();
671
+ if (!rect || rect.width <= 0 || rect.height <= 0) return;
672
+ }
673
+ const viewport = list.getWindowSize();
674
+ if (viewport.width <= 0) return;
675
+ const layout = list.getLayout(index);
676
+ if (!layout) return;
677
+ const offset = listInstance.indexScrollRequested
678
+ ? listInstance.offset
679
+ : list.getAbsoluteLastScrollOffset();
680
+ const top = layout.y + list.getFirstItemOffset();
681
+ const nextOffset = treeRevealOffset(
682
+ { y: top, height: layout.height },
683
+ offset,
684
+ viewport.height,
685
+ );
686
+ if (nextOffset === undefined) return;
687
+ if (Math.abs(nextOffset - offset) < 1) {
688
+ let visible = top < offset + viewport.height && top + layout.height > offset;
689
+ if (!listInstance.indexScrollRequested) {
690
+ const range = list.computeVisibleIndices();
691
+ visible = visible && index >= range.startIndex && index <= range.endIndex;
692
+ }
693
+ if (
694
+ revealIntent.current === intent &&
695
+ layout.isHeightMeasured &&
696
+ renderedRows.current.has(intent.id) &&
697
+ visible
698
+ ) {
699
+ intent.complete = true;
700
+ onRevealComplete?.({ id: intent.id, requestId: intent.requestId });
701
+ }
702
+ return;
703
+ }
704
+ // FlashList's scrollToIndex has uncancellable delayed steps. Immediate offsets
705
+ // let the next layout/scroll event correct estimates using only the latest intent.
706
+ list.scrollToOffset({ offset: nextOffset, animated: false });
707
+ };
708
+ scrollStep.current = step;
709
+ scheduleScroll();
710
+ }, [
711
+ nodes,
712
+ expandedIds,
713
+ setExpandedIds,
714
+ flatNodes,
715
+ revealRequest?.id,
716
+ revealRequest?.requestId,
717
+ onRevealComplete,
718
+ height,
719
+ motion,
720
+ scheduleScroll,
721
+ cancelInitialScroll,
722
+ observeOffset,
723
+ scrollBounds,
724
+ listInstance,
725
+ ]);
726
+
435
727
  const scrollToIndex = useCallback(
436
728
  (index: number) => {
437
729
  if (index >= 0 && index < flatNodes.length) {
730
+ cancelInitialScroll();
731
+ listInstance.indexScrollRequested = true;
438
732
  flashListRef.current?.scrollToIndex({ index, animated: true });
439
733
  }
440
734
  },
441
- [flatNodes.length],
735
+ [flatNodes.length, cancelInitialScroll, listInstance],
442
736
  );
443
737
 
444
738
  const focusRow = useCallback(
@@ -732,6 +1026,7 @@ export function TreeView<T = unknown>({
732
1026
  if (!container?.getBoundingClientRect) return false;
733
1027
  const session = treeKeyboardLift(nodes, flat.node.id);
734
1028
  if (!session) return false;
1029
+ cancelInitialScroll();
735
1030
  const rowRect = rowEl.getBoundingClientRect();
736
1031
  const containerRect = container.getBoundingClientRect();
737
1032
  grabOffsetRef.current = { x: clientX - rowRect.left, y: clientY - rowRect.top };
@@ -759,7 +1054,7 @@ export function TreeView<T = unknown>({
759
1054
  startAutoScroll();
760
1055
  return true;
761
1056
  },
762
- [announce, nodes, startAutoScroll],
1057
+ [announce, nodes, startAutoScroll, cancelInitialScroll],
763
1058
  );
764
1059
 
765
1060
  const handleDragHandlePointerDown = useCallback(
@@ -906,6 +1201,7 @@ export function TreeView<T = unknown>({
906
1201
  if (!flat) return false;
907
1202
  const session = treeKeyboardLift(nodes, flat.node.id);
908
1203
  if (!session) return false;
1204
+ cancelInitialScroll();
909
1205
  const ui: TreeDragUi<T> = {
910
1206
  mode: "keyboard",
911
1207
  node: flat.node,
@@ -923,7 +1219,16 @@ export function TreeView<T = unknown>({
923
1219
  dragUiRef.current = ui;
924
1220
  announce(describeTreeDragState("lift", session, nodes, flat.node.label));
925
1221
  return true;
926
- }, [announce, canReorder, dndRows, flatNodes, focusedIndex, getRowHeight, nodes]);
1222
+ }, [
1223
+ announce,
1224
+ canReorder,
1225
+ dndRows,
1226
+ flatNodes,
1227
+ focusedIndex,
1228
+ getRowHeight,
1229
+ nodes,
1230
+ cancelInitialScroll,
1231
+ ]);
927
1232
 
928
1233
  const moveKeyboardDrag = useCallback(
929
1234
  (direction: "up" | "down" | "left" | "right"): boolean => {
@@ -1153,16 +1458,28 @@ export function TreeView<T = unknown>({
1153
1458
  onFocus={handleContainerFocus}
1154
1459
  onBlur={handleContainerBlur}
1155
1460
  onKeyDown={handleTreeKeyDown}
1461
+ onLayout={scheduleScroll}
1462
+ {...(isWeb && { onWheel: cancelInitialScroll, onPointerDown: cancelInitialScroll })}
1156
1463
  position="relative"
1157
1464
  style={{
1158
1465
  height,
1159
1466
  }}
1160
1467
  >
1161
1468
  <FlashList
1162
- ref={flashListRef}
1469
+ ref={attachList}
1163
1470
  data={flatNodes}
1164
1471
  drawDistance={overscan * estimateSize}
1165
1472
  keyExtractor={(item) => item.node.id}
1473
+ onLoad={() => {
1474
+ if (!scrollActive.current || !listInstance.attached) return;
1475
+ listInstance.loaded = true;
1476
+ scheduleScroll();
1477
+ }}
1478
+ onCommitLayoutEffect={scheduleScroll}
1479
+ onContentSizeChange={scheduleScroll}
1480
+ onScrollBeginDrag={() => {
1481
+ if (scrollActive.current && listInstance.attached) cancelInitialScroll();
1482
+ }}
1166
1483
  extraData={{
1167
1484
  focused: effectiveFocused,
1168
1485
  selectedId,
@@ -1173,16 +1490,17 @@ export function TreeView<T = unknown>({
1173
1490
  keyboardRing,
1174
1491
  collapsedSelectedAncestors,
1175
1492
  }}
1176
- {...(canReorder && {
1177
- onScroll: (e: any) => {
1178
- const y = e?.nativeEvent?.contentOffset?.y;
1179
- if (typeof y === "number") {
1180
- scrollOffsetRef.current = y;
1181
- if (dragUiRef.current) setDragScrollY(y);
1182
- }
1183
- },
1184
- scrollEventThrottle: 16,
1185
- })}
1493
+ onScroll={(e: any) => {
1494
+ if (!scrollActive.current || !listInstance.attached) return;
1495
+ const y = e?.nativeEvent?.contentOffset?.y;
1496
+ if (typeof y === "number" && Number.isFinite(y)) {
1497
+ scrollOffsetRef.current = y;
1498
+ if (dragUiRef.current) setDragScrollY(y);
1499
+ observeOffset(y, true);
1500
+ }
1501
+ scheduleScroll();
1502
+ }}
1503
+ scrollEventThrottle={16}
1186
1504
  renderItem={({ item, index }) => {
1187
1505
  const { node, depth, hasChildren, isExpanded } = item;
1188
1506
  const isFocused = effectiveFocused === index;
@@ -1239,6 +1557,11 @@ export function TreeView<T = unknown>({
1239
1557
  // Remount when a recycled cell switches nodes so state fills
1240
1558
  // and the caret never tween across two different rows.
1241
1559
  key={node.id}
1560
+ ref={(row) => {
1561
+ if (row) renderedRows.current.add(node.id);
1562
+ else renderedRows.current.delete(node.id);
1563
+ scheduleScroll();
1564
+ }}
1242
1565
  id={`${treeId}-node-${index}`}
1243
1566
  {...{ "data-treeview-row": node.id }}
1244
1567
  {...(isDropParent && { "data-treeview-drop-parent": "true" })}
@@ -1265,6 +1588,7 @@ export function TreeView<T = unknown>({
1265
1588
  onLayout={(e: any) => {
1266
1589
  const h = e?.nativeEvent?.layout?.height;
1267
1590
  if (typeof h === "number" && h > 0) rowHeights.current.set(node.id, h);
1591
+ scheduleScroll();
1268
1592
  }}
1269
1593
  backgroundColor={
1270
1594
  // Nest-into parent highlight wins while a drag is active
@@ -0,0 +1,83 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { treeRevealExpansion, treeRevealOffset } from "./treeViewReveal";
3
+
4
+ const nodes = [
5
+ {
6
+ id: "folders",
7
+ children: [
8
+ {
9
+ id: "archive",
10
+ children: Array.from({ length: 500 }, (_, index) => ({ id: `item-${index}` })),
11
+ },
12
+ ],
13
+ },
14
+ { id: "other", children: [{ id: "other-item" }] },
15
+ ];
16
+
17
+ describe("tree reveal ancestors", () => {
18
+ it("finds the exact last leaf of a 500-item branch and preserves unrelated expansion", () => {
19
+ const expanded = new Set(["other"]);
20
+ expect(treeRevealExpansion(nodes, expanded, "item-499")).toEqual(
21
+ new Set(["other", "folders", "archive"]),
22
+ );
23
+ expect(expanded).toEqual(new Set(["other"]));
24
+ });
25
+
26
+ it("does not expand the target branch itself", () => {
27
+ expect(treeRevealExpansion(nodes, new Set(), "archive")).toEqual(new Set(["folders"]));
28
+ });
29
+
30
+ it("preserves the set when the target is already exposed", () => {
31
+ const expanded = new Set(["folders", "archive"]);
32
+ expect(treeRevealExpansion(nodes, expanded, "item-499")).toBe(expanded);
33
+ });
34
+
35
+ it("does not substitute a prefix, ancestor or similarly named leaf for a missing ID", () => {
36
+ expect(treeRevealExpansion(nodes, new Set(), "item-500")).toBeUndefined();
37
+ expect(treeRevealExpansion(nodes, new Set(), "item")).toBeUndefined();
38
+ expect(treeRevealExpansion([], new Set(), "folders")).toBeUndefined();
39
+ });
40
+
41
+ it("reveals a root without expanding its children", () => {
42
+ const expanded = new Set<string>();
43
+ expect(treeRevealExpansion(nodes, expanded, "folders")).toBe(expanded);
44
+ });
45
+ });
46
+
47
+ describe("tree reveal measured offsets", () => {
48
+ it("uses the actual layout of a distant variable-height row, not index times an estimate", () => {
49
+ const heights = Array.from({ length: 500 }, (_, index) => (index % 2 === 0 ? 32 : 80));
50
+ const y = heights.slice(0, 499).reduce((sum, height) => sum + height, 0);
51
+ expect(y).toBe(27920);
52
+ expect(treeRevealOffset({ y, height: heights[499] }, 0, 240)).toBe(27760);
53
+ expect(treeRevealOffset({ y, height: heights[499] }, 27760, 240)).toBe(27760);
54
+ });
55
+
56
+ it("corrects again when virtualization replaces an estimate with a taller measurement", () => {
57
+ expect(treeRevealOffset({ y: 27920, height: 32 }, 0, 240)).toBe(27712);
58
+ expect(treeRevealOffset({ y: 27920, height: 80 }, 27712, 240)).toBe(27760);
59
+ });
60
+
61
+ it("leaves a fully visible row in place", () => {
62
+ expect(treeRevealOffset({ y: 300, height: 80 }, 250, 240)).toBe(250);
63
+ });
64
+
65
+ it("reveals a row above the viewport at its top", () => {
66
+ expect(treeRevealOffset({ y: 120, height: 80 }, 250, 240)).toBe(120);
67
+ });
68
+
69
+ it("aligns the top of a row taller than the viewport without oscillating", () => {
70
+ expect(treeRevealOffset({ y: 120, height: 300 }, 250, 240)).toBe(120);
71
+ expect(treeRevealOffset({ y: 120, height: 300 }, 120, 240)).toBe(120);
72
+ });
73
+
74
+ it.each([
75
+ [{ y: 20, height: 32 }, 0, 0],
76
+ [{ y: 20, height: 0 }, 0, 240],
77
+ [{ y: Number.NaN, height: 32 }, 0, 240],
78
+ [{ y: 20, height: 32 }, Number.NaN, 240],
79
+ [{ y: 20, height: 32 }, 0, Number.POSITIVE_INFINITY],
80
+ ])("waits for usable geometry: %o, %s, %s", (layout, offset, height) => {
81
+ expect(treeRevealOffset(layout, offset, height)).toBeUndefined();
82
+ });
83
+ });
@@ -0,0 +1,45 @@
1
+ interface RevealTreeNode {
2
+ id: string;
3
+ children?: readonly RevealTreeNode[];
4
+ }
5
+
6
+ export function treeRevealExpansion(
7
+ nodes: readonly RevealTreeNode[],
8
+ expandedIds: ReadonlySet<string>,
9
+ targetId: string,
10
+ ): ReadonlySet<string> | undefined {
11
+ const ancestors: string[] = [];
12
+ const find = (items: readonly RevealTreeNode[]): boolean => {
13
+ for (const node of items) {
14
+ if (node.id === targetId) return true;
15
+ if (node.children?.length) {
16
+ ancestors.push(node.id);
17
+ if (find(node.children)) return true;
18
+ ancestors.pop();
19
+ }
20
+ }
21
+ return false;
22
+ };
23
+ if (!find(nodes)) return undefined;
24
+ if (ancestors.every((id) => expandedIds.has(id))) return expandedIds;
25
+ return new Set([...expandedIds, ...ancestors]);
26
+ }
27
+
28
+ export function treeRevealOffset(
29
+ layout: { y: number; height: number },
30
+ offset: number,
31
+ viewportHeight: number,
32
+ ): number | undefined {
33
+ if (
34
+ ![layout.y, layout.height, offset, viewportHeight].every(Number.isFinite) ||
35
+ viewportHeight <= 0 ||
36
+ layout.height <= 0
37
+ ) {
38
+ return undefined;
39
+ }
40
+ if (layout.height > viewportHeight || layout.y < offset) return Math.max(0, layout.y);
41
+ if (layout.y + layout.height > offset + viewportHeight) {
42
+ return Math.max(0, layout.y + layout.height - viewportHeight);
43
+ }
44
+ return offset;
45
+ }