@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
@@ -32,6 +32,7 @@ import {
32
32
  useDateNavigation,
33
33
  yearPageSize,
34
34
  } from "./parts";
35
+ import { FieldValueSync } from "./fieldValueSync";
35
36
 
36
37
  import type { ViewMode } from "./parts";
37
38
 
@@ -414,19 +415,25 @@ export function MultiDatePicker<
414
415
  setOpen(true);
415
416
  }, [setDisplayDate]);
416
417
 
417
- // Keep standalone/controlled state in sync when the parent value changes.
418
- // Skip setState when the calendar days are unchanged so a new array identity
419
- // from the parent cannot force a render storm.
420
- useEffect(() => {
421
- if (valueProp === undefined) return;
422
- const next = Array.isArray(valueProp) ? valueProp : [];
418
+ // Adopt an externally-applied value (controlled `value` prop or the live
419
+ // TanStack field value) into the display state without emitting onChange
420
+ // (LC-14). Skip setState when the calendar days are unchanged so a new
421
+ // array identity from the parent cannot force a render storm.
422
+ const adoptExternalValue = useCallback((raw: unknown) => {
423
+ const next = Array.isArray(raw) ? (raw as Date[]) : [];
423
424
  const prev = selectedDatesRef.current;
424
425
  const same =
425
426
  prev.length === next.length && prev.every((d, i) => next[i] != null && isSameDay(d, next[i]));
426
427
  if (same) return;
427
428
  selectedDatesRef.current = next;
428
429
  setSelectedDates(next);
429
- }, [valueProp]);
430
+ }, []);
431
+
432
+ // Keep standalone/controlled state in sync when the parent value changes.
433
+ useEffect(() => {
434
+ if (valueProp === undefined) return;
435
+ adoptExternalValue(valueProp);
436
+ }, [valueProp, adoptExternalValue]);
430
437
 
431
438
  const handleToggleDate = useCallback(
432
439
  (date: Date, fieldChange?: (value: any) => void) => {
@@ -560,26 +567,31 @@ export function MultiDatePicker<
560
567
  const resolvedError = getFieldError(field, errorProp);
561
568
 
562
569
  return (
563
- <FieldLayout
564
- id={id}
565
- label={label}
566
- labelProps={labelProps}
567
- error={resolvedError}
568
- helperText={helperText}
569
- required={required}
570
- size={size}
571
- knobProps={knobProps}
572
- // LC-40: see standalone branch — FieldLayout carries dimWhole.
573
- disabled={disabled}
574
- >
575
- <InputParts size={size || knobProps.sizeToken}>
576
- {renderPopover(
577
- !!resolvedError,
578
- mergeFieldHandler(field, "handleBlur", _onBlur),
579
- mergeFieldHandler(field, "handleChange"),
580
- )}
581
- </InputParts>
582
- </FieldLayout>
570
+ <>
571
+ {/* Form branch displays from `selectedDates` — keep it synced to
572
+ the live field value (late seeds / programmatic updates). */}
573
+ <FieldValueSync value={field.state.value} onSync={adoptExternalValue} />
574
+ <FieldLayout
575
+ id={id}
576
+ label={label}
577
+ labelProps={labelProps}
578
+ error={resolvedError}
579
+ helperText={helperText}
580
+ required={required}
581
+ size={size}
582
+ knobProps={knobProps}
583
+ // LC-40: see standalone branch — FieldLayout carries dimWhole.
584
+ disabled={disabled}
585
+ >
586
+ <InputParts size={size || knobProps.sizeToken}>
587
+ {renderPopover(
588
+ !!resolvedError,
589
+ mergeFieldHandler(field, "handleBlur", _onBlur),
590
+ mergeFieldHandler(field, "handleChange"),
591
+ )}
592
+ </InputParts>
593
+ </FieldLayout>
594
+ </>
583
595
  );
584
596
  }}
585
597
  </Field>
@@ -32,3 +32,26 @@ export function DatePickerFieldValueSync({
32
32
  }, [value, onSync]);
33
33
  return null;
34
34
  }
35
+
36
+ /**
37
+ * Shape-agnostic variant for pickers whose display state is not a single
38
+ * Date (MonthPicker's {month,year}, MultiDatePicker's Date[],
39
+ * DateRangePicker's {start,end}): mirrors the live TanStack field value into
40
+ * the picker's adoption callback whenever it changes (including mount, so a
41
+ * Field-level defaultValue displays too). Each picker's `onSync` owns its
42
+ * coercion + identity-preserving equality guard, and must never emit
43
+ * onChange from this path (LC-14 — a programmatic value application is not
44
+ * a user edit).
45
+ */
46
+ export function FieldValueSync({
47
+ value,
48
+ onSync,
49
+ }: {
50
+ value: unknown;
51
+ onSync: (value: unknown) => void;
52
+ }) {
53
+ useEffect(() => {
54
+ onSync(value);
55
+ }, [value, onSync]);
56
+ return null;
57
+ }
@@ -107,6 +107,15 @@ export const Basic: Story = {
107
107
  };
108
108
 
109
109
  export const Disabled: Story = {
110
+ parameters: {
111
+ // LC-57 allowlist: disabled-dial demo; FileUpload's internal upload Button
112
+ // has no disabledReason plumbing yet (component-contract follow-up).
113
+ guardrailSpecimen: {
114
+ warns: ["bare-disabled"],
115
+ reason:
116
+ "Disabled-dial specimen: FileUpload's internal upload Button has no disabledReason plumbing yet.",
117
+ },
118
+ },
110
119
  render: (args) => <FileUpload {...args} />,
111
120
  args: {
112
121
  label: "Disabled Upload",
@@ -119,7 +128,8 @@ export const WithError: Story = {
119
128
  render: (args) => <FileUpload {...args} />,
120
129
  args: {
121
130
  label: "Required File",
122
- error: "Please upload a file",
131
+ // DG-COPY-02: error copy states the rule broken — never "please"/"invalid"/"error".
132
+ error: "A file is required",
123
133
  required: true,
124
134
  },
125
135
  };
@@ -95,6 +95,15 @@ export const Basic: Story = {
95
95
  };
96
96
 
97
97
  export const Disabled: Story = {
98
+ parameters: {
99
+ // LC-57 allowlist: disabled-dial demo; Geolocation's internal action
100
+ // Buttons have no disabledReason plumbing yet (component-contract follow-up).
101
+ guardrailSpecimen: {
102
+ warns: ["bare-disabled"],
103
+ reason:
104
+ "Disabled-dial specimen: Geolocation's internal action Buttons have no disabledReason plumbing yet.",
105
+ },
106
+ },
98
107
  args: {
99
108
  label: "Location",
100
109
  helperText: "This field is disabled",
@@ -36,10 +36,10 @@ function MapFallback({ lat, lng, minHeight }: { lat: number; lng: number; minHei
36
36
  backgroundColor="$backgroundHover"
37
37
  gap="$1"
38
38
  >
39
- <Text color="$colorSubtle" fontSize="$2">
39
+ <Text color="$color11" fontSize="$2">
40
40
  {t("Map preview unavailable — requires expo-maps")}
41
41
  </Text>
42
- <Text color="$colorSubtle" fontSize="$1">
42
+ <Text color="$color11" fontSize="$1">
43
43
  {`${lat.toFixed(4)}, ${lng.toFixed(4)}`}
44
44
  </Text>
45
45
  </YStack>
@@ -135,7 +135,8 @@ export const ReadOnly: Story = {
135
135
  export const WithError: Story = {
136
136
  args: {
137
137
  label: "Required Image",
138
- error: "Please upload an image",
138
+ // DG-COPY-02: error copy states the rule broken — never "please"/"invalid"/"error".
139
+ error: "An image is required",
139
140
  required: true,
140
141
  imageWidth: 64,
141
142
  imageHeight: 64,
@@ -115,7 +115,26 @@ export const SkeletonState: Story = {
115
115
  args: { label: "Full name", skeleton: true },
116
116
  };
117
117
 
118
+ /**
119
+ * FOREIGN CONTRAST SPECIMEN (LC-56 STORY-HONESTY): the right column renders the
120
+ * RAW tamagui Input beside the house Input strictly so the difference stays
121
+ * visible. Raw primitives here are by design and labeled — knob/contrast probes
122
+ * skip this story via `parameters.foreignContrastSpecimen`.
123
+ */
118
124
  export const Comparison: StoryObj = {
125
+ name: "Foreign contrast specimen (raw tamagui)",
126
+ parameters: {
127
+ // Machine-readable exemption for knob/contrast probes and the LC-56 linter arm.
128
+ foreignContrastSpecimen: true,
129
+ docs: {
130
+ description: {
131
+ story:
132
+ "FOREIGN CONTRAST SPECIMEN (LC-56 STORY-HONESTY). The right column is the raw " +
133
+ "tamagui Input, rendered for contrast only: it ignores house knobs and field " +
134
+ "recipes by design. Probes asserting house rendering must skip this story.",
135
+ },
136
+ },
137
+ },
119
138
  render: () => {
120
139
  const ComparisonExample = () => {
121
140
  const [customVal, setCustomVal] = useState("");
@@ -169,7 +169,26 @@ export const Interactive: Story = {
169
169
  },
170
170
  };
171
171
 
172
+ /**
173
+ * FOREIGN CONTRAST SPECIMEN (LC-56 STORY-HONESTY): the right column renders the
174
+ * RAW tamagui Progress beside the house Progress strictly so the difference
175
+ * stays visible. Raw primitives here are by design and labeled — knob/contrast
176
+ * probes skip this story via `parameters.foreignContrastSpecimen`.
177
+ */
172
178
  export const Comparison: Story = {
179
+ name: "Foreign contrast specimen (raw tamagui)",
180
+ parameters: {
181
+ // Machine-readable exemption for knob/contrast probes and the LC-56 linter arm.
182
+ foreignContrastSpecimen: true,
183
+ docs: {
184
+ description: {
185
+ story:
186
+ "FOREIGN CONTRAST SPECIMEN (LC-56 STORY-HONESTY). The right column is the raw " +
187
+ "tamagui Progress, rendered for contrast only: it ignores house knobs and " +
188
+ "recipes by design. Probes asserting house rendering must skip this story.",
189
+ },
190
+ },
191
+ },
173
192
  render: () => (
174
193
  <XStack gap="$6" alignItems="flex-start" padding="$4">
175
194
  <YStack gap="$3" width={300}>
@@ -1,8 +1,16 @@
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 { afterEach, beforeEach, 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 { RadioGroup } from "./index";
@@ -13,6 +21,21 @@ const mockOptions = [
13
21
  { label: "Option 3", value: "option3" },
14
22
  ];
15
23
 
24
+ // House builder themes for LC-05 mark assertions — created at MODULE scope:
25
+ // tamagui registers a config's theme variables globally at createTamagui
26
+ // time, and a config created after the first render (inside a test body)
27
+ // never resolves its tokens (Progress.spec pattern).
28
+ const houseThemes = createThemesBuilder(
29
+ defaultBaseTheme,
30
+ defaultAccentTheme,
31
+ defaultBuilderOptions,
32
+ ).themes() as Record<string, Record<string, string>>;
33
+ const houseConfig = createTamagui({
34
+ ...configWithoutAnimations,
35
+ animations: animationsCSS,
36
+ themes: houseThemes as any,
37
+ });
38
+
16
39
  let consoleErrorSpy: ReturnType<typeof vi.spyOn> | null = null;
17
40
  beforeEach(() => {
18
41
  consoleErrorSpy = vi.spyOn(console, "error").mockImplementation((...args: unknown[]) => {
@@ -266,6 +289,95 @@ describe("RadioGroup Components", () => {
266
289
  });
267
290
  });
268
291
 
292
+ describe("late-arriving value display (create-mode seeding)", () => {
293
+ // The non-card variant fed the current value into tamagui as mount-only
294
+ // `defaultValue`, so a value applied AFTER first paint (create-mode
295
+ // engine seeding lands once meta loads) never displayed. The group must
296
+ // render controlled — and adopting a programmatic value must not emit
297
+ // onChange (LC-14: a seeded value is not a user edit).
298
+ const getRadioByValue = (container: ParentNode, value: string) =>
299
+ [...container.querySelectorAll("[role='radio']")].find((radio) =>
300
+ radio.id.endsWith(`-${value}`),
301
+ );
302
+
303
+ it("standalone controlled: a value arriving after mount renders checked, without onChange", async () => {
304
+ const onChange = vi.fn();
305
+ const onValueChange = vi.fn();
306
+ const result = renderWithProviders(
307
+ <RadioGroup
308
+ label="Status"
309
+ value=""
310
+ options={mockOptions}
311
+ onChange={onChange}
312
+ onValueChange={onValueChange}
313
+ />,
314
+ );
315
+ for (const radio of result.container.querySelectorAll("[role='radio']")) {
316
+ expect(radio.getAttribute("aria-checked")).not.toBe("true");
317
+ }
318
+
319
+ // Late seed: the upstream doc value arrives after first paint.
320
+ result.rerender(
321
+ <RadioGroup
322
+ label="Status"
323
+ value="option2"
324
+ options={mockOptions}
325
+ onChange={onChange}
326
+ onValueChange={onValueChange}
327
+ />,
328
+ );
329
+ await waitFor(() => {
330
+ expect(getRadioByValue(result.container, "option2")?.getAttribute("aria-checked")).toBe(
331
+ "true",
332
+ );
333
+ });
334
+ expect(onChange).not.toHaveBeenCalled();
335
+ expect(onValueChange).not.toHaveBeenCalled();
336
+ });
337
+
338
+ it("form-integrated: a field value set after mount renders checked, without onChange", async () => {
339
+ const onChange = vi.fn();
340
+ let formApi: { setFieldValue: (name: "option", value: string) => void } | undefined;
341
+ const TestComponent = () => {
342
+ const form = useForm({ defaultValues: { option: "" } });
343
+ formApi = form as unknown as typeof formApi;
344
+ return (
345
+ <Form form={form}>
346
+ <RadioGroup label="Status" name="option" options={mockOptions} onChange={onChange} />
347
+ </Form>
348
+ );
349
+ };
350
+ const result = renderWithProviders(<TestComponent />);
351
+
352
+ await act(async () => {
353
+ formApi?.setFieldValue("option", "option3");
354
+ });
355
+ await waitFor(() => {
356
+ expect(getRadioByValue(result.container, "option3")?.getAttribute("aria-checked")).toBe(
357
+ "true",
358
+ );
359
+ });
360
+ expect(onChange).not.toHaveBeenCalled();
361
+ });
362
+
363
+ it("still emits exactly once for a real click after a late seed", async () => {
364
+ const onChange = vi.fn();
365
+ const result = renderWithProviders(
366
+ <RadioGroup label="Status" value="" options={mockOptions} onChange={onChange} />,
367
+ );
368
+ result.rerender(
369
+ <RadioGroup label="Status" value="option1" options={mockOptions} onChange={onChange} />,
370
+ );
371
+ const radio = getRadioByValue(result.container, "option2");
372
+ expect(radio).toBeTruthy();
373
+ await act(async () => {
374
+ if (radio) fireEvent.click(radio);
375
+ });
376
+ expect(onChange).toHaveBeenCalledTimes(1);
377
+ expect(onChange).toHaveBeenCalledWith("option2");
378
+ });
379
+ });
380
+
269
381
  describe("card mode (LC-22 SELECTION-CARD)", () => {
270
382
  const cardOptions = [
271
383
  { label: "Card A", value: "a", description: "Description for A" },
@@ -326,5 +438,30 @@ describe("RadioGroup Components", () => {
326
438
  expect(new Set(ids).size).toBe(2);
327
439
  });
328
440
  });
441
+
442
+ describe("selected mark (LC-05 ONE-EMPHASIS / D-01)", () => {
443
+ it("paints the checked radio's indicator dot with the accent mark, never ink", () => {
444
+ const { container } = render(
445
+ <TamaguiProvider config={houseConfig} defaultTheme="light" disableInjectCSS>
446
+ <RadioGroup label="Choose" value="option2" options={mockOptions} />
447
+ </TamaguiProvider>,
448
+ );
449
+ const checked = container.querySelector('[role="radio"][aria-checked="true"]');
450
+ expect(checked).toBeTruthy();
451
+ // The indicator dot is the selection mark: it fills with the shared
452
+ // accent mark token ($accentBackground), not the ink/label tier.
453
+ const marked = Array.from(checked!.querySelectorAll("*")).filter((el) =>
454
+ (el as HTMLElement).className?.toString().includes("_bg-accentBackg"),
455
+ );
456
+ expect(marked.length).toBeGreaterThan(0);
457
+ // Unchecked radios render no accent mark.
458
+ const unchecked = container.querySelector('[role="radio"][aria-checked="false"]');
459
+ expect(unchecked).toBeTruthy();
460
+ const uncheckedMarked = Array.from(unchecked!.querySelectorAll("*")).filter((el) =>
461
+ (el as HTMLElement).className?.toString().includes("_bg-accentBackg"),
462
+ );
463
+ expect(uncheckedMarked.length).toBe(0);
464
+ });
465
+ });
329
466
  });
330
467
  });
@@ -280,7 +280,7 @@ export function RadioGroup({
280
280
  borderWidth={radioRingWidth(knobProps.borderRadius.borderWidth)}
281
281
  borderRadius={itemRadius}
282
282
  outlined={knobProps.outlined}
283
- {...(knobProps.transition ? { animation: knobProps.transition } : undefined)}
283
+ {...(knobProps.transition ? { transition: knobProps.transition } : undefined)}
284
284
  >
285
285
  <RadioIndicatorFrame
286
286
  borderRadius={indicatorRadius}
@@ -288,7 +288,7 @@ export function RadioGroup({
288
288
  // accent — `textAccentColor` is the LABEL ink tier ($color/$color11)
289
289
  // and made the checked radio read as ink, not as chosen (D-01).
290
290
  backgroundColor={formSelectedColors.mark}
291
- {...(knobProps.transition ? { animation: knobProps.transition } : undefined)}
291
+ {...(knobProps.transition ? { transition: knobProps.transition } : undefined)}
292
292
  />
293
293
  </RadioGlyph>
294
294
  );
@@ -1,6 +1,15 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
- import { fireEvent } from "@testing-library/react";
2
+ import { fireEvent, render } from "@testing-library/react";
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 { TamaguiProvider, createTamagui } from "tamagui";
4
13
  import { Form } from "../../Form";
5
14
  import { Rating } from "./index";
6
15
 
@@ -8,6 +17,21 @@ const MockStarIcon = ({ size, weight, color }: { size: number; weight: string; c
8
17
  <span data-testid="star" data-size={size} data-weight={weight} data-color={color} />
9
18
  );
10
19
 
20
+ // House builder themes for the LC-05 mark assertion — created at MODULE
21
+ // scope: tamagui registers a config's theme variables globally at
22
+ // createTamagui time, and a config created after the first render (inside a
23
+ // test body) never resolves its tokens (Progress.spec pattern).
24
+ const houseThemes = createThemesBuilder(
25
+ defaultBaseTheme,
26
+ defaultAccentTheme,
27
+ defaultBuilderOptions,
28
+ ).themes() as Record<string, Record<string, string>>;
29
+ const houseConfig = createTamagui({
30
+ ...configWithoutAnimations,
31
+ animations: animationsCSS,
32
+ themes: houseThemes as any,
33
+ });
34
+
11
35
  describe("Rating", () => {
12
36
  describe("without form context", () => {
13
37
  it("renders with label", () => {
@@ -97,6 +121,24 @@ describe("Rating", () => {
97
121
  expect(star.getAttribute("aria-disabled")).toBe("true");
98
122
  }
99
123
  });
124
+
125
+ // LC-05 ONE-EMPHASIS / D-01: the FILLED star is the selection mark, so it
126
+ // resolves the shared accent mark token; the empty star stays neutral
127
+ // chrome. Stock test themes carry no $accentBackground, so mount the
128
+ // house builder themes for real resolution.
129
+ it("fills selected stars with the accent mark and empty stars with neutral chrome", () => {
130
+ const { container } = render(
131
+ <TamaguiProvider config={houseConfig} defaultTheme="light" disableInjectCSS>
132
+ <Rating label="Rate" value={0.6} starIcon={MockStarIcon} />
133
+ </TamaguiProvider>,
134
+ );
135
+ const filled = container.querySelector("[data-rating-star='filled'] [data-testid='star']");
136
+ const empty = container.querySelector("[data-rating-star='empty'] [data-testid='star']");
137
+ expect(filled).toBeTruthy();
138
+ expect(empty).toBeTruthy();
139
+ expect(filled!.getAttribute("data-color")).toContain("accentBackground");
140
+ expect(empty!.getAttribute("data-color")).not.toContain("accentBackground");
141
+ });
100
142
  });
101
143
 
102
144
  describe("with form context", () => {
@@ -49,7 +49,10 @@ function PointyStar({
49
49
  <Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
50
50
  <Path
51
51
  d={d}
52
- fill={weight === "fill" ? color : "none"}
52
+ // `transparent` rather than `none`: an empty->filled star has to
53
+ // interpolate its fill (LC-25), and `none` is not a colour so it can
54
+ // only jump. Paints identically.
55
+ fill={weight === "fill" ? color : "transparent"}
53
56
  stroke={color}
54
57
  strokeWidth={1.5}
55
58
  strokeLinejoin="miter"
@@ -80,6 +83,32 @@ function getTransitionStyle(transition: TransitionProp | undefined, live: boolea
80
83
  };
81
84
  }
82
85
 
86
+ // LC-25, star ink: the visible change is the SVG `fill`/`stroke` of the glyph
87
+ // CHILD, and `transition` does not inherit — a declaration on the wrapper can
88
+ // never reach it, while the swappable `starIcon` contract ({ size, weight,
89
+ // color }) has no style passthrough to declare it on. A one-shot descendant
90
+ // stylesheet (Spinner/Wheel pattern) arms the tween on whatever glyph the
91
+ // contract renders, so the contract itself stays untouched.
92
+ const INK_STYLE_ID = "mp1-rating-ink";
93
+ const inkClassName = (token: string) => `mp1-rating-ink-${token}`;
94
+ function ensureRatingInkStyles() {
95
+ if (!isWeb || typeof document === "undefined") return;
96
+ if (document.getElementById(INK_STYLE_ID)) return;
97
+ const style = document.createElement("style");
98
+ style.id = INK_STYLE_ID;
99
+ // Shape elements only, never the <svg> root: a Phosphor-shaped glyph paints
100
+ // the root's `fill` and the shape INHERITS it, so arming both would leave
101
+ // the shape chasing an already-animating value and stretch the tween well
102
+ // past the token.
103
+ style.textContent = Object.entries(cssTimings)
104
+ .map(
105
+ ([token, timing]) =>
106
+ `.${inkClassName(token)} svg * { transition: fill ${timing}, stroke ${timing}; }`,
107
+ )
108
+ .join("\n");
109
+ document.head.appendChild(style);
110
+ }
111
+
83
112
  const StarWrapper = styled(View, {
84
113
  variants: {
85
114
  filled: {
@@ -124,10 +153,19 @@ function StarRow({
124
153
  const [scrubFilled, setScrubFilled] = useState<number | null>(null);
125
154
  const scrubbing = scrubFilled != null;
126
155
  const displayFilled = scrubFilled ?? filledStars;
127
- // LC-25: discrete star jumps tween via knob transition (default "quick").
128
- // During pointer scrub, omit transition so fill tracks the pointer 1:1.
129
- const motionTransition = (knobProps.transition as TransitionProp | undefined) || "quick";
156
+ // LC-25: discrete star jumps tween via knob transition. During pointer
157
+ // scrub, omit transition so fill tracks the pointer 1:1. `transition` is
158
+ // undefined exactly at animation="none" (which prefers-reduced-motion maps
159
+ // onto), and Axiom 3 says that position is NO motion — not a default one.
160
+ const motionTransition = knobProps.transition as TransitionProp | undefined;
130
161
  const live = scrubbing;
162
+ ensureRatingInkStyles();
163
+ const inkToken =
164
+ isWeb && !live && motionTransition
165
+ ? (motionTransition as string) in cssTimings
166
+ ? (motionTransition as string)
167
+ : "quick"
168
+ : undefined;
131
169
 
132
170
  // DG-A11Y keyboard operability: one roving tab stop (ToggleGroup pattern —
133
171
  // a group of N buttons is one Tab entry, arrows move within). The stop
@@ -227,6 +265,7 @@ function StarRow({
227
265
  data-rating-star={isFilled ? "filled" : "empty"}
228
266
  data-rating-index={starNumber}
229
267
  {...(!isWeb && !live ? { transition: motionTransition } : {})}
268
+ {...(inkToken ? { className: inkClassName(inkToken) } : undefined)}
230
269
  style={getTransitionStyle(live ? undefined : motionTransition, live)}
231
270
  >
232
271
  <StarIcon
@@ -21,6 +21,17 @@ describe("SearchInput", () => {
21
21
  expect(result.container.querySelector("svg")).toBeTruthy();
22
22
  });
23
23
 
24
+ it("bidi-isolates the default placeholder (LC-58) and passes explicit ones through", () => {
25
+ // Default: FSI…PDI framing so the trailing "..." cannot reorder to
26
+ // "…Search" under RTL; the visible run stays byte-identical inside.
27
+ const result = renderWithProviders(<SearchInput />);
28
+ const input = result.container.querySelector("input");
29
+ expect(input?.getAttribute("placeholder")).toBe("\u2068Search...\u2069");
30
+ // Explicit placeholders are the consumer's interpolation site — the
31
+ // component must not double-wrap or mutate them (asserted above too).
32
+ expect(input?.getAttribute("placeholder")?.slice(1, -1)).toBe("Search...");
33
+ });
34
+
24
35
  it("fires onChange per keystroke and onSearch after debounce", () => {
25
36
  const onChange = vi.fn();
26
37
  const onSearch = vi.fn();
@@ -12,7 +12,7 @@ import type { ReactNode } from "react";
12
12
  import { useCallback, useEffect, useRef, useState } from "react";
13
13
  import type { SizeTokens } from "tamagui";
14
14
  import { View } from "tamagui";
15
- import { t } from "../../shared/t";
15
+ import { bidiIsolate, t } from "../../shared/t";
16
16
  import { Input } from "../Input";
17
17
  import type { InputProps } from "../Input";
18
18
 
@@ -60,7 +60,11 @@ export function SearchInput({
60
60
  onSearch,
61
61
  onClear,
62
62
  debounceMs = 300,
63
- placeholder = t("Search..."),
63
+ // Bidi-isolated at the interpolation site (LC-58): under RTL the trailing
64
+ // weak "..." otherwise reorders to the start ("…Search"). First-strong
65
+ // isolation keeps a translated RTL placeholder rendering RTL. Consumer
66
+ // placeholders are the consumer's interpolation site — passed through.
67
+ placeholder = bidiIsolate(t("Search...")),
64
68
  clearable = true,
65
69
  disabled,
66
70
  size,
@@ -303,7 +303,26 @@ export const DynamicOptions: StoryObj<typeof Select> = {
303
303
  },
304
304
  };
305
305
 
306
+ /**
307
+ * FOREIGN CONTRAST SPECIMEN (LC-56 STORY-HONESTY): the right column renders the
308
+ * RAW tamagui Select beside the house Select strictly so the difference stays
309
+ * visible. Raw primitives here are by design and labeled — knob/contrast probes
310
+ * skip this story via `parameters.foreignContrastSpecimen`.
311
+ */
306
312
  export const Comparison: StoryObj<typeof Select> = {
313
+ name: "Foreign contrast specimen (raw tamagui)",
314
+ parameters: {
315
+ // Machine-readable exemption for knob/contrast probes and the LC-56 linter arm.
316
+ foreignContrastSpecimen: true,
317
+ docs: {
318
+ description: {
319
+ story:
320
+ "FOREIGN CONTRAST SPECIMEN (LC-56 STORY-HONESTY). The right column is the raw " +
321
+ "tamagui Select, rendered for contrast only: it ignores house knobs and control " +
322
+ "recipes by design. Probes asserting house rendering must skip this story.",
323
+ },
324
+ },
325
+ },
307
326
  render: () => {
308
327
  const ComparisonExample = () => {
309
328
  const [customValue, setCustomValue] = useState<string>();