@multiplatform.one/components 7.10.0 → 7.15.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 +100 -2
  2. package/dist/cjs/Accordion/index.cjs +2 -6
  3. package/dist/cjs/Accordion/index.native.js +2 -6
  4. package/dist/cjs/Accordion/index.native.js.map +1 -1
  5. package/dist/cjs/AnimateHeight/index.cjs +1 -3
  6. package/dist/cjs/AnimateHeight/index.native.js +1 -3
  7. package/dist/cjs/AnimateHeight/index.native.js.map +1 -1
  8. package/dist/cjs/Chip/index.cjs +8 -1
  9. package/dist/cjs/Chip/index.native.js +8 -1
  10. package/dist/cjs/Chip/index.native.js.map +1 -1
  11. package/dist/cjs/SheetModal/index.cjs +22 -3
  12. package/dist/cjs/SheetModal/index.native.js +115 -27
  13. package/dist/cjs/SheetModal/index.native.js.map +1 -1
  14. package/dist/cjs/Tabs/index.cjs +35 -18
  15. package/dist/cjs/Tabs/index.native.js +39 -18
  16. package/dist/cjs/Tabs/index.native.js.map +1 -1
  17. package/dist/cjs/Text.cjs +2 -0
  18. package/dist/cjs/Text.native.js +2 -0
  19. package/dist/cjs/Text.native.js.map +1 -1
  20. package/dist/cjs/TreeSelect/index.cjs +55 -52
  21. package/dist/cjs/TreeSelect/index.native.js +55 -52
  22. package/dist/cjs/TreeSelect/index.native.js.map +1 -1
  23. package/dist/cjs/Video/PlayerChrome.cjs +3 -9
  24. package/dist/cjs/Video/PlayerChrome.native.js +3 -9
  25. package/dist/cjs/Video/PlayerChrome.native.js.map +1 -1
  26. package/dist/cjs/code/Code/index.cjs +6 -0
  27. package/dist/cjs/code/Code/index.native.js +7 -4
  28. package/dist/cjs/code/Code/index.native.js.map +1 -1
  29. package/dist/cjs/code/CodeBlock/index.cjs +4 -5
  30. package/dist/cjs/code/CodeBlock/index.native.js +4 -5
  31. package/dist/cjs/code/CodeBlock/index.native.js.map +1 -1
  32. package/dist/cjs/icons/themed.cjs +2 -1
  33. package/dist/cjs/icons/themed.native.js +2 -1
  34. package/dist/cjs/icons/themed.native.js.map +1 -1
  35. package/dist/cjs/knobImmuneExports.cjs +3 -3
  36. package/dist/cjs/knobImmuneExports.native.js +3 -3
  37. package/dist/cjs/layouts/PaneScaffold.cjs +70 -42
  38. package/dist/cjs/layouts/PaneScaffold.native.js +75 -48
  39. package/dist/cjs/layouts/PaneScaffold.native.js.map +1 -1
  40. package/dist/cjs/layouts/page.cjs +2 -1
  41. package/dist/cjs/layouts/page.native.js +2 -1
  42. package/dist/cjs/layouts/page.native.js.map +1 -1
  43. package/dist/cjs/layouts/sidebar.cjs +1 -3
  44. package/dist/cjs/layouts/sidebar.native.js +1 -3
  45. package/dist/cjs/layouts/sidebar.native.js.map +1 -1
  46. package/dist/cjs/layouts/tags.native.js.map +1 -1
  47. package/dist/cjs/surfaces.cjs +0 -32
  48. package/dist/cjs/surfaces.native.js +0 -32
  49. package/dist/cjs/surfaces.native.js.map +1 -1
  50. package/dist/cjs/views/List.cjs +1 -3
  51. package/dist/cjs/views/List.native.js +1 -3
  52. package/dist/cjs/views/List.native.js.map +1 -1
  53. package/dist/cjs/views/Matrix.cjs +568 -0
  54. package/dist/cjs/views/Matrix.native.js +612 -0
  55. package/dist/cjs/views/Matrix.native.js.map +1 -0
  56. package/dist/cjs/views/TreeView.cjs +175 -43
  57. package/dist/cjs/views/TreeView.native.js +181 -46
  58. package/dist/cjs/views/TreeView.native.js.map +1 -1
  59. package/dist/cjs/views/ganttMath.cjs +2 -0
  60. package/dist/cjs/views/ganttMath.native.js +2 -0
  61. package/dist/cjs/views/ganttMath.native.js.map +1 -1
  62. package/dist/cjs/views/index.cjs +16 -0
  63. package/dist/cjs/views/index.native.js +16 -0
  64. package/dist/cjs/views/index.native.js.map +1 -1
  65. package/dist/cjs/views/matrixMath.cjs +157 -0
  66. package/dist/cjs/views/matrixMath.native.js +229 -0
  67. package/dist/cjs/views/matrixMath.native.js.map +1 -0
  68. package/dist/esm/Accordion/index.mjs +3 -7
  69. package/dist/esm/Accordion/index.mjs.map +1 -1
  70. package/dist/esm/Accordion/index.native.js +3 -7
  71. package/dist/esm/Accordion/index.native.js.map +1 -1
  72. package/dist/esm/AnimateHeight/index.mjs +2 -4
  73. package/dist/esm/AnimateHeight/index.mjs.map +1 -1
  74. package/dist/esm/AnimateHeight/index.native.js +2 -4
  75. package/dist/esm/AnimateHeight/index.native.js.map +1 -1
  76. package/dist/esm/Avatar/getInitials.native.js.map +1 -1
  77. package/dist/esm/Avatar/index.native.js.map +1 -1
  78. package/dist/esm/Badge/index.native.js.map +1 -1
  79. package/dist/esm/Button.native.js.map +1 -1
  80. package/dist/esm/Carousel/index.native.js.map +1 -1
  81. package/dist/esm/Chip/index.mjs +9 -2
  82. package/dist/esm/Chip/index.mjs.map +1 -1
  83. package/dist/esm/Chip/index.native.js +9 -2
  84. package/dist/esm/Chip/index.native.js.map +1 -1
  85. package/dist/esm/Collapsible.native.js.map +1 -1
  86. package/dist/esm/ContextMenu/index.native.js.map +1 -1
  87. package/dist/esm/CopyField/index.native.js.map +1 -1
  88. package/dist/esm/DotIndicator/index.native.js.map +1 -1
  89. package/dist/esm/DropdownMenu/index.native.js.map +1 -1
  90. package/dist/esm/ErrorBoundary/index.native.js.map +1 -1
  91. package/dist/esm/Heading.native.js.map +1 -1
  92. package/dist/esm/Meter/index.native.js.map +1 -1
  93. package/dist/esm/Meter/meter.native.js.map +1 -1
  94. package/dist/esm/Pagination.native.js.map +1 -1
  95. package/dist/esm/ProgressSteps/index.native.js.map +1 -1
  96. package/dist/esm/Quote/index.native.js.map +1 -1
  97. package/dist/esm/Separator/index.native.js.map +1 -1
  98. package/dist/esm/SheetModal/detents.native.js.map +1 -1
  99. package/dist/esm/SheetModal/index.mjs +22 -3
  100. package/dist/esm/SheetModal/index.mjs.map +1 -1
  101. package/dist/esm/SheetModal/index.native.js +117 -29
  102. package/dist/esm/SheetModal/index.native.js.map +1 -1
  103. package/dist/esm/Skeleton/index.native.js.map +1 -1
  104. package/dist/esm/SkipLink/index.native.js.map +1 -1
  105. package/dist/esm/SortableList/SortableHandle.native.js.map +1 -1
  106. package/dist/esm/SortableList/index.native.js.map +1 -1
  107. package/dist/esm/SortableList/useSortableRows.native.js.map +1 -1
  108. package/dist/esm/Tabs/index.mjs +36 -19
  109. package/dist/esm/Tabs/index.mjs.map +1 -1
  110. package/dist/esm/Tabs/index.native.js +40 -19
  111. package/dist/esm/Tabs/index.native.js.map +1 -1
  112. package/dist/esm/Text.mjs +3 -1
  113. package/dist/esm/Text.mjs.map +1 -1
  114. package/dist/esm/Text.native.js +3 -1
  115. package/dist/esm/Text.native.js.map +1 -1
  116. package/dist/esm/Toast/index.native.js.map +1 -1
  117. package/dist/esm/Toast/store.native.js.map +1 -1
  118. package/dist/esm/Tooltip/nativePress.native.js.map +1 -1
  119. package/dist/esm/Tooltip/tooltipShared.native.js.map +1 -1
  120. package/dist/esm/TreeSelect/index.mjs +56 -53
  121. package/dist/esm/TreeSelect/index.mjs.map +1 -1
  122. package/dist/esm/TreeSelect/index.native.js +56 -53
  123. package/dist/esm/TreeSelect/index.native.js.map +1 -1
  124. package/dist/esm/TreeSelect/treeSelectModel.native.js.map +1 -1
  125. package/dist/esm/Video/PlayerChrome.mjs +4 -10
  126. package/dist/esm/Video/PlayerChrome.mjs.map +1 -1
  127. package/dist/esm/Video/PlayerChrome.native.js +4 -10
  128. package/dist/esm/Video/PlayerChrome.native.js.map +1 -1
  129. package/dist/esm/Video/VideoFrame.native.js.map +1 -1
  130. package/dist/esm/Video/types.native.js.map +1 -1
  131. package/dist/esm/Wheel/index.native.js.map +1 -1
  132. package/dist/esm/Wizard/index.native.js.map +1 -1
  133. package/dist/esm/actions/ActionBar.native.js.map +1 -1
  134. package/dist/esm/actions/DestructiveAction.native.js.map +1 -1
  135. package/dist/esm/actions/devWarn.native.js.map +1 -1
  136. package/dist/esm/actions/index.native.js.map +1 -1
  137. package/dist/esm/actions/severity.native.js.map +1 -1
  138. package/dist/esm/charts/Axis.native.js.map +1 -1
  139. package/dist/esm/charts/BarChart.native.js.map +1 -1
  140. package/dist/esm/charts/ChartDataView.native.js.map +1 -1
  141. package/dist/esm/charts/ChartSurface.native.js.map +1 -1
  142. package/dist/esm/charts/LineChart.native.js.map +1 -1
  143. package/dist/esm/charts/PieChart.native.js.map +1 -1
  144. package/dist/esm/charts/Sparkline.native.js.map +1 -1
  145. package/dist/esm/charts/accessibility.native.js.map +1 -1
  146. package/dist/esm/charts/composeChart.native.js.map +1 -1
  147. package/dist/esm/charts/index.native.js.map +1 -1
  148. package/dist/esm/charts/math.native.js.map +1 -1
  149. package/dist/esm/charts/series.native.js.map +1 -1
  150. package/dist/esm/charts/svg.native.js.map +1 -1
  151. package/dist/esm/charts/types.native.js.map +1 -1
  152. package/dist/esm/charts/useChartTheme.native.js.map +1 -1
  153. package/dist/esm/code/Code/index.mjs +6 -0
  154. package/dist/esm/code/Code/index.mjs.map +1 -1
  155. package/dist/esm/code/Code/index.native.js +10 -7
  156. package/dist/esm/code/Code/index.native.js.map +1 -1
  157. package/dist/esm/code/CodeBlock/index.mjs +5 -6
  158. package/dist/esm/code/CodeBlock/index.mjs.map +1 -1
  159. package/dist/esm/code/CodeBlock/index.native.js +5 -6
  160. package/dist/esm/code/CodeBlock/index.native.js.map +1 -1
  161. package/dist/esm/code/HighlightedCode/index.native.js.map +1 -1
  162. package/dist/esm/code/Highlighter/index.native.js.map +1 -1
  163. package/dist/esm/code/Pre/index.native.js.map +1 -1
  164. package/dist/esm/code/index.native.js.map +1 -1
  165. package/dist/esm/componentColors.native.js.map +1 -1
  166. package/dist/esm/feedback/ConfirmDialog.native.js.map +1 -1
  167. package/dist/esm/feedback/NotifyHost.native.js.map +1 -1
  168. package/dist/esm/feedback/copy.native.js.map +1 -1
  169. package/dist/esm/feedback/index.native.js.map +1 -1
  170. package/dist/esm/feedback/notify.native.js.map +1 -1
  171. package/dist/esm/feedback/route.native.js.map +1 -1
  172. package/dist/esm/feedback/store.native.js.map +1 -1
  173. package/dist/esm/hooks/index.native.js.map +1 -1
  174. package/dist/esm/hooks/useClipboard.native.js.map +1 -1
  175. package/dist/esm/hooks/useDocumentTitle.native.js.map +1 -1
  176. package/dist/esm/icons/CheckRegular.native.js.map +1 -1
  177. package/dist/esm/icons/MinusRegular.native.js.map +1 -1
  178. package/dist/esm/icons/index.native.js.map +1 -1
  179. package/dist/esm/icons/lucide.native.js.map +1 -1
  180. package/dist/esm/icons/themed.mjs +3 -2
  181. package/dist/esm/icons/themed.mjs.map +1 -1
  182. package/dist/esm/icons/themed.native.js +3 -2
  183. package/dist/esm/icons/themed.native.js.map +1 -1
  184. package/dist/esm/images/Image/ImageFrame.native.js.map +1 -1
  185. package/dist/esm/images/Image/shared.native.js.map +1 -1
  186. package/dist/esm/images/Image/types.native.js.map +1 -1
  187. package/dist/esm/images/index.native.js.map +1 -1
  188. package/dist/esm/index.native.js.map +1 -1
  189. package/dist/esm/knobImmuneExports.mjs +3 -3
  190. package/dist/esm/knobImmuneExports.native.js +3 -3
  191. package/dist/esm/knobImmuneExports.native.js.map +1 -1
  192. package/dist/esm/layouts/AsyncBoundary.native.js.map +1 -1
  193. package/dist/esm/layouts/Debug/index.native.js.map +1 -1
  194. package/dist/esm/layouts/EmptyState.native.js.map +1 -1
  195. package/dist/esm/layouts/KeyboardAvoidingWrapper/behavior.native.js.map +1 -1
  196. package/dist/esm/layouts/PageTemplates.native.js.map +1 -1
  197. package/dist/esm/layouts/PaneScaffold.mjs +70 -42
  198. package/dist/esm/layouts/PaneScaffold.mjs.map +1 -1
  199. package/dist/esm/layouts/PaneScaffold.native.js +75 -48
  200. package/dist/esm/layouts/PaneScaffold.native.js.map +1 -1
  201. package/dist/esm/layouts/SafeAreaWrapper/insets.native.js.map +1 -1
  202. package/dist/esm/layouts/ViewSwitcher.native.js.map +1 -1
  203. package/dist/esm/layouts/attachments.native.js.map +1 -1
  204. package/dist/esm/layouts/breadcrumbs.native.js.map +1 -1
  205. package/dist/esm/layouts/index.native.js.map +1 -1
  206. package/dist/esm/layouts/page.mjs +3 -2
  207. package/dist/esm/layouts/page.mjs.map +1 -1
  208. package/dist/esm/layouts/page.native.js +3 -2
  209. package/dist/esm/layouts/page.native.js.map +1 -1
  210. package/dist/esm/layouts/pageHeadingScope.native.js.map +1 -1
  211. package/dist/esm/layouts/sidebar.mjs +2 -4
  212. package/dist/esm/layouts/sidebar.mjs.map +1 -1
  213. package/dist/esm/layouts/sidebar.native.js +2 -4
  214. package/dist/esm/layouts/sidebar.native.js.map +1 -1
  215. package/dist/esm/layouts/tags.mjs.map +1 -1
  216. package/dist/esm/layouts/tags.native.js.map +1 -1
  217. package/dist/esm/layouts/timeline.native.js.map +1 -1
  218. package/dist/esm/layouts/toolbar.native.js.map +1 -1
  219. package/dist/esm/shared/actionScan.native.js.map +1 -1
  220. package/dist/esm/shared/i18n.native.js.map +1 -1
  221. package/dist/esm/shared/t.native.js.map +1 -1
  222. package/dist/esm/shared/textRidesText.native.js.map +1 -1
  223. package/dist/esm/shared/useReturnFocusOnClose.native.js.map +1 -1
  224. package/dist/esm/surfaces.mjs +1 -33
  225. package/dist/esm/surfaces.mjs.map +1 -1
  226. package/dist/esm/surfaces.native.js +1 -33
  227. package/dist/esm/surfaces.native.js.map +1 -1
  228. package/dist/esm/tamagui.native.js.map +1 -1
  229. package/dist/esm/testSetup.native.js.map +1 -1
  230. package/dist/esm/utils/setTinted.native.js.map +1 -1
  231. package/dist/esm/utils/unwrapText.native.js.map +1 -1
  232. package/dist/esm/views/Calendar.native.js.map +1 -1
  233. package/dist/esm/views/Dashboard.native.js.map +1 -1
  234. package/dist/esm/views/Gantt.native.js.map +1 -1
  235. package/dist/esm/views/ImageGrid.native.js.map +1 -1
  236. package/dist/esm/views/List.mjs +2 -4
  237. package/dist/esm/views/List.mjs.map +1 -1
  238. package/dist/esm/views/List.native.js +2 -4
  239. package/dist/esm/views/List.native.js.map +1 -1
  240. package/dist/esm/views/Matrix.mjs +543 -0
  241. package/dist/esm/views/Matrix.mjs.map +1 -0
  242. package/dist/esm/views/Matrix.native.js +573 -0
  243. package/dist/esm/views/Matrix.native.js.map +1 -0
  244. package/dist/esm/views/ReportBuilder.native.js.map +1 -1
  245. package/dist/esm/views/TreeView.mjs +175 -43
  246. package/dist/esm/views/TreeView.mjs.map +1 -1
  247. package/dist/esm/views/TreeView.native.js +181 -46
  248. package/dist/esm/views/TreeView.native.js.map +1 -1
  249. package/dist/esm/views/calendarMath.native.js.map +1 -1
  250. package/dist/esm/views/ganttMath.mjs +1 -1
  251. package/dist/esm/views/ganttMath.mjs.map +1 -1
  252. package/dist/esm/views/ganttMath.native.js +1 -1
  253. package/dist/esm/views/ganttMath.native.js.map +1 -1
  254. package/dist/esm/views/index.mjs +4 -2
  255. package/dist/esm/views/index.mjs.map +1 -1
  256. package/dist/esm/views/index.native.js +4 -2
  257. package/dist/esm/views/index.native.js.map +1 -1
  258. package/dist/esm/views/kanbanDnd.native.js.map +1 -1
  259. package/dist/esm/views/matrixMath.mjs +122 -0
  260. package/dist/esm/views/matrixMath.mjs.map +1 -0
  261. package/dist/esm/views/matrixMath.native.js +191 -0
  262. package/dist/esm/views/matrixMath.native.js.map +1 -0
  263. package/dist/esm/views/treeViewDnd.native.js.map +1 -1
  264. package/dist/esm/views/treeViewLayout.native.js.map +1 -1
  265. package/dist/esm/views/treeViewMotion.native.js.map +1 -1
  266. package/dist/esm/views/treeViewReveal.native.js.map +1 -1
  267. package/dist/jsx/Accordion/index.mjs +3 -7
  268. package/dist/jsx/Accordion/index.mjs.map +1 -1
  269. package/dist/jsx/Accordion/index.native.js +2 -6
  270. package/dist/jsx/Accordion/index.native.js.map +1 -1
  271. package/dist/jsx/AnimateHeight/index.mjs +2 -4
  272. package/dist/jsx/AnimateHeight/index.mjs.map +1 -1
  273. package/dist/jsx/AnimateHeight/index.native.js +1 -3
  274. package/dist/jsx/AnimateHeight/index.native.js.map +1 -1
  275. package/dist/jsx/Chip/index.mjs +9 -2
  276. package/dist/jsx/Chip/index.mjs.map +1 -1
  277. package/dist/jsx/Chip/index.native.js +8 -1
  278. package/dist/jsx/Chip/index.native.js.map +1 -1
  279. package/dist/jsx/SheetModal/index.mjs +22 -3
  280. package/dist/jsx/SheetModal/index.mjs.map +1 -1
  281. package/dist/jsx/SheetModal/index.native.js +115 -27
  282. package/dist/jsx/SheetModal/index.native.js.map +1 -1
  283. package/dist/jsx/Tabs/index.mjs +36 -19
  284. package/dist/jsx/Tabs/index.mjs.map +1 -1
  285. package/dist/jsx/Tabs/index.native.js +39 -18
  286. package/dist/jsx/Tabs/index.native.js.map +1 -1
  287. package/dist/jsx/Text.mjs +3 -1
  288. package/dist/jsx/Text.mjs.map +1 -1
  289. package/dist/jsx/Text.native.js +2 -0
  290. package/dist/jsx/Text.native.js.map +1 -1
  291. package/dist/jsx/TreeSelect/index.mjs +56 -53
  292. package/dist/jsx/TreeSelect/index.mjs.map +1 -1
  293. package/dist/jsx/TreeSelect/index.native.js +55 -52
  294. package/dist/jsx/TreeSelect/index.native.js.map +1 -1
  295. package/dist/jsx/Video/PlayerChrome.mjs +4 -10
  296. package/dist/jsx/Video/PlayerChrome.mjs.map +1 -1
  297. package/dist/jsx/Video/PlayerChrome.native.js +3 -9
  298. package/dist/jsx/Video/PlayerChrome.native.js.map +1 -1
  299. package/dist/jsx/code/Code/index.mjs +6 -0
  300. package/dist/jsx/code/Code/index.mjs.map +1 -1
  301. package/dist/jsx/code/Code/index.native.js +7 -4
  302. package/dist/jsx/code/Code/index.native.js.map +1 -1
  303. package/dist/jsx/code/CodeBlock/index.mjs +5 -6
  304. package/dist/jsx/code/CodeBlock/index.mjs.map +1 -1
  305. package/dist/jsx/code/CodeBlock/index.native.js +4 -5
  306. package/dist/jsx/code/CodeBlock/index.native.js.map +1 -1
  307. package/dist/jsx/icons/themed.mjs +3 -2
  308. package/dist/jsx/icons/themed.mjs.map +1 -1
  309. package/dist/jsx/icons/themed.native.js +2 -1
  310. package/dist/jsx/icons/themed.native.js.map +1 -1
  311. package/dist/jsx/knobImmuneExports.mjs +3 -3
  312. package/dist/jsx/knobImmuneExports.native.js +3 -3
  313. package/dist/jsx/layouts/PaneScaffold.mjs +70 -42
  314. package/dist/jsx/layouts/PaneScaffold.mjs.map +1 -1
  315. package/dist/jsx/layouts/PaneScaffold.native.js +75 -48
  316. package/dist/jsx/layouts/PaneScaffold.native.js.map +1 -1
  317. package/dist/jsx/layouts/page.mjs +3 -2
  318. package/dist/jsx/layouts/page.mjs.map +1 -1
  319. package/dist/jsx/layouts/page.native.js +2 -1
  320. package/dist/jsx/layouts/page.native.js.map +1 -1
  321. package/dist/jsx/layouts/sidebar.mjs +2 -4
  322. package/dist/jsx/layouts/sidebar.mjs.map +1 -1
  323. package/dist/jsx/layouts/sidebar.native.js +1 -3
  324. package/dist/jsx/layouts/sidebar.native.js.map +1 -1
  325. package/dist/jsx/layouts/tags.mjs.map +1 -1
  326. package/dist/jsx/layouts/tags.native.js.map +1 -1
  327. package/dist/jsx/surfaces.mjs +1 -33
  328. package/dist/jsx/surfaces.mjs.map +1 -1
  329. package/dist/jsx/surfaces.native.js +0 -32
  330. package/dist/jsx/surfaces.native.js.map +1 -1
  331. package/dist/jsx/views/List.mjs +2 -4
  332. package/dist/jsx/views/List.mjs.map +1 -1
  333. package/dist/jsx/views/List.native.js +1 -3
  334. package/dist/jsx/views/List.native.js.map +1 -1
  335. package/dist/jsx/views/Matrix.mjs +543 -0
  336. package/dist/jsx/views/Matrix.mjs.map +1 -0
  337. package/dist/jsx/views/Matrix.native.js +612 -0
  338. package/dist/jsx/views/Matrix.native.js.map +1 -0
  339. package/dist/jsx/views/TreeView.mjs +175 -43
  340. package/dist/jsx/views/TreeView.mjs.map +1 -1
  341. package/dist/jsx/views/TreeView.native.js +181 -46
  342. package/dist/jsx/views/TreeView.native.js.map +1 -1
  343. package/dist/jsx/views/ganttMath.mjs +1 -1
  344. package/dist/jsx/views/ganttMath.mjs.map +1 -1
  345. package/dist/jsx/views/ganttMath.native.js +2 -0
  346. package/dist/jsx/views/ganttMath.native.js.map +1 -1
  347. package/dist/jsx/views/index.mjs +4 -2
  348. package/dist/jsx/views/index.mjs.map +1 -1
  349. package/dist/jsx/views/index.native.js +16 -0
  350. package/dist/jsx/views/index.native.js.map +1 -1
  351. package/dist/jsx/views/matrixMath.mjs +122 -0
  352. package/dist/jsx/views/matrixMath.mjs.map +1 -0
  353. package/dist/jsx/views/matrixMath.native.js +229 -0
  354. package/dist/jsx/views/matrixMath.native.js.map +1 -0
  355. package/package.json +12 -12
  356. package/src/Accordion/Accordion.spec.tsx +20 -0
  357. package/src/Accordion/index.tsx +8 -4
  358. package/src/AnimateHeight/index.tsx +143 -142
  359. package/src/Chip/Chip.borderLadder.spec.tsx +54 -0
  360. package/src/Chip/Chip.pressSlop.spec.tsx +57 -0
  361. package/src/Chip/index.tsx +15 -1
  362. package/src/Heading.native.spec.tsx +1 -1
  363. package/src/Separator/index.tsx +3 -3
  364. package/src/SheetModal/SheetModal.detentLayout.spec.tsx +173 -0
  365. package/src/SheetModal/SheetModal.fill.spec.tsx +147 -0
  366. package/src/SheetModal/SheetModal.ownedBody.spec.tsx +200 -0
  367. package/src/SheetModal/SheetModal.stories.spec.tsx +23 -2
  368. package/src/SheetModal/SheetModal.stories.tsx +275 -2
  369. package/src/SheetModal/SheetModal.touch.spec.tsx +63 -0
  370. package/src/SheetModal/index.native.tsx +143 -21
  371. package/src/SheetModal/index.tsx +40 -3
  372. package/src/Tabs/Tabs.hydration.spec.tsx +123 -0
  373. package/src/Tabs/Tabs.lifecycle.spec.tsx +123 -0
  374. package/src/Tabs/Tabs.measurement.spec.tsx +186 -0
  375. package/src/Tabs/Tabs.stories.tsx +43 -0
  376. package/src/Tabs/index.tsx +45 -15
  377. package/src/Text.spec.tsx +32 -2
  378. package/src/Text.tsx +7 -6
  379. package/src/Toast/Toast.spec.tsx +50 -0
  380. package/src/Tooltip/Tooltip.spec.tsx +55 -2
  381. package/src/TreeSelect/TreeSelect.spec.tsx +15 -15
  382. package/src/TreeSelect/index.tsx +53 -50
  383. package/src/Video/PlayerChrome.tsx +4 -3
  384. package/src/Wheel/Wheel.spec.tsx +4 -4
  385. package/src/adjacency.l1.spec.tsx +175 -0
  386. package/src/code/Code/code.spec.tsx +2 -19
  387. package/src/code/Code/index.tsx +0 -6
  388. package/src/code/CodeBlock/index.tsx +5 -3
  389. package/src/code/highlightedCode.spec.tsx +2 -2
  390. package/src/code/hostProps.spec.tsx +55 -0
  391. package/src/icons/themed.hydration.spec.tsx +60 -0
  392. package/src/icons/themed.tsx +3 -6
  393. package/src/knobImmuneExports.ts +3 -3
  394. package/src/layouts/PaneScaffold.spec.tsx +148 -2
  395. package/src/layouts/PaneScaffold.stories.tsx +33 -1
  396. package/src/layouts/PaneScaffold.tsx +101 -48
  397. package/src/layouts/page.designLaw.spec.tsx +20 -0
  398. package/src/layouts/page.tsx +2 -0
  399. package/src/layouts/sidebar.tsx +2 -3
  400. package/src/layouts/tags.tsx +2 -1
  401. package/src/overlayElevation.spec.tsx +39 -51
  402. package/src/surfaces.spec.tsx +13 -10
  403. package/src/surfaces.tsx +4 -33
  404. package/src/tamagui.spec.ts +4 -3
  405. package/src/textAccentSecondary.spec.tsx +100 -2
  406. package/src/views/List.tsx +2 -1
  407. package/src/views/Matrix.spec.tsx +339 -0
  408. package/src/views/Matrix.stories.spec.tsx +68 -0
  409. package/src/views/Matrix.stories.tsx +153 -0
  410. package/src/views/Matrix.tsx +738 -0
  411. package/src/views/TreeView.reveal.spec.tsx +7 -0
  412. package/src/views/TreeView.reveal.stories.tsx +134 -0
  413. package/src/views/TreeView.scroll-index.spec.tsx +246 -0
  414. package/src/views/TreeView.scroll.spec.tsx +711 -50
  415. package/src/views/TreeView.tsx +230 -49
  416. package/src/views/ganttMath.ts +4 -2
  417. package/src/views/index.ts +30 -0
  418. package/src/views/matrixMath.spec.ts +237 -0
  419. package/src/views/matrixMath.ts +251 -0
  420. package/src/views/views.spec.tsx +3 -1
  421. package/types/Accordion/index.d.ts.map +1 -1
  422. package/types/AnimateHeight/index.d.ts.map +1 -1
  423. package/types/Chip/index.d.ts.map +1 -1
  424. package/types/Separator/index.d.ts +3 -3
  425. package/types/SheetModal/index.d.ts +8 -1
  426. package/types/SheetModal/index.d.ts.map +1 -1
  427. package/types/SheetModal/index.native.d.ts +1 -1
  428. package/types/SheetModal/index.native.d.ts.map +1 -1
  429. package/types/Tabs/index.d.ts.map +1 -1
  430. package/types/Text.d.ts.map +1 -1
  431. package/types/TreeSelect/index.d.ts.map +1 -1
  432. package/types/Video/PlayerChrome.d.ts.map +1 -1
  433. package/types/code/Code/index.d.ts.map +1 -1
  434. package/types/code/CodeBlock/index.d.ts.map +1 -1
  435. package/types/icons/themed.d.ts.map +1 -1
  436. package/types/layouts/PaneScaffold.d.ts +14 -1
  437. package/types/layouts/PaneScaffold.d.ts.map +1 -1
  438. package/types/layouts/page.d.ts.map +1 -1
  439. package/types/layouts/sidebar.d.ts.map +1 -1
  440. package/types/layouts/tags.d.ts.map +1 -1
  441. package/types/surfaces.d.ts.map +1 -1
  442. package/types/views/List.d.ts.map +1 -1
  443. package/types/views/Matrix.d.ts +102 -0
  444. package/types/views/Matrix.d.ts.map +1 -0
  445. package/types/views/TreeView.d.ts +10 -1
  446. package/types/views/TreeView.d.ts.map +1 -1
  447. package/types/views/ganttMath.d.ts +4 -0
  448. package/types/views/ganttMath.d.ts.map +1 -1
  449. package/types/views/index.d.ts +3 -1
  450. package/types/views/index.d.ts.map +1 -1
  451. package/types/views/matrixMath.d.ts +114 -0
  452. package/types/views/matrixMath.d.ts.map +1 -0
@@ -1,6 +1,15 @@
1
- import { Button } from "@multiplatform.one/forms";
1
+ import {
2
+ Paragraph as FixtureText,
3
+ ScrollView,
4
+ SheetModal as PublicSheetModal,
5
+ XStack,
6
+ YStack as FixtureStack,
7
+ type TamaguiElement,
8
+ } from "@multiplatform.one/components";
9
+ import { Button, Input } from "@multiplatform.one/forms";
2
10
  import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
3
- import { useState } from "react";
11
+ import { useRef, useState } from "react";
12
+ import { Platform, useWindowDimensions, type LayoutChangeEvent } from "react-native";
4
13
  import { Paragraph, SizableText, YStack } from "tamagui";
5
14
  import { ActionBar } from "../actions/ActionBar";
6
15
  import { SheetModal } from "./index";
@@ -107,3 +116,267 @@ export const OpenOnMount: Story = {
107
116
  name: "Open",
108
117
  render: () => <Harness startOpen />,
109
118
  };
119
+
120
+ type Bounds = { x: number; y: number; width: number; height: number };
121
+ type Reading<T> = T | "unmeasured" | "unavailable" | "invalid";
122
+ type FixtureMode = "subject" | "control-240";
123
+ type FixtureReadings = {
124
+ frameLayout: Reading<Bounds>;
125
+ toolbarLayout: Reading<Bounds>;
126
+ viewportLayout: Reading<Bounds>;
127
+ scrollLayout: Reading<Bounds>;
128
+ frameWindow: Reading<Bounds>;
129
+ viewportWindow: Reading<Bounds>;
130
+ scrollWindow: Reading<Bounds>;
131
+ tailWindow: Reading<Bounds>;
132
+ content: Reading<{ width: number; height: number }>;
133
+ offset: Reading<{ x: number; y: number }>;
134
+ sampleWindow: ReturnType<typeof useWindowDimensions> | "unmeasured";
135
+ sample: number;
136
+ tailPresses: number;
137
+ };
138
+
139
+ function emptyReadings(): FixtureReadings {
140
+ return {
141
+ frameLayout: "unmeasured",
142
+ toolbarLayout: "unmeasured",
143
+ viewportLayout: "unmeasured",
144
+ scrollLayout: "unmeasured",
145
+ frameWindow: "unmeasured",
146
+ viewportWindow: "unmeasured",
147
+ scrollWindow: "unmeasured",
148
+ tailWindow: "unmeasured",
149
+ content: "unmeasured",
150
+ offset: "unmeasured",
151
+ sampleWindow: "unmeasured",
152
+ sample: 0,
153
+ tailPresses: 0,
154
+ };
155
+ }
156
+
157
+ function bounds(x: number, y: number, width: number, height: number): Reading<Bounds> {
158
+ return [x, y, width, height].every(Number.isFinite) && width >= 0 && height >= 0
159
+ ? { x, y, width, height }
160
+ : "invalid";
161
+ }
162
+
163
+ function OwnedBodyFixture({ measured }: { measured: boolean }) {
164
+ const window = useWindowDimensions();
165
+ const [open, setOpen] = useState(false);
166
+ const [mode, setMode] = useState<FixtureMode>("subject");
167
+ const [fill, setFill] = useState(false);
168
+ const [readings, setReadings] = useState(emptyReadings);
169
+ const active = useRef(false);
170
+ const generation = useRef(0);
171
+ const sample = useRef(0);
172
+ const frameRef = useRef<TamaguiElement>(null);
173
+ const viewportRef = useRef<TamaguiElement>(null);
174
+ const scrollRef = useRef<React.ComponentRef<typeof ScrollView>>(null);
175
+ const tailRef = useRef<TamaguiElement>(null);
176
+ const presentation = generation.current;
177
+ const kind = measured ? "measured" : "scroll";
178
+
179
+ function present(nextMode: FixtureMode, nextFill = false) {
180
+ generation.current += 1;
181
+ sample.current = 0;
182
+ active.current = true;
183
+ setMode(nextMode);
184
+ setFill(nextFill);
185
+ setReadings(emptyReadings());
186
+ setOpen(true);
187
+ }
188
+
189
+ function close() {
190
+ active.current = false;
191
+ setOpen(false);
192
+ }
193
+
194
+ function record(patch: Partial<FixtureReadings>) {
195
+ if (!active.current || generation.current !== presentation) return;
196
+ setReadings((previous) => {
197
+ const next = { ...previous, ...patch };
198
+ return JSON.stringify(next) === JSON.stringify(previous) ? previous : next;
199
+ });
200
+ }
201
+
202
+ function onLayout(key: "frameLayout" | "toolbarLayout" | "viewportLayout" | "scrollLayout") {
203
+ return ({ nativeEvent: { layout } }: LayoutChangeEvent) =>
204
+ record({ [key]: bounds(layout.x, layout.y, layout.width, layout.height) });
205
+ }
206
+
207
+ function measureBounds() {
208
+ const currentSample = ++sample.current;
209
+ record({
210
+ sample: currentSample,
211
+ frameWindow: "unmeasured",
212
+ viewportWindow: "unmeasured",
213
+ scrollWindow: "unmeasured",
214
+ tailWindow: "unmeasured",
215
+ sampleWindow: { ...window },
216
+ });
217
+ for (const [key, node] of [
218
+ ["frameWindow", frameRef.current],
219
+ ["viewportWindow", viewportRef.current],
220
+ ["scrollWindow", scrollRef.current?.getNativeScrollRef?.()],
221
+ ["tailWindow", tailRef.current],
222
+ ] as const) {
223
+ if (!node || typeof node.measureInWindow !== "function") {
224
+ record({ [key]: "unavailable" });
225
+ continue;
226
+ }
227
+ node.measureInWindow((x, y, width, height) => {
228
+ if (sample.current === currentSample) record({ [key]: bounds(x, y, width, height) });
229
+ });
230
+ }
231
+ }
232
+
233
+ const viewport = readings.viewportLayout;
234
+ const showRows = !measured || (typeof viewport === "object" && viewport.height > 0);
235
+ const height = (value: Reading<Bounds>) => (typeof value === "object" ? value.height : value);
236
+ const diagnostics = {
237
+ kind,
238
+ mode,
239
+ fill,
240
+ open,
241
+ presentation,
242
+ platform: Platform.OS,
243
+ window,
244
+ sheetFrameWindow: "unavailable: SheetModal has no public frame ref",
245
+ ...readings,
246
+ };
247
+
248
+ return (
249
+ <FixtureStack gap="$3" padding="$3">
250
+ <FixtureText>{measured ? "Owned measured viewport" : "Owned ScrollView"}</FixtureText>
251
+ <Button onPress={() => present("subject")}>Open flex subject</Button>
252
+ <Button onPress={() => present("subject", true)}>Open flex subject (fill)</Button>
253
+ <Button onPress={() => present("control-240")}>Open 240pt control</Button>
254
+ <FixtureText>
255
+ Measure after the sheet settles. Close it to read the captured bounds below. Repeat after
256
+ dragging, scrolling, rotating or using the keyboard. A declared size is not a measurement.
257
+ </FixtureText>
258
+ <FixtureText testID="owned-sheet-readings" selectable>
259
+ {JSON.stringify(diagnostics, null, 2)}
260
+ </FixtureText>
261
+ <PublicSheetModal
262
+ open={open}
263
+ onOpenChange={(next) => {
264
+ if (!next) close();
265
+ }}
266
+ snapPoint={88}
267
+ scrollable={false}
268
+ fill={fill}
269
+ header={
270
+ <FixtureStack gap="$1" flexShrink={0}>
271
+ <FixtureText numberOfLines={1}>
272
+ {kind} / {mode} / fill {fill ? "on" : "off"}
273
+ </FixtureText>
274
+ <FixtureText numberOfLines={1}>
275
+ Body {height(readings.frameLayout)} / toolbar {height(readings.toolbarLayout)} /
276
+ viewport {height(viewport)}
277
+ </FixtureText>
278
+ <FixtureText numberOfLines={1}>
279
+ Offset {typeof readings.offset === "object" ? readings.offset.y : readings.offset} /
280
+ tail presses {readings.tailPresses}
281
+ </FixtureText>
282
+ <XStack gap="$2">
283
+ <Button flex={1} onPress={measureBounds}>
284
+ Measure bounds
285
+ </Button>
286
+ <Button flex={1} onPress={close}>
287
+ Close fixture
288
+ </Button>
289
+ </XStack>
290
+ </FixtureStack>
291
+ }
292
+ >
293
+ <FixtureStack
294
+ key={presentation}
295
+ ref={frameRef}
296
+ testID="owned-sheet-frame"
297
+ collapsable={false}
298
+ minHeight={0}
299
+ minWidth={0}
300
+ {...(mode === "control-240" ? { height: 240, flexGrow: 0, flexShrink: 0 } : { flex: 1 })}
301
+ onLayout={onLayout("frameLayout")}
302
+ >
303
+ <FixtureStack
304
+ testID="owned-sheet-toolbar"
305
+ collapsable={false}
306
+ flexShrink={0}
307
+ onLayout={onLayout("toolbarLayout")}
308
+ >
309
+ <FixtureText>Records toolbar</FixtureText>
310
+ </FixtureStack>
311
+ <FixtureStack
312
+ ref={viewportRef}
313
+ testID="owned-sheet-viewport"
314
+ collapsable={false}
315
+ flex={1}
316
+ minHeight={0}
317
+ minWidth={0}
318
+ onLayout={onLayout("viewportLayout")}
319
+ >
320
+ {showRows && (
321
+ <ScrollView
322
+ ref={scrollRef}
323
+ testID="owned-sheet-scroll"
324
+ flex={1}
325
+ minHeight={0}
326
+ keyboardShouldPersistTaps="handled"
327
+ scrollEventThrottle={32}
328
+ onLayout={onLayout("scrollLayout")}
329
+ onContentSizeChange={(width, height) =>
330
+ record({
331
+ content:
332
+ [width, height].every(Number.isFinite) && width >= 0 && height >= 0
333
+ ? { width, height }
334
+ : "invalid",
335
+ })
336
+ }
337
+ onScroll={({ nativeEvent: { contentOffset } }) =>
338
+ record({
339
+ offset: [contentOffset.x, contentOffset.y].every(Number.isFinite)
340
+ ? { x: contentOffset.x, y: contentOffset.y }
341
+ : "invalid",
342
+ })
343
+ }
344
+ >
345
+ <FixtureStack gap="$2">
346
+ <Input label="Keyboard probe" placeholder="Generic text only" />
347
+ {Array.from({ length: 60 }, (_, index) => (
348
+ <FixtureStack key={index} gap="$1" paddingVertical="$2">
349
+ <FixtureText>Record {index + 1}</FixtureText>
350
+ {Array.from({ length: 1 + (index % 4) }, (_, line) => (
351
+ <FixtureText key={line}>
352
+ Generic detail {line + 1} for this record.
353
+ </FixtureText>
354
+ ))}
355
+ </FixtureStack>
356
+ ))}
357
+ <FixtureStack ref={tailRef} testID="owned-sheet-tail" collapsable={false}>
358
+ <Button onPress={() => record({ tailPresses: readings.tailPresses + 1 })}>
359
+ Activate Record 60
360
+ </Button>
361
+ </FixtureStack>
362
+ </FixtureStack>
363
+ </ScrollView>
364
+ )}
365
+ </FixtureStack>
366
+ </FixtureStack>
367
+ </PublicSheetModal>
368
+ </FixtureStack>
369
+ );
370
+ }
371
+
372
+ export const OwnedMeasuredViewport: Story = {
373
+ name: "Owned measured viewport",
374
+ argTypes: { snapPoint: { control: false }, scrollable: { control: false } },
375
+ render: () => <OwnedBodyFixture measured />,
376
+ };
377
+
378
+ export const OwnedScrollView: Story = {
379
+ name: "Owned ScrollView",
380
+ argTypes: { snapPoint: { control: false }, scrollable: { control: false } },
381
+ render: () => <OwnedBodyFixture measured={false} />,
382
+ };
@@ -0,0 +1,63 @@
1
+ /**
2
+ * MPO-299: an owned ScrollView inside the native sheet never scrolled on
3
+ * the iOS simulator, while the same swipe scrolled the Combobox sheet.
4
+ *
5
+ * On native, tamagui turns a press or focus-visible pseudo into a JS
6
+ * responder claim (`onStartShouldSetResponder` returning true). A surface
7
+ * holding that claim takes every touch that starts on plain content inside
8
+ * it, and a responder ancestor stops the ScrollView's own pan.
9
+ */
10
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
11
+ import { cleanup } from "@testing-library/react";
12
+ import { afterEach, expect, it, vi } from "vitest";
13
+ import { Text } from "tamagui";
14
+ import { SheetModal } from "./index.native";
15
+
16
+ const stacks = vi.hoisted(() => new Map<string, Record<string, unknown>>());
17
+
18
+ vi.mock("react-native-web", async (importOriginal) => ({
19
+ ...(await importOriginal<typeof import("react-native")>()),
20
+ ActionSheetIOS: undefined,
21
+ Modal: ({ children }: { children: import("react").ReactNode }) => children,
22
+ }));
23
+ vi.mock("tamagui", async (importOriginal) => {
24
+ const actual = await importOriginal<typeof import("tamagui")>();
25
+ const React = await import("react");
26
+ const YStack = React.forwardRef((props: Record<string, unknown>, ref) => {
27
+ if (typeof props.testID === "string") stacks.set(props.testID, props);
28
+ return React.createElement(actual.YStack as never, { ...props, ref });
29
+ });
30
+ return { ...actual, YStack };
31
+ });
32
+
33
+ afterEach(() => {
34
+ cleanup();
35
+ stacks.clear();
36
+ });
37
+
38
+ const claimsResponder = [
39
+ "pressStyle",
40
+ "focusStyle",
41
+ "focusVisibleStyle",
42
+ "onPress",
43
+ "onPressIn",
44
+ "onPressOut",
45
+ "onLongPress",
46
+ ];
47
+
48
+ it("keeps every native sheet stack out of the touch responder", () => {
49
+ for (const fill of [false, true]) {
50
+ renderWithProviders(
51
+ <SheetModal open onOpenChange={() => {}} fill={fill} scrollable>
52
+ <Text>row</Text>
53
+ </SheetModal>,
54
+ );
55
+ for (const id of ["sheet-modal-surface", "sheet-modal-content"]) {
56
+ const stack = stacks.get(id);
57
+ expect(stack, id).toBeDefined();
58
+ for (const key of claimsResponder) expect(stack, `${id} ${key}`).not.toHaveProperty(key);
59
+ }
60
+ cleanup();
61
+ stacks.clear();
62
+ }
63
+ });
@@ -10,7 +10,15 @@
10
10
  * height with a large-detent cap. LC-71: the frame is not a control.
11
11
  */
12
12
 
13
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
13
+ import {
14
+ useCallback,
15
+ useContext,
16
+ useEffect,
17
+ useLayoutEffect,
18
+ useMemo,
19
+ useRef,
20
+ useState,
21
+ } from "react";
14
22
  import {
15
23
  Animated,
16
24
  Easing,
@@ -23,6 +31,7 @@ import {
23
31
  View as RNView,
24
32
  useWindowDimensions,
25
33
  } from "react-native";
34
+ import { SafeAreaInsetsContext, initialWindowMetrics } from "react-native-safe-area-context";
26
35
  import { useResolvedKnobs } from "@multiplatform.one/theme";
27
36
  import { minSheetHeight, resolveSheetDetents } from "./detents";
28
37
  import { useTranslation } from "../shared/i18n";
@@ -40,6 +49,14 @@ const backdropStrip = 48;
40
49
  // Released more than this far below the smallest detent, the sheet dismisses.
41
50
  const dismissThreshold = 56;
42
51
  const IN_DIALOG_REGION = { inDialogRegion: true } as const;
52
+ /*
53
+ * MPO-299 fill: an owned viewport bootstraps from zero unless EVERY link
54
+ * between the capped frame and the body gives way at once — frame height,
55
+ * surface, padded stack, body slot. Opening only the outer one keeps the
56
+ * measured-zero failure, so these are applied together or not at all.
57
+ */
58
+ const fillSlot = { flex: 1, minHeight: 0 } as const;
59
+ const nonShrinking = { flexShrink: 0 } as const;
43
60
 
44
61
  export function SheetModal({
45
62
  open,
@@ -48,11 +65,13 @@ export function SheetModal({
48
65
  header,
49
66
  snapPoint,
50
67
  scrollable,
68
+ fill = false,
51
69
  }: SheetModalProps) {
52
70
  const { t } = useTranslation();
53
71
  const { knobProps } = useResolvedKnobs();
54
72
  const { height: windowHeight } = useWindowDimensions();
55
73
  const keyboardInset = useKeyboardInset();
74
+ const topInset = useContext(SafeAreaInsetsContext)?.top ?? initialWindowMetrics?.insets.top ?? 0;
56
75
  const [backdropArmed, setBackdropArmed] = useState(false);
57
76
  const armTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
58
77
 
@@ -62,11 +81,12 @@ export function SheetModal({
62
81
  if (backdropArmed) close();
63
82
  }, [backdropArmed, close]);
64
83
 
65
- // MPO-64: the sheet ends above the keyboard rather than running under it.
84
+ // MPO-64: the sheet ends above the keyboard rather than running under it,
85
+ // and below the status bar with a strip of backdrop still showing.
66
86
  const available = Math.max(0, windowHeight - keyboardInset);
67
87
  const maxHeight = Math.max(
68
88
  minSheetHeight,
69
- Math.round(Math.min(windowHeight * sheetMaxHeightRatio, available - backdropStrip)),
89
+ Math.round(Math.min(windowHeight * sheetMaxHeightRatio, available - topInset - backdropStrip)),
70
90
  );
71
91
 
72
92
  /*
@@ -118,8 +138,59 @@ export function SheetModal({
118
138
  const presentationRef = useRef(0);
119
139
  const activeRef = useRef(false);
120
140
 
141
+ /*
142
+ * MPO-68 / MPO-299: a sheet at rest below full height is LAID OUT at that
143
+ * height. Translating a full-height frame down pushed its bottom off the
144
+ * screen, or under the keyboard, and the scroll owner inside still measured
145
+ * the full height, so the tail could never be scrolled into view.
146
+ *
147
+ * `frameCap` rides the same Animated.View as `translateY`, so the swap
148
+ * between "full height, translated" and "detent height, at 0" lands in one
149
+ * commit and the top edge does not move. `restHeightRef` is null at full
150
+ * height; a gesture always runs uncapped, in `travel` coordinates.
151
+ */
152
+ const frameCap = useRef(new Animated.Value(maxHeight)).current;
153
+ const maxHeightRef = useRef(maxHeight);
154
+ const restHeightRef = useRef<number | null>(null);
155
+ // The last uncapped layout, and whether it filled the max height. A capped
156
+ // frame measures only its cap, so when the keyboard moves the max height
157
+ // under a resting sheet, its full height is re-derived from these.
158
+ const naturalRef = useRef(0);
159
+ const filledRef = useRef(false);
160
+
161
+ useLayoutEffect(() => {
162
+ maxHeightRef.current = maxHeight;
163
+ const rest = restHeightRef.current;
164
+ frameCap.setValue(rest === null ? maxHeight : Math.min(maxHeight, rest));
165
+ if (rest !== null && naturalRef.current > 0) {
166
+ setMeasuredHeight(filledRef.current ? maxHeight : Math.min(naturalRef.current, maxHeight));
167
+ }
168
+ }, [frameCap, maxHeight]);
169
+
170
+ const restAt = useCallback(
171
+ (height: number) => {
172
+ restHeightRef.current = height;
173
+ frameCap.setValue(Math.min(maxHeightRef.current, height));
174
+ restRef.current = 0;
175
+ translateY.setValue(0);
176
+ },
177
+ [frameCap, translateY],
178
+ );
179
+
180
+ const releaseRest = useCallback(() => {
181
+ const rest = restHeightRef.current;
182
+ if (rest === null) return;
183
+ restHeightRef.current = null;
184
+ const offset = Math.max(0, travelRef.current - Math.min(maxHeightRef.current, rest));
185
+ frameCap.setValue(maxHeightRef.current);
186
+ restRef.current = offset;
187
+ translateY.setValue(offset);
188
+ }, [frameCap, translateY]);
189
+
121
190
  const settleTo = useCallback(
122
- (toValue: number, velocity = 0) => {
191
+ (height: number, velocity = 0) => {
192
+ const presentation = presentationRef.current;
193
+ const toValue = Math.max(0, travelRef.current - height);
123
194
  restRef.current = toValue;
124
195
  Animated.spring(translateY, {
125
196
  toValue,
@@ -132,9 +203,12 @@ export function SheetModal({
132
203
  // Modal does not advance on the New Architecture, and the drag path
133
204
  // already writes this value from JS.
134
205
  useNativeDriver: false,
135
- }).start();
206
+ }).start(({ finished }) => {
207
+ if (!finished || toValue === 0) return;
208
+ if (activeRef.current && presentation === presentationRef.current) restAt(height);
209
+ });
136
210
  },
137
- [translateY],
211
+ [restAt, translateY],
138
212
  );
139
213
 
140
214
  const dismiss = useCallback(
@@ -162,10 +236,15 @@ export function SheetModal({
162
236
  PanResponder.create({
163
237
  onStartShouldSetPanResponder: () => true,
164
238
  onMoveShouldSetPanResponder: (_evt, g) => Math.abs(g.dy) > 2,
239
+ // Lifting a rest cap can scroll the list inside, and a scroll lets
240
+ // any ancestor ScrollView the finger started under claim the touch,
241
+ // ending the drag and blurring a focused input on release.
242
+ onPanResponderTerminationRequest: () => false,
165
243
  onPanResponderGrant: () => {
166
244
  translateY.stopAnimation((value) => {
167
245
  if (typeof value === "number" && Number.isFinite(value)) restRef.current = value;
168
246
  });
247
+ releaseRest();
169
248
  },
170
249
  onPanResponderMove: (_evt, g) => {
171
250
  const next = restRef.current + g.dy;
@@ -190,11 +269,11 @@ export function SheetModal({
190
269
  best = height;
191
270
  }
192
271
  }
193
- settleTo(tvl - best, g.vy);
272
+ settleTo(best, g.vy);
194
273
  },
195
- onPanResponderTerminate: () => settleTo(restRef.current),
274
+ onPanResponderTerminate: () => settleTo(travelRef.current - restRef.current),
196
275
  }),
197
- [dismiss, settleTo, translateY],
276
+ [dismiss, releaseRest, settleTo, translateY],
198
277
  );
199
278
 
200
279
  /*
@@ -237,6 +316,8 @@ export function SheetModal({
237
316
  useEffect(() => {
238
317
  presentationRef.current += 1;
239
318
  activeRef.current = open;
319
+ restHeightRef.current = null;
320
+ frameCap.setValue(maxHeightRef.current);
240
321
  if (open) {
241
322
  enter();
242
323
  } else {
@@ -256,7 +337,7 @@ export function SheetModal({
256
337
  }
257
338
  translateY.stopAnimation();
258
339
  };
259
- }, [open, enter, translateY]);
340
+ }, [open, enter, frameCap, translateY]);
260
341
 
261
342
  return (
262
343
  <Modal
@@ -278,31 +359,43 @@ export function SheetModal({
278
359
  </Pressable>
279
360
  <Animated.View
280
361
  onLayout={(e) => {
362
+ if (restHeightRef.current !== null) return;
281
363
  const h = Math.round(e.nativeEvent.layout.height);
282
- if (h > 0 && h !== measuredHeight) setMeasuredHeight(h);
364
+ if (h <= 0) return;
365
+ naturalRef.current = h;
366
+ filledRef.current = h >= maxHeightRef.current - 1;
367
+ if (h !== measuredHeight) setMeasuredHeight(h);
283
368
  }}
369
+ testID="sheet-modal-frame"
284
370
  style={{
285
371
  // MPO-64: flex children default to flexShrink 0, so without this
286
372
  // the sheet cannot give way to the keyboard.
287
373
  flexShrink: 1,
288
374
  minHeight: 0,
289
- maxHeight,
375
+ maxHeight: frameCap,
376
+ // The cap is already keyboard- and window-aware; fill only asks
377
+ // for it as a definite height rather than an upper bound.
378
+ ...(fill ? { height: frameCap } : null),
290
379
  transform: [{ translateY }],
291
380
  }}
292
381
  >
293
382
  <YStack
383
+ testID="sheet-modal-surface"
294
384
  {...knobProps.surface}
295
385
  {...knobProps.borderRadius}
296
386
  borderBottomLeftRadius={0}
297
387
  borderBottomRightRadius={0}
298
388
  elevation={knobProps.elevation}
299
389
  maxHeight={maxHeight}
390
+ flexShrink={1}
391
+ minHeight={0}
392
+ {...(fill ? fillSlot : null)}
300
393
  paddingTop="$2"
301
394
  paddingBottom={keyboardInset > 0 ? 8 : bottomInset}
302
395
  overflow="hidden"
303
- outlineWidth={0}
304
- focusStyle={{ outlineWidth: 0 }}
305
- focusVisibleStyle={{ outlineWidth: 0 }}
396
+ // No focus or press pseudo on this surface: on native tamagui
397
+ // turns one into a touch-responder claim, and a responder here
398
+ // takes every drag from the ScrollView inside (MPO-299).
306
399
  accessibilityViewIsModal
307
400
  onAccessibilityEscape={close}
308
401
  >
@@ -316,11 +409,13 @@ export function SheetModal({
316
409
  {...panResponder.panHandlers}
317
410
  accessibilityRole="adjustable"
318
411
  accessibilityLabel={t("Drag to resize")}
412
+ testID="sheet-modal-grabber"
319
413
  style={{
320
414
  alignSelf: "stretch",
321
415
  alignItems: "center",
322
416
  paddingTop: 3,
323
417
  paddingBottom: 11,
418
+ ...(fill ? nonShrinking : null),
324
419
  }}
325
420
  >
326
421
  <View
@@ -334,17 +429,44 @@ export function SheetModal({
334
429
  </RNView>
335
430
  <DialogRegionContext.Provider value={IN_DIALOG_REGION}>
336
431
  <YStack
432
+ testID="sheet-modal-content"
337
433
  padding={knobProps.panelPadding.padding}
338
434
  gap={knobProps.gap.gap}
339
435
  flexShrink={1}
436
+ {...(fill ? fillSlot : null)}
340
437
  >
341
- {header}
342
- {scrollable ? (
343
- <RNScrollView style={{ flexGrow: 0 }} keyboardShouldPersistTaps="handled">
344
- {children}
345
- </RNScrollView>
438
+ {fill ? (
439
+ <>
440
+ {header ? <RNView style={nonShrinking}>{header}</RNView> : null}
441
+ <RNView testID="sheet-modal-body" style={fillSlot}>
442
+ {scrollable ? (
443
+ <RNScrollView
444
+ testID="sheet-modal-scroll"
445
+ style={fillSlot}
446
+ keyboardShouldPersistTaps="handled"
447
+ >
448
+ {children}
449
+ </RNScrollView>
450
+ ) : (
451
+ children
452
+ )}
453
+ </RNView>
454
+ </>
346
455
  ) : (
347
- children
456
+ <>
457
+ {header}
458
+ {scrollable ? (
459
+ <RNScrollView
460
+ testID="sheet-modal-scroll"
461
+ style={{ flexGrow: 0 }}
462
+ keyboardShouldPersistTaps="handled"
463
+ >
464
+ {children}
465
+ </RNScrollView>
466
+ ) : (
467
+ children
468
+ )}
469
+ </>
348
470
  )}
349
471
  </YStack>
350
472
  </DialogRegionContext.Provider>