@multiplatform.one/components 7.8.0 → 7.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (452) hide show
  1. package/README.md +98 -0
  2. package/dist/cjs/Accordion/index.cjs +3 -3
  3. package/dist/cjs/Accordion/index.native.js +4 -4
  4. package/dist/cjs/Accordion/index.native.js.map +1 -1
  5. package/dist/cjs/Chip/index.cjs +1 -1
  6. package/dist/cjs/Chip/index.native.js +1 -1
  7. package/dist/cjs/Chip/index.native.js.map +1 -1
  8. package/dist/cjs/ContextMenu/index.cjs +3 -3
  9. package/dist/cjs/ContextMenu/index.native.js +3 -3
  10. package/dist/cjs/ContextMenu/index.native.js.map +1 -1
  11. package/dist/cjs/DropdownMenu/index.cjs +3 -3
  12. package/dist/cjs/DropdownMenu/index.native.js +3 -3
  13. package/dist/cjs/DropdownMenu/index.native.js.map +1 -1
  14. package/dist/cjs/Meter/index.cjs +3 -2
  15. package/dist/cjs/Meter/index.native.js +3 -2
  16. package/dist/cjs/Meter/index.native.js.map +1 -1
  17. package/dist/cjs/Meter/meter.cjs +1 -2
  18. package/dist/cjs/Meter/meter.native.js +1 -2
  19. package/dist/cjs/Meter/meter.native.js.map +1 -1
  20. package/dist/cjs/Pagination.cjs +2 -2
  21. package/dist/cjs/Pagination.native.js +2 -2
  22. package/dist/cjs/Pagination.native.js.map +1 -1
  23. package/dist/cjs/ProgressSteps/index.cjs +3 -3
  24. package/dist/cjs/ProgressSteps/index.native.js +3 -3
  25. package/dist/cjs/ProgressSteps/index.native.js.map +1 -1
  26. package/dist/cjs/Quote/index.cjs +1 -2
  27. package/dist/cjs/Quote/index.native.js +1 -2
  28. package/dist/cjs/Quote/index.native.js.map +1 -1
  29. package/dist/cjs/SheetModal/index.cjs +22 -3
  30. package/dist/cjs/SheetModal/index.native.js +50 -10
  31. package/dist/cjs/SheetModal/index.native.js.map +1 -1
  32. package/dist/cjs/Tabs/index.cjs +34 -13
  33. package/dist/cjs/Tabs/index.native.js +38 -13
  34. package/dist/cjs/Tabs/index.native.js.map +1 -1
  35. package/dist/cjs/Toast/index.cjs +4 -5
  36. package/dist/cjs/Toast/index.native.js +4 -5
  37. package/dist/cjs/Toast/index.native.js.map +1 -1
  38. package/dist/cjs/TreeSelect/index.cjs +46 -7
  39. package/dist/cjs/TreeSelect/index.native.js +58 -7
  40. package/dist/cjs/TreeSelect/index.native.js.map +1 -1
  41. package/dist/cjs/charts/PieChart.cjs +1 -2
  42. package/dist/cjs/charts/PieChart.native.js +1 -2
  43. package/dist/cjs/charts/PieChart.native.js.map +1 -1
  44. package/dist/cjs/code/HighlightedCode/index.cjs +1 -1
  45. package/dist/cjs/code/HighlightedCode/index.native.js +1 -1
  46. package/dist/cjs/code/HighlightedCode/index.native.js.map +1 -1
  47. package/dist/cjs/componentColors.cjs +6 -4
  48. package/dist/cjs/componentColors.native.js +6 -4
  49. package/dist/cjs/componentColors.native.js.map +1 -1
  50. package/dist/cjs/images/Image/ImageFrame.cjs +4 -2
  51. package/dist/cjs/images/Image/ImageFrame.native.js +4 -2
  52. package/dist/cjs/images/Image/ImageFrame.native.js.map +1 -1
  53. package/dist/cjs/layouts/AsyncBoundary.cjs +1 -2
  54. package/dist/cjs/layouts/AsyncBoundary.native.js +1 -2
  55. package/dist/cjs/layouts/AsyncBoundary.native.js.map +1 -1
  56. package/dist/cjs/layouts/EmptyState.cjs +6 -2
  57. package/dist/cjs/layouts/EmptyState.native.js +10 -6
  58. package/dist/cjs/layouts/EmptyState.native.js.map +1 -1
  59. package/dist/cjs/layouts/attachments.cjs +4 -4
  60. package/dist/cjs/layouts/attachments.native.js +4 -4
  61. package/dist/cjs/layouts/attachments.native.js.map +1 -1
  62. package/dist/cjs/layouts/breadcrumbs.cjs +1 -2
  63. package/dist/cjs/layouts/breadcrumbs.native.js +1 -2
  64. package/dist/cjs/layouts/breadcrumbs.native.js.map +1 -1
  65. package/dist/cjs/layouts/page.cjs +2 -2
  66. package/dist/cjs/layouts/page.native.js +2 -2
  67. package/dist/cjs/layouts/page.native.js.map +1 -1
  68. package/dist/cjs/layouts/sidebar.cjs +2 -2
  69. package/dist/cjs/layouts/sidebar.native.js +2 -2
  70. package/dist/cjs/layouts/sidebar.native.js.map +1 -1
  71. package/dist/cjs/layouts/tags.cjs +6 -7
  72. package/dist/cjs/layouts/tags.native.js +6 -9
  73. package/dist/cjs/layouts/tags.native.js.map +1 -1
  74. package/dist/cjs/layouts/timeline.cjs +5 -5
  75. package/dist/cjs/layouts/timeline.native.js +5 -5
  76. package/dist/cjs/layouts/timeline.native.js.map +1 -1
  77. package/dist/cjs/surfaces.cjs +1 -0
  78. package/dist/cjs/surfaces.native.js +1 -0
  79. package/dist/cjs/surfaces.native.js.map +1 -1
  80. package/dist/cjs/views/Calendar.cjs +11 -8
  81. package/dist/cjs/views/Calendar.native.js +11 -8
  82. package/dist/cjs/views/Calendar.native.js.map +1 -1
  83. package/dist/cjs/views/Dashboard.cjs +6 -6
  84. package/dist/cjs/views/Dashboard.native.js +7 -7
  85. package/dist/cjs/views/Dashboard.native.js.map +1 -1
  86. package/dist/cjs/views/Gantt.cjs +3 -3
  87. package/dist/cjs/views/Gantt.native.js +3 -3
  88. package/dist/cjs/views/Gantt.native.js.map +1 -1
  89. package/dist/cjs/views/Kanban.cjs +2 -2
  90. package/dist/cjs/views/Kanban.native.js +2 -2
  91. package/dist/cjs/views/Kanban.native.js.map +1 -1
  92. package/dist/cjs/views/ReportBuilder.cjs +4 -4
  93. package/dist/cjs/views/ReportBuilder.native.js +4 -4
  94. package/dist/cjs/views/ReportBuilder.native.js.map +1 -1
  95. package/dist/cjs/views/TreeView.cjs +248 -19
  96. package/dist/cjs/views/TreeView.native.js +264 -20
  97. package/dist/cjs/views/TreeView.native.js.map +1 -1
  98. package/dist/cjs/views/treeViewReveal.cjs +55 -0
  99. package/dist/cjs/views/treeViewReveal.native.js +80 -0
  100. package/dist/cjs/views/treeViewReveal.native.js.map +1 -0
  101. package/dist/esm/Accordion/index.mjs +3 -3
  102. package/dist/esm/Accordion/index.mjs.map +1 -1
  103. package/dist/esm/Accordion/index.native.js +4 -4
  104. package/dist/esm/Accordion/index.native.js.map +1 -1
  105. package/dist/esm/Chip/index.mjs +1 -1
  106. package/dist/esm/Chip/index.mjs.map +1 -1
  107. package/dist/esm/Chip/index.native.js +1 -1
  108. package/dist/esm/Chip/index.native.js.map +1 -1
  109. package/dist/esm/ContextMenu/index.mjs +3 -3
  110. package/dist/esm/ContextMenu/index.mjs.map +1 -1
  111. package/dist/esm/ContextMenu/index.native.js +3 -3
  112. package/dist/esm/ContextMenu/index.native.js.map +1 -1
  113. package/dist/esm/DropdownMenu/index.mjs +3 -3
  114. package/dist/esm/DropdownMenu/index.mjs.map +1 -1
  115. package/dist/esm/DropdownMenu/index.native.js +3 -3
  116. package/dist/esm/DropdownMenu/index.native.js.map +1 -1
  117. package/dist/esm/Meter/index.mjs +3 -2
  118. package/dist/esm/Meter/index.mjs.map +1 -1
  119. package/dist/esm/Meter/index.native.js +3 -2
  120. package/dist/esm/Meter/index.native.js.map +1 -1
  121. package/dist/esm/Meter/meter.mjs +1 -2
  122. package/dist/esm/Meter/meter.mjs.map +1 -1
  123. package/dist/esm/Meter/meter.native.js +1 -2
  124. package/dist/esm/Meter/meter.native.js.map +1 -1
  125. package/dist/esm/Pagination.mjs +2 -2
  126. package/dist/esm/Pagination.mjs.map +1 -1
  127. package/dist/esm/Pagination.native.js +2 -2
  128. package/dist/esm/Pagination.native.js.map +1 -1
  129. package/dist/esm/ProgressSteps/index.mjs +3 -3
  130. package/dist/esm/ProgressSteps/index.mjs.map +1 -1
  131. package/dist/esm/ProgressSteps/index.native.js +3 -3
  132. package/dist/esm/ProgressSteps/index.native.js.map +1 -1
  133. package/dist/esm/Quote/index.mjs +1 -2
  134. package/dist/esm/Quote/index.mjs.map +1 -1
  135. package/dist/esm/Quote/index.native.js +1 -2
  136. package/dist/esm/Quote/index.native.js.map +1 -1
  137. package/dist/esm/SheetModal/index.mjs +22 -3
  138. package/dist/esm/SheetModal/index.mjs.map +1 -1
  139. package/dist/esm/SheetModal/index.native.js +51 -11
  140. package/dist/esm/SheetModal/index.native.js.map +1 -1
  141. package/dist/esm/Tabs/index.mjs +34 -13
  142. package/dist/esm/Tabs/index.mjs.map +1 -1
  143. package/dist/esm/Tabs/index.native.js +38 -13
  144. package/dist/esm/Tabs/index.native.js.map +1 -1
  145. package/dist/esm/Toast/index.mjs +4 -5
  146. package/dist/esm/Toast/index.mjs.map +1 -1
  147. package/dist/esm/Toast/index.native.js +4 -5
  148. package/dist/esm/Toast/index.native.js.map +1 -1
  149. package/dist/esm/TreeSelect/index.mjs +46 -7
  150. package/dist/esm/TreeSelect/index.mjs.map +1 -1
  151. package/dist/esm/TreeSelect/index.native.js +58 -7
  152. package/dist/esm/TreeSelect/index.native.js.map +1 -1
  153. package/dist/esm/charts/PieChart.mjs +1 -2
  154. package/dist/esm/charts/PieChart.mjs.map +1 -1
  155. package/dist/esm/charts/PieChart.native.js +1 -2
  156. package/dist/esm/charts/PieChart.native.js.map +1 -1
  157. package/dist/esm/code/HighlightedCode/index.mjs +1 -1
  158. package/dist/esm/code/HighlightedCode/index.mjs.map +1 -1
  159. package/dist/esm/code/HighlightedCode/index.native.js +1 -1
  160. package/dist/esm/code/HighlightedCode/index.native.js.map +1 -1
  161. package/dist/esm/componentColors.mjs +6 -4
  162. package/dist/esm/componentColors.mjs.map +1 -1
  163. package/dist/esm/componentColors.native.js +6 -4
  164. package/dist/esm/componentColors.native.js.map +1 -1
  165. package/dist/esm/images/Image/ImageFrame.mjs +4 -2
  166. package/dist/esm/images/Image/ImageFrame.mjs.map +1 -1
  167. package/dist/esm/images/Image/ImageFrame.native.js +4 -2
  168. package/dist/esm/images/Image/ImageFrame.native.js.map +1 -1
  169. package/dist/esm/layouts/AsyncBoundary.mjs +1 -2
  170. package/dist/esm/layouts/AsyncBoundary.mjs.map +1 -1
  171. package/dist/esm/layouts/AsyncBoundary.native.js +1 -2
  172. package/dist/esm/layouts/AsyncBoundary.native.js.map +1 -1
  173. package/dist/esm/layouts/EmptyState.mjs +7 -3
  174. package/dist/esm/layouts/EmptyState.mjs.map +1 -1
  175. package/dist/esm/layouts/EmptyState.native.js +11 -7
  176. package/dist/esm/layouts/EmptyState.native.js.map +1 -1
  177. package/dist/esm/layouts/attachments.mjs +4 -4
  178. package/dist/esm/layouts/attachments.mjs.map +1 -1
  179. package/dist/esm/layouts/attachments.native.js +4 -4
  180. package/dist/esm/layouts/attachments.native.js.map +1 -1
  181. package/dist/esm/layouts/breadcrumbs.mjs +1 -2
  182. package/dist/esm/layouts/breadcrumbs.mjs.map +1 -1
  183. package/dist/esm/layouts/breadcrumbs.native.js +1 -2
  184. package/dist/esm/layouts/breadcrumbs.native.js.map +1 -1
  185. package/dist/esm/layouts/page.mjs +2 -2
  186. package/dist/esm/layouts/page.mjs.map +1 -1
  187. package/dist/esm/layouts/page.native.js +2 -2
  188. package/dist/esm/layouts/page.native.js.map +1 -1
  189. package/dist/esm/layouts/sidebar.mjs +2 -2
  190. package/dist/esm/layouts/sidebar.mjs.map +1 -1
  191. package/dist/esm/layouts/sidebar.native.js +2 -2
  192. package/dist/esm/layouts/sidebar.native.js.map +1 -1
  193. package/dist/esm/layouts/tags.mjs +6 -7
  194. package/dist/esm/layouts/tags.mjs.map +1 -1
  195. package/dist/esm/layouts/tags.native.js +6 -9
  196. package/dist/esm/layouts/tags.native.js.map +1 -1
  197. package/dist/esm/layouts/timeline.mjs +5 -5
  198. package/dist/esm/layouts/timeline.mjs.map +1 -1
  199. package/dist/esm/layouts/timeline.native.js +5 -5
  200. package/dist/esm/layouts/timeline.native.js.map +1 -1
  201. package/dist/esm/surfaces.mjs +1 -0
  202. package/dist/esm/surfaces.mjs.map +1 -1
  203. package/dist/esm/surfaces.native.js +1 -0
  204. package/dist/esm/surfaces.native.js.map +1 -1
  205. package/dist/esm/views/Calendar.mjs +11 -8
  206. package/dist/esm/views/Calendar.mjs.map +1 -1
  207. package/dist/esm/views/Calendar.native.js +11 -8
  208. package/dist/esm/views/Calendar.native.js.map +1 -1
  209. package/dist/esm/views/Dashboard.mjs +6 -6
  210. package/dist/esm/views/Dashboard.mjs.map +1 -1
  211. package/dist/esm/views/Dashboard.native.js +7 -7
  212. package/dist/esm/views/Dashboard.native.js.map +1 -1
  213. package/dist/esm/views/Gantt.mjs +3 -3
  214. package/dist/esm/views/Gantt.mjs.map +1 -1
  215. package/dist/esm/views/Gantt.native.js +3 -3
  216. package/dist/esm/views/Gantt.native.js.map +1 -1
  217. package/dist/esm/views/Kanban.mjs +2 -2
  218. package/dist/esm/views/Kanban.mjs.map +1 -1
  219. package/dist/esm/views/Kanban.native.js +2 -2
  220. package/dist/esm/views/Kanban.native.js.map +1 -1
  221. package/dist/esm/views/ReportBuilder.mjs +4 -4
  222. package/dist/esm/views/ReportBuilder.mjs.map +1 -1
  223. package/dist/esm/views/ReportBuilder.native.js +4 -4
  224. package/dist/esm/views/ReportBuilder.native.js.map +1 -1
  225. package/dist/esm/views/TreeView.mjs +249 -20
  226. package/dist/esm/views/TreeView.mjs.map +1 -1
  227. package/dist/esm/views/TreeView.native.js +265 -21
  228. package/dist/esm/views/TreeView.native.js.map +1 -1
  229. package/dist/esm/views/treeViewReveal.mjs +29 -0
  230. package/dist/esm/views/treeViewReveal.mjs.map +1 -0
  231. package/dist/esm/views/treeViewReveal.native.js +51 -0
  232. package/dist/esm/views/treeViewReveal.native.js.map +1 -0
  233. package/dist/jsx/Accordion/index.mjs +3 -3
  234. package/dist/jsx/Accordion/index.mjs.map +1 -1
  235. package/dist/jsx/Accordion/index.native.js +4 -4
  236. package/dist/jsx/Accordion/index.native.js.map +1 -1
  237. package/dist/jsx/Chip/index.mjs +1 -1
  238. package/dist/jsx/Chip/index.mjs.map +1 -1
  239. package/dist/jsx/Chip/index.native.js +1 -1
  240. package/dist/jsx/Chip/index.native.js.map +1 -1
  241. package/dist/jsx/ContextMenu/index.mjs +3 -3
  242. package/dist/jsx/ContextMenu/index.mjs.map +1 -1
  243. package/dist/jsx/ContextMenu/index.native.js +3 -3
  244. package/dist/jsx/ContextMenu/index.native.js.map +1 -1
  245. package/dist/jsx/DropdownMenu/index.mjs +3 -3
  246. package/dist/jsx/DropdownMenu/index.mjs.map +1 -1
  247. package/dist/jsx/DropdownMenu/index.native.js +3 -3
  248. package/dist/jsx/DropdownMenu/index.native.js.map +1 -1
  249. package/dist/jsx/Meter/index.mjs +3 -2
  250. package/dist/jsx/Meter/index.mjs.map +1 -1
  251. package/dist/jsx/Meter/index.native.js +3 -2
  252. package/dist/jsx/Meter/index.native.js.map +1 -1
  253. package/dist/jsx/Meter/meter.mjs +1 -2
  254. package/dist/jsx/Meter/meter.mjs.map +1 -1
  255. package/dist/jsx/Meter/meter.native.js +1 -2
  256. package/dist/jsx/Meter/meter.native.js.map +1 -1
  257. package/dist/jsx/Pagination.mjs +2 -2
  258. package/dist/jsx/Pagination.mjs.map +1 -1
  259. package/dist/jsx/Pagination.native.js +2 -2
  260. package/dist/jsx/Pagination.native.js.map +1 -1
  261. package/dist/jsx/ProgressSteps/index.mjs +3 -3
  262. package/dist/jsx/ProgressSteps/index.mjs.map +1 -1
  263. package/dist/jsx/ProgressSteps/index.native.js +3 -3
  264. package/dist/jsx/ProgressSteps/index.native.js.map +1 -1
  265. package/dist/jsx/Quote/index.mjs +1 -2
  266. package/dist/jsx/Quote/index.mjs.map +1 -1
  267. package/dist/jsx/Quote/index.native.js +1 -2
  268. package/dist/jsx/Quote/index.native.js.map +1 -1
  269. package/dist/jsx/SheetModal/index.mjs +22 -3
  270. package/dist/jsx/SheetModal/index.mjs.map +1 -1
  271. package/dist/jsx/SheetModal/index.native.js +50 -10
  272. package/dist/jsx/SheetModal/index.native.js.map +1 -1
  273. package/dist/jsx/Tabs/index.mjs +34 -13
  274. package/dist/jsx/Tabs/index.mjs.map +1 -1
  275. package/dist/jsx/Tabs/index.native.js +38 -13
  276. package/dist/jsx/Tabs/index.native.js.map +1 -1
  277. package/dist/jsx/Toast/index.mjs +4 -5
  278. package/dist/jsx/Toast/index.mjs.map +1 -1
  279. package/dist/jsx/Toast/index.native.js +4 -5
  280. package/dist/jsx/Toast/index.native.js.map +1 -1
  281. package/dist/jsx/TreeSelect/index.mjs +46 -7
  282. package/dist/jsx/TreeSelect/index.mjs.map +1 -1
  283. package/dist/jsx/TreeSelect/index.native.js +58 -7
  284. package/dist/jsx/TreeSelect/index.native.js.map +1 -1
  285. package/dist/jsx/charts/PieChart.mjs +1 -2
  286. package/dist/jsx/charts/PieChart.mjs.map +1 -1
  287. package/dist/jsx/charts/PieChart.native.js +1 -2
  288. package/dist/jsx/charts/PieChart.native.js.map +1 -1
  289. package/dist/jsx/code/HighlightedCode/index.mjs +1 -1
  290. package/dist/jsx/code/HighlightedCode/index.mjs.map +1 -1
  291. package/dist/jsx/code/HighlightedCode/index.native.js +1 -1
  292. package/dist/jsx/code/HighlightedCode/index.native.js.map +1 -1
  293. package/dist/jsx/componentColors.mjs +6 -4
  294. package/dist/jsx/componentColors.mjs.map +1 -1
  295. package/dist/jsx/componentColors.native.js +6 -4
  296. package/dist/jsx/componentColors.native.js.map +1 -1
  297. package/dist/jsx/images/Image/ImageFrame.mjs +4 -2
  298. package/dist/jsx/images/Image/ImageFrame.mjs.map +1 -1
  299. package/dist/jsx/images/Image/ImageFrame.native.js +4 -2
  300. package/dist/jsx/images/Image/ImageFrame.native.js.map +1 -1
  301. package/dist/jsx/layouts/AsyncBoundary.mjs +1 -2
  302. package/dist/jsx/layouts/AsyncBoundary.mjs.map +1 -1
  303. package/dist/jsx/layouts/AsyncBoundary.native.js +1 -2
  304. package/dist/jsx/layouts/AsyncBoundary.native.js.map +1 -1
  305. package/dist/jsx/layouts/EmptyState.mjs +7 -3
  306. package/dist/jsx/layouts/EmptyState.mjs.map +1 -1
  307. package/dist/jsx/layouts/EmptyState.native.js +10 -6
  308. package/dist/jsx/layouts/EmptyState.native.js.map +1 -1
  309. package/dist/jsx/layouts/attachments.mjs +4 -4
  310. package/dist/jsx/layouts/attachments.mjs.map +1 -1
  311. package/dist/jsx/layouts/attachments.native.js +4 -4
  312. package/dist/jsx/layouts/attachments.native.js.map +1 -1
  313. package/dist/jsx/layouts/breadcrumbs.mjs +1 -2
  314. package/dist/jsx/layouts/breadcrumbs.mjs.map +1 -1
  315. package/dist/jsx/layouts/breadcrumbs.native.js +1 -2
  316. package/dist/jsx/layouts/breadcrumbs.native.js.map +1 -1
  317. package/dist/jsx/layouts/page.mjs +2 -2
  318. package/dist/jsx/layouts/page.mjs.map +1 -1
  319. package/dist/jsx/layouts/page.native.js +2 -2
  320. package/dist/jsx/layouts/page.native.js.map +1 -1
  321. package/dist/jsx/layouts/sidebar.mjs +2 -2
  322. package/dist/jsx/layouts/sidebar.mjs.map +1 -1
  323. package/dist/jsx/layouts/sidebar.native.js +2 -2
  324. package/dist/jsx/layouts/sidebar.native.js.map +1 -1
  325. package/dist/jsx/layouts/tags.mjs +6 -7
  326. package/dist/jsx/layouts/tags.mjs.map +1 -1
  327. package/dist/jsx/layouts/tags.native.js +6 -9
  328. package/dist/jsx/layouts/tags.native.js.map +1 -1
  329. package/dist/jsx/layouts/timeline.mjs +5 -5
  330. package/dist/jsx/layouts/timeline.mjs.map +1 -1
  331. package/dist/jsx/layouts/timeline.native.js +5 -5
  332. package/dist/jsx/layouts/timeline.native.js.map +1 -1
  333. package/dist/jsx/surfaces.mjs +1 -0
  334. package/dist/jsx/surfaces.mjs.map +1 -1
  335. package/dist/jsx/surfaces.native.js +1 -0
  336. package/dist/jsx/surfaces.native.js.map +1 -1
  337. package/dist/jsx/views/Calendar.mjs +11 -8
  338. package/dist/jsx/views/Calendar.mjs.map +1 -1
  339. package/dist/jsx/views/Calendar.native.js +11 -8
  340. package/dist/jsx/views/Calendar.native.js.map +1 -1
  341. package/dist/jsx/views/Dashboard.mjs +6 -6
  342. package/dist/jsx/views/Dashboard.mjs.map +1 -1
  343. package/dist/jsx/views/Dashboard.native.js +7 -7
  344. package/dist/jsx/views/Dashboard.native.js.map +1 -1
  345. package/dist/jsx/views/Gantt.mjs +3 -3
  346. package/dist/jsx/views/Gantt.mjs.map +1 -1
  347. package/dist/jsx/views/Gantt.native.js +3 -3
  348. package/dist/jsx/views/Gantt.native.js.map +1 -1
  349. package/dist/jsx/views/Kanban.mjs +2 -2
  350. package/dist/jsx/views/Kanban.mjs.map +1 -1
  351. package/dist/jsx/views/Kanban.native.js +2 -2
  352. package/dist/jsx/views/Kanban.native.js.map +1 -1
  353. package/dist/jsx/views/ReportBuilder.mjs +4 -4
  354. package/dist/jsx/views/ReportBuilder.mjs.map +1 -1
  355. package/dist/jsx/views/ReportBuilder.native.js +4 -4
  356. package/dist/jsx/views/ReportBuilder.native.js.map +1 -1
  357. package/dist/jsx/views/TreeView.mjs +249 -20
  358. package/dist/jsx/views/TreeView.mjs.map +1 -1
  359. package/dist/jsx/views/TreeView.native.js +264 -20
  360. package/dist/jsx/views/TreeView.native.js.map +1 -1
  361. package/dist/jsx/views/treeViewReveal.mjs +29 -0
  362. package/dist/jsx/views/treeViewReveal.mjs.map +1 -0
  363. package/dist/jsx/views/treeViewReveal.native.js +80 -0
  364. package/dist/jsx/views/treeViewReveal.native.js.map +1 -0
  365. package/package.json +11 -11
  366. package/src/Accordion/index.tsx +4 -6
  367. package/src/Chip/index.tsx +1 -1
  368. package/src/ContextMenu/index.tsx +3 -3
  369. package/src/DropdownMenu/index.tsx +3 -3
  370. package/src/Meter/Meter.spec.tsx +12 -4
  371. package/src/Meter/index.tsx +3 -2
  372. package/src/Meter/meter.ts +6 -1
  373. package/src/Pagination.tsx +2 -2
  374. package/src/ProgressSteps/index.tsx +3 -3
  375. package/src/Quote/index.tsx +1 -3
  376. package/src/SheetModal/SheetModal.fill.spec.tsx +147 -0
  377. package/src/SheetModal/SheetModal.ownedBody.spec.tsx +200 -0
  378. package/src/SheetModal/SheetModal.stories.spec.tsx +23 -2
  379. package/src/SheetModal/SheetModal.stories.tsx +275 -2
  380. package/src/SheetModal/index.native.tsx +50 -6
  381. package/src/SheetModal/index.tsx +40 -3
  382. package/src/Tabs/Tabs.lifecycle.spec.tsx +123 -0
  383. package/src/Tabs/Tabs.measurement.spec.tsx +186 -0
  384. package/src/Tabs/Tabs.stories.tsx +43 -0
  385. package/src/Tabs/index.tsx +44 -15
  386. package/src/Toast/Toast.spec.tsx +120 -48
  387. package/src/Toast/index.tsx +5 -3
  388. package/src/TreeSelect/index.tsx +61 -1
  389. package/src/charts/PieChart.tsx +1 -2
  390. package/src/code/HighlightedCode/index.tsx +1 -1
  391. package/src/componentColors.ts +6 -4
  392. package/src/images/Image/ImageFrame.tsx +2 -2
  393. package/src/layouts/AsyncBoundary.tsx +1 -2
  394. package/src/layouts/EmptyState.spec.tsx +58 -1
  395. package/src/layouts/EmptyState.tsx +11 -7
  396. package/src/layouts/KeyboardAvoidingWrapper/KeyboardAvoidingWrapper.stories.tsx +3 -3
  397. package/src/layouts/PaneScaffold.stories.tsx +3 -8
  398. package/src/layouts/SafeAreaWrapper/SafeAreaWrapper.stories.tsx +3 -3
  399. package/src/layouts/attachments.tsx +4 -4
  400. package/src/layouts/breadcrumbs.tsx +1 -2
  401. package/src/layouts/page.tsx +2 -2
  402. package/src/layouts/sidebar.tsx +2 -2
  403. package/src/layouts/tags-trigger.spec.tsx +32 -0
  404. package/src/layouts/tags.tsx +7 -9
  405. package/src/layouts/timeline.tsx +5 -5
  406. package/src/surfaces.spec.tsx +97 -3
  407. package/src/surfaces.tsx +4 -2
  408. package/src/textAccentSecondary.spec.tsx +187 -0
  409. package/src/views/Calendar.tsx +9 -8
  410. package/src/views/Dashboard.tsx +13 -8
  411. package/src/views/Gantt.tsx +3 -3
  412. package/src/views/Kanban.native.tsx +2 -2
  413. package/src/views/Kanban.tsx +2 -2
  414. package/src/views/ReportBuilder.tsx +4 -4
  415. package/src/views/TreeView.reveal.spec.tsx +174 -0
  416. package/src/views/TreeView.reveal.stories.tsx +249 -0
  417. package/src/views/TreeView.scroll-index.spec.tsx +246 -0
  418. package/src/views/TreeView.scroll.spec.tsx +857 -0
  419. package/src/views/TreeView.spec.tsx +1 -1
  420. package/src/views/TreeView.tsx +353 -29
  421. package/src/views/treeViewReveal.spec.ts +83 -0
  422. package/src/views/treeViewReveal.ts +45 -0
  423. package/src/views/views.spec.tsx +13 -12
  424. package/types/Accordion/index.d.ts.map +1 -1
  425. package/types/Meter/index.d.ts.map +1 -1
  426. package/types/Meter/meter.d.ts +5 -1
  427. package/types/Meter/meter.d.ts.map +1 -1
  428. package/types/Quote/index.d.ts.map +1 -1
  429. package/types/SheetModal/index.d.ts +8 -1
  430. package/types/SheetModal/index.d.ts.map +1 -1
  431. package/types/SheetModal/index.native.d.ts +1 -1
  432. package/types/SheetModal/index.native.d.ts.map +1 -1
  433. package/types/Tabs/index.d.ts.map +1 -1
  434. package/types/Toast/index.d.ts.map +1 -1
  435. package/types/TreeSelect/index.d.ts +9 -1
  436. package/types/TreeSelect/index.d.ts.map +1 -1
  437. package/types/charts/PieChart.d.ts.map +1 -1
  438. package/types/componentColors.d.ts +1 -4
  439. package/types/componentColors.d.ts.map +1 -1
  440. package/types/images/Image/ImageFrame.d.ts.map +1 -1
  441. package/types/layouts/AsyncBoundary.d.ts.map +1 -1
  442. package/types/layouts/EmptyState.d.ts.map +1 -1
  443. package/types/layouts/breadcrumbs.d.ts.map +1 -1
  444. package/types/layouts/tags.d.ts.map +1 -1
  445. package/types/surfaces.d.ts.map +1 -1
  446. package/types/views/Calendar.d.ts.map +1 -1
  447. package/types/views/Dashboard.d.ts +7 -3
  448. package/types/views/Dashboard.d.ts.map +1 -1
  449. package/types/views/TreeView.d.ts +19 -1
  450. package/types/views/TreeView.d.ts.map +1 -1
  451. package/types/views/treeViewReveal.d.ts +11 -0
  452. package/types/views/treeViewReveal.d.ts.map +1 -0
@@ -0,0 +1,186 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import { act, cleanup, screen } from "@testing-library/react";
3
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
4
+ import { Tabs, tabsScrollIntoViewX } from "./index";
5
+
6
+ const observed = vi.hoisted(() => ({ scroll: null as any, interactions: new Map<string, any>() }));
7
+
8
+ // The actual component and primitives render. Only public measurement events
9
+ // and the imperative scroll boundary are controlled; this is not browser proof.
10
+ vi.mock("tamagui", async (importOriginal) => {
11
+ const actual = await importOriginal<any>();
12
+ const React = await import("react");
13
+ return {
14
+ ...actual,
15
+ ScrollView: React.forwardRef((props: any, ref) => {
16
+ observed.scroll = props;
17
+ return React.createElement(actual.ScrollView, { ...props, ref });
18
+ }),
19
+ styled: (...args: any[]) => {
20
+ const Component = actual.styled(...args);
21
+ if (args[0] !== actual.Tabs.Tab) return Component;
22
+ return React.forwardRef((props: any, ref) => {
23
+ observed.interactions.set(props.value, props.onInteraction);
24
+ return React.createElement(Component, { ...props, ref });
25
+ });
26
+ },
27
+ };
28
+ });
29
+
30
+ const items = Array.from({ length: 6 }, (_, index) => ({
31
+ value: `tab-${index}`,
32
+ label: `Tab ${index + 1}`,
33
+ }));
34
+ const last = { x: 500, y: 0, width: 100, height: 40 };
35
+
36
+ afterEach(() => {
37
+ cleanup();
38
+ vi.restoreAllMocks();
39
+ observed.scroll = null;
40
+ observed.interactions.clear();
41
+ document.documentElement.removeAttribute("dir");
42
+ });
43
+
44
+ function fixture(mode: "value" | "defaultValue" = "value") {
45
+ const rendered = renderWithProviders(<Tabs items={items} {...{ [mode]: "tab-5" }} />);
46
+ expect(screen.getByRole("tab", { name: "Tab 6" })).toHaveAttribute("aria-selected", "true");
47
+ const host = rendered.container.querySelector('[data-tabs-scroll="true"]') as any;
48
+ expect(typeof host.scrollTo).toBe("function");
49
+ const scroll = vi.spyOn(host, "scrollTo").mockImplementation(() => {});
50
+ const viewport = (width: number) =>
51
+ act(() => observed.scroll.onLayout({ nativeEvent: { layout: { width } } }));
52
+ const content = (width: number) => act(() => observed.scroll.onContentSizeChange(width, 40));
53
+ const active = (layout = last) => act(() => observed.interactions.get("tab-5")("select", layout));
54
+ const manualScroll = (x: number) =>
55
+ act(() => observed.scroll.onScroll({ nativeEvent: { contentOffset: { x } } }));
56
+ return { ...rendered, scroll, viewport, content, active, manualScroll };
57
+ }
58
+
59
+ describe.each(["value", "defaultValue"] as const)(
60
+ "Tabs public measurement ordering (%s)",
61
+ (mode) => {
62
+ it("reveals initial last tab when metrics arrive before its layout (control)", () => {
63
+ const tree = fixture(mode);
64
+ tree.viewport(390);
65
+ tree.content(600);
66
+ tree.active();
67
+ expect(tree.scroll).toHaveBeenCalledWith({ x: 210, animated: true });
68
+ });
69
+
70
+ it("reveals initial last tab when content arrives after its layout", () => {
71
+ const tree = fixture(mode);
72
+ tree.viewport(390);
73
+ tree.active();
74
+ expect(tree.scroll).not.toHaveBeenCalled();
75
+ tree.content(600);
76
+ expect(tree.scroll).toHaveBeenCalled();
77
+ });
78
+
79
+ it("corrects after a viewport shrink with unchanged active-tab bounds", () => {
80
+ const tree = fixture(mode);
81
+ tree.viewport(800);
82
+ tree.content(600);
83
+ tree.active();
84
+ expect(tree.scroll).not.toHaveBeenCalled();
85
+ tree.viewport(390);
86
+ expect(tree.scroll).toHaveBeenCalled();
87
+ });
88
+
89
+ it("does not force a manual scroll back after the active tab was revealed", () => {
90
+ const tree = fixture(mode);
91
+ tree.viewport(390);
92
+ tree.content(600);
93
+ tree.active();
94
+ expect(tree.scroll).toHaveBeenCalledWith({ x: 210, animated: true });
95
+ tree.scroll.mockClear();
96
+ tree.manualScroll(0);
97
+ expect(tree.scroll).not.toHaveBeenCalled();
98
+ });
99
+ },
100
+ );
101
+
102
+ it.each(["value", "defaultValue"] as const)(
103
+ "reveals after late viewport measurement (%s)",
104
+ (mode) => {
105
+ const tree = fixture(mode);
106
+ tree.content(600);
107
+ tree.active();
108
+ expect(tree.scroll).not.toHaveBeenCalled();
109
+ tree.viewport(390);
110
+ expect(tree.scroll).toHaveBeenCalledExactlyOnceWith({ x: 210, animated: true });
111
+ },
112
+ );
113
+
114
+ it("retries after a hidden viewport becomes usable and ignores identical measurements", () => {
115
+ const tree = fixture();
116
+ tree.viewport(0);
117
+ tree.content(600);
118
+ tree.active();
119
+ expect(tree.scroll).not.toHaveBeenCalled();
120
+ tree.viewport(390);
121
+ expect(tree.scroll).toHaveBeenCalledExactlyOnceWith({ x: 210, animated: true });
122
+ tree.manualScroll(210);
123
+ tree.scroll.mockClear();
124
+ tree.viewport(390);
125
+ tree.content(600);
126
+ tree.active();
127
+ expect(tree.scroll).not.toHaveBeenCalled();
128
+ tree.viewport(800);
129
+ expect(tree.scroll).not.toHaveBeenCalled();
130
+ });
131
+
132
+ it("rechecks changed content width without fighting subsequent manual scroll", () => {
133
+ const tree = fixture();
134
+ tree.viewport(390);
135
+ tree.content(390);
136
+ tree.active();
137
+ expect(tree.scroll).not.toHaveBeenCalled();
138
+ tree.content(600);
139
+ expect(tree.scroll).toHaveBeenCalledExactlyOnceWith({ x: 210, animated: true });
140
+ tree.manualScroll(210);
141
+ tree.scroll.mockClear();
142
+ tree.content(620);
143
+ expect(tree.scroll).toHaveBeenCalledExactlyOnceWith({ x: 230, animated: true });
144
+ tree.manualScroll(230);
145
+ tree.scroll.mockClear();
146
+ tree.manualScroll(0);
147
+ tree.active();
148
+ tree.viewport(390);
149
+ tree.content(620);
150
+ expect(tree.scroll).not.toHaveBeenCalled();
151
+ tree.content(650);
152
+ expect(tree.scroll).toHaveBeenCalledExactlyOnceWith({ x: 242, animated: true });
153
+ });
154
+
155
+ it("waits for the latest selected tab's layout when dimensions were pending", () => {
156
+ const tree = fixture();
157
+ tree.viewport(390);
158
+ tree.active();
159
+ tree.rerender(<Tabs items={items} value="tab-0" />);
160
+ tree.content(600);
161
+ expect(tree.scroll).not.toHaveBeenCalled();
162
+ act(() => observed.interactions.get("tab-0")("select", { ...last, x: 0 }));
163
+ expect(tree.scroll).not.toHaveBeenCalled();
164
+ expect(screen.getByRole("tab", { name: "Tab 1" })).toHaveAttribute("aria-selected", "true");
165
+ });
166
+
167
+ it("uses the negative RTL offset range after delayed dimensions", () => {
168
+ document.documentElement.setAttribute("dir", "rtl");
169
+ const tree = fixture();
170
+ tree.viewport(390);
171
+ tree.active({ ...last, x: 0 });
172
+ tree.content(600);
173
+ expect(tree.scroll).toHaveBeenCalledExactlyOnceWith({ x: -210, animated: true });
174
+ tree.manualScroll(-210);
175
+ tree.scroll.mockClear();
176
+ tree.manualScroll(0);
177
+ expect(tree.scroll).not.toHaveBeenCalled();
178
+ });
179
+
180
+ it.each([0, -1, Number.NaN, Number.POSITIVE_INFINITY])(
181
+ "does not scroll with unusable width %s",
182
+ (width) => {
183
+ expect(tabsScrollIntoViewX(last, 0, width, 600, "ltr")).toBeUndefined();
184
+ expect(tabsScrollIntoViewX({ ...last, width }, 0, 390, 600, "ltr")).toBeUndefined();
185
+ },
186
+ );
@@ -362,3 +362,46 @@ export const EqualSmall: Story = {
362
362
  </YStack>
363
363
  ),
364
364
  };
365
+
366
+ export const InitialSixthActive: Story = {
367
+ args: { defaultValue: "section-6", ariaLabel: "Example sections" },
368
+ render: (args) => {
369
+ const [wide, setWide] = React.useState(false);
370
+ const [fullLabels, setFullLabels] = React.useState(true);
371
+ const [hidden, setHidden] = React.useState(false);
372
+ const [mount, setMount] = React.useState(0);
373
+ const items: TabsItem[] = Array.from({ length: 6 }, (_, index) => ({
374
+ value: `section-${index + 1}`,
375
+ label: fullLabels ? `Section ${index + 1} overview` : `${index + 1}`,
376
+ content: (
377
+ <Panel
378
+ title={`Section ${index + 1}`}
379
+ body="The selected section stays visible when the strip is measured or resized."
380
+ />
381
+ ),
382
+ }));
383
+ return (
384
+ <YStack gap="$3">
385
+ <XStack gap="$2" flexWrap="wrap">
386
+ <Button onPress={() => setWide((value) => !value)}>
387
+ {wide ? "Set width to 390px" : "Set width to 800px"}
388
+ </Button>
389
+ <Button onPress={() => setFullLabels((value) => !value)}>
390
+ {fullLabels ? "Use short labels" : "Load full labels"}
391
+ </Button>
392
+ <Button onPress={() => setHidden((value) => !value)}>
393
+ {hidden ? "Show strip" : "Hide strip"}
394
+ </Button>
395
+ <Button onPress={() => setMount((value) => value + 1)}>Reset to sixth tab</Button>
396
+ </XStack>
397
+ <Text color="$color11">
398
+ Use short labels, then load full labels to change content width. Hide and show the strip
399
+ to defer usable measurements. Scroll manually after reveal to check that it stays put.
400
+ </Text>
401
+ <YStack width={wide ? 800 : 390} maxWidth="100%" display={hidden ? "none" : "flex"}>
402
+ <Tabs key={mount} {...args} items={items} />
403
+ </YStack>
404
+ </YStack>
405
+ );
406
+ },
407
+ };
@@ -174,6 +174,14 @@ export function tabsScrollIntoViewX(
174
174
  direction: TabsDirection,
175
175
  pad = 32,
176
176
  ): number | undefined {
177
+ if (
178
+ ![layout.x, layout.width, layout.height, offset, viewport, content].every(Number.isFinite) ||
179
+ layout.width <= 0 ||
180
+ layout.height <= 0 ||
181
+ viewport <= 0 ||
182
+ content <= 0
183
+ )
184
+ return undefined;
177
185
  const range = Math.max(content - viewport, 0);
178
186
  if (range <= 0) return undefined;
179
187
  const minScrollLeft = direction === "rtl" ? -range : 0;
@@ -366,23 +374,28 @@ function TabsRoot({
366
374
  };
367
375
 
368
376
  // Active tab layout (relative to the list) for the sliding indicator
369
- const [activeLayout, setActiveLayout] = React.useState<TabLayout | null>(null);
370
- const handleInteraction = (type: string, layout: TabLayout | null) => {
371
- if (type !== "select" || !layout) return;
377
+ const [activeLayout, setActiveLayout] = React.useState<(TabLayout & { value: string }) | null>(
378
+ null,
379
+ );
380
+ const handleInteraction = (tabValue: string, type: string, layout: TabLayout | null) => {
381
+ if (type !== "select" || !layout || tabValue !== currentValue) return;
372
382
  setActiveLayout((prev) =>
373
383
  prev &&
384
+ prev.value === tabValue &&
374
385
  prev.x === layout.x &&
375
386
  prev.y === layout.y &&
376
387
  prev.width === layout.width &&
377
388
  prev.height === layout.height
378
389
  ? prev
379
- : layout,
390
+ : { ...layout, value: tabValue },
380
391
  );
381
392
  };
382
393
 
383
394
  // Overflow tracking for edge fades + keep-active-visible scrolling
384
395
  const scrollRef = React.useRef<ScrollView>(null);
396
+ const tabRefs = React.useRef(new Map<string, HTMLElement>());
385
397
  const scrollMetrics = React.useRef({ offset: 0, viewport: 0, content: 0 });
398
+ const [sizeRevision, setSizeRevision] = React.useState(0);
386
399
  const [fades, setFades] = React.useState({ left: false, right: false });
387
400
  const updateFades = () => {
388
401
  const { offset, viewport, content } = scrollMetrics.current;
@@ -390,20 +403,38 @@ function TabsRoot({
390
403
  setFades((prev) => (prev.left === next.left && prev.right === next.right ? prev : next));
391
404
  };
392
405
 
406
+ const updateScrollSize = (dimension: "viewport" | "content", size: number) => {
407
+ if (!Object.is(scrollMetrics.current[dimension], size)) {
408
+ scrollMetrics.current[dimension] = size;
409
+ if (isWeb) {
410
+ const selected = tabRefs.current.get(currentValue);
411
+ if (selected && selected.offsetWidth > 0 && selected.offsetHeight > 0) {
412
+ handleInteraction(currentValue, "select", {
413
+ x: selected.offsetLeft,
414
+ y: selected.offsetTop,
415
+ width: selected.offsetWidth,
416
+ height: selected.offsetHeight,
417
+ });
418
+ }
419
+ }
420
+ setSizeRevision((current) => current + 1);
421
+ }
422
+ updateFades();
423
+ };
424
+
393
425
  // Scroll the newly active tab into view when it is clipped
394
426
  React.useEffect(() => {
395
- if (!activeLayout) return;
427
+ if (!activeLayout || activeLayout.value !== currentValue) return;
396
428
  const { offset, viewport, content } = scrollMetrics.current;
397
429
  const target = tabsScrollIntoViewX(activeLayout, offset, viewport, content, direction);
398
- if (target !== undefined) {
430
+ if (target !== undefined && Math.abs(target - offset) >= 1) {
399
431
  scrollRef.current?.scrollTo({ x: target, animated: true });
400
432
  }
401
- }, [activeLayout, direction]);
433
+ }, [activeLayout, currentValue, direction, sizeRevision]);
402
434
 
403
435
  const isUnderline = variant === "underline";
404
436
  const isBand = variant === "band";
405
437
  const equalDistribution = isBand || distribution === "equal";
406
- const tabRefs = React.useRef(new Map<string, HTMLElement>());
407
438
  const onTabKeyDown = (event: React.KeyboardEvent, value: string) => {
408
439
  if (!["Home", "End", "ArrowLeft", "ArrowRight"].includes(event.key)) return;
409
440
  const enabled = items.filter((item) => !disabled && !item.disabled);
@@ -576,7 +607,7 @@ function TabsRoot({
576
607
  accessibilityState: { selected: isActive, disabled: Boolean(isDisabled) },
577
608
  })}
578
609
  hitSlop={tabHitSlop}
579
- onInteraction={handleInteraction}
610
+ onInteraction={(type, layout) => handleInteraction(item.value, type, layout)}
580
611
  cursor={isDisabled ? "not-allowed" : "pointer"}
581
612
  backgroundColor={
582
613
  isBand
@@ -645,12 +676,12 @@ function TabsRoot({
645
676
  fontWeight={!isBand && isActive ? "700" : knobProps.label.fontWeight}
646
677
  color={
647
678
  isDisabled
648
- ? componentColors.text.muted
679
+ ? disabledState.textKnobProps.color
649
680
  : isActive
650
681
  ? variant === "contained" && onSelectedTint
651
682
  ? onSelectedTint
652
683
  : componentColors.text.primary
653
- : componentColors.text.secondary
684
+ : knobProps.textAccentColor
654
685
  }
655
686
  >
656
687
  {wrapBareTextChildren(item.label, (label) => label)}
@@ -716,12 +747,10 @@ function TabsRoot({
716
747
  updateFades();
717
748
  }}
718
749
  onLayout={(e) => {
719
- scrollMetrics.current.viewport = e.nativeEvent.layout.width;
720
- updateFades();
750
+ updateScrollSize("viewport", e.nativeEvent.layout.width);
721
751
  }}
722
752
  onContentSizeChange={(w) => {
723
- scrollMetrics.current.content = w;
724
- updateFades();
753
+ updateScrollSize("content", w);
725
754
  }}
726
755
  >
727
756
  {tabList}
@@ -1,15 +1,19 @@
1
1
  import { fireEvent, screen, act, cleanup } from "@testing-library/react";
2
- import type { ReactElement } from "react";
2
+ import type { ReactElement, ReactNode } from "react";
3
3
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
4
  import { renderWithProviders } from "@multiplatform.one/test-utils";
5
- import { Button, SizableText, Theme, useTheme, YStack } from "tamagui";
5
+ import { Button, SizableText, TamaguiProvider, useTheme, YStack } from "tamagui";
6
6
  import {
7
7
  __resetDevWarnSeen,
8
8
  aaTextContrastRatio,
9
- contrastRatio,
9
+ createDefaultThemeConfig,
10
+ createThemesBuilder,
11
+ defaultAccentTheme,
12
+ defaultBaseTheme,
13
+ defaultBuilderOptions,
14
+ measureContrast,
10
15
  normalizeToHex,
11
16
  Preset,
12
- relativeLuminance,
13
17
  useAccentOnSurface,
14
18
  } from "@multiplatform.one/theme";
15
19
  import { Toast, ToastViewport, useToast, nativeToastA11y, nativeToastControlA11y } from "./index";
@@ -514,35 +518,91 @@ function tokenVal(theme: ReturnType<typeof useTheme>, key: string): string {
514
518
  return typeof raw === "string" ? raw : "";
515
519
  }
516
520
 
517
- function ContrastProbe({ intent }: { intent: "accent" | "success" | "warning" | "error" }) {
518
- const onSurface = useAccentOnSurface();
519
- const theme = useTheme();
520
- const paper = tokenVal(theme, "background") || tokenVal(theme, "color1");
521
- const color11 = tokenVal(theme, "color11");
522
- const fg =
523
- intent === "accent"
524
- ? onSurface.startsWith("$")
525
- ? tokenVal(theme, onSurface.slice(1)) || onSurface
526
- : onSurface
527
- : color11;
521
+ const TOAST_INTENTS = ["accent", "success", "warning", "error"] as const;
522
+ const CONTRAST_SCHEMES = ["light", "dark"] as const;
523
+
524
+ const houseConfig = createDefaultThemeConfig();
525
+ const houseThemes = createThemesBuilder(
526
+ defaultBaseTheme,
527
+ defaultAccentTheme,
528
+ defaultBuilderOptions,
529
+ ).themes() as Record<string, Record<string, string>>;
530
+
531
+ /**
532
+ * The HOUSE config, not the stock one `renderWithProviders` mounts. The
533
+ * intent sub-themes the Toast wraps in (`light_warning`, ...) exist only
534
+ * there; under stock the wrap falls back to the base ramp and the arm
535
+ * certifies a pair the product never paints.
536
+ */
537
+ function ContrastScheme({
538
+ scheme,
539
+ children,
540
+ }: {
541
+ scheme: (typeof CONTRAST_SCHEMES)[number];
542
+ children: ReactNode;
543
+ }) {
528
544
  return (
529
- <>
530
- <span data-testid="contrast-probe" data-fg={fg} data-bg={paper} />
531
- <Toast
532
- title="Archived"
533
- intent={intent}
534
- action={{ label: ACTION, onPress: () => {} }}
535
- onDismiss={() => {}}
536
- />
537
- </>
545
+ <TamaguiProvider config={houseConfig.tamagui} defaultTheme={scheme} disableInjectCSS>
546
+ <YStack backgroundColor="$background">{children}</YStack>
547
+ </TamaguiProvider>
538
548
  );
539
549
  }
540
550
 
541
- function measuredRatio(fg: string, bg: string): number {
542
- const f = normalizeToHex(fg);
543
- const b = normalizeToHex(bg);
544
- if (!f || !b) return Number.NaN;
545
- return contrastRatio(relativeLuminance(f), relativeLuminance(b));
551
+ /**
552
+ * Accent toasts stay on the BASE theme and take their ink from
553
+ * `useAccentOnSurface()` -- a picked value, not a token the label can name in
554
+ * a class. Stamp it from inside the theme the Toast mounts in.
555
+ */
556
+ function AccentInkProbe() {
557
+ const onSurface = useAccentOnSurface();
558
+ const theme = useTheme();
559
+ const ink = onSurface.startsWith("$")
560
+ ? tokenVal(theme, onSurface.slice(1)) || onSurface
561
+ : onSurface;
562
+ return <span data-testid="accent-ink" data-fg={ink} />;
563
+ }
564
+
565
+ /**
566
+ * The RESTING atom for a prefix. Tamagui spells state atoms `_o-0hover-1` /
567
+ * `_bg-0active-color5`, so a bare prefix read picks up hover paint and
568
+ * certifies a surface nobody rests on.
569
+ */
570
+ function restAtom(node: Element, prefix: string): string | undefined {
571
+ for (const cls of Array.from(node.classList)) {
572
+ if (!cls.startsWith(prefix)) continue;
573
+ const value = cls.slice(prefix.length);
574
+ if (/^\d*(hover|active|focus|press|disabled)/.test(value)) continue;
575
+ return value;
576
+ }
577
+ return undefined;
578
+ }
579
+
580
+ /** `_o-0--9` is Tamagui's atom for 0.9; no resting atom means no rest dim. */
581
+ function restOpacity(node: Element): number {
582
+ const atom = restAtom(node, "_o-");
583
+ if (atom === undefined) return 1;
584
+ const value = Number(atom.replaceAll("--", "."));
585
+ return Number.isFinite(value) ? value : 1;
586
+ }
587
+
588
+ /**
589
+ * The ink as PAINTED. An opacity on the press target multiplies the label
590
+ * toward the fill under it, so the token pair is not what a reader sees:
591
+ * MPO-128's warning action measures 4.66:1 as tokens and 3.88:1 as pixels.
592
+ */
593
+ function paintedInk(ink: string, ground: string, alpha: number): string {
594
+ const f = normalizeToHex(ink);
595
+ const b = normalizeToHex(ground);
596
+ if (!f || !b) return ink;
597
+ if (alpha >= 1) return f;
598
+ const channel = (offset: number) =>
599
+ Math.round(
600
+ Number.parseInt(f.slice(offset, offset + 2), 16) * alpha +
601
+ Number.parseInt(b.slice(offset, offset + 2), 16) * (1 - alpha),
602
+ )
603
+ .toString(16)
604
+ .padStart(2, "0");
605
+ return `#${channel(1)}${channel(3)}${channel(5)}`;
546
606
  }
547
607
 
548
608
  describe("Toast overlay shadow (standalone === viewport)", () => {
@@ -694,28 +754,40 @@ describe("Toast action label weight and contrast (TEXT NODE)", () => {
694
754
  expect(screen.getByTestId("toast").contains(textNode)).toBe(true);
695
755
  });
696
756
 
697
- it.each(["accent", "success", "warning", "error"] as const)(
698
- "%s action label clears 4.5:1 vs its pill in light and dark",
757
+ it.each(TOAST_INTENTS)(
758
+ "%s action label clears the AA floor AS PAINTED, in light and dark",
699
759
  (intent) => {
700
- for (const scheme of ["light", "dark"] as const) {
760
+ for (const scheme of CONTRAST_SCHEMES) {
701
761
  renderWithProviders(
702
- <Theme name={scheme}>
703
- <ContrastProbe intent={intent} />
704
- </Theme>,
705
- );
706
- const probe = screen.getByTestId("contrast-probe");
707
- const fg = probe.getAttribute("data-fg") ?? "";
708
- const bg = probe.getAttribute("data-bg") ?? "";
709
- const label = screen.getByText(ACTION);
710
- const pill = label.parentElement as HTMLElement;
711
- const labelColor = (label as HTMLElement).style.color;
712
- const pillBg = (pill as HTMLElement).style.backgroundColor;
713
- const fromProbe = measuredRatio(fg, bg);
714
- const fromNodes = measuredRatio(labelColor || fg, pillBg || bg);
715
- const ratio = Number.isFinite(fromNodes) ? fromNodes : fromProbe;
716
- expect(ratio, `${scheme} ${intent} ${fg} on ${bg}`).toBeGreaterThanOrEqual(
717
- aaTextContrastRatio,
762
+ <ContrastScheme scheme={scheme}>
763
+ <AccentInkProbe />
764
+ <Toast
765
+ title="Archived"
766
+ intent={intent}
767
+ action={{ label: ACTION, onPress: () => {} }}
768
+ onDismiss={() => {}}
769
+ />
770
+ </ContrastScheme>,
718
771
  );
772
+ // Non-accent intents ride their hue sub-theme; accent stays on base.
773
+ const theme = houseThemes[intent === "accent" ? scheme : `${scheme}_${intent}`];
774
+ const groundToken = restAtom(screen.getByTestId("toast"), "_bg-");
775
+ const inkToken = restAtom(screen.getByText(ACTION), "_col-");
776
+ const ground = theme?.[groundToken ?? ""] ?? "";
777
+ const ink =
778
+ intent === "accent"
779
+ ? (screen.getByTestId("accent-ink").getAttribute("data-fg") ?? "")
780
+ : (theme?.[inkToken ?? ""] ?? "");
781
+ const where = `${scheme} ${intent}: ${inkToken ?? "?"} on ${groundToken ?? "?"}`;
782
+ expect(ground, `${where} -- the toast frame paints no resting fill`).toBeTruthy();
783
+ expect(ink, `${where} -- the action label emits no resting ink`).toBeTruthy();
784
+ const alpha = restOpacity(screen.getByLabelText(ACTION));
785
+ const report = measureContrast({
786
+ foreground: paintedInk(ink, ground, alpha),
787
+ background: ground,
788
+ label: `${where} at rest opacity ${alpha}`,
789
+ });
790
+ expect(report.ratio, report.label).toBeGreaterThanOrEqual(aaTextContrastRatio);
719
791
  cleanup();
720
792
  }
721
793
  },
@@ -283,12 +283,14 @@ const ToastActionTarget = styled(View, {
283
283
  alignItems: "center",
284
284
  justifyContent: "center",
285
285
  flexShrink: 0,
286
- opacity: 0.9,
286
+ // No REST dim. The label is $color11 — the lowest AA-safe step of the hue —
287
+ // sitting straight on the toast fill, so it carries no headroom for a
288
+ // multiplier: 0.9 composited every light intent under 4.5 (MPO-128). Hover
289
+ // and press read off the pill's own fill instead.
287
290
  outlineWidth: 0,
288
- hoverStyle: { opacity: 1 },
289
291
  pressStyle: { opacity: 0.95 },
290
292
  focusStyle: { outlineWidth: 0 },
291
- focusVisibleStyle: { opacity: 1, outlineWidth: 0 },
293
+ focusVisibleStyle: { outlineWidth: 0 },
292
294
  });
293
295
 
294
296
  const ToastActionPill = styled(View, {