@multiplatform.one/components 7.11.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 (413) hide show
  1. package/README.md +2 -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.native.js +70 -22
  12. package/dist/cjs/SheetModal/index.native.js.map +1 -1
  13. package/dist/cjs/Tabs/index.cjs +2 -6
  14. package/dist/cjs/Tabs/index.native.js +2 -6
  15. package/dist/cjs/Tabs/index.native.js.map +1 -1
  16. package/dist/cjs/Text.cjs +2 -0
  17. package/dist/cjs/Text.native.js +2 -0
  18. package/dist/cjs/Text.native.js.map +1 -1
  19. package/dist/cjs/TreeSelect/index.cjs +55 -52
  20. package/dist/cjs/TreeSelect/index.native.js +55 -52
  21. package/dist/cjs/TreeSelect/index.native.js.map +1 -1
  22. package/dist/cjs/Video/PlayerChrome.cjs +3 -9
  23. package/dist/cjs/Video/PlayerChrome.native.js +3 -9
  24. package/dist/cjs/Video/PlayerChrome.native.js.map +1 -1
  25. package/dist/cjs/code/Code/index.cjs +6 -0
  26. package/dist/cjs/code/Code/index.native.js +7 -4
  27. package/dist/cjs/code/Code/index.native.js.map +1 -1
  28. package/dist/cjs/code/CodeBlock/index.cjs +4 -5
  29. package/dist/cjs/code/CodeBlock/index.native.js +4 -5
  30. package/dist/cjs/code/CodeBlock/index.native.js.map +1 -1
  31. package/dist/cjs/icons/themed.cjs +2 -1
  32. package/dist/cjs/icons/themed.native.js +2 -1
  33. package/dist/cjs/icons/themed.native.js.map +1 -1
  34. package/dist/cjs/knobImmuneExports.cjs +3 -3
  35. package/dist/cjs/knobImmuneExports.native.js +3 -3
  36. package/dist/cjs/layouts/PaneScaffold.cjs +70 -42
  37. package/dist/cjs/layouts/PaneScaffold.native.js +75 -48
  38. package/dist/cjs/layouts/PaneScaffold.native.js.map +1 -1
  39. package/dist/cjs/layouts/page.cjs +2 -1
  40. package/dist/cjs/layouts/page.native.js +2 -1
  41. package/dist/cjs/layouts/page.native.js.map +1 -1
  42. package/dist/cjs/layouts/sidebar.cjs +1 -3
  43. package/dist/cjs/layouts/sidebar.native.js +1 -3
  44. package/dist/cjs/layouts/sidebar.native.js.map +1 -1
  45. package/dist/cjs/surfaces.cjs +0 -32
  46. package/dist/cjs/surfaces.native.js +0 -32
  47. package/dist/cjs/surfaces.native.js.map +1 -1
  48. package/dist/cjs/views/List.cjs +1 -3
  49. package/dist/cjs/views/List.native.js +1 -3
  50. package/dist/cjs/views/List.native.js.map +1 -1
  51. package/dist/cjs/views/Matrix.cjs +568 -0
  52. package/dist/cjs/views/Matrix.native.js +612 -0
  53. package/dist/cjs/views/Matrix.native.js.map +1 -0
  54. package/dist/cjs/views/ganttMath.cjs +2 -0
  55. package/dist/cjs/views/ganttMath.native.js +2 -0
  56. package/dist/cjs/views/ganttMath.native.js.map +1 -1
  57. package/dist/cjs/views/index.cjs +16 -0
  58. package/dist/cjs/views/index.native.js +16 -0
  59. package/dist/cjs/views/index.native.js.map +1 -1
  60. package/dist/cjs/views/matrixMath.cjs +157 -0
  61. package/dist/cjs/views/matrixMath.native.js +229 -0
  62. package/dist/cjs/views/matrixMath.native.js.map +1 -0
  63. package/dist/esm/Accordion/index.mjs +3 -7
  64. package/dist/esm/Accordion/index.mjs.map +1 -1
  65. package/dist/esm/Accordion/index.native.js +3 -7
  66. package/dist/esm/Accordion/index.native.js.map +1 -1
  67. package/dist/esm/AnimateHeight/index.mjs +2 -4
  68. package/dist/esm/AnimateHeight/index.mjs.map +1 -1
  69. package/dist/esm/AnimateHeight/index.native.js +2 -4
  70. package/dist/esm/AnimateHeight/index.native.js.map +1 -1
  71. package/dist/esm/Avatar/getInitials.native.js.map +1 -1
  72. package/dist/esm/Avatar/index.native.js.map +1 -1
  73. package/dist/esm/Badge/index.native.js.map +1 -1
  74. package/dist/esm/Button.native.js.map +1 -1
  75. package/dist/esm/Carousel/index.native.js.map +1 -1
  76. package/dist/esm/Chip/index.mjs +9 -2
  77. package/dist/esm/Chip/index.mjs.map +1 -1
  78. package/dist/esm/Chip/index.native.js +9 -2
  79. package/dist/esm/Chip/index.native.js.map +1 -1
  80. package/dist/esm/Collapsible.native.js.map +1 -1
  81. package/dist/esm/ContextMenu/index.native.js.map +1 -1
  82. package/dist/esm/CopyField/index.native.js.map +1 -1
  83. package/dist/esm/DotIndicator/index.native.js.map +1 -1
  84. package/dist/esm/DropdownMenu/index.native.js.map +1 -1
  85. package/dist/esm/ErrorBoundary/index.native.js.map +1 -1
  86. package/dist/esm/Heading.native.js.map +1 -1
  87. package/dist/esm/Meter/index.native.js.map +1 -1
  88. package/dist/esm/Meter/meter.native.js.map +1 -1
  89. package/dist/esm/Pagination.native.js.map +1 -1
  90. package/dist/esm/ProgressSteps/index.native.js.map +1 -1
  91. package/dist/esm/Quote/index.native.js.map +1 -1
  92. package/dist/esm/Separator/index.native.js.map +1 -1
  93. package/dist/esm/SheetModal/detents.native.js.map +1 -1
  94. package/dist/esm/SheetModal/index.native.js +71 -23
  95. package/dist/esm/SheetModal/index.native.js.map +1 -1
  96. package/dist/esm/Skeleton/index.native.js.map +1 -1
  97. package/dist/esm/SkipLink/index.native.js.map +1 -1
  98. package/dist/esm/SortableList/SortableHandle.native.js.map +1 -1
  99. package/dist/esm/SortableList/index.native.js.map +1 -1
  100. package/dist/esm/SortableList/useSortableRows.native.js.map +1 -1
  101. package/dist/esm/Tabs/index.mjs +3 -7
  102. package/dist/esm/Tabs/index.mjs.map +1 -1
  103. package/dist/esm/Tabs/index.native.js +3 -7
  104. package/dist/esm/Tabs/index.native.js.map +1 -1
  105. package/dist/esm/Text.mjs +3 -1
  106. package/dist/esm/Text.mjs.map +1 -1
  107. package/dist/esm/Text.native.js +3 -1
  108. package/dist/esm/Text.native.js.map +1 -1
  109. package/dist/esm/Toast/index.native.js.map +1 -1
  110. package/dist/esm/Toast/store.native.js.map +1 -1
  111. package/dist/esm/Tooltip/nativePress.native.js.map +1 -1
  112. package/dist/esm/Tooltip/tooltipShared.native.js.map +1 -1
  113. package/dist/esm/TreeSelect/index.mjs +56 -53
  114. package/dist/esm/TreeSelect/index.mjs.map +1 -1
  115. package/dist/esm/TreeSelect/index.native.js +56 -53
  116. package/dist/esm/TreeSelect/index.native.js.map +1 -1
  117. package/dist/esm/TreeSelect/treeSelectModel.native.js.map +1 -1
  118. package/dist/esm/Video/PlayerChrome.mjs +4 -10
  119. package/dist/esm/Video/PlayerChrome.mjs.map +1 -1
  120. package/dist/esm/Video/PlayerChrome.native.js +4 -10
  121. package/dist/esm/Video/PlayerChrome.native.js.map +1 -1
  122. package/dist/esm/Video/VideoFrame.native.js.map +1 -1
  123. package/dist/esm/Video/types.native.js.map +1 -1
  124. package/dist/esm/Wheel/index.native.js.map +1 -1
  125. package/dist/esm/Wizard/index.native.js.map +1 -1
  126. package/dist/esm/actions/ActionBar.native.js.map +1 -1
  127. package/dist/esm/actions/DestructiveAction.native.js.map +1 -1
  128. package/dist/esm/actions/devWarn.native.js.map +1 -1
  129. package/dist/esm/actions/index.native.js.map +1 -1
  130. package/dist/esm/actions/severity.native.js.map +1 -1
  131. package/dist/esm/charts/Axis.native.js.map +1 -1
  132. package/dist/esm/charts/BarChart.native.js.map +1 -1
  133. package/dist/esm/charts/ChartDataView.native.js.map +1 -1
  134. package/dist/esm/charts/ChartSurface.native.js.map +1 -1
  135. package/dist/esm/charts/LineChart.native.js.map +1 -1
  136. package/dist/esm/charts/PieChart.native.js.map +1 -1
  137. package/dist/esm/charts/Sparkline.native.js.map +1 -1
  138. package/dist/esm/charts/accessibility.native.js.map +1 -1
  139. package/dist/esm/charts/composeChart.native.js.map +1 -1
  140. package/dist/esm/charts/index.native.js.map +1 -1
  141. package/dist/esm/charts/math.native.js.map +1 -1
  142. package/dist/esm/charts/series.native.js.map +1 -1
  143. package/dist/esm/charts/svg.native.js.map +1 -1
  144. package/dist/esm/charts/types.native.js.map +1 -1
  145. package/dist/esm/charts/useChartTheme.native.js.map +1 -1
  146. package/dist/esm/code/Code/index.mjs +6 -0
  147. package/dist/esm/code/Code/index.mjs.map +1 -1
  148. package/dist/esm/code/Code/index.native.js +10 -7
  149. package/dist/esm/code/Code/index.native.js.map +1 -1
  150. package/dist/esm/code/CodeBlock/index.mjs +5 -6
  151. package/dist/esm/code/CodeBlock/index.mjs.map +1 -1
  152. package/dist/esm/code/CodeBlock/index.native.js +5 -6
  153. package/dist/esm/code/CodeBlock/index.native.js.map +1 -1
  154. package/dist/esm/code/HighlightedCode/index.native.js.map +1 -1
  155. package/dist/esm/code/Highlighter/index.native.js.map +1 -1
  156. package/dist/esm/code/Pre/index.native.js.map +1 -1
  157. package/dist/esm/code/index.native.js.map +1 -1
  158. package/dist/esm/componentColors.native.js.map +1 -1
  159. package/dist/esm/feedback/ConfirmDialog.native.js.map +1 -1
  160. package/dist/esm/feedback/NotifyHost.native.js.map +1 -1
  161. package/dist/esm/feedback/copy.native.js.map +1 -1
  162. package/dist/esm/feedback/index.native.js.map +1 -1
  163. package/dist/esm/feedback/notify.native.js.map +1 -1
  164. package/dist/esm/feedback/route.native.js.map +1 -1
  165. package/dist/esm/feedback/store.native.js.map +1 -1
  166. package/dist/esm/hooks/index.native.js.map +1 -1
  167. package/dist/esm/hooks/useClipboard.native.js.map +1 -1
  168. package/dist/esm/hooks/useDocumentTitle.native.js.map +1 -1
  169. package/dist/esm/icons/CheckRegular.native.js.map +1 -1
  170. package/dist/esm/icons/MinusRegular.native.js.map +1 -1
  171. package/dist/esm/icons/index.native.js.map +1 -1
  172. package/dist/esm/icons/lucide.native.js.map +1 -1
  173. package/dist/esm/icons/themed.mjs +3 -2
  174. package/dist/esm/icons/themed.mjs.map +1 -1
  175. package/dist/esm/icons/themed.native.js +3 -2
  176. package/dist/esm/icons/themed.native.js.map +1 -1
  177. package/dist/esm/images/Image/ImageFrame.native.js.map +1 -1
  178. package/dist/esm/images/Image/shared.native.js.map +1 -1
  179. package/dist/esm/images/Image/types.native.js.map +1 -1
  180. package/dist/esm/images/index.native.js.map +1 -1
  181. package/dist/esm/index.native.js.map +1 -1
  182. package/dist/esm/knobImmuneExports.mjs +3 -3
  183. package/dist/esm/knobImmuneExports.native.js +3 -3
  184. package/dist/esm/knobImmuneExports.native.js.map +1 -1
  185. package/dist/esm/layouts/AsyncBoundary.native.js.map +1 -1
  186. package/dist/esm/layouts/Debug/index.native.js.map +1 -1
  187. package/dist/esm/layouts/EmptyState.native.js.map +1 -1
  188. package/dist/esm/layouts/KeyboardAvoidingWrapper/behavior.native.js.map +1 -1
  189. package/dist/esm/layouts/PageTemplates.native.js.map +1 -1
  190. package/dist/esm/layouts/PaneScaffold.mjs +70 -42
  191. package/dist/esm/layouts/PaneScaffold.mjs.map +1 -1
  192. package/dist/esm/layouts/PaneScaffold.native.js +75 -48
  193. package/dist/esm/layouts/PaneScaffold.native.js.map +1 -1
  194. package/dist/esm/layouts/SafeAreaWrapper/insets.native.js.map +1 -1
  195. package/dist/esm/layouts/ViewSwitcher.native.js.map +1 -1
  196. package/dist/esm/layouts/attachments.native.js.map +1 -1
  197. package/dist/esm/layouts/breadcrumbs.native.js.map +1 -1
  198. package/dist/esm/layouts/index.native.js.map +1 -1
  199. package/dist/esm/layouts/page.mjs +3 -2
  200. package/dist/esm/layouts/page.mjs.map +1 -1
  201. package/dist/esm/layouts/page.native.js +3 -2
  202. package/dist/esm/layouts/page.native.js.map +1 -1
  203. package/dist/esm/layouts/pageHeadingScope.native.js.map +1 -1
  204. package/dist/esm/layouts/sidebar.mjs +2 -4
  205. package/dist/esm/layouts/sidebar.mjs.map +1 -1
  206. package/dist/esm/layouts/sidebar.native.js +2 -4
  207. package/dist/esm/layouts/sidebar.native.js.map +1 -1
  208. package/dist/esm/layouts/tags.native.js.map +1 -1
  209. package/dist/esm/layouts/timeline.native.js.map +1 -1
  210. package/dist/esm/layouts/toolbar.native.js.map +1 -1
  211. package/dist/esm/shared/actionScan.native.js.map +1 -1
  212. package/dist/esm/shared/i18n.native.js.map +1 -1
  213. package/dist/esm/shared/t.native.js.map +1 -1
  214. package/dist/esm/shared/textRidesText.native.js.map +1 -1
  215. package/dist/esm/shared/useReturnFocusOnClose.native.js.map +1 -1
  216. package/dist/esm/surfaces.mjs +1 -33
  217. package/dist/esm/surfaces.mjs.map +1 -1
  218. package/dist/esm/surfaces.native.js +1 -33
  219. package/dist/esm/surfaces.native.js.map +1 -1
  220. package/dist/esm/tamagui.native.js.map +1 -1
  221. package/dist/esm/testSetup.native.js.map +1 -1
  222. package/dist/esm/utils/setTinted.native.js.map +1 -1
  223. package/dist/esm/utils/unwrapText.native.js.map +1 -1
  224. package/dist/esm/views/Calendar.native.js.map +1 -1
  225. package/dist/esm/views/Dashboard.native.js.map +1 -1
  226. package/dist/esm/views/Gantt.native.js.map +1 -1
  227. package/dist/esm/views/ImageGrid.native.js.map +1 -1
  228. package/dist/esm/views/List.mjs +2 -4
  229. package/dist/esm/views/List.mjs.map +1 -1
  230. package/dist/esm/views/List.native.js +2 -4
  231. package/dist/esm/views/List.native.js.map +1 -1
  232. package/dist/esm/views/Matrix.mjs +543 -0
  233. package/dist/esm/views/Matrix.mjs.map +1 -0
  234. package/dist/esm/views/Matrix.native.js +573 -0
  235. package/dist/esm/views/Matrix.native.js.map +1 -0
  236. package/dist/esm/views/ReportBuilder.native.js.map +1 -1
  237. package/dist/esm/views/TreeView.native.js.map +1 -1
  238. package/dist/esm/views/calendarMath.native.js.map +1 -1
  239. package/dist/esm/views/ganttMath.mjs +1 -1
  240. package/dist/esm/views/ganttMath.mjs.map +1 -1
  241. package/dist/esm/views/ganttMath.native.js +1 -1
  242. package/dist/esm/views/ganttMath.native.js.map +1 -1
  243. package/dist/esm/views/index.mjs +4 -2
  244. package/dist/esm/views/index.mjs.map +1 -1
  245. package/dist/esm/views/index.native.js +4 -2
  246. package/dist/esm/views/index.native.js.map +1 -1
  247. package/dist/esm/views/kanbanDnd.native.js.map +1 -1
  248. package/dist/esm/views/matrixMath.mjs +122 -0
  249. package/dist/esm/views/matrixMath.mjs.map +1 -0
  250. package/dist/esm/views/matrixMath.native.js +191 -0
  251. package/dist/esm/views/matrixMath.native.js.map +1 -0
  252. package/dist/esm/views/treeViewDnd.native.js.map +1 -1
  253. package/dist/esm/views/treeViewLayout.native.js.map +1 -1
  254. package/dist/esm/views/treeViewMotion.native.js.map +1 -1
  255. package/dist/esm/views/treeViewReveal.native.js.map +1 -1
  256. package/dist/jsx/Accordion/index.mjs +3 -7
  257. package/dist/jsx/Accordion/index.mjs.map +1 -1
  258. package/dist/jsx/Accordion/index.native.js +2 -6
  259. package/dist/jsx/Accordion/index.native.js.map +1 -1
  260. package/dist/jsx/AnimateHeight/index.mjs +2 -4
  261. package/dist/jsx/AnimateHeight/index.mjs.map +1 -1
  262. package/dist/jsx/AnimateHeight/index.native.js +1 -3
  263. package/dist/jsx/AnimateHeight/index.native.js.map +1 -1
  264. package/dist/jsx/Chip/index.mjs +9 -2
  265. package/dist/jsx/Chip/index.mjs.map +1 -1
  266. package/dist/jsx/Chip/index.native.js +8 -1
  267. package/dist/jsx/Chip/index.native.js.map +1 -1
  268. package/dist/jsx/SheetModal/index.native.js +70 -22
  269. package/dist/jsx/SheetModal/index.native.js.map +1 -1
  270. package/dist/jsx/Tabs/index.mjs +3 -7
  271. package/dist/jsx/Tabs/index.mjs.map +1 -1
  272. package/dist/jsx/Tabs/index.native.js +2 -6
  273. package/dist/jsx/Tabs/index.native.js.map +1 -1
  274. package/dist/jsx/Text.mjs +3 -1
  275. package/dist/jsx/Text.mjs.map +1 -1
  276. package/dist/jsx/Text.native.js +2 -0
  277. package/dist/jsx/Text.native.js.map +1 -1
  278. package/dist/jsx/TreeSelect/index.mjs +56 -53
  279. package/dist/jsx/TreeSelect/index.mjs.map +1 -1
  280. package/dist/jsx/TreeSelect/index.native.js +55 -52
  281. package/dist/jsx/TreeSelect/index.native.js.map +1 -1
  282. package/dist/jsx/Video/PlayerChrome.mjs +4 -10
  283. package/dist/jsx/Video/PlayerChrome.mjs.map +1 -1
  284. package/dist/jsx/Video/PlayerChrome.native.js +3 -9
  285. package/dist/jsx/Video/PlayerChrome.native.js.map +1 -1
  286. package/dist/jsx/code/Code/index.mjs +6 -0
  287. package/dist/jsx/code/Code/index.mjs.map +1 -1
  288. package/dist/jsx/code/Code/index.native.js +7 -4
  289. package/dist/jsx/code/Code/index.native.js.map +1 -1
  290. package/dist/jsx/code/CodeBlock/index.mjs +5 -6
  291. package/dist/jsx/code/CodeBlock/index.mjs.map +1 -1
  292. package/dist/jsx/code/CodeBlock/index.native.js +4 -5
  293. package/dist/jsx/code/CodeBlock/index.native.js.map +1 -1
  294. package/dist/jsx/icons/themed.mjs +3 -2
  295. package/dist/jsx/icons/themed.mjs.map +1 -1
  296. package/dist/jsx/icons/themed.native.js +2 -1
  297. package/dist/jsx/icons/themed.native.js.map +1 -1
  298. package/dist/jsx/knobImmuneExports.mjs +3 -3
  299. package/dist/jsx/knobImmuneExports.native.js +3 -3
  300. package/dist/jsx/layouts/PaneScaffold.mjs +70 -42
  301. package/dist/jsx/layouts/PaneScaffold.mjs.map +1 -1
  302. package/dist/jsx/layouts/PaneScaffold.native.js +75 -48
  303. package/dist/jsx/layouts/PaneScaffold.native.js.map +1 -1
  304. package/dist/jsx/layouts/page.mjs +3 -2
  305. package/dist/jsx/layouts/page.mjs.map +1 -1
  306. package/dist/jsx/layouts/page.native.js +2 -1
  307. package/dist/jsx/layouts/page.native.js.map +1 -1
  308. package/dist/jsx/layouts/sidebar.mjs +2 -4
  309. package/dist/jsx/layouts/sidebar.mjs.map +1 -1
  310. package/dist/jsx/layouts/sidebar.native.js +1 -3
  311. package/dist/jsx/layouts/sidebar.native.js.map +1 -1
  312. package/dist/jsx/surfaces.mjs +1 -33
  313. package/dist/jsx/surfaces.mjs.map +1 -1
  314. package/dist/jsx/surfaces.native.js +0 -32
  315. package/dist/jsx/surfaces.native.js.map +1 -1
  316. package/dist/jsx/views/List.mjs +2 -4
  317. package/dist/jsx/views/List.mjs.map +1 -1
  318. package/dist/jsx/views/List.native.js +1 -3
  319. package/dist/jsx/views/List.native.js.map +1 -1
  320. package/dist/jsx/views/Matrix.mjs +543 -0
  321. package/dist/jsx/views/Matrix.mjs.map +1 -0
  322. package/dist/jsx/views/Matrix.native.js +612 -0
  323. package/dist/jsx/views/Matrix.native.js.map +1 -0
  324. package/dist/jsx/views/ganttMath.mjs +1 -1
  325. package/dist/jsx/views/ganttMath.mjs.map +1 -1
  326. package/dist/jsx/views/ganttMath.native.js +2 -0
  327. package/dist/jsx/views/ganttMath.native.js.map +1 -1
  328. package/dist/jsx/views/index.mjs +4 -2
  329. package/dist/jsx/views/index.mjs.map +1 -1
  330. package/dist/jsx/views/index.native.js +16 -0
  331. package/dist/jsx/views/index.native.js.map +1 -1
  332. package/dist/jsx/views/matrixMath.mjs +122 -0
  333. package/dist/jsx/views/matrixMath.mjs.map +1 -0
  334. package/dist/jsx/views/matrixMath.native.js +229 -0
  335. package/dist/jsx/views/matrixMath.native.js.map +1 -0
  336. package/package.json +10 -10
  337. package/src/Accordion/Accordion.spec.tsx +20 -0
  338. package/src/Accordion/index.tsx +7 -3
  339. package/src/AnimateHeight/index.tsx +143 -142
  340. package/src/Chip/Chip.borderLadder.spec.tsx +54 -0
  341. package/src/Chip/Chip.pressSlop.spec.tsx +57 -0
  342. package/src/Chip/index.tsx +15 -1
  343. package/src/Heading.native.spec.tsx +1 -1
  344. package/src/Separator/index.tsx +3 -3
  345. package/src/SheetModal/SheetModal.detentLayout.spec.tsx +173 -0
  346. package/src/SheetModal/SheetModal.touch.spec.tsx +63 -0
  347. package/src/SheetModal/index.native.tsx +95 -17
  348. package/src/Tabs/Tabs.hydration.spec.tsx +123 -0
  349. package/src/Tabs/index.tsx +3 -2
  350. package/src/Text.spec.tsx +32 -2
  351. package/src/Text.tsx +7 -6
  352. package/src/Toast/Toast.spec.tsx +50 -0
  353. package/src/Tooltip/Tooltip.spec.tsx +55 -2
  354. package/src/TreeSelect/TreeSelect.spec.tsx +15 -15
  355. package/src/TreeSelect/index.tsx +53 -50
  356. package/src/Video/PlayerChrome.tsx +4 -3
  357. package/src/Wheel/Wheel.spec.tsx +4 -4
  358. package/src/adjacency.l1.spec.tsx +175 -0
  359. package/src/code/Code/code.spec.tsx +2 -19
  360. package/src/code/Code/index.tsx +0 -6
  361. package/src/code/CodeBlock/index.tsx +5 -3
  362. package/src/code/highlightedCode.spec.tsx +2 -2
  363. package/src/code/hostProps.spec.tsx +55 -0
  364. package/src/icons/themed.hydration.spec.tsx +60 -0
  365. package/src/icons/themed.tsx +3 -6
  366. package/src/knobImmuneExports.ts +3 -3
  367. package/src/layouts/PaneScaffold.spec.tsx +148 -2
  368. package/src/layouts/PaneScaffold.stories.tsx +33 -1
  369. package/src/layouts/PaneScaffold.tsx +101 -48
  370. package/src/layouts/page.designLaw.spec.tsx +20 -0
  371. package/src/layouts/page.tsx +2 -0
  372. package/src/layouts/sidebar.tsx +2 -3
  373. package/src/overlayElevation.spec.tsx +39 -51
  374. package/src/surfaces.spec.tsx +13 -10
  375. package/src/surfaces.tsx +4 -33
  376. package/src/tamagui.spec.ts +4 -3
  377. package/src/textAccentSecondary.spec.tsx +100 -2
  378. package/src/views/List.tsx +2 -1
  379. package/src/views/Matrix.spec.tsx +339 -0
  380. package/src/views/Matrix.stories.spec.tsx +68 -0
  381. package/src/views/Matrix.stories.tsx +153 -0
  382. package/src/views/Matrix.tsx +738 -0
  383. package/src/views/ganttMath.ts +4 -2
  384. package/src/views/index.ts +30 -0
  385. package/src/views/matrixMath.spec.ts +237 -0
  386. package/src/views/matrixMath.ts +251 -0
  387. package/src/views/views.spec.tsx +3 -1
  388. package/types/Accordion/index.d.ts.map +1 -1
  389. package/types/AnimateHeight/index.d.ts.map +1 -1
  390. package/types/Chip/index.d.ts.map +1 -1
  391. package/types/Separator/index.d.ts +3 -3
  392. package/types/SheetModal/index.native.d.ts.map +1 -1
  393. package/types/Tabs/index.d.ts.map +1 -1
  394. package/types/Text.d.ts.map +1 -1
  395. package/types/TreeSelect/index.d.ts.map +1 -1
  396. package/types/Video/PlayerChrome.d.ts.map +1 -1
  397. package/types/code/Code/index.d.ts.map +1 -1
  398. package/types/code/CodeBlock/index.d.ts.map +1 -1
  399. package/types/icons/themed.d.ts.map +1 -1
  400. package/types/layouts/PaneScaffold.d.ts +14 -1
  401. package/types/layouts/PaneScaffold.d.ts.map +1 -1
  402. package/types/layouts/page.d.ts.map +1 -1
  403. package/types/layouts/sidebar.d.ts.map +1 -1
  404. package/types/surfaces.d.ts.map +1 -1
  405. package/types/views/List.d.ts.map +1 -1
  406. package/types/views/Matrix.d.ts +102 -0
  407. package/types/views/Matrix.d.ts.map +1 -0
  408. package/types/views/ganttMath.d.ts +4 -0
  409. package/types/views/ganttMath.d.ts.map +1 -1
  410. package/types/views/index.d.ts +3 -1
  411. package/types/views/index.d.ts.map +1 -1
  412. package/types/views/matrixMath.d.ts +114 -0
  413. package/types/views/matrixMath.d.ts.map +1 -0
@@ -735,6 +735,56 @@ describe("Toast data attributes reach the DOM", () => {
735
735
  });
736
736
  });
737
737
 
738
+ describe("Toast density moves padX and gap on the host (MPO-42)", () => {
739
+ afterEach(cleanup);
740
+
741
+ const padX = ["_px-", "_pl-", "_pr-"];
742
+ const gap = ["_gap-"];
743
+
744
+ function hostAtoms(node: ReactElement) {
745
+ renderWithProviders(node);
746
+ const host = screen.getByTestId("toast");
747
+ const measured = {
748
+ density: host.getAttribute("data-density"),
749
+ padX: classesByPrefix(host, padX),
750
+ gap: classesByPrefix(host, gap),
751
+ };
752
+ cleanup();
753
+ return measured;
754
+ }
755
+
756
+ it("the density knob changes padX and gap between the two stops", () => {
757
+ const comfortable = hostAtoms(
758
+ <Preset overrides={{ density: "comfortable" }}>
759
+ <Toast title="Saved" onDismiss={() => {}} />
760
+ </Preset>,
761
+ );
762
+ const compact = hostAtoms(
763
+ <Preset overrides={{ density: "compact" }}>
764
+ <Toast title="Saved" onDismiss={() => {}} />
765
+ </Preset>,
766
+ );
767
+ expect(comfortable.density).toBe("comfortable");
768
+ expect(compact.density).toBe("compact");
769
+ expect(comfortable.padX.length).toBeGreaterThan(0);
770
+ expect(comfortable.gap.length).toBeGreaterThan(0);
771
+ expect(compact.padX).not.toEqual(comfortable.padX);
772
+ expect(compact.gap).not.toEqual(comfortable.gap);
773
+ });
774
+
775
+ it("the compact prop lands the same padX and gap as the density knob", () => {
776
+ const knob = hostAtoms(
777
+ <Preset overrides={{ density: "compact" }}>
778
+ <Toast title="Saved" onDismiss={() => {}} />
779
+ </Preset>,
780
+ );
781
+ const prop = hostAtoms(<Toast compact title="Saved" onDismiss={() => {}} />);
782
+ expect(prop.density).toBe("compact");
783
+ expect(prop.padX).toEqual(knob.padX);
784
+ expect(prop.gap).toEqual(knob.gap);
785
+ });
786
+ });
787
+
738
788
  describe("Toast action label weight and contrast (TEXT NODE)", () => {
739
789
  it("Undo is weight 400 on the TEXT NODE, never 600 (R7 / §6.1)", () => {
740
790
  const action = renderActionWeight(
@@ -2,8 +2,8 @@
2
2
  * @vitest-environment jsdom
3
3
  */
4
4
 
5
- import { fireEvent } from "@testing-library/react";
6
- import { OVERLAY_ANCHOR_GAP, __resetDevWarnSeen } from "@multiplatform.one/theme";
5
+ import { act, fireEvent } from "@testing-library/react";
6
+ import { OVERLAY_ANCHOR_GAP, Preset, __resetDevWarnSeen } from "@multiplatform.one/theme";
7
7
  import { renderWithProviders } from "@multiplatform.one/test-utils";
8
8
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
9
9
  import { Button } from "@multiplatform.one/forms";
@@ -99,6 +99,59 @@ describe("Tooltip interaction", () => {
99
99
  });
100
100
  });
101
101
 
102
+ describe("Tooltip content rides the border, elevation and animation knobs", () => {
103
+ const stops = [
104
+ { borderWidth: "none", elevation: "none", animation: "none" },
105
+ { borderWidth: "small", elevation: "small", animation: "quick" },
106
+ { borderWidth: "large", elevation: "large", animation: "slow" },
107
+ ] as const;
108
+
109
+ async function measure(stop: (typeof stops)[number]) {
110
+ const result = renderWithProviders(
111
+ <Preset overrides={stop}>
112
+ <Tooltip content="Hint" open>
113
+ <Button>Edit</Button>
114
+ </Tooltip>
115
+ </Preset>,
116
+ );
117
+ await act(async () => {
118
+ await new Promise((resolve) => setTimeout(resolve, 50));
119
+ });
120
+ const content = document.querySelector<HTMLElement>("[data-tooltip='content']");
121
+ const classes = content?.className.split(/\s+/) ?? [];
122
+ const measured = {
123
+ mounted: content !== null,
124
+ border: classes.find((c) => c.startsWith("_btw-")),
125
+ shadow: classes.find((c) => c.startsWith("_bxsh-"))?.match(/^_bxsh-\d+px\d+px\d+px/)?.[0],
126
+ transition: content?.style.transition.match(/^all \d+ms/)?.[0],
127
+ };
128
+ result.unmount();
129
+ return measured;
130
+ }
131
+
132
+ it("follows each stop on the mounted bubble, and none carries no border, shadow or motion", async () => {
133
+ const none = await measure(stops[0]);
134
+ const small = await measure(stops[1]);
135
+ const large = await measure(stops[2]);
136
+ expect(none).toEqual({
137
+ mounted: true,
138
+ border: "_btw-0px",
139
+ shadow: undefined,
140
+ transition: undefined,
141
+ });
142
+ expect(small).toMatchObject({
143
+ mounted: true,
144
+ border: "_btw-0--5px",
145
+ shadow: "_bxsh-0px3px6px",
146
+ });
147
+ // Overlay large is the ladder's own $5 dialog stop, not the control $4 (MPO-99).
148
+ expect(large).toMatchObject({ mounted: true, border: "_btw-2px", shadow: "_bxsh-0px12px24px" });
149
+ expect(small.transition).toMatch(/^all \d+ms$/);
150
+ expect(large.transition).toMatch(/^all \d+ms$/);
151
+ expect(large.transition).not.toBe(small.transition);
152
+ });
153
+ });
154
+
102
155
  describe("Tooltip native long-press open path", () => {
103
156
  beforeEach(() => {
104
157
  vi.useFakeTimers();
@@ -84,7 +84,7 @@ describe("TreeSelect cover + width (MPO-53 AC-3)", () => {
84
84
  open
85
85
  />,
86
86
  );
87
- const trigger = result.container.querySelector(
87
+ const trigger = result.baseElement.querySelector(
88
88
  '[data-testid="tree-select-trigger"]',
89
89
  ) as HTMLElement;
90
90
  expect(trigger).toBeTruthy();
@@ -93,7 +93,7 @@ describe("TreeSelect cover + width (MPO-53 AC-3)", () => {
93
93
 
94
94
  try {
95
95
  await waitFor(() => {
96
- const panel = result.container.querySelector(
96
+ const panel = result.baseElement.querySelector(
97
97
  '[data-testid="tree-select-panel"]',
98
98
  ) as HTMLElement | null;
99
99
  expect(panel).toBeTruthy();
@@ -114,7 +114,7 @@ describe("TreeSelect LC-49 one tab stop", () => {
114
114
  const result = renderWithProviders(
115
115
  <TreeSelect label="Account" nodes={accounts} value="hdfc" />,
116
116
  );
117
- const closedStops = tabStops(result.container);
117
+ const closedStops = tabStops(result.baseElement);
118
118
  expect(closedStops.length).toBe(1);
119
119
  expect(closedStops[0]?.getAttribute("data-testid")).toBe("tree-select-trigger");
120
120
 
@@ -128,7 +128,7 @@ describe("TreeSelect LC-49 one tab stop", () => {
128
128
  />,
129
129
  );
130
130
  await waitFor(() => {
131
- expect(result.container.querySelector('[data-testid="tree-select-search"]')).toBeTruthy();
131
+ expect(result.baseElement.querySelector('[data-testid="tree-select-search"]')).toBeTruthy();
132
132
  });
133
133
  const openRoot = result.baseElement;
134
134
  const search = openRoot.querySelector(
@@ -169,16 +169,16 @@ describe("TreeSelect LC-21 commit vs expand", () => {
169
169
  />,
170
170
  );
171
171
  await waitFor(() => {
172
- expect(result.container.querySelector("[data-treeselect-caret]")).toBeTruthy();
172
+ expect(result.baseElement.querySelector("[data-treeselect-caret]")).toBeTruthy();
173
173
  });
174
- const caret = result.container.querySelector("[data-treeselect-caret]") as HTMLElement;
174
+ const caret = result.baseElement.querySelector("[data-treeselect-caret]") as HTMLElement;
175
175
  await act(async () => {
176
176
  fireEvent.click(caret);
177
177
  });
178
178
  expect(onChange).not.toHaveBeenCalled();
179
179
  expect(onOpenChange).not.toHaveBeenCalledWith(false);
180
180
 
181
- const cash = result.container.querySelector(
181
+ const cash = result.baseElement.querySelector(
182
182
  '[data-treeselect-row="liabilities"]',
183
183
  ) as HTMLElement;
184
184
  await act(async () => {
@@ -201,10 +201,10 @@ describe("TreeSelect sheet at <=640", () => {
201
201
  />,
202
202
  );
203
203
  await waitFor(() => {
204
- expect(desktop.container.querySelector("[data-treeselect-row]")).toBeTruthy();
204
+ expect(desktop.baseElement.querySelector("[data-treeselect-row]")).toBeTruthy();
205
205
  });
206
- const desktopTree = desktop.container.querySelector('[data-testid="tree-select-tree"]');
207
- const desktopRows = [...desktop.container.querySelectorAll("[data-treeselect-row]")].map(
206
+ const desktopTree = desktop.baseElement.querySelector('[data-testid="tree-select-tree"]');
207
+ const desktopRows = [...desktop.baseElement.querySelectorAll("[data-treeselect-row]")].map(
208
208
  (el) => ({
209
209
  id: el.getAttribute("data-treeselect-row"),
210
210
  depth: el.getAttribute("data-depth"),
@@ -226,7 +226,7 @@ describe("TreeSelect sheet at <=640", () => {
226
226
  await waitFor(() => {
227
227
  expect(mobile.baseElement.querySelector("[data-treeselect-row]")).toBeTruthy();
228
228
  });
229
- expect(mobile.container.querySelector('[data-testid="floating-panel-viewport"]')).toBeNull();
229
+ expect(mobile.baseElement.querySelector('[data-testid="floating-panel-viewport"]')).toBeNull();
230
230
  const mobileTree = mobile.baseElement.querySelector('[data-testid="tree-select-tree"]');
231
231
  const mobileRows = [...mobile.baseElement.querySelectorAll("[data-treeselect-row]")].map(
232
232
  (el) => ({
@@ -247,19 +247,19 @@ describe("TreeSelect empty filter", () => {
247
247
  <TreeSelect label="Account" nodes={accounts} open emptyMessage="No matching accounts" />,
248
248
  );
249
249
  await waitFor(() => {
250
- expect(result.container.querySelector('[data-testid="tree-select-search"]')).toBeTruthy();
250
+ expect(result.baseElement.querySelector('[data-testid="tree-select-search"]')).toBeTruthy();
251
251
  });
252
- const search = result.container.querySelector(
252
+ const search = result.baseElement.querySelector(
253
253
  '[data-testid="tree-select-search"] input',
254
254
  ) as HTMLInputElement;
255
255
  await act(async () => {
256
256
  fireEvent.change(search, { target: { value: "xyzzy" } });
257
257
  });
258
- const empty = result.container.querySelector(
258
+ const empty = result.baseElement.querySelector(
259
259
  '[data-testid="tree-select-empty"]',
260
260
  ) as HTMLElement;
261
261
  expect(empty).toBeTruthy();
262
262
  expect(empty.textContent).toContain("No matching accounts");
263
- expect(result.container.querySelector('[data-testid="empty-state"]')).toBeNull();
263
+ expect(result.baseElement.querySelector('[data-testid="empty-state"]')).toBeNull();
264
264
  });
265
265
  });
@@ -3,6 +3,7 @@ import {
3
3
  FloatingPanel,
4
4
  formCommonColors,
5
5
  InputParts,
6
+ PanelPortal,
6
7
  useFloatingPanel,
7
8
  useFormField,
8
9
  useViewportGtSm,
@@ -751,59 +752,61 @@ function FloatingTreeSelect({
751
752
  accessibilityLabel={accessibilityLabel}
752
753
  />
753
754
  {panel.mounted && (
754
- <FloatingFocusManager context={panel.context} modal={false} initialFocus={-1}>
755
- <YStack
756
- {...getFloatingProps({
757
- ref: panel.refs.setFloating,
758
- })}
759
- data-testid="tree-select-panel"
760
- data-cover=""
761
- data-width-mode={TREE_SELECT_WIDTH_MODE}
762
- data-dismiss-class="commit"
763
- data-fp-panel=""
764
- outlineWidth={0}
765
- pointerEvents={panel.visible ? "auto" : "none"}
766
- {...panel.knobProps.elevatedSurface}
767
- borderRadius={panel.dropdownRadius}
768
- overflow="hidden"
769
- style={
770
- {
771
- position: "fixed",
772
- top: overlayStyle.current?.top ?? 0,
773
- left: 0,
774
- zIndex: zIndex.dropdown,
775
- opacity: panel.visible ? 1 : 0,
776
- transform: panel.visible ? "scale(1)" : "scale(0.96)",
777
- transition: panelTransition,
778
- boxSizing: "border-box",
779
- } as Record<string, unknown>
780
- }
781
- >
782
- <SearchRow
783
- value={state.filter}
784
- placeholder={searchPlaceholder}
785
- inputRef={searchRef}
786
- onChange={state.setFilter}
787
- onKeyDown={onSearchKeyDown}
788
- treeId={treeId}
789
- activeDescendant={activeDescendant}
790
- sizeToken={sizeToken}
791
- />
792
- <YStack overflow="scroll" maxHeight={320} id={treeId}>
793
- <TreeSelectPanelBody
794
- rows={state.rows}
795
- selectedId={value}
796
- activeIndex={state.activeIndex}
797
- keyboardRing={state.keyboardRing}
755
+ <PanelPortal anchor={panel.triggerRef.current}>
756
+ <FloatingFocusManager context={panel.context} modal={false} initialFocus={-1}>
757
+ <YStack
758
+ {...getFloatingProps({
759
+ ref: panel.refs.setFloating,
760
+ })}
761
+ data-testid="tree-select-panel"
762
+ data-cover=""
763
+ data-width-mode={TREE_SELECT_WIDTH_MODE}
764
+ data-dismiss-class="commit"
765
+ data-fp-panel=""
766
+ outlineWidth={0}
767
+ pointerEvents={panel.visible ? "auto" : "none"}
768
+ {...panel.knobProps.elevatedSurface}
769
+ borderRadius={panel.dropdownRadius}
770
+ overflow="hidden"
771
+ style={
772
+ {
773
+ position: "fixed",
774
+ top: overlayStyle.current?.top ?? 0,
775
+ left: 0,
776
+ zIndex: zIndex.dropdown,
777
+ opacity: panel.visible ? 1 : 0,
778
+ transform: panel.visible ? "scale(1)" : "scale(0.96)",
779
+ transition: panelTransition,
780
+ boxSizing: "border-box",
781
+ } as Record<string, unknown>
782
+ }
783
+ >
784
+ <SearchRow
785
+ value={state.filter}
786
+ placeholder={searchPlaceholder}
787
+ inputRef={searchRef}
788
+ onChange={state.setFilter}
789
+ onKeyDown={onSearchKeyDown}
798
790
  treeId={treeId}
799
- emptyMessage={emptyMessage}
800
- loading={loading}
801
- onCommit={commit}
802
- onToggle={state.toggle}
791
+ activeDescendant={activeDescendant}
792
+ sizeToken={sizeToken}
803
793
  />
794
+ <YStack overflow="scroll" maxHeight={320} id={treeId}>
795
+ <TreeSelectPanelBody
796
+ rows={state.rows}
797
+ selectedId={value}
798
+ activeIndex={state.activeIndex}
799
+ keyboardRing={state.keyboardRing}
800
+ treeId={treeId}
801
+ emptyMessage={emptyMessage}
802
+ loading={loading}
803
+ onCommit={commit}
804
+ onToggle={state.toggle}
805
+ />
806
+ </YStack>
804
807
  </YStack>
805
- </YStack>
806
- </FloatingFocusManager>
808
+ </FloatingFocusManager>
809
+ </PanelPortal>
807
810
  )}
808
811
  </>
809
812
  );
@@ -2,6 +2,7 @@ import {
2
2
  MIN_PRESS_TARGET,
3
3
  ensureKeyboardModalityTracking,
4
4
  useResolvedKnobs,
5
+ transitionProps,
5
6
  } from "@multiplatform.one/theme";
6
7
  import {
7
8
  CornersInIcon,
@@ -83,7 +84,7 @@ function ChromeButton({
83
84
  hoverStyle={{ backgroundColor: "$color4" }}
84
85
  pressStyle={{ opacity: 0.85 }}
85
86
  focusVisibleStyle={{ ...control.focusVisibleKnobProps }}
86
- {...(knobProps.transition ? { transition: knobProps.transition } : {})}
87
+ {...transitionProps(knobProps.transition)}
87
88
  onPress={(e: { stopPropagation?: () => void }) => {
88
89
  e.stopPropagation?.();
89
90
  onPress();
@@ -205,7 +206,7 @@ function SeekBar({
205
206
  backgroundColor="$color8"
206
207
  borderRadius={radius}
207
208
  overflow="hidden"
208
- {...(knobProps.transition ? { transition: knobProps.transition } : {})}
209
+ {...transitionProps(knobProps.transition)}
209
210
  >
210
211
  <YStack
211
212
  height="100%"
@@ -300,7 +301,7 @@ export function PlayerChrome({
300
301
  onFocusChange(false);
301
302
  },
302
303
  } as Record<string, unknown>)}
303
- {...(knobProps.transition ? { transition: knobProps.transition } : {})}
304
+ {...transitionProps(knobProps.transition)}
304
305
  >
305
306
  <YStack flex={1} alignItems="center" justifyContent="center" pointerEvents="none">
306
307
  {playing ? null : (
@@ -275,7 +275,7 @@ describe("Wheel design-law (MPO-60)", () => {
275
275
  expect("default" in mod).toBe(false);
276
276
  });
277
277
 
278
- it("selected row type stays on body weight 400, never 600 (R7 / design-audit)", () => {
278
+ it("selected row type stays on the body weight channel (400/700), never 500/600 (R7 / design-audit)", () => {
279
279
  renderWithProviders(
280
280
  <Wheel items={[1, 2, 3, 4, 5]} value={3} onChange={() => {}} visibleItems={5} />,
281
281
  );
@@ -284,7 +284,7 @@ describe("Wheel design-law (MPO-60)", () => {
284
284
  .querySelector('[data-testid="wheel-item"][data-selected="true"]') as HTMLElement;
285
285
  expect(selected).toBeTruthy();
286
286
  const text = selected.querySelector("[class]") as HTMLElement;
287
- expect(text.className).toMatch(/_fow-400\b/);
287
+ expect(text.className).toMatch(/_fow-(400|700|bold)\b/);
288
288
  expect(text.className).not.toMatch(/_fow-600\b/);
289
289
  expect(text.className).not.toMatch(/_fow-500\b/);
290
290
  });
@@ -298,7 +298,7 @@ describe("Wheel design-law (MPO-60)", () => {
298
298
  const noneFrame = none.container.querySelector('[data-testid="wheel"]') as HTMLElement;
299
299
  const noneAtoms = String(noneFrame.className)
300
300
  .split(" ")
301
- .filter((c) => c.startsWith("_btlr-"));
301
+ .filter((c) => c.startsWith("_btlr-") && c !== "_btlr-0px");
302
302
  none.unmount();
303
303
 
304
304
  const full = renderWithProviders(
@@ -309,7 +309,7 @@ describe("Wheel design-law (MPO-60)", () => {
309
309
  const fullFrame = full.container.querySelector('[data-testid="wheel"]') as HTMLElement;
310
310
  const fullAtoms = String(fullFrame.className)
311
311
  .split(" ")
312
- .filter((c) => c.startsWith("_btlr-"));
312
+ .filter((c) => c.startsWith("_btlr-") && c !== "_btlr-0px");
313
313
  expect(noneAtoms).toEqual([]);
314
314
  expect(fullAtoms).toEqual([]);
315
315
  });
@@ -0,0 +1,175 @@
1
+ /**
2
+ * `00` L1 test 1, the adjacency check (MPO-23, gap ledger GL-41). A Button,
3
+ * an Input, a Select trigger and a ListItem in one `XStack` at the same size
4
+ * token, with only a `gap`, must agree on height and on radius. They differ
5
+ * only in interior padding (`00` L9f). `constraintAudit` checks that each
6
+ * value lands on a token step; nothing checked that siblings agree.
7
+ *
8
+ * The DOM here cannot lay out and does not resolve Tamagui's CSS variables,
9
+ * so each frame's box is read from the atoms it paints (`_h-44px`,
10
+ * `_btlr-t-radius-4`, `_mih-t-size-4`) and resolved through the live token
11
+ * table. That is the same box a browser computes for these frames: none of
12
+ * them grows past its height or min-height with one line of content.
13
+ *
14
+ * The law holds for height at every size stop and for the controls' radius at
15
+ * the default stop. ListItem joins the height check only: it is a flush row
16
+ * (SP-EDGE, radius 0) and never takes the controls' radius (Clay 2026-09-26,
17
+ * MPO-23 ruling 1A). Two disagreements are open and pinned below as expected
18
+ * failures, each naming its culprit. Fixing one makes its `it.fails` go red,
19
+ * which is the signal to turn it into a plain assertion.
20
+ */
21
+
22
+ import { Input, Select } from "@multiplatform.one/forms";
23
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
24
+ import { Preset, type Size } from "@multiplatform.one/theme";
25
+ import { cleanup } from "@testing-library/react";
26
+ import type { ReactElement } from "react";
27
+ import { getTokens } from "tamagui";
28
+ import { afterEach, describe, expect, it } from "vitest";
29
+ import { Button } from "./Button";
30
+ import { ListItem, XStack } from "./tamagui";
31
+
32
+ afterEach(cleanup);
33
+
34
+ type Box = { height: number; radius: number };
35
+ type Row = Record<"Button" | "Input" | "Select trigger" | "ListItem", Box>;
36
+
37
+ const stops = [
38
+ ["small", "$3"],
39
+ ["medium", "$4"],
40
+ ["large", "$5"],
41
+ ] as const satisfies readonly (readonly [Size, string])[];
42
+
43
+ function atom(el: HTMLElement, prop: string) {
44
+ for (const cls of String(el.className || "").split(/\s+/)) {
45
+ if (!cls.startsWith(`_${prop}-`)) continue;
46
+ const value = cls.slice(prop.length + 2);
47
+ if (/^-?\d+(?:\.\d+)?px$|^t-(?:size|radius)-\d+$/.test(value)) return value;
48
+ }
49
+ return undefined;
50
+ }
51
+
52
+ function px(value: string | undefined): number | undefined {
53
+ if (value === undefined) return undefined;
54
+ const literal = value.match(/^(-?\d+(?:\.\d+)?)px$/);
55
+ if (literal) return Number(literal[1]);
56
+ const [, scale, step] = value.match(/^t-(size|radius)-(\d+)$/) ?? [];
57
+ const tokens = getTokens() as unknown as Record<string, Record<string, { val: unknown }>>;
58
+ return Number.parseFloat(String(tokens[scale ?? ""]?.[`$${step}`]?.val));
59
+ }
60
+
61
+ function box(el: HTMLElement | null, name: string): Box {
62
+ if (!el) throw new Error(`${name} frame not rendered`);
63
+ const height = px(atom(el, "h") ?? atom(el, "mih"));
64
+ if (height === undefined) throw new Error(`${name} paints no height`);
65
+ return { height, radius: px(atom(el, "btlr")) ?? 0 };
66
+ }
67
+
68
+ function measure(ui: ReactElement): Row {
69
+ const { container } = renderWithProviders(ui);
70
+ const frame = (selector: string) => container.querySelector<HTMLElement>(selector);
71
+ return {
72
+ Button: box(frame("[data-mp-button-height]"), "Button"),
73
+ Input: box(frame("[data-mp-input-box]"), "Input"),
74
+ "Select trigger": box(frame('[data-testid="select-trigger"]'), "Select trigger"),
75
+ ListItem: box(frame('[data-testid="l1-list-item"]'), "ListItem"),
76
+ };
77
+ }
78
+
79
+ /** The mpo size channel: the size knob, which every house control reads. */
80
+ function atKnob(knob: Size, token: string) {
81
+ return measure(
82
+ <Preset overrides={{ size: knob }}>
83
+ <XStack gap="$2">
84
+ <Button>Save</Button>
85
+ <Input placeholder="Name" />
86
+ <Select options={[{ value: "a", label: "A" }]} placeholder="Pick" />
87
+ <ListItem size={token as "$4"} title="Row" data-testid="l1-list-item" />
88
+ </XStack>
89
+ </Preset>,
90
+ );
91
+ }
92
+
93
+ /** The same token handed to each component directly. */
94
+ function atSizeProp(token: "$3" | "$4" | "$5") {
95
+ return measure(
96
+ <XStack gap="$2">
97
+ <Button size={token}>Save</Button>
98
+ <Input size={token} placeholder="Name" />
99
+ <Select size={token} options={[{ value: "a", label: "A" }]} placeholder="Pick" />
100
+ <ListItem size={token} title="Row" data-testid="l1-list-item" />
101
+ </XStack>,
102
+ );
103
+ }
104
+
105
+ function heights(row: Row) {
106
+ return Object.fromEntries(Object.entries(row).map(([name, b]) => [name, b.height]));
107
+ }
108
+
109
+ function radii(row: Row, names: (keyof Row)[]) {
110
+ return Object.fromEntries(names.map((name) => [name, row[name].radius]));
111
+ }
112
+
113
+ function agreed(values: Record<string, number>) {
114
+ const first = Object.values(values)[0];
115
+ return Object.fromEntries(Object.keys(values).map((name) => [name, first]));
116
+ }
117
+
118
+ const controls: (keyof Row)[] = ["Button", "Input", "Select trigger"];
119
+
120
+ describe("00 L1 adjacency: Button, Input, Select trigger and ListItem at one size token", () => {
121
+ for (const [knob, token] of stops) {
122
+ it(`all four paint one height at size ${knob} (${token})`, () => {
123
+ const row = heights(atKnob(knob, token));
124
+ expect(row).toEqual(agreed(row));
125
+ });
126
+ }
127
+
128
+ it("catches a deliberate violation: a Button ejected to its own height", () => {
129
+ const row = heights(
130
+ measure(
131
+ <XStack gap="$2">
132
+ <Button height={30} sizeRecipeEscape="L1 adjacency fixture">
133
+ Save
134
+ </Button>
135
+ <Input placeholder="Name" />
136
+ <Select options={[{ value: "a", label: "A" }]} placeholder="Pick" />
137
+ <ListItem size="$4" title="Row" data-testid="l1-list-item" />
138
+ </XStack>,
139
+ ),
140
+ );
141
+ expect(row.Button).toBe(30);
142
+ expect(row).not.toEqual(agreed(row));
143
+ });
144
+
145
+ it("Button, Input and Select trigger paint one radius at the default size", () => {
146
+ const row = radii(atKnob("medium", "$4"), controls);
147
+ expect(row).toEqual(agreed(row));
148
+ expect(row.Button).toBeGreaterThan(0);
149
+ });
150
+
151
+ it("ListItem stays a flush row beside them: radius 0, never the controls' radius", () => {
152
+ const row = radii(atKnob("medium", "$4"), [...controls, "ListItem"]);
153
+ expect(row.ListItem).toBe(0);
154
+ expect(row.Button).toBeGreaterThan(0);
155
+ });
156
+
157
+ for (const [knob, token] of stops.filter(([k]) => k !== "medium")) {
158
+ it.fails(`OPEN: Button and Input paint one radius at size ${knob} (Button's radius follows size, Input's only the radius knob)`, () => {
159
+ const row = radii(atKnob(knob, token), controls);
160
+ expect(row).toEqual(agreed(row));
161
+ });
162
+ }
163
+
164
+ it("an explicit size prop agrees with itself at the default token", () => {
165
+ const row = heights(atSizeProp("$4"));
166
+ expect(row).toEqual(agreed(row));
167
+ });
168
+
169
+ for (const token of ["$3", "$5"] as const) {
170
+ it.fails(`OPEN: an explicit size=${token} moves Input and Select trigger with Button (Input paints the knob height, Select ignores size)`, () => {
171
+ const row = heights(atSizeProp(token));
172
+ expect(row).toEqual(agreed(row));
173
+ });
174
+ }
175
+ });
@@ -1,5 +1,5 @@
1
1
  import { renderWithProviders } from "@multiplatform.one/test-utils";
2
- import { describe, expect, it, vi } from "vitest";
2
+ import { describe, expect, it } from "vitest";
3
3
  import { Code, CodeInline } from "./index";
4
4
 
5
5
  describe("Code", () => {
@@ -27,7 +27,7 @@ describe("Code", () => {
27
27
  const codeElement = container.querySelector("code");
28
28
  expect(codeElement).toBeTruthy();
29
29
  expect(codeElement!.getAttribute("data-code-font")).toBe("mono");
30
- // React lowercases an unknown dataSet prop to dataset="[object Object]".
30
+ // An RN prop map on this host would land as dataset="[object Object]".
31
31
  expect(codeElement!.hasAttribute("dataset")).toBe(false);
32
32
  });
33
33
  });
@@ -51,21 +51,4 @@ describe("CodeInline", () => {
51
51
  const codeElement = container.querySelector("code");
52
52
  expect(codeElement).toBeDefined();
53
53
  });
54
-
55
- it("emits data-code-font on web without a dataSet host-prop leak", () => {
56
- const leak = /does not recognize the `dataSet` prop on a DOM element/;
57
- const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
58
- try {
59
- const { container } = renderWithProviders(<CodeInline>npm install</CodeInline>);
60
- const codeElement = container.querySelector("code");
61
- expect(codeElement).toBeDefined();
62
- expect(codeElement?.getAttribute("data-code-font")).toBe("mono");
63
- const leaks = errorSpy.mock.calls
64
- .map((args) => args.map(String).join(" "))
65
- .filter((message) => leak.test(message));
66
- expect(leaks).toEqual([]);
67
- } finally {
68
- errorSpy.mockRestore();
69
- }
70
- });
71
54
  });
@@ -81,12 +81,6 @@ export const CodeInline = CodeInlineFrame.styleable((props, ref) => {
81
81
  fontFamily="$mono"
82
82
  letterSpacing={0}
83
83
  data-code-font="mono"
84
- // react-native-web reads dataSet; React-DOM warns on the camel host
85
- // prop. Keep the kebab attribute on every target; spread dataSet
86
- // only when TAMAGUI_TARGET is native.
87
- {...(process.env.TAMAGUI_TARGET === "native"
88
- ? ({ dataSet: { codeFont: "mono" } } as object)
89
- : {})}
90
84
  />
91
85
  );
92
86
  });