@multiplatform.one/forms 6.4.3 → 6.6.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 (320) hide show
  1. package/dist/cjs/Button/index.cjs +23 -8
  2. package/dist/cjs/Button/index.native.js +23 -8
  3. package/dist/cjs/Button/index.native.js.map +1 -1
  4. package/dist/cjs/Skeleton/index.cjs +1 -0
  5. package/dist/cjs/Skeleton/index.native.js +4 -0
  6. package/dist/cjs/Skeleton/index.native.js.map +1 -1
  7. package/dist/cjs/fields/Checkbox/Checkbox.cjs +22 -15
  8. package/dist/cjs/fields/Checkbox/Checkbox.native.js +11 -6
  9. package/dist/cjs/fields/Checkbox/Checkbox.native.js.map +1 -1
  10. package/dist/cjs/fields/Checkbox/index.cjs +9 -3
  11. package/dist/cjs/fields/Checkbox/index.native.js +9 -3
  12. package/dist/cjs/fields/Checkbox/index.native.js.map +1 -1
  13. package/dist/cjs/fields/Combobox/index.cjs +9 -4
  14. package/dist/cjs/fields/Combobox/index.native.js +9 -4
  15. package/dist/cjs/fields/Combobox/index.native.js.map +1 -1
  16. package/dist/cjs/fields/DatePicker/DateRangePicker.cjs +30 -20
  17. package/dist/cjs/fields/DatePicker/DateRangePicker.native.js +36 -21
  18. package/dist/cjs/fields/DatePicker/DateRangePicker.native.js.map +1 -1
  19. package/dist/cjs/fields/DatePicker/DatetimePicker.cjs +1 -1
  20. package/dist/cjs/fields/DatePicker/DatetimePicker.native.js +1 -1
  21. package/dist/cjs/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  22. package/dist/cjs/fields/DatePicker/MonthPicker.cjs +24 -14
  23. package/dist/cjs/fields/DatePicker/MonthPicker.native.js +36 -15
  24. package/dist/cjs/fields/DatePicker/MonthPicker.native.js.map +1 -1
  25. package/dist/cjs/fields/DatePicker/MultiDatePicker.cjs +27 -18
  26. package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js +32 -19
  27. package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
  28. package/dist/cjs/fields/DatePicker/fieldValueSync.cjs +11 -1
  29. package/dist/cjs/fields/DatePicker/fieldValueSync.native.js +12 -1
  30. package/dist/cjs/fields/DatePicker/fieldValueSync.native.js.map +1 -1
  31. package/dist/cjs/fields/Geolocation/GeolocationMap.native.js +2 -2
  32. package/dist/cjs/fields/RadioGroup/index.cjs +2 -2
  33. package/dist/cjs/fields/RadioGroup/index.native.js +2 -2
  34. package/dist/cjs/fields/RadioGroup/index.native.js.map +1 -1
  35. package/dist/cjs/fields/Rating/index.cjs +17 -2
  36. package/dist/cjs/fields/Rating/index.native.js +25 -2
  37. package/dist/cjs/fields/Rating/index.native.js.map +1 -1
  38. package/dist/cjs/fields/SearchInput/index.cjs +5 -1
  39. package/dist/cjs/fields/SearchInput/index.native.js +5 -1
  40. package/dist/cjs/fields/SearchInput/index.native.js.map +1 -1
  41. package/dist/cjs/fields/Select/index.cjs +6 -10
  42. package/dist/cjs/fields/Select/index.native.js +12 -10
  43. package/dist/cjs/fields/Select/index.native.js.map +1 -1
  44. package/dist/cjs/fields/Signature/index.cjs +1 -1
  45. package/dist/cjs/fields/Signature/index.native.js +1 -1
  46. package/dist/cjs/fields/Signature/index.native.js.map +1 -1
  47. package/dist/cjs/fields/Slider/index.cjs +1 -0
  48. package/dist/cjs/fields/Slider/index.native.js +4 -0
  49. package/dist/cjs/fields/Slider/index.native.js.map +1 -1
  50. package/dist/cjs/fields/TextArea/index.cjs +3 -2
  51. package/dist/cjs/fields/TextArea/index.native.js +5 -2
  52. package/dist/cjs/fields/TextArea/index.native.js.map +1 -1
  53. package/dist/cjs/fields/TimePicker/index.cjs +1 -1
  54. package/dist/cjs/fields/TimePicker/index.native.js +1 -1
  55. package/dist/cjs/fields/TimePicker/index.native.js.map +1 -1
  56. package/dist/cjs/fields/ToggleGroup/index.cjs +6 -22
  57. package/dist/cjs/fields/ToggleGroup/index.native.js +11 -28
  58. package/dist/cjs/fields/ToggleGroup/index.native.js.map +1 -1
  59. package/dist/cjs/shared/TimeColumn.cjs +1 -1
  60. package/dist/cjs/shared/TimeColumn.native.js +1 -1
  61. package/dist/cjs/shared/TimeColumn.native.js.map +1 -1
  62. package/dist/cjs/shared/focusManagement.cjs +7 -1
  63. package/dist/cjs/shared/focusManagement.native.js +5 -1
  64. package/dist/cjs/shared/focusManagement.native.js.map +1 -1
  65. package/dist/cjs/shared/t.cjs +5 -0
  66. package/dist/cjs/shared/t.native.js +5 -0
  67. package/dist/cjs/shared/t.native.js.map +1 -1
  68. package/dist/esm/Button/index.mjs +24 -9
  69. package/dist/esm/Button/index.mjs.map +1 -1
  70. package/dist/esm/Button/index.native.js +24 -9
  71. package/dist/esm/Button/index.native.js.map +1 -1
  72. package/dist/esm/Skeleton/index.mjs +2 -1
  73. package/dist/esm/Skeleton/index.mjs.map +1 -1
  74. package/dist/esm/Skeleton/index.native.js +5 -1
  75. package/dist/esm/Skeleton/index.native.js.map +1 -1
  76. package/dist/esm/fields/Checkbox/Checkbox.mjs +24 -17
  77. package/dist/esm/fields/Checkbox/Checkbox.mjs.map +1 -1
  78. package/dist/esm/fields/Checkbox/Checkbox.native.js +13 -8
  79. package/dist/esm/fields/Checkbox/Checkbox.native.js.map +1 -1
  80. package/dist/esm/fields/Checkbox/index.mjs +11 -5
  81. package/dist/esm/fields/Checkbox/index.mjs.map +1 -1
  82. package/dist/esm/fields/Checkbox/index.native.js +11 -5
  83. package/dist/esm/fields/Checkbox/index.native.js.map +1 -1
  84. package/dist/esm/fields/Combobox/index.mjs +9 -4
  85. package/dist/esm/fields/Combobox/index.mjs.map +1 -1
  86. package/dist/esm/fields/Combobox/index.native.js +9 -4
  87. package/dist/esm/fields/Combobox/index.native.js.map +1 -1
  88. package/dist/esm/fields/DatePicker/DateRangePicker.mjs +31 -21
  89. package/dist/esm/fields/DatePicker/DateRangePicker.mjs.map +1 -1
  90. package/dist/esm/fields/DatePicker/DateRangePicker.native.js +37 -22
  91. package/dist/esm/fields/DatePicker/DateRangePicker.native.js.map +1 -1
  92. package/dist/esm/fields/DatePicker/DatetimePicker.mjs +1 -1
  93. package/dist/esm/fields/DatePicker/DatetimePicker.mjs.map +1 -1
  94. package/dist/esm/fields/DatePicker/DatetimePicker.native.js +1 -1
  95. package/dist/esm/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  96. package/dist/esm/fields/DatePicker/MonthPicker.mjs +25 -15
  97. package/dist/esm/fields/DatePicker/MonthPicker.mjs.map +1 -1
  98. package/dist/esm/fields/DatePicker/MonthPicker.native.js +37 -16
  99. package/dist/esm/fields/DatePicker/MonthPicker.native.js.map +1 -1
  100. package/dist/esm/fields/DatePicker/MultiDatePicker.mjs +28 -19
  101. package/dist/esm/fields/DatePicker/MultiDatePicker.mjs.map +1 -1
  102. package/dist/esm/fields/DatePicker/MultiDatePicker.native.js +33 -20
  103. package/dist/esm/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
  104. package/dist/esm/fields/DatePicker/fieldValueSync.mjs +10 -1
  105. package/dist/esm/fields/DatePicker/fieldValueSync.mjs.map +1 -1
  106. package/dist/esm/fields/DatePicker/fieldValueSync.native.js +11 -1
  107. package/dist/esm/fields/DatePicker/fieldValueSync.native.js.map +1 -1
  108. package/dist/esm/fields/Geolocation/GeolocationMap.native.js +2 -2
  109. package/dist/esm/fields/RadioGroup/index.mjs +2 -2
  110. package/dist/esm/fields/RadioGroup/index.mjs.map +1 -1
  111. package/dist/esm/fields/RadioGroup/index.native.js +2 -2
  112. package/dist/esm/fields/RadioGroup/index.native.js.map +1 -1
  113. package/dist/esm/fields/Rating/index.mjs +17 -2
  114. package/dist/esm/fields/Rating/index.mjs.map +1 -1
  115. package/dist/esm/fields/Rating/index.native.js +25 -2
  116. package/dist/esm/fields/Rating/index.native.js.map +1 -1
  117. package/dist/esm/fields/SearchInput/index.mjs +6 -2
  118. package/dist/esm/fields/SearchInput/index.mjs.map +1 -1
  119. package/dist/esm/fields/SearchInput/index.native.js +6 -2
  120. package/dist/esm/fields/SearchInput/index.native.js.map +1 -1
  121. package/dist/esm/fields/Select/index.mjs +6 -10
  122. package/dist/esm/fields/Select/index.mjs.map +1 -1
  123. package/dist/esm/fields/Select/index.native.js +12 -10
  124. package/dist/esm/fields/Select/index.native.js.map +1 -1
  125. package/dist/esm/fields/Signature/index.mjs +1 -1
  126. package/dist/esm/fields/Signature/index.mjs.map +1 -1
  127. package/dist/esm/fields/Signature/index.native.js +1 -1
  128. package/dist/esm/fields/Signature/index.native.js.map +1 -1
  129. package/dist/esm/fields/Slider/index.mjs +1 -0
  130. package/dist/esm/fields/Slider/index.mjs.map +1 -1
  131. package/dist/esm/fields/Slider/index.native.js +4 -0
  132. package/dist/esm/fields/Slider/index.native.js.map +1 -1
  133. package/dist/esm/fields/TextArea/index.mjs +3 -2
  134. package/dist/esm/fields/TextArea/index.mjs.map +1 -1
  135. package/dist/esm/fields/TextArea/index.native.js +5 -2
  136. package/dist/esm/fields/TextArea/index.native.js.map +1 -1
  137. package/dist/esm/fields/TimePicker/index.mjs +1 -1
  138. package/dist/esm/fields/TimePicker/index.mjs.map +1 -1
  139. package/dist/esm/fields/TimePicker/index.native.js +1 -1
  140. package/dist/esm/fields/TimePicker/index.native.js.map +1 -1
  141. package/dist/esm/fields/ToggleGroup/index.mjs +7 -23
  142. package/dist/esm/fields/ToggleGroup/index.mjs.map +1 -1
  143. package/dist/esm/fields/ToggleGroup/index.native.js +12 -29
  144. package/dist/esm/fields/ToggleGroup/index.native.js.map +1 -1
  145. package/dist/esm/shared/TimeColumn.mjs +1 -1
  146. package/dist/esm/shared/TimeColumn.mjs.map +1 -1
  147. package/dist/esm/shared/TimeColumn.native.js +1 -1
  148. package/dist/esm/shared/TimeColumn.native.js.map +1 -1
  149. package/dist/esm/shared/focusManagement.mjs +7 -1
  150. package/dist/esm/shared/focusManagement.mjs.map +1 -1
  151. package/dist/esm/shared/focusManagement.native.js +5 -1
  152. package/dist/esm/shared/focusManagement.native.js.map +1 -1
  153. package/dist/esm/shared/t.mjs +5 -1
  154. package/dist/esm/shared/t.mjs.map +1 -1
  155. package/dist/esm/shared/t.native.js +5 -1
  156. package/dist/esm/shared/t.native.js.map +1 -1
  157. package/dist/jsx/Button/index.mjs +24 -9
  158. package/dist/jsx/Button/index.mjs.map +1 -1
  159. package/dist/jsx/Button/index.native.js +23 -8
  160. package/dist/jsx/Button/index.native.js.map +1 -1
  161. package/dist/jsx/Skeleton/index.mjs +2 -1
  162. package/dist/jsx/Skeleton/index.mjs.map +1 -1
  163. package/dist/jsx/Skeleton/index.native.js +4 -0
  164. package/dist/jsx/Skeleton/index.native.js.map +1 -1
  165. package/dist/jsx/fields/Checkbox/Checkbox.mjs +24 -17
  166. package/dist/jsx/fields/Checkbox/Checkbox.mjs.map +1 -1
  167. package/dist/jsx/fields/Checkbox/Checkbox.native.js +11 -6
  168. package/dist/jsx/fields/Checkbox/Checkbox.native.js.map +1 -1
  169. package/dist/jsx/fields/Checkbox/index.mjs +11 -5
  170. package/dist/jsx/fields/Checkbox/index.mjs.map +1 -1
  171. package/dist/jsx/fields/Checkbox/index.native.js +9 -3
  172. package/dist/jsx/fields/Checkbox/index.native.js.map +1 -1
  173. package/dist/jsx/fields/Combobox/index.mjs +9 -4
  174. package/dist/jsx/fields/Combobox/index.mjs.map +1 -1
  175. package/dist/jsx/fields/Combobox/index.native.js +9 -4
  176. package/dist/jsx/fields/Combobox/index.native.js.map +1 -1
  177. package/dist/jsx/fields/DatePicker/DateRangePicker.mjs +31 -21
  178. package/dist/jsx/fields/DatePicker/DateRangePicker.mjs.map +1 -1
  179. package/dist/jsx/fields/DatePicker/DateRangePicker.native.js +36 -21
  180. package/dist/jsx/fields/DatePicker/DateRangePicker.native.js.map +1 -1
  181. package/dist/jsx/fields/DatePicker/DatetimePicker.mjs +1 -1
  182. package/dist/jsx/fields/DatePicker/DatetimePicker.mjs.map +1 -1
  183. package/dist/jsx/fields/DatePicker/DatetimePicker.native.js +1 -1
  184. package/dist/jsx/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  185. package/dist/jsx/fields/DatePicker/MonthPicker.mjs +25 -15
  186. package/dist/jsx/fields/DatePicker/MonthPicker.mjs.map +1 -1
  187. package/dist/jsx/fields/DatePicker/MonthPicker.native.js +36 -15
  188. package/dist/jsx/fields/DatePicker/MonthPicker.native.js.map +1 -1
  189. package/dist/jsx/fields/DatePicker/MultiDatePicker.mjs +28 -19
  190. package/dist/jsx/fields/DatePicker/MultiDatePicker.mjs.map +1 -1
  191. package/dist/jsx/fields/DatePicker/MultiDatePicker.native.js +32 -19
  192. package/dist/jsx/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
  193. package/dist/jsx/fields/DatePicker/fieldValueSync.mjs +10 -1
  194. package/dist/jsx/fields/DatePicker/fieldValueSync.mjs.map +1 -1
  195. package/dist/jsx/fields/DatePicker/fieldValueSync.native.js +12 -1
  196. package/dist/jsx/fields/DatePicker/fieldValueSync.native.js.map +1 -1
  197. package/dist/jsx/fields/Geolocation/GeolocationMap.native.js +2 -2
  198. package/dist/jsx/fields/RadioGroup/index.mjs +2 -2
  199. package/dist/jsx/fields/RadioGroup/index.mjs.map +1 -1
  200. package/dist/jsx/fields/RadioGroup/index.native.js +2 -2
  201. package/dist/jsx/fields/RadioGroup/index.native.js.map +1 -1
  202. package/dist/jsx/fields/Rating/index.mjs +17 -2
  203. package/dist/jsx/fields/Rating/index.mjs.map +1 -1
  204. package/dist/jsx/fields/Rating/index.native.js +25 -2
  205. package/dist/jsx/fields/Rating/index.native.js.map +1 -1
  206. package/dist/jsx/fields/SearchInput/index.mjs +6 -2
  207. package/dist/jsx/fields/SearchInput/index.mjs.map +1 -1
  208. package/dist/jsx/fields/SearchInput/index.native.js +5 -1
  209. package/dist/jsx/fields/SearchInput/index.native.js.map +1 -1
  210. package/dist/jsx/fields/Select/index.mjs +6 -10
  211. package/dist/jsx/fields/Select/index.mjs.map +1 -1
  212. package/dist/jsx/fields/Select/index.native.js +12 -10
  213. package/dist/jsx/fields/Select/index.native.js.map +1 -1
  214. package/dist/jsx/fields/Signature/index.mjs +1 -1
  215. package/dist/jsx/fields/Signature/index.mjs.map +1 -1
  216. package/dist/jsx/fields/Signature/index.native.js +1 -1
  217. package/dist/jsx/fields/Signature/index.native.js.map +1 -1
  218. package/dist/jsx/fields/Slider/index.mjs +1 -0
  219. package/dist/jsx/fields/Slider/index.mjs.map +1 -1
  220. package/dist/jsx/fields/Slider/index.native.js +4 -0
  221. package/dist/jsx/fields/Slider/index.native.js.map +1 -1
  222. package/dist/jsx/fields/TextArea/index.mjs +3 -2
  223. package/dist/jsx/fields/TextArea/index.mjs.map +1 -1
  224. package/dist/jsx/fields/TextArea/index.native.js +5 -2
  225. package/dist/jsx/fields/TextArea/index.native.js.map +1 -1
  226. package/dist/jsx/fields/TimePicker/index.mjs +1 -1
  227. package/dist/jsx/fields/TimePicker/index.mjs.map +1 -1
  228. package/dist/jsx/fields/TimePicker/index.native.js +1 -1
  229. package/dist/jsx/fields/TimePicker/index.native.js.map +1 -1
  230. package/dist/jsx/fields/ToggleGroup/index.mjs +7 -23
  231. package/dist/jsx/fields/ToggleGroup/index.mjs.map +1 -1
  232. package/dist/jsx/fields/ToggleGroup/index.native.js +11 -28
  233. package/dist/jsx/fields/ToggleGroup/index.native.js.map +1 -1
  234. package/dist/jsx/shared/TimeColumn.mjs +1 -1
  235. package/dist/jsx/shared/TimeColumn.mjs.map +1 -1
  236. package/dist/jsx/shared/TimeColumn.native.js +1 -1
  237. package/dist/jsx/shared/TimeColumn.native.js.map +1 -1
  238. package/dist/jsx/shared/focusManagement.mjs +7 -1
  239. package/dist/jsx/shared/focusManagement.mjs.map +1 -1
  240. package/dist/jsx/shared/focusManagement.native.js +5 -1
  241. package/dist/jsx/shared/focusManagement.native.js.map +1 -1
  242. package/dist/jsx/shared/t.mjs +5 -1
  243. package/dist/jsx/shared/t.mjs.map +1 -1
  244. package/dist/jsx/shared/t.native.js +5 -0
  245. package/dist/jsx/shared/t.native.js.map +1 -1
  246. package/package.json +8 -7
  247. package/src/Button/Button.stories.tsx +19 -0
  248. package/src/Button/index.tsx +45 -12
  249. package/src/ControlGroup/ControlGroup.stories.tsx +12 -0
  250. package/src/FieldLayout.stories.tsx +3 -2
  251. package/src/Form/Form.stories.tsx +13 -0
  252. package/src/Skeleton/index.tsx +5 -1
  253. package/src/fields/Checkbox/Checkbox.spec.tsx +50 -1
  254. package/src/fields/Checkbox/Checkbox.tsx +47 -19
  255. package/src/fields/Checkbox/index.tsx +23 -5
  256. package/src/fields/ColorPicker/ColorPicker.stories.tsx +2 -1
  257. package/src/fields/Combobox/index.tsx +8 -3
  258. package/src/fields/DatePicker/DatePicker.spec.tsx +93 -0
  259. package/src/fields/DatePicker/DatePicker.stories.tsx +2 -1
  260. package/src/fields/DatePicker/DateRangePicker.spec.tsx +72 -0
  261. package/src/fields/DatePicker/DateRangePicker.tsx +44 -30
  262. package/src/fields/DatePicker/DatetimePicker.spec.tsx +39 -0
  263. package/src/fields/DatePicker/DatetimePicker.tsx +1 -1
  264. package/src/fields/DatePicker/MonthPicker.spec.tsx +61 -0
  265. package/src/fields/DatePicker/MonthPicker.tsx +47 -21
  266. package/src/fields/DatePicker/MultiDatePicker.spec.tsx +34 -0
  267. package/src/fields/DatePicker/MultiDatePicker.tsx +39 -27
  268. package/src/fields/DatePicker/fieldValueSync.tsx +23 -0
  269. package/src/fields/FileUpload/FileUpload.stories.tsx +11 -1
  270. package/src/fields/Geolocation/Geolocation.stories.tsx +9 -0
  271. package/src/fields/Geolocation/GeolocationMap.native.tsx +2 -2
  272. package/src/fields/ImageField/ImageField.stories.tsx +2 -1
  273. package/src/fields/Input/Input.stories.tsx +19 -0
  274. package/src/fields/Progress/Progress.stories.tsx +19 -0
  275. package/src/fields/RadioGroup/RadioGroup.spec.tsx +139 -2
  276. package/src/fields/RadioGroup/index.tsx +2 -2
  277. package/src/fields/Rating/Rating.spec.tsx +43 -1
  278. package/src/fields/Rating/index.tsx +43 -4
  279. package/src/fields/SearchInput/SearchInput.spec.tsx +11 -0
  280. package/src/fields/SearchInput/index.tsx +6 -2
  281. package/src/fields/Select/Select.stories.tsx +19 -0
  282. package/src/fields/Select/index.tsx +12 -5
  283. package/src/fields/Signature/index.tsx +1 -1
  284. package/src/fields/Slider/Slider.spec.tsx +89 -4
  285. package/src/fields/Slider/Slider.stories.tsx +19 -0
  286. package/src/fields/Slider/index.tsx +4 -0
  287. package/src/fields/Stepper/Stepper.stories.tsx +12 -0
  288. package/src/fields/Switch/Switch.spec.tsx +50 -2
  289. package/src/fields/TextArea/TextArea.spec.tsx +57 -0
  290. package/src/fields/TextArea/TextArea.stories.tsx +19 -0
  291. package/src/fields/TextArea/index.tsx +10 -2
  292. package/src/fields/TimePicker/index.tsx +1 -1
  293. package/src/fields/ToggleGroup/ToggleGroup.spec.tsx +64 -1
  294. package/src/fields/ToggleGroup/ToggleGroup.stories.tsx +19 -0
  295. package/src/fields/ToggleGroup/index.tsx +31 -29
  296. package/src/shared/FloatingListScrollArrows.stories.tsx +4 -2
  297. package/src/shared/TimeColumn.tsx +1 -1
  298. package/src/shared/focusManagement.ts +12 -1
  299. package/src/shared/t.spec.ts +39 -0
  300. package/src/shared/t.ts +20 -0
  301. package/types/Button/index.d.ts +9 -1
  302. package/types/Button/index.d.ts.map +1 -1
  303. package/types/Skeleton/index.d.ts.map +1 -1
  304. package/types/fields/Checkbox/Checkbox.d.ts.map +1 -1
  305. package/types/fields/Checkbox/index.d.ts.map +1 -1
  306. package/types/fields/Combobox/index.d.ts.map +1 -1
  307. package/types/fields/DatePicker/DateRangePicker.d.ts.map +1 -1
  308. package/types/fields/DatePicker/MonthPicker.d.ts.map +1 -1
  309. package/types/fields/DatePicker/MultiDatePicker.d.ts.map +1 -1
  310. package/types/fields/DatePicker/fieldValueSync.d.ts +14 -0
  311. package/types/fields/DatePicker/fieldValueSync.d.ts.map +1 -1
  312. package/types/fields/Rating/index.d.ts.map +1 -1
  313. package/types/fields/SearchInput/index.d.ts.map +1 -1
  314. package/types/fields/Select/index.d.ts.map +1 -1
  315. package/types/fields/Slider/index.d.ts.map +1 -1
  316. package/types/fields/TextArea/index.d.ts.map +1 -1
  317. package/types/fields/ToggleGroup/index.d.ts.map +1 -1
  318. package/types/shared/focusManagement.d.ts.map +1 -1
  319. package/types/shared/t.d.ts +16 -0
  320. package/types/shared/t.d.ts.map +1 -1
@@ -311,7 +311,7 @@ const SelectItem = memo(function SelectItem({
311
311
  : formInputColors.background.focus,
312
312
  },
313
313
  })}
314
- {...(ctx.knobProps.transition ? { animation: ctx.knobProps.transition } : undefined)}
314
+ {...(ctx.knobProps.transition ? { transition: ctx.knobProps.transition } : undefined)}
315
315
  >
316
316
  <Text
317
317
  fontFamily={ctx.knobProps.body.fontFamily}
@@ -839,9 +839,14 @@ function FloatingSelectCore({
839
839
  cursor="pointer"
840
840
  disabled={disabled}
841
841
  size={knobProps.sizeToken}
842
+ // LC-54 PRESS-FLOOR: the size knob may shrink chrome (compact → $3
843
+ // = 36px) but the trigger's press target never drops below 44 —
844
+ // same minHeight floor the house Button holds. Without it a compact
845
+ // Select sat 36px beside 44px Inputs/Buttons in one control row.
846
+ minHeight={44}
842
847
  {...(inTableCell
843
- ? // Chromeless in cells, but keep a ≥44px pressable target (SP-FIXED).
844
- { minHeight: 44 }
848
+ ? // Chromeless in cells (the floor above still applies).
849
+ {}
845
850
  : {
846
851
  borderRadius: knobProps.borderRadius.borderRadius,
847
852
  borderWidth: knobProps.borderRadius.borderWidth,
@@ -1257,9 +1262,11 @@ function NativeSelectCore({
1257
1262
  <InputParts.Box
1258
1263
  disabled={disabled}
1259
1264
  size={knobProps.sizeToken}
1265
+ // LC-54 PRESS-FLOOR: ≥44px trigger target at every size (matches
1266
+ // the web trigger branch above).
1267
+ minHeight={44}
1260
1268
  {...(inTableCell
1261
- ? // Chromeless in cells, but keep a ≥44px pressable target (SP-FIXED).
1262
- { minHeight: 44 }
1269
+ ? {}
1263
1270
  : {
1264
1271
  borderRadius: knobProps.borderRadius.borderRadius,
1265
1272
  borderWidth: knobProps.borderRadius.borderWidth,
@@ -454,7 +454,7 @@ function SignatureCanvasImpl({
454
454
  opacity={disabled ? 0.6 : 1}
455
455
  aspectRatio={aspectRatio}
456
456
  minHeight={minHeightPx}
457
- {...(knobProps.transition ? { animation: knobProps.transition } : undefined)}
457
+ {...(knobProps.transition ? { transition: knobProps.transition } : undefined)}
458
458
  aria-required={required || undefined}
459
459
  aria-invalid={hasError || undefined}
460
460
  >
@@ -1,3 +1,4 @@
1
+ import { useState } from "react";
1
2
  import { useForm } from "@tanstack/react-form";
2
3
  import { act, fireEvent, renderHook, waitFor } from "@testing-library/react";
3
4
  import { getSize } from "@tamagui/get-token";
@@ -205,6 +206,76 @@ describe("Slider", () => {
205
206
  });
206
207
  });
207
208
 
209
+ describe("late-arriving value display (create-mode seeding)", () => {
210
+ // The upstream value was fed into tamagui as mount-only `defaultValue`,
211
+ // so a value applied AFTER first paint (create-mode engine seeding,
212
+ // form.setFieldValue, live server merge) never moved the thumb. The
213
+ // slider must run controlled when an upstream value drives it — and
214
+ // adopting a programmatic value must not emit onChange (LC-14: a seeded
215
+ // value is not a user edit).
216
+ const getThumb = (container: ParentNode) =>
217
+ container.querySelector('[role="slider"]') as HTMLElement | null;
218
+
219
+ it("standalone controlled: a value arriving after mount renders, without onChange", async () => {
220
+ const onChange = vi.fn();
221
+ const onValueChange = vi.fn();
222
+ const result = renderWithProviders(
223
+ <Slider label="Progress" value={[25]} onChange={onChange} onValueChange={onValueChange} />,
224
+ );
225
+ expect(getThumb(result.container)?.getAttribute("aria-valuenow")).toBe("25");
226
+
227
+ // Late seed: the upstream doc value arrives after first paint.
228
+ result.rerender(
229
+ <Slider label="Progress" value={[75]} onChange={onChange} onValueChange={onValueChange} />,
230
+ );
231
+ await waitFor(() => {
232
+ expect(getThumb(result.container)?.getAttribute("aria-valuenow")).toBe("75");
233
+ });
234
+ expect(onChange).not.toHaveBeenCalled();
235
+ expect(onValueChange).not.toHaveBeenCalled();
236
+ });
237
+
238
+ it("form-integrated: a field value set after mount renders, without onChange", async () => {
239
+ const onChange = vi.fn();
240
+ let formApi: { setFieldValue: (name: "volume", value: number[]) => void } | undefined;
241
+ const TestComponent = () => {
242
+ const form = useForm({ defaultValues: { volume: [10] } });
243
+ formApi = form as unknown as typeof formApi;
244
+ return (
245
+ <Form form={form}>
246
+ <Slider label="Volume" name="volume" onChange={onChange} />
247
+ </Form>
248
+ );
249
+ };
250
+ const result = renderWithProviders(<TestComponent />);
251
+ expect(getThumb(result.container)?.getAttribute("aria-valuenow")).toBe("10");
252
+
253
+ await act(async () => {
254
+ formApi?.setFieldValue("volume", [60]);
255
+ });
256
+ await waitFor(() => {
257
+ expect(getThumb(result.container)?.getAttribute("aria-valuenow")).toBe("60");
258
+ });
259
+ expect(onChange).not.toHaveBeenCalled();
260
+ });
261
+
262
+ it("uncontrolled standalone (defaultValue only) still moves on keyboard input", async () => {
263
+ const onChange = vi.fn();
264
+ const result = renderWithProviders(
265
+ <Slider label="Free" defaultValue={[40]} onChange={onChange} />,
266
+ );
267
+ const thumb = getThumb(result.container);
268
+ expect(thumb?.getAttribute("aria-valuenow")).toBe("40");
269
+ await act(async () => {
270
+ if (thumb) fireEvent.keyDown(thumb, { key: "ArrowRight" });
271
+ });
272
+ await waitFor(() => {
273
+ expect(getThumb(result.container)?.getAttribute("aria-valuenow")).toBe("41");
274
+ });
275
+ expect(onChange).toHaveBeenCalledWith([41]);
276
+ });
277
+ });
278
+
208
279
  describe("field integration", () => {
209
280
  it("should render error text when provided", () => {
210
281
  const result = renderWithProviders(
@@ -688,10 +759,24 @@ describe("Slider", () => {
688
759
  });
689
760
 
690
761
  it("track click arms the left/transform tween then disarms after the jump window", async () => {
691
- const result = renderWithProviders(
692
- // min > 0 so the degenerate unmeasured-layout pointer value stays truthy
693
- <Slider label="Jump" name="jump" value={[60]} min={10} max={110} />,
694
- );
762
+ // Controlled slider with the standard feedback loop (value + onChange
763
+ // echoing back) a bare static `value` is a frozen controlled input
764
+ // now that the upstream value really drives tamagui.
765
+ const JumpExample = () => {
766
+ const [values, setValues] = useState([60]);
767
+ return (
768
+ // min > 0 so the degenerate unmeasured-layout pointer value stays truthy
769
+ <Slider
770
+ label="Jump"
771
+ name="jump"
772
+ value={values}
773
+ onChange={setValues}
774
+ min={10}
775
+ max={110}
776
+ />
777
+ );
778
+ };
779
+ const result = renderWithProviders(<JumpExample />);
695
780
  const thumb = result.container.querySelector('[role="slider"]') as HTMLElement;
696
781
  const track = getTrack(result.container, thumb);
697
782
  expect(track).toBeDefined();
@@ -199,7 +199,26 @@ export const Interactive: Story = {
199
199
  },
200
200
  };
201
201
 
202
+ /**
203
+ * FOREIGN CONTRAST SPECIMEN (LC-56 STORY-HONESTY): the right column renders the
204
+ * RAW tamagui Slider beside the house Slider strictly so the difference stays
205
+ * visible. Raw primitives here are by design and labeled — knob/contrast probes
206
+ * skip this story via `parameters.foreignContrastSpecimen`.
207
+ */
202
208
  export const Comparison: Story = {
209
+ name: "Foreign contrast specimen (raw tamagui)",
210
+ parameters: {
211
+ // Machine-readable exemption for knob/contrast probes and the LC-56 linter arm.
212
+ foreignContrastSpecimen: true,
213
+ docs: {
214
+ description: {
215
+ story:
216
+ "FOREIGN CONTRAST SPECIMEN (LC-56 STORY-HONESTY). The right column is the raw " +
217
+ "tamagui Slider, rendered for contrast only: it ignores house knobs and control " +
218
+ "recipes by design. Probes asserting house rendering must skip this story.",
219
+ },
220
+ },
221
+ },
203
222
  render: () => {
204
223
  const ComparisonExample = () => {
205
224
  const [customVal, setCustomVal] = useState([50]);
@@ -333,6 +333,10 @@ function SliderControl({
333
333
  const trackActive = (
334
334
  <TamaguiSlider.TrackActive
335
335
  backgroundColor={formControlColors.border}
336
+ // The fill rides the same radius as the track it sits in: without it
337
+ // tamagui's default left a pill fill inside a square track at
338
+ // `borderRadius:none` — an undeclared knob-immune shape (D-09 / LC-60).
339
+ borderRadius={borderRadius}
336
340
  transition={transitionProp}
337
341
  />
338
342
  );
@@ -1,4 +1,5 @@
1
1
  import { action } from "@multiplatform.one/storybook";
2
+ import type { GuardrailSpecimenParameter } from "@multiplatform.one/theme";
2
3
  import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
3
4
  import { useForm } from "@tanstack/react-form";
4
5
  import { useState } from "react";
@@ -94,6 +95,17 @@ export const Basic: Story = {
94
95
  };
95
96
 
96
97
  export const Disabled: Story = {
98
+ parameters: {
99
+ // LC-57 allowlist: this story IS the disabled-dial demo. Stepper has no
100
+ // disabledReason plumbing (component-contract change, tracked separately),
101
+ // so its +/- buttons bare-disable here by design. The warn still fires;
102
+ // the harvester tolerates exactly this code on this story.
103
+ guardrailSpecimen: {
104
+ warns: ["bare-disabled"],
105
+ reason:
106
+ "Disabled-dial specimen: the whole field rests disabled and Stepper has no disabledReason plumbing yet.",
107
+ } satisfies GuardrailSpecimenParameter,
108
+ },
97
109
  args: {
98
110
  label: "Disabled Field",
99
111
  disabled: true,
@@ -1,12 +1,35 @@
1
1
  import { useForm } from "@tanstack/react-form";
2
- import { act, fireEvent, waitFor } from "@testing-library/react";
3
- import { YStack } from "tamagui";
2
+ import { act, fireEvent, render, waitFor } from "@testing-library/react";
3
+ import { TamaguiProvider, YStack, createTamagui } from "tamagui";
4
4
  import { describe, expect, it, vi } from "vitest";
5
5
  import { renderWithProviders } from "@multiplatform.one/test-utils";
6
+ import {
7
+ createThemesBuilder,
8
+ defaultAccentTheme,
9
+ defaultBaseTheme,
10
+ defaultBuilderOptions,
11
+ } from "@multiplatform.one/theme";
12
+ import { configWithoutAnimations } from "@tamagui/config";
13
+ import { animationsCSS } from "@tamagui/config/v5-css";
6
14
  import { Button } from "../../Button";
7
15
  import { Form } from "../../Form";
8
16
  import { Switch } from "./index";
9
17
 
18
+ // House builder themes for the LC-05 mark assertion — created at MODULE
19
+ // scope: tamagui registers a config's theme variables globally at
20
+ // createTamagui time, and a config created after the first render (inside a
21
+ // test body) never resolves its tokens (Progress.spec pattern).
22
+ const houseThemes = createThemesBuilder(
23
+ defaultBaseTheme,
24
+ defaultAccentTheme,
25
+ defaultBuilderOptions,
26
+ ).themes() as Record<string, Record<string, string>>;
27
+ const houseConfig = createTamagui({
28
+ ...configWithoutAnimations,
29
+ animations: animationsCSS,
30
+ themes: houseThemes as any,
31
+ });
32
+
10
33
  describe("Switch", () => {
11
34
  describe("without form context", () => {
12
35
  it("should render with label and helper text correctly", () => {
@@ -315,4 +338,29 @@ describe("Switch", () => {
315
338
  });
316
339
  });
317
340
  });
341
+
342
+ describe("checked track mark (LC-05 ONE-EMPHASIS / UX-002)", () => {
343
+ // The checked Switch track IS a selection mark, so it resolves the shared
344
+ // accent mark token; the unchecked track stays neutral chrome. Stock test
345
+ // themes carry no $accentBackground, so mount the house builder themes
346
+ // for real resolution.
347
+ it("paints the checked track with the accent mark and the unchecked track neutral", () => {
348
+ const { container } = render(
349
+ <TamaguiProvider config={houseConfig} defaultTheme="light" disableInjectCSS>
350
+ <YStack>
351
+ <Switch label="On" name="on" defaultValue={true} />
352
+ <Switch label="Off" name="off" defaultValue={false} />
353
+ </YStack>
354
+ </TamaguiProvider>,
355
+ );
356
+ const switches = Array.from(container.querySelectorAll("[role='switch']"));
357
+ expect(switches.length).toBe(2);
358
+ const on = switches.find((s) => s.getAttribute("aria-checked") === "true") as HTMLElement;
359
+ const off = switches.find((s) => s.getAttribute("aria-checked") === "false") as HTMLElement;
360
+ expect(on).toBeTruthy();
361
+ expect(off).toBeTruthy();
362
+ expect(on.className).toContain("_bg-accentBackg");
363
+ expect(off.className).not.toContain("_bg-accentBackg");
364
+ });
365
+ });
318
366
  });
@@ -182,6 +182,63 @@ describe("TextArea", () => {
182
182
  });
183
183
  });
184
184
 
185
+ describe("late-arriving value display (create-mode seeding)", () => {
186
+ // The DOM textarea itself adopts late `defaultValue` updates while
187
+ // pristine (React DOM keeps the value attribute synced), but `charCount`
188
+ // was mount-only state — a value applied AFTER first paint (create-mode
189
+ // engine seeding, setFieldValue) rendered a stale count. Adopting a
190
+ // programmatic value must not emit onChange (LC-14).
191
+ it("form-integrated: a field value set after mount displays with a live count, without onChange", async () => {
192
+ const onChange = vi.fn();
193
+ const onChangeText = vi.fn();
194
+ let formApi: { setFieldValue: (name: "notes", value: string) => void } | undefined;
195
+ const TestComponent = () => {
196
+ const form = useForm({ defaultValues: { notes: "" } });
197
+ formApi = form as unknown as typeof formApi;
198
+ return (
199
+ <Form form={form}>
200
+ <TextArea
201
+ label="Notes"
202
+ name="notes"
203
+ showCount
204
+ onChange={onChange}
205
+ onChangeText={onChangeText}
206
+ />
207
+ </Form>
208
+ );
209
+ };
210
+ const result = renderWithProviders(<TestComponent />);
211
+ expect(result.findTextElement("0 characters")).toBeDefined();
212
+
213
+ await act(async () => {
214
+ formApi?.setFieldValue("notes", "seeded text");
215
+ });
216
+ await waitFor(() => {
217
+ expect((result.getTextArea() as HTMLTextAreaElement)?.value).toBe("seeded text");
218
+ expect(result.findTextElement("11 characters")).toBeDefined();
219
+ });
220
+ expect(onChange).not.toHaveBeenCalled();
221
+ expect(onChangeText).not.toHaveBeenCalled();
222
+ });
223
+
224
+ it("standalone controlled: a value arriving after mount updates the count, without onChange", async () => {
225
+ const onChangeText = vi.fn();
226
+ const result = renderWithProviders(
227
+ <TextArea label="Notes" value="" showCount onChangeText={onChangeText} />,
228
+ );
229
+ expect(result.findTextElement("0 characters")).toBeDefined();
230
+
231
+ result.rerender(
232
+ <TextArea label="Notes" value="late value" showCount onChangeText={onChangeText} />,
233
+ );
234
+ await waitFor(() => {
235
+ expect((result.getTextArea() as HTMLTextAreaElement)?.value).toBe("late value");
236
+ expect(result.findTextElement("10 characters")).toBeDefined();
237
+ });
238
+ expect(onChangeText).not.toHaveBeenCalled();
239
+ });
240
+ });
241
+
185
242
  describe("with useForm hook directly", () => {
186
243
  it("should handle form submission with useForm", async () => {
187
244
  const onSubmitMock = vi.fn();
@@ -190,7 +190,26 @@ export const AutoResize: Story = {
190
190
  },
191
191
  };
192
192
 
193
+ /**
194
+ * FOREIGN CONTRAST SPECIMEN (LC-56 STORY-HONESTY): the right column renders the
195
+ * RAW tamagui TextArea beside the house TextArea strictly so the difference
196
+ * stays visible. Raw primitives here are by design and labeled — knob/contrast
197
+ * probes skip this story via `parameters.foreignContrastSpecimen`.
198
+ */
193
199
  export const Comparison: Story = {
200
+ name: "Foreign contrast specimen (raw tamagui)",
201
+ parameters: {
202
+ // Machine-readable exemption for knob/contrast probes and the LC-56 linter arm.
203
+ foreignContrastSpecimen: true,
204
+ docs: {
205
+ description: {
206
+ story:
207
+ "FOREIGN CONTRAST SPECIMEN (LC-56 STORY-HONESTY). The right column is the raw " +
208
+ "tamagui TextArea, rendered for contrast only: it ignores house knobs and field " +
209
+ "recipes by design. Probes asserting house rendering must skip this story.",
210
+ },
211
+ },
212
+ },
194
213
  render: () => {
195
214
  const ComparisonExample = () => {
196
215
  const [customVal, setCustomVal] = useState("");
@@ -171,6 +171,14 @@ export function TextArea({
171
171
 
172
172
  const isControlled = value !== undefined;
173
173
 
174
+ // Count from the live value whenever an upstream source drives the field
175
+ // (controlled `value` prop or TanStack field state): the mount-only
176
+ // charCount state froze counts for values applied after first paint
177
+ // (create-mode seeding, setFieldValue). Uncontrolled standalone keeps the
178
+ // keystroke-fed local state (its defaultValue never changes underneath).
179
+ const countFrom = (textValue: string | undefined) =>
180
+ isControlled || !!(resolvedForm && name) ? (textValue ?? "").length : charCount;
181
+
174
182
  const renderTextArea = (
175
183
  textValue: string | undefined,
176
184
  handleChange?: (text: string) => void,
@@ -178,7 +186,7 @@ export function TextArea({
178
186
  hasError?: boolean,
179
187
  ) => (
180
188
  <>
181
- {showCount && countPosition.startsWith("top") && renderCount(charCount)}
189
+ {showCount && countPosition.startsWith("top") && renderCount(countFrom(textValue))}
182
190
  <YStack position="relative">
183
191
  <InputParts.Box
184
192
  height="auto"
@@ -232,7 +240,7 @@ export function TextArea({
232
240
  </InputParts.Box>
233
241
  {renderOverlay?.()}
234
242
  </YStack>
235
- {showCount && countPosition.startsWith("bottom") && renderCount(charCount)}
243
+ {showCount && countPosition.startsWith("bottom") && renderCount(countFrom(textValue))}
236
244
  </>
237
245
  );
238
246
 
@@ -106,7 +106,7 @@ function AmPmColumn({
106
106
  backgroundColor={isSelected ? formInputColors.background.focus : "transparent"}
107
107
  hoverStyle={{ backgroundColor: formInputColors.background.focus }}
108
108
  onPress={() => onChange(period)}
109
- {...(knobProps.transition ? { animation: knobProps.transition } : undefined)}
109
+ {...(knobProps.transition ? { transition: knobProps.transition } : undefined)}
110
110
  >
111
111
  <Text
112
112
  fontSize="$3"
@@ -1,10 +1,52 @@
1
- import { fireEvent, screen } from "@testing-library/react";
1
+ import { fireEvent, render, screen } from "@testing-library/react";
2
2
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
3
  import { renderWithProviders } from "@multiplatform.one/test-utils";
4
+ import {
5
+ createThemesBuilder,
6
+ defaultAccentTheme,
7
+ defaultBaseTheme,
8
+ defaultBuilderOptions,
9
+ } from "@multiplatform.one/theme";
10
+ import { configWithoutAnimations } from "@tamagui/config";
11
+ import { animationsCSS } from "@tamagui/config/v5-css";
12
+ import type { ReactElement } from "react";
13
+ import { TamaguiProvider, createTamagui } from "tamagui";
4
14
  import { __resetDevWarnSeen } from "../../shared/devWarn";
5
15
  import { Form } from "../../Form";
6
16
  import { ToggleGroup } from "./index";
7
17
 
18
+ // The stock test themes carry no accent ramp, so token resolution for the
19
+ // LC-05 selected mark is invisible under renderWithProviders. Mount the house
20
+ // builder themes (the same ones apps run) to assert the real resolved fill —
21
+ // the pattern Progress.spec.tsx established.
22
+ const houseThemes = createThemesBuilder(
23
+ defaultBaseTheme,
24
+ defaultAccentTheme,
25
+ defaultBuilderOptions,
26
+ ).themes() as Record<string, Record<string, string>>;
27
+
28
+ const houseConfig = createTamagui({
29
+ ...configWithoutAnimations,
30
+ animations: animationsCSS,
31
+ themes: houseThemes as any,
32
+ });
33
+
34
+ function renderWithHouseThemes(ui: ReactElement) {
35
+ return render(
36
+ <TamaguiProvider config={houseConfig} defaultTheme="light" disableInjectCSS>
37
+ {ui}
38
+ </TamaguiProvider>,
39
+ );
40
+ }
41
+
42
+ /**
43
+ * First three numeric groups (h, s, l) of an hsla() string — the digit prefix
44
+ * Tamagui embeds in the atomic class it generates for a concrete color value.
45
+ */
46
+ function hslDigits(hsla: string): string {
47
+ return (hsla.match(/\d+/g) ?? []).slice(0, 3).join("");
48
+ }
49
+
8
50
  const options = [
9
51
  { label: "Left", value: "left" },
10
52
  { label: "Center", value: "center" },
@@ -162,4 +204,25 @@ describe("ToggleGroup", () => {
162
204
  expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("bare-disabled"));
163
205
  });
164
206
  });
207
+
208
+ describe("selected mark (LC-05 ONE-EMPHASIS / D-01)", () => {
209
+ it("fills the selected segment with the accent-tinted surface, never the neutral tier", () => {
210
+ const { container } = renderWithHouseThemes(
211
+ <ToggleGroup label="Alignment" options={options} defaultValue="left" />,
212
+ );
213
+ // Segmented-control selection strength is pinned at TINTED (D-01
214
+ // ViewSwitcher rider): the accent ramp's surface tier in a light scheme
215
+ // is its palest step (accent11) — the same channel ViewSwitcher takes.
216
+ const tint = houseThemes.light.accent11;
217
+ expect(tint).toBeTruthy();
218
+ const selected = container.querySelector('[data-state="on"]') as HTMLElement;
219
+ const idle = container.querySelector('[data-state="off"]') as HTMLElement;
220
+ expect(selected).toBeTruthy();
221
+ expect(idle).toBeTruthy();
222
+ expect(selected.className).toContain(`_bg-hsla${hslDigits(tint)}`);
223
+ expect(idle.className).not.toContain(`_bg-hsla${hslDigits(tint)}`);
224
+ // Ink marks selection nowhere: the old neutral $color8 fill is gone.
225
+ expect(selected.className).not.toContain("_bg-color8");
226
+ });
227
+ });
165
228
  });
@@ -252,7 +252,26 @@ const alignmentOptions = [
252
252
  { label: "Right", value: "right" },
253
253
  ];
254
254
 
255
+ /**
256
+ * FOREIGN CONTRAST SPECIMEN (LC-56 STORY-HONESTY): the right column renders the
257
+ * RAW tamagui ToggleGroup beside the house ToggleGroup strictly so the
258
+ * difference stays visible. Raw primitives here are by design and labeled —
259
+ * knob/contrast probes skip this story via `parameters.foreignContrastSpecimen`.
260
+ */
255
261
  export const Comparison: Story = {
262
+ name: "Foreign contrast specimen (raw tamagui)",
263
+ parameters: {
264
+ // Machine-readable exemption for knob/contrast probes and the LC-56 linter arm.
265
+ foreignContrastSpecimen: true,
266
+ docs: {
267
+ description: {
268
+ story:
269
+ "FOREIGN CONTRAST SPECIMEN (LC-56 STORY-HONESTY). The right column is the raw " +
270
+ "tamagui ToggleGroup, rendered for contrast only: it ignores house knobs and " +
271
+ "control recipes by design. Probes asserting house rendering must skip this story.",
272
+ },
273
+ },
274
+ },
256
275
  render: () => {
257
276
  const ComparisonExample = () => {
258
277
  const [customVal, setCustomVal] = useState("left");
@@ -4,6 +4,7 @@ import {
4
4
  ensureFocusVisibleRing,
5
5
  ensureKeyboardModalityTracking,
6
6
  keyboardFocusRingProps,
7
+ useAccentTintedSurface,
7
8
  useReadableTextOn,
8
9
  wasKeyboardFocus,
9
10
  } from "@multiplatform.one/theme";
@@ -30,6 +31,11 @@ import {
30
31
  useResolvedValidators,
31
32
  } from "../../shared/utils";
32
33
 
34
+ // NOTE: no `active` variant here — the selected fill is runtime-resolved
35
+ // (accent-tinted surface, LC-05/D-01), so it rides direct props re-applied
36
+ // through hover/press. A static variant would expand at its key position and
37
+ // paint its stale value OVER those direct props (the exact drift the LC-40
38
+ // wash comment below records for the disabled tier).
33
39
  const ToggleItemFrame = styled(TamaguiToggleGroup.Item, {
34
40
  cursor: "pointer",
35
41
  userSelect: "none",
@@ -56,20 +62,6 @@ const ToggleItemFrame = styled(TamaguiToggleGroup.Item, {
56
62
  },
57
63
  },
58
64
  },
59
- active: {
60
- true: {
61
- backgroundColor: formButtonColors.border.active,
62
- borderColor: formButtonColors.border.active,
63
- hoverStyle: {
64
- backgroundColor: formButtonColors.border.active,
65
- borderColor: formButtonColors.border.active,
66
- },
67
- pressStyle: {
68
- backgroundColor: formButtonColors.border.active,
69
- borderColor: formButtonColors.border.active,
70
- },
71
- },
72
- },
73
65
  } as const,
74
66
  });
75
67
 
@@ -244,11 +236,19 @@ export function ToggleGroup({
244
236
  id: idProp,
245
237
  compact,
246
238
  });
247
- // UX-018 / Axiom 15: the selected segment fills with `$color8`
248
- // (formButtonColors.border.active). Under a bright tint the label inherits
249
- // the scheme `$color` (near-white in dark) → 1.24:1 on bright yellow. The
250
- // readable label is the paper/ink anchor that reads on the actual fill.
251
- const onSelectedLabel = useReadableTextOn("$color8");
239
+ // LC-05 / D-01: selection is ONE accent language across every choice-control
240
+ // family this selected segment filled with the neutral `$color8` tier, the
241
+ // catalog's last ink/neutral-marked selection surface. Segmented-control
242
+ // STRENGTH is pinned at TINTED (the D-01 ViewSwitcher rider not the solid
243
+ // an accent CTA takes), so the fill is the accent ramp's surface tier
244
+ // mirrored by scheme, the same channel ViewSwitcher resolves. When the
245
+ // accent ramp is absent (tint sub-themes) the neutral tier stays as the
246
+ // declared fallback so selection never goes invisible.
247
+ // UX-018 / Axiom 15 kept: the label is the paper/ink anchor luminance-picked
248
+ // for the ACTUAL fill, never an assumed scheme token.
249
+ const selectedTint = useAccentTintedSurface();
250
+ const selectedFill = selectedTint ?? formButtonColors.border.active;
251
+ const onSelectedLabel = useReadableTextOn(selectedFill);
252
252
  // Chromium drops keyboard modality when tamagui's group redirects focus to
253
253
  // an item, so :focus-visible/focusVisibleStyle can miss — paint the ring
254
254
  // manually on keyboard-origin focus (see theme keyboardFocusRing).
@@ -344,9 +344,13 @@ export function ToggleGroup({
344
344
  const itemDisabled = Boolean(disabled || option.disabled);
345
345
  const itemDescribedBy = option.disabled ? itemReasonIdFor(option.disabledReason) : undefined;
346
346
  const isTabStop = index === tabStopIndex;
347
+ // The accent-tinted selected fill (border rides the fill, as the
348
+ // neutral chip's did) — re-applied through hover/press below so the
349
+ // neutral state layer never paints over the selection mark (the LC-05
350
+ // rule the checked Switch track and ViewSwitcher chip already follow).
347
351
  const activeColors = {
348
- backgroundColor: formButtonColors.border.active,
349
- borderColor: formButtonColors.border.active,
352
+ backgroundColor: selectedFill,
353
+ borderColor: selectedFill,
350
354
  };
351
355
 
352
356
  return (
@@ -365,18 +369,16 @@ export function ToggleGroup({
365
369
  paddingHorizontal="$2"
366
370
  borderWidth={knobProps.borderRadius.borderWidth}
367
371
  outlined={knobProps.outlined}
368
- animation={knobProps.transition}
372
+ transition={knobProps.transition}
369
373
  disabled={itemDisabled}
370
374
  {...(itemDescribedBy ? { "aria-describedby": itemDescribedBy } : undefined)}
371
375
  // LC-40 DISABLED-VISIBLE (was DG-ST-03 hand-roll): segments are
372
- // text-bearing chrome — a disabled segment drops the live active
373
- // fill entirely (the `active` variant expands at its key position
374
- // and would beat the wash), so the wash owns the chrome and the
375
- // label pins to the lowest AA tier below. A SELECTED disabled
376
- // segment takes the recipe's stronger selected tier so the chosen
377
- // option stays visible (Axiom 6), not aria-only.
376
+ // text-bearing chrome — a disabled segment drops the live selected
377
+ // fill entirely, so the wash owns the chrome and the label pins to
378
+ // the lowest AA tier below. A SELECTED disabled segment takes the
379
+ // recipe's stronger selected tier so the chosen option stays
380
+ // visible (Axiom 6), not aria-only.
378
381
  {...(selected && !itemDisabled ? activeColors : undefined)}
379
- {...(selected && !itemDisabled ? { active: true, activeStyle: activeColors } : undefined)}
380
382
  {...(itemDisabled
381
383
  ? selected
382
384
  ? disabledState.selectedSurfaceKnobProps
@@ -15,7 +15,9 @@ const manyOptions = Array.from({ length: 60 }, (_, i) => ({
15
15
  label: `Option ${i + 1}`,
16
16
  }));
17
17
 
18
- const fewOptions = Array.from({ length: 3 }, (_, i) => ({
18
+ // Six entries: short enough that the floating list never scrolls (the story's
19
+ // point) while clearing the ≤5 `select-too-few-options` guardrail (DG-CTRL-01).
20
+ const fewOptions = Array.from({ length: 6 }, (_, i) => ({
19
21
  value: `option-${i + 1}`,
20
22
  label: `Option ${i + 1}`,
21
23
  }));
@@ -45,7 +47,7 @@ export const ShortSelect: Story = {
45
47
  <YStack maxWidth={320}>
46
48
  <Select
47
49
  id="fls-short"
48
- label="Three options"
50
+ label="Six options"
49
51
  options={fewOptions}
50
52
  helperText="No arrows expected"
51
53
  />