@multiplatform.one/forms 7.65.2 → 7.67.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 (367) hide show
  1. package/dist/cjs/FloatingPanel/RegularPanel.cjs +164 -0
  2. package/dist/cjs/FloatingPanel/RegularPanel.native.js +169 -0
  3. package/dist/cjs/FloatingPanel/RegularPanel.native.js.map +1 -0
  4. package/dist/cjs/FloatingPanel/compactFace.cjs +6 -0
  5. package/dist/cjs/FloatingPanel/compactFace.native.js +6 -0
  6. package/dist/cjs/FloatingPanel/compactFace.native.js.map +1 -1
  7. package/dist/cjs/FloatingPanel/containingBlock.cjs +46 -0
  8. package/dist/cjs/FloatingPanel/containingBlock.native.js +51 -0
  9. package/dist/cjs/FloatingPanel/containingBlock.native.js.map +1 -0
  10. package/dist/cjs/FloatingPanel/index.cjs +2 -20
  11. package/dist/cjs/FloatingPanel/index.native.js +79 -45
  12. package/dist/cjs/FloatingPanel/index.native.js.map +1 -1
  13. package/dist/cjs/FloatingPanel/pointAnchor.cjs +25 -0
  14. package/dist/cjs/FloatingPanel/pointAnchor.native.js +26 -0
  15. package/dist/cjs/FloatingPanel/pointAnchor.native.js.map +1 -1
  16. package/dist/cjs/FloatingPanel/regularPlacement.cjs +61 -0
  17. package/dist/cjs/FloatingPanel/regularPlacement.native.js +65 -0
  18. package/dist/cjs/FloatingPanel/regularPlacement.native.js.map +1 -0
  19. package/dist/cjs/fields/Combobox/index.cjs +8 -10
  20. package/dist/cjs/fields/Combobox/index.native.js +10 -10
  21. package/dist/cjs/fields/Combobox/index.native.js.map +1 -1
  22. package/dist/cjs/fields/DatePicker/parts.cjs +1 -0
  23. package/dist/cjs/fields/DatePicker/parts.native.js +3 -0
  24. package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
  25. package/dist/cjs/fields/Input/index.cjs +5 -0
  26. package/dist/cjs/fields/Input/index.native.js +5 -0
  27. package/dist/cjs/fields/Input/index.native.js.map +1 -1
  28. package/dist/cjs/fields/Select/InlineChoices.cjs +3 -2
  29. package/dist/cjs/fields/Select/InlineChoices.native.js +5 -2
  30. package/dist/cjs/fields/Select/InlineChoices.native.js.map +1 -1
  31. package/dist/cjs/fields/Select/SheetPickerRow.cjs +154 -0
  32. package/dist/cjs/fields/Select/SheetPickerRow.native.js +165 -0
  33. package/dist/cjs/fields/Select/SheetPickerRow.native.js.map +1 -0
  34. package/dist/cjs/fields/Select/index.cjs +64 -5
  35. package/dist/cjs/fields/Select/index.native.js +86 -27
  36. package/dist/cjs/fields/Select/index.native.js.map +1 -1
  37. package/dist/cjs/fields/Select/shouldInlineChoices.cjs +14 -1
  38. package/dist/cjs/fields/Select/shouldInlineChoices.native.js +15 -1
  39. package/dist/cjs/fields/Select/shouldInlineChoices.native.js.map +1 -1
  40. package/dist/cjs/fields/Slider/index.cjs +96 -4
  41. package/dist/cjs/fields/Slider/index.native.js +103 -5
  42. package/dist/cjs/fields/Slider/index.native.js.map +1 -1
  43. package/dist/cjs/fields/Switch/index.cjs +71 -1
  44. package/dist/cjs/fields/Switch/index.native.js +72 -1
  45. package/dist/cjs/fields/Switch/index.native.js.map +1 -1
  46. package/dist/cjs/fields/TimePicker/parts.cjs +1 -0
  47. package/dist/cjs/fields/TimePicker/parts.native.js +1 -0
  48. package/dist/cjs/fields/TimePicker/parts.native.js.map +1 -1
  49. package/dist/cjs/fields/ToggleGroup/index.cjs +67 -1
  50. package/dist/cjs/fields/ToggleGroup/index.native.js +75 -1
  51. package/dist/cjs/fields/ToggleGroup/index.native.js.map +1 -1
  52. package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.cjs +34 -19
  53. package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js +44 -21
  54. package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
  55. package/dist/cjs/fields/nativeDateTime/sheetKeyboard.cjs +54 -0
  56. package/dist/cjs/fields/nativeDateTime/sheetKeyboard.native.js +57 -0
  57. package/dist/cjs/fields/nativeDateTime/sheetKeyboard.native.js.map +1 -0
  58. package/dist/cjs/index.cjs +11 -0
  59. package/dist/cjs/index.native.js +11 -0
  60. package/dist/cjs/index.native.js.map +1 -1
  61. package/dist/cjs/shared/overlaySizeClass.cjs +42 -0
  62. package/dist/cjs/shared/overlaySizeClass.native.js +45 -0
  63. package/dist/cjs/shared/overlaySizeClass.native.js.map +1 -0
  64. package/dist/cjs/shared/sheetNavigation.cjs +76 -0
  65. package/dist/cjs/shared/sheetNavigation.native.js +129 -0
  66. package/dist/cjs/shared/sheetNavigation.native.js.map +1 -0
  67. package/dist/cjs/shared/systemControlModules.cjs +34 -0
  68. package/dist/cjs/shared/systemControlModules.gnome.cjs +34 -0
  69. package/dist/cjs/shared/systemControlModules.gnome.native.js +37 -0
  70. package/dist/cjs/shared/systemControlModules.gnome.native.js.map +1 -0
  71. package/dist/cjs/shared/systemControlModules.native.js +77 -0
  72. package/dist/cjs/shared/systemControlModules.native.js.map +1 -0
  73. package/dist/cjs/shared/systemControls.cjs +47 -0
  74. package/dist/cjs/shared/systemControls.native.js +51 -0
  75. package/dist/cjs/shared/systemControls.native.js.map +1 -0
  76. package/dist/esm/FloatingPanel/RegularPanel.mjs +139 -0
  77. package/dist/esm/FloatingPanel/RegularPanel.mjs.map +1 -0
  78. package/dist/esm/FloatingPanel/RegularPanel.native.js +141 -0
  79. package/dist/esm/FloatingPanel/RegularPanel.native.js.map +1 -0
  80. package/dist/esm/FloatingPanel/compactFace.mjs +5 -1
  81. package/dist/esm/FloatingPanel/compactFace.mjs.map +1 -1
  82. package/dist/esm/FloatingPanel/compactFace.native.js +5 -1
  83. package/dist/esm/FloatingPanel/compactFace.native.js.map +1 -1
  84. package/dist/esm/FloatingPanel/containingBlock.mjs +21 -0
  85. package/dist/esm/FloatingPanel/containingBlock.mjs.map +1 -0
  86. package/dist/esm/FloatingPanel/containingBlock.native.js +23 -0
  87. package/dist/esm/FloatingPanel/containingBlock.native.js.map +1 -0
  88. package/dist/esm/FloatingPanel/index.mjs +1 -19
  89. package/dist/esm/FloatingPanel/index.mjs.map +1 -1
  90. package/dist/esm/FloatingPanel/index.native.js +83 -49
  91. package/dist/esm/FloatingPanel/index.native.js.map +1 -1
  92. package/dist/esm/FloatingPanel/pointAnchor.mjs +25 -1
  93. package/dist/esm/FloatingPanel/pointAnchor.mjs.map +1 -1
  94. package/dist/esm/FloatingPanel/pointAnchor.native.js +26 -1
  95. package/dist/esm/FloatingPanel/pointAnchor.native.js.map +1 -1
  96. package/dist/esm/FloatingPanel/regularPlacement.mjs +35 -0
  97. package/dist/esm/FloatingPanel/regularPlacement.mjs.map +1 -0
  98. package/dist/esm/FloatingPanel/regularPlacement.native.js +36 -0
  99. package/dist/esm/FloatingPanel/regularPlacement.native.js.map +1 -0
  100. package/dist/esm/fields/Combobox/index.mjs +9 -11
  101. package/dist/esm/fields/Combobox/index.mjs.map +1 -1
  102. package/dist/esm/fields/Combobox/index.native.js +11 -11
  103. package/dist/esm/fields/Combobox/index.native.js.map +1 -1
  104. package/dist/esm/fields/DatePicker/parts.mjs +1 -0
  105. package/dist/esm/fields/DatePicker/parts.mjs.map +1 -1
  106. package/dist/esm/fields/DatePicker/parts.native.js +3 -0
  107. package/dist/esm/fields/DatePicker/parts.native.js.map +1 -1
  108. package/dist/esm/fields/Input/index.mjs +6 -1
  109. package/dist/esm/fields/Input/index.mjs.map +1 -1
  110. package/dist/esm/fields/Input/index.native.js +6 -1
  111. package/dist/esm/fields/Input/index.native.js.map +1 -1
  112. package/dist/esm/fields/Select/InlineChoices.mjs +3 -2
  113. package/dist/esm/fields/Select/InlineChoices.mjs.map +1 -1
  114. package/dist/esm/fields/Select/InlineChoices.native.js +5 -2
  115. package/dist/esm/fields/Select/InlineChoices.native.js.map +1 -1
  116. package/dist/esm/fields/Select/SheetPickerRow.mjs +129 -0
  117. package/dist/esm/fields/Select/SheetPickerRow.mjs.map +1 -0
  118. package/dist/esm/fields/Select/SheetPickerRow.native.js +137 -0
  119. package/dist/esm/fields/Select/SheetPickerRow.native.js.map +1 -0
  120. package/dist/esm/fields/Select/index.mjs +66 -7
  121. package/dist/esm/fields/Select/index.mjs.map +1 -1
  122. package/dist/esm/fields/Select/index.native.js +88 -29
  123. package/dist/esm/fields/Select/index.native.js.map +1 -1
  124. package/dist/esm/fields/Select/shouldInlineChoices.mjs +12 -1
  125. package/dist/esm/fields/Select/shouldInlineChoices.mjs.map +1 -1
  126. package/dist/esm/fields/Select/shouldInlineChoices.native.js +13 -1
  127. package/dist/esm/fields/Select/shouldInlineChoices.native.js.map +1 -1
  128. package/dist/esm/fields/Slider/index.mjs +96 -4
  129. package/dist/esm/fields/Slider/index.mjs.map +1 -1
  130. package/dist/esm/fields/Slider/index.native.js +103 -5
  131. package/dist/esm/fields/Slider/index.native.js.map +1 -1
  132. package/dist/esm/fields/Switch/index.mjs +71 -1
  133. package/dist/esm/fields/Switch/index.mjs.map +1 -1
  134. package/dist/esm/fields/Switch/index.native.js +72 -1
  135. package/dist/esm/fields/Switch/index.native.js.map +1 -1
  136. package/dist/esm/fields/TimePicker/parts.mjs +1 -0
  137. package/dist/esm/fields/TimePicker/parts.mjs.map +1 -1
  138. package/dist/esm/fields/TimePicker/parts.native.js +1 -0
  139. package/dist/esm/fields/TimePicker/parts.native.js.map +1 -1
  140. package/dist/esm/fields/ToggleGroup/index.mjs +67 -1
  141. package/dist/esm/fields/ToggleGroup/index.mjs.map +1 -1
  142. package/dist/esm/fields/ToggleGroup/index.native.js +75 -1
  143. package/dist/esm/fields/ToggleGroup/index.native.js.map +1 -1
  144. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs +36 -21
  145. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -1
  146. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js +46 -23
  147. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
  148. package/dist/esm/fields/nativeDateTime/sheetKeyboard.mjs +21 -0
  149. package/dist/esm/fields/nativeDateTime/sheetKeyboard.mjs.map +1 -0
  150. package/dist/esm/fields/nativeDateTime/sheetKeyboard.native.js +21 -0
  151. package/dist/esm/fields/nativeDateTime/sheetKeyboard.native.js.map +1 -0
  152. package/dist/esm/index.mjs +3 -1
  153. package/dist/esm/index.mjs.map +1 -1
  154. package/dist/esm/index.native.js +3 -1
  155. package/dist/esm/index.native.js.map +1 -1
  156. package/dist/esm/shared/overlaySizeClass.mjs +14 -0
  157. package/dist/esm/shared/overlaySizeClass.mjs.map +1 -0
  158. package/dist/esm/shared/overlaySizeClass.native.js +14 -0
  159. package/dist/esm/shared/overlaySizeClass.native.js.map +1 -0
  160. package/dist/esm/shared/sheetNavigation.mjs +47 -0
  161. package/dist/esm/shared/sheetNavigation.mjs.map +1 -0
  162. package/dist/esm/shared/sheetNavigation.native.js +97 -0
  163. package/dist/esm/shared/sheetNavigation.native.js.map +1 -0
  164. package/dist/esm/shared/systemControlModules.gnome.mjs +8 -0
  165. package/dist/esm/shared/systemControlModules.gnome.mjs.map +1 -0
  166. package/dist/esm/shared/systemControlModules.gnome.native.js +8 -0
  167. package/dist/esm/shared/systemControlModules.gnome.native.js.map +1 -0
  168. package/dist/esm/shared/systemControlModules.mjs +8 -0
  169. package/dist/esm/shared/systemControlModules.mjs.map +1 -0
  170. package/dist/esm/shared/systemControlModules.native.js +47 -0
  171. package/dist/esm/shared/systemControlModules.native.js.map +1 -0
  172. package/dist/esm/shared/systemControls.mjs +19 -0
  173. package/dist/esm/shared/systemControls.mjs.map +1 -0
  174. package/dist/esm/shared/systemControls.native.js +20 -0
  175. package/dist/esm/shared/systemControls.native.js.map +1 -0
  176. package/dist/jsx/FloatingPanel/RegularPanel.mjs +139 -0
  177. package/dist/jsx/FloatingPanel/RegularPanel.mjs.map +1 -0
  178. package/dist/jsx/FloatingPanel/RegularPanel.native.js +169 -0
  179. package/dist/jsx/FloatingPanel/RegularPanel.native.js.map +1 -0
  180. package/dist/jsx/FloatingPanel/compactFace.mjs +5 -1
  181. package/dist/jsx/FloatingPanel/compactFace.mjs.map +1 -1
  182. package/dist/jsx/FloatingPanel/compactFace.native.js +6 -0
  183. package/dist/jsx/FloatingPanel/compactFace.native.js.map +1 -1
  184. package/dist/jsx/FloatingPanel/containingBlock.mjs +21 -0
  185. package/dist/jsx/FloatingPanel/containingBlock.mjs.map +1 -0
  186. package/dist/jsx/FloatingPanel/containingBlock.native.js +51 -0
  187. package/dist/jsx/FloatingPanel/containingBlock.native.js.map +1 -0
  188. package/dist/jsx/FloatingPanel/index.mjs +1 -19
  189. package/dist/jsx/FloatingPanel/index.mjs.map +1 -1
  190. package/dist/jsx/FloatingPanel/index.native.js +79 -45
  191. package/dist/jsx/FloatingPanel/index.native.js.map +1 -1
  192. package/dist/jsx/FloatingPanel/pointAnchor.mjs +25 -1
  193. package/dist/jsx/FloatingPanel/pointAnchor.mjs.map +1 -1
  194. package/dist/jsx/FloatingPanel/pointAnchor.native.js +26 -0
  195. package/dist/jsx/FloatingPanel/pointAnchor.native.js.map +1 -1
  196. package/dist/jsx/FloatingPanel/regularPlacement.mjs +35 -0
  197. package/dist/jsx/FloatingPanel/regularPlacement.mjs.map +1 -0
  198. package/dist/jsx/FloatingPanel/regularPlacement.native.js +65 -0
  199. package/dist/jsx/FloatingPanel/regularPlacement.native.js.map +1 -0
  200. package/dist/jsx/fields/Combobox/index.mjs +9 -11
  201. package/dist/jsx/fields/Combobox/index.mjs.map +1 -1
  202. package/dist/jsx/fields/Combobox/index.native.js +10 -10
  203. package/dist/jsx/fields/Combobox/index.native.js.map +1 -1
  204. package/dist/jsx/fields/DatePicker/parts.mjs +1 -0
  205. package/dist/jsx/fields/DatePicker/parts.mjs.map +1 -1
  206. package/dist/jsx/fields/DatePicker/parts.native.js +3 -0
  207. package/dist/jsx/fields/DatePicker/parts.native.js.map +1 -1
  208. package/dist/jsx/fields/Input/index.mjs +6 -1
  209. package/dist/jsx/fields/Input/index.mjs.map +1 -1
  210. package/dist/jsx/fields/Input/index.native.js +5 -0
  211. package/dist/jsx/fields/Input/index.native.js.map +1 -1
  212. package/dist/jsx/fields/Select/InlineChoices.mjs +3 -2
  213. package/dist/jsx/fields/Select/InlineChoices.mjs.map +1 -1
  214. package/dist/jsx/fields/Select/InlineChoices.native.js +5 -2
  215. package/dist/jsx/fields/Select/InlineChoices.native.js.map +1 -1
  216. package/dist/jsx/fields/Select/SheetPickerRow.mjs +129 -0
  217. package/dist/jsx/fields/Select/SheetPickerRow.mjs.map +1 -0
  218. package/dist/jsx/fields/Select/SheetPickerRow.native.js +165 -0
  219. package/dist/jsx/fields/Select/SheetPickerRow.native.js.map +1 -0
  220. package/dist/jsx/fields/Select/index.mjs +66 -7
  221. package/dist/jsx/fields/Select/index.mjs.map +1 -1
  222. package/dist/jsx/fields/Select/index.native.js +86 -27
  223. package/dist/jsx/fields/Select/index.native.js.map +1 -1
  224. package/dist/jsx/fields/Select/shouldInlineChoices.mjs +12 -1
  225. package/dist/jsx/fields/Select/shouldInlineChoices.mjs.map +1 -1
  226. package/dist/jsx/fields/Select/shouldInlineChoices.native.js +15 -1
  227. package/dist/jsx/fields/Select/shouldInlineChoices.native.js.map +1 -1
  228. package/dist/jsx/fields/Slider/index.mjs +96 -4
  229. package/dist/jsx/fields/Slider/index.mjs.map +1 -1
  230. package/dist/jsx/fields/Slider/index.native.js +103 -5
  231. package/dist/jsx/fields/Slider/index.native.js.map +1 -1
  232. package/dist/jsx/fields/Switch/index.mjs +71 -1
  233. package/dist/jsx/fields/Switch/index.mjs.map +1 -1
  234. package/dist/jsx/fields/Switch/index.native.js +72 -1
  235. package/dist/jsx/fields/Switch/index.native.js.map +1 -1
  236. package/dist/jsx/fields/TimePicker/parts.mjs +1 -0
  237. package/dist/jsx/fields/TimePicker/parts.mjs.map +1 -1
  238. package/dist/jsx/fields/TimePicker/parts.native.js +1 -0
  239. package/dist/jsx/fields/TimePicker/parts.native.js.map +1 -1
  240. package/dist/jsx/fields/ToggleGroup/index.mjs +67 -1
  241. package/dist/jsx/fields/ToggleGroup/index.mjs.map +1 -1
  242. package/dist/jsx/fields/ToggleGroup/index.native.js +75 -1
  243. package/dist/jsx/fields/ToggleGroup/index.native.js.map +1 -1
  244. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs +36 -21
  245. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -1
  246. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js +44 -21
  247. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
  248. package/dist/jsx/fields/nativeDateTime/sheetKeyboard.mjs +21 -0
  249. package/dist/jsx/fields/nativeDateTime/sheetKeyboard.mjs.map +1 -0
  250. package/dist/jsx/fields/nativeDateTime/sheetKeyboard.native.js +57 -0
  251. package/dist/jsx/fields/nativeDateTime/sheetKeyboard.native.js.map +1 -0
  252. package/dist/jsx/index.js +3 -1
  253. package/dist/jsx/index.js.map +1 -1
  254. package/dist/jsx/index.mjs +3 -1
  255. package/dist/jsx/index.mjs.map +1 -1
  256. package/dist/jsx/index.native.js +11 -0
  257. package/dist/jsx/index.native.js.map +1 -1
  258. package/dist/jsx/shared/overlaySizeClass.mjs +14 -0
  259. package/dist/jsx/shared/overlaySizeClass.mjs.map +1 -0
  260. package/dist/jsx/shared/overlaySizeClass.native.js +45 -0
  261. package/dist/jsx/shared/overlaySizeClass.native.js.map +1 -0
  262. package/dist/jsx/shared/sheetNavigation.mjs +47 -0
  263. package/dist/jsx/shared/sheetNavigation.mjs.map +1 -0
  264. package/dist/jsx/shared/sheetNavigation.native.js +129 -0
  265. package/dist/jsx/shared/sheetNavigation.native.js.map +1 -0
  266. package/dist/jsx/shared/systemControlModules.gnome.mjs +8 -0
  267. package/dist/jsx/shared/systemControlModules.gnome.mjs.map +1 -0
  268. package/dist/jsx/shared/systemControlModules.gnome.native.js +37 -0
  269. package/dist/jsx/shared/systemControlModules.gnome.native.js.map +1 -0
  270. package/dist/jsx/shared/systemControlModules.mjs +8 -0
  271. package/dist/jsx/shared/systemControlModules.mjs.map +1 -0
  272. package/dist/jsx/shared/systemControlModules.native.js +77 -0
  273. package/dist/jsx/shared/systemControlModules.native.js.map +1 -0
  274. package/dist/jsx/shared/systemControls.mjs +19 -0
  275. package/dist/jsx/shared/systemControls.mjs.map +1 -0
  276. package/dist/jsx/shared/systemControls.native.js +51 -0
  277. package/dist/jsx/shared/systemControls.native.js.map +1 -0
  278. package/package.json +16 -8
  279. package/src/FloatingPanel/FloatingPanel.menuFace.native.spec.tsx +73 -3
  280. package/src/FloatingPanel/FloatingPanel.regularWidth.native.spec.tsx +193 -0
  281. package/src/FloatingPanel/RegularPanel.tsx +159 -0
  282. package/src/FloatingPanel/compactFace.spec.ts +24 -1
  283. package/src/FloatingPanel/compactFace.ts +16 -0
  284. package/src/FloatingPanel/containingBlock.ts +32 -0
  285. package/src/FloatingPanel/index.native.tsx +80 -35
  286. package/src/FloatingPanel/index.tsx +1 -33
  287. package/src/FloatingPanel/pointAnchor.spec.ts +29 -0
  288. package/src/FloatingPanel/pointAnchor.ts +30 -0
  289. package/src/FloatingPanel/regularPlacement.spec.ts +65 -0
  290. package/src/FloatingPanel/regularPlacement.ts +40 -0
  291. package/src/fields/Combobox/Combobox.regularWidth.native.spec.tsx +94 -0
  292. package/src/fields/Combobox/index.tsx +11 -34
  293. package/src/fields/DatePicker/parts.tsx +3 -0
  294. package/src/fields/DatePicker/touchWeb.spec.tsx +13 -0
  295. package/src/fields/Input/Input.formName.spec.tsx +53 -0
  296. package/src/fields/Input/index.tsx +6 -1
  297. package/src/fields/Select/InlineChoices.tsx +6 -3
  298. package/src/fields/Select/SheetPickerRow.tsx +160 -0
  299. package/src/fields/Select/index.tsx +67 -5
  300. package/src/fields/Select/shouldInlineChoices.spec.ts +21 -1
  301. package/src/fields/Select/shouldInlineChoices.ts +26 -0
  302. package/src/fields/Slider/Slider.system.native.spec.tsx +168 -0
  303. package/src/fields/Slider/index.tsx +166 -30
  304. package/src/fields/Switch/Switch.system.native.spec.tsx +162 -0
  305. package/src/fields/Switch/index.tsx +98 -0
  306. package/src/fields/TimePicker/parts.tsx +1 -0
  307. package/src/fields/ToggleGroup/ToggleGroup.system.native.spec.tsx +192 -0
  308. package/src/fields/ToggleGroup/index.tsx +111 -0
  309. package/src/fields/nativeDateTime/OsDateTimeSheet.presentation.spec.tsx +167 -0
  310. package/src/fields/nativeDateTime/OsDateTimeSheet.tsx +36 -18
  311. package/src/fields/nativeDateTime/sheetKeyboard.spec.ts +60 -0
  312. package/src/fields/nativeDateTime/sheetKeyboard.ts +45 -0
  313. package/src/index.ts +15 -0
  314. package/src/shared/overlaySizeClass.spec.ts +27 -0
  315. package/src/shared/overlaySizeClass.ts +36 -0
  316. package/src/shared/sheetNavigation.ts +82 -0
  317. package/src/shared/systemControlModules.gnome.ts +10 -0
  318. package/src/shared/systemControlModules.native.spec.ts +52 -0
  319. package/src/shared/systemControlModules.native.ts +66 -0
  320. package/src/shared/systemControlModules.ts +10 -0
  321. package/src/shared/systemControls.ts +70 -0
  322. package/types/FloatingPanel/RegularPanel.d.ts +29 -0
  323. package/types/FloatingPanel/RegularPanel.d.ts.map +1 -0
  324. package/types/FloatingPanel/compactFace.d.ts +12 -0
  325. package/types/FloatingPanel/compactFace.d.ts.map +1 -1
  326. package/types/FloatingPanel/containingBlock.d.ts +14 -0
  327. package/types/FloatingPanel/containingBlock.d.ts.map +1 -0
  328. package/types/FloatingPanel/index.d.ts.map +1 -1
  329. package/types/FloatingPanel/index.native.d.ts +6 -1
  330. package/types/FloatingPanel/index.native.d.ts.map +1 -1
  331. package/types/FloatingPanel/pointAnchor.d.ts +12 -0
  332. package/types/FloatingPanel/pointAnchor.d.ts.map +1 -1
  333. package/types/FloatingPanel/regularPlacement.d.ts +20 -0
  334. package/types/FloatingPanel/regularPlacement.d.ts.map +1 -0
  335. package/types/fields/Combobox/index.d.ts.map +1 -1
  336. package/types/fields/DatePicker/parts.d.ts.map +1 -1
  337. package/types/fields/Input/index.d.ts.map +1 -1
  338. package/types/fields/Select/InlineChoices.d.ts.map +1 -1
  339. package/types/fields/Select/SheetPickerRow.d.ts +23 -0
  340. package/types/fields/Select/SheetPickerRow.d.ts.map +1 -0
  341. package/types/fields/Select/index.d.ts.map +1 -1
  342. package/types/fields/Select/shouldInlineChoices.d.ts +15 -0
  343. package/types/fields/Select/shouldInlineChoices.d.ts.map +1 -1
  344. package/types/fields/Slider/index.d.ts +7 -1
  345. package/types/fields/Slider/index.d.ts.map +1 -1
  346. package/types/fields/Switch/index.d.ts +7 -1
  347. package/types/fields/Switch/index.d.ts.map +1 -1
  348. package/types/fields/TimePicker/parts.d.ts.map +1 -1
  349. package/types/fields/ToggleGroup/index.d.ts +8 -1
  350. package/types/fields/ToggleGroup/index.d.ts.map +1 -1
  351. package/types/fields/nativeDateTime/OsDateTimeSheet.d.ts.map +1 -1
  352. package/types/fields/nativeDateTime/sheetKeyboard.d.ts +20 -0
  353. package/types/fields/nativeDateTime/sheetKeyboard.d.ts.map +1 -0
  354. package/types/index.d.ts +4 -0
  355. package/types/index.d.ts.map +1 -1
  356. package/types/shared/overlaySizeClass.d.ts +24 -0
  357. package/types/shared/overlaySizeClass.d.ts.map +1 -0
  358. package/types/shared/sheetNavigation.d.ts +31 -0
  359. package/types/shared/sheetNavigation.d.ts.map +1 -0
  360. package/types/shared/systemControlModules.d.ts +5 -0
  361. package/types/shared/systemControlModules.d.ts.map +1 -0
  362. package/types/shared/systemControlModules.gnome.d.ts +5 -0
  363. package/types/shared/systemControlModules.gnome.d.ts.map +1 -0
  364. package/types/shared/systemControlModules.native.d.ts +20 -0
  365. package/types/shared/systemControlModules.native.d.ts.map +1 -0
  366. package/types/shared/systemControls.d.ts +53 -0
  367. package/types/shared/systemControls.d.ts.map +1 -0
@@ -0,0 +1,160 @@
1
+ import { keyboardFocusRingProps, useGlyphColor } from "@multiplatform.one/theme";
2
+ import { CaretRightIcon } from "@phosphor-icons/react";
3
+ import { useId, useLayoutEffect, useEffect } from "react";
4
+ import { Pressable } from "react-native";
5
+ import { isWeb, Text, View, XStack } from "tamagui";
6
+ import { useFieldDescribedBy } from "../../fieldLayout";
7
+ import { formInputColors } from "../../shared/colorRamps";
8
+ import { useSheetPageOpen, type SheetNavigationStore } from "../../shared/sheetNavigation";
9
+ import { InlineChoices, type InlineChoice } from "./InlineChoices";
10
+
11
+ const useIsoLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
12
+
13
+ export interface SheetPickerRowProps {
14
+ store: SheetNavigationStore;
15
+ label: string;
16
+ options: InlineChoice[];
17
+ value?: string;
18
+ placeholder?: string;
19
+ onValueChange: (value: string) => void;
20
+ onBlur?: (...args: any[]) => void;
21
+ disabled?: boolean;
22
+ id?: string;
23
+ knobProps: any;
24
+ markColor: string;
25
+ }
26
+
27
+ /**
28
+ * A long choice list inside a sheet (LC-262, FZ-7830 round 3): a Settings row
29
+ * with the label, the current value and a chevron. Pressing it pushes the
30
+ * checkmark list onto the sheet's own navigation stack; Back returns with the
31
+ * value set. Nothing floats over the field.
32
+ */
33
+ export function SheetPickerRow({
34
+ store,
35
+ label,
36
+ options,
37
+ value,
38
+ placeholder,
39
+ onValueChange,
40
+ onBlur,
41
+ disabled,
42
+ id,
43
+ knobProps,
44
+ markColor,
45
+ }: SheetPickerRowProps) {
46
+ const pageId = `sheet-picker-${useId()}`;
47
+ const pushed = useSheetPageOpen(store, pageId);
48
+ const describedBy = useFieldDescribedBy();
49
+ const current = options.find((option) => option.value === value);
50
+ const textColor = knobProps.textAccentColor;
51
+ const chevronInk = useGlyphColor("$textMuted");
52
+
53
+ const page = {
54
+ id: pageId,
55
+ title: label,
56
+ content: (
57
+ <InlineChoices
58
+ options={options}
59
+ value={value}
60
+ onValueChange={onValueChange}
61
+ disabled={disabled}
62
+ aria-label={label}
63
+ knobProps={knobProps}
64
+ markColor={markColor}
65
+ />
66
+ ),
67
+ };
68
+
69
+ useIsoLayoutEffect(() => {
70
+ if (pushed) store.update(page);
71
+ });
72
+ useEffect(() => () => store.remove(pageId), [store, pageId]);
73
+
74
+ const open = () => {
75
+ if (disabled) return;
76
+ store.push(page);
77
+ };
78
+
79
+ const row = (
80
+ <XStack
81
+ alignItems="center"
82
+ minHeight={knobProps.menuRow.minHeight}
83
+ paddingHorizontal={knobProps.panelPadding.padding}
84
+ {...knobProps.gap}
85
+ pointerEvents="none"
86
+ >
87
+ <Text
88
+ fontFamily={knobProps.body.fontFamily}
89
+ fontWeight={knobProps.body.fontWeight}
90
+ color={textColor}
91
+ numberOfLines={1}
92
+ flex={1}
93
+ >
94
+ {label}
95
+ </Text>
96
+ <Text
97
+ testID="sheet-picker-value"
98
+ fontFamily={knobProps.body.fontFamily}
99
+ color="$textMuted"
100
+ numberOfLines={1}
101
+ flexShrink={1}
102
+ >
103
+ {current?.label ?? placeholder}
104
+ </Text>
105
+ <CaretRightIcon size={14} weight="bold" color={chevronInk} />
106
+ </XStack>
107
+ );
108
+
109
+ const surface = {
110
+ ...knobProps.borderRadius,
111
+ borderColor: formInputColors.border.base,
112
+ backgroundColor: formInputColors.background.base,
113
+ overflow: "hidden" as const,
114
+ width: "100%" as const,
115
+ opacity: disabled ? 0.5 : 1,
116
+ };
117
+
118
+ if (isWeb) {
119
+ return (
120
+ <View
121
+ id={id}
122
+ role="button"
123
+ aria-label={label}
124
+ aria-describedby={describedBy}
125
+ aria-disabled={disabled || undefined}
126
+ data-testid="sheet-picker-row"
127
+ tabIndex={disabled ? -1 : 0}
128
+ cursor={disabled ? "default" : "pointer"}
129
+ onPress={open}
130
+ onKeyDown={(event: any) => {
131
+ if (event.key === "Enter" || event.key === " ") {
132
+ event.preventDefault();
133
+ open();
134
+ }
135
+ }}
136
+ onBlur={onBlur}
137
+ outlineWidth={0}
138
+ focusVisibleStyle={keyboardFocusRingProps}
139
+ {...surface}
140
+ >
141
+ {row}
142
+ </View>
143
+ );
144
+ }
145
+ return (
146
+ <View {...surface}>
147
+ <Pressable
148
+ testID="sheet-picker-row"
149
+ accessibilityRole="button"
150
+ accessibilityLabel={label}
151
+ accessibilityValue={{ text: current?.label ?? placeholder ?? "" }}
152
+ accessibilityState={{ disabled: !!disabled }}
153
+ disabled={disabled}
154
+ onPress={open}
155
+ >
156
+ {row}
157
+ </Pressable>
158
+ </View>
159
+ );
160
+ }
@@ -24,6 +24,7 @@ import type { AnyFormApi } from "../../types";
24
24
  import {
25
25
  ensureKeyboardModalityTracking,
26
26
  keyboardFocusRingProps,
27
+ menuRowChecked,
27
28
  menuRowFrame,
28
29
  sizeRecipeForToken,
29
30
  useGlyphColor,
@@ -65,6 +66,7 @@ import {
65
66
  } from "../../FloatingPanel";
66
67
  import { pickerCompactFace, resolveCompactFace } from "../../FloatingPanel/compactFace";
67
68
  import { PanelPortal } from "../../FloatingPanel/PanelPortal";
69
+ import { fixedContainingBlockOrigin } from "../../FloatingPanel/containingBlock";
68
70
  import { useInsideOverlay, useOverlayZIndex } from "../../shared/overlayStack";
69
71
  import { useDragScroll } from "../../shared/useDragScroll";
70
72
  import { useIsInTableCell } from "../../shared/tableCellContext";
@@ -72,7 +74,9 @@ import { t } from "../../shared/t";
72
74
  import { orderOptionsBySelected, type SelectedOrder } from "../selectedOrder";
73
75
  import { shouldUseIosNativeSelect } from "../nativeSelect";
74
76
  import { InlineChoices } from "./InlineChoices";
75
- import { shouldInlineChoices } from "./shouldInlineChoices";
77
+ import { SheetPickerRow } from "./SheetPickerRow";
78
+ import { opensBesideTrigger, shouldInlineChoices, shouldPushChoices } from "./shouldInlineChoices";
79
+ import { useSheetNavigation } from "../../shared/sheetNavigation";
76
80
 
77
81
  /** Undefined on web (RNW ships no ActionSheetIOS); real on iOS. */
78
82
  const actionSheetIOS = (ReactNative as { ActionSheetIOS?: typeof ReactNative.ActionSheetIOS })
@@ -170,7 +174,7 @@ const SelectRow = styled(View, {
170
174
  variants: {
171
175
  // LC-262: the accent check alone marks the chosen row, never a grey fill.
172
176
  selected: {
173
- true: { backgroundColor: "transparent", borderRadius: 0 },
177
+ true: { ...menuRowChecked, borderRadius: 0 },
174
178
  false: { backgroundColor: "transparent" },
175
179
  },
176
180
  active: {
@@ -539,6 +543,7 @@ function FloatingSelectCore({
539
543
  const { style: elevatedSurfaceStyle, ...elevatedSurfaceProps } = knobProps.elevatedSurface;
540
544
  const hydrationTouch = useTouchSurface();
541
545
  const inTableCell = useIsInTableCell();
546
+ const beside = useInsideOverlay() && !inTableCell && opensBesideTrigger(options.length);
542
547
  const describedBy = useFieldDescribedBy();
543
548
  const glyphColor = useGlyphColor();
544
549
  const [open, setOpen] = useState(false);
@@ -671,8 +676,31 @@ function FloatingSelectCore({
671
676
  const selectedItemIndex = selectedIndex >= 0 ? selectedIndex : 0;
672
677
  const selectedItem = listItemsRef.current[selectedItemIndex];
673
678
  const trigger = panel.triggerRef.current;
674
- if (!dropdownElement || !selectedItem || !trigger) return;
679
+ if (!dropdownElement || !trigger) return;
680
+
681
+ if (beside) {
682
+ const isInitial = panel.initializingRef.current;
683
+ if (isInitial) dropdownElement.style.transition = "none";
684
+ const bounds = trigger.getBoundingClientRect();
685
+ const contentHeight = dropdownElement.scrollHeight;
686
+ const below = window.innerHeight - panelViewportPadding - bounds.bottom;
687
+ const above = bounds.top - panelViewportPadding;
688
+ const opensUp = contentHeight > below && above > below;
689
+ const maxHeight = Math.min(contentHeight || Infinity, opensUp ? above : below);
690
+ const top =
691
+ (opensUp ? bounds.top - maxHeight : bounds.bottom) -
692
+ fixedContainingBlockOrigin(dropdownElement).top;
693
+ dropdownElement.style.top = `${top}px`;
694
+ dropdownElement.style.maxHeight = `${maxHeight}px`;
695
+ dropdownElement.style.height = "";
696
+ overlayStyle.current = { top, maxHeight };
697
+ panel.initializingRef.current = false;
698
+ void dropdownElement.offsetHeight;
699
+ if (isInitial) dropdownElement.style.transition = panel.panelTransition;
700
+ return;
701
+ }
675
702
 
703
+ if (!selectedItem) return;
676
704
  const isInitial = panel.initializingRef.current;
677
705
  if (isInitial) dropdownElement.style.transition = "none";
678
706
 
@@ -812,6 +840,7 @@ function FloatingSelectCore({
812
840
  panel.placement,
813
841
  panel.panelTransition,
814
842
  options.length,
843
+ beside,
815
844
  ]);
816
845
 
817
846
  repositionRef.current = repositionOverlay;
@@ -1573,6 +1602,9 @@ function NativeSelectCore({
1573
1602
  // A short list opens as a menu over its trigger; a long one keeps its
1574
1603
  // sheet at page level and opens as a menu inside another overlay (FZ-7830).
1575
1604
  compactFace={compactFace}
1605
+ {...(asMenu && insideOverlay && opensBesideTrigger(options.length)
1606
+ ? { anchorMode: "attach" as const, triggerEcho: false }
1607
+ : null)}
1576
1608
  triggerA11y={{
1577
1609
  label: ariaLabel ?? fieldA11y?.label,
1578
1610
  value: triggerValueText,
@@ -1761,6 +1793,13 @@ export function Select({
1761
1793
  const shouldUseSheet = !isWeb || (!effectiveGtSm && !compactMenu);
1762
1794
  const inTableCell = useIsInTableCell();
1763
1795
  const inline = !inTableCell && shouldInlineChoices({ insideOverlay, multiple, options });
1796
+ const sheetNavigation = useSheetNavigation();
1797
+ const rowLabel = typeof label === "string" ? label : ariaLabel;
1798
+ const pushed =
1799
+ !inline &&
1800
+ !inTableCell &&
1801
+ Boolean(rowLabel) &&
1802
+ shouldPushChoices({ inSheet: Boolean(sheetNavigation), multiple, options });
1764
1803
  // DG-CTRL-01 / DG-CTRL-05 — short single-select lists prefer RadioGroup,
1765
1804
  // which the inline face already is.
1766
1805
  if (!inline) {
@@ -1836,6 +1875,27 @@ export function Select({
1836
1875
  handleBlur?: (...args: any[]) => void,
1837
1876
  ) => {
1838
1877
  if (readOnly) return renderReadOnly(selectValue);
1878
+ if (pushed && sheetNavigation) {
1879
+ return (
1880
+ <SheetPickerRow
1881
+ store={sheetNavigation}
1882
+ label={rowLabel as string}
1883
+ options={options}
1884
+ value={typeof selectValue === "string" ? selectValue : undefined}
1885
+ placeholder={placeholder}
1886
+ onValueChange={(val) => {
1887
+ if (disabled) return;
1888
+ handleValueChange?.(val);
1889
+ emitValueChange(val);
1890
+ }}
1891
+ onBlur={handleBlur}
1892
+ disabled={disabled}
1893
+ id={id}
1894
+ knobProps={knobProps}
1895
+ markColor={markColor}
1896
+ />
1897
+ );
1898
+ }
1839
1899
  if (inline) {
1840
1900
  return (
1841
1901
  <InlineChoices
@@ -1886,7 +1946,8 @@ export function Select({
1886
1946
  return (
1887
1947
  <FieldLayout
1888
1948
  id={id}
1889
- label={label}
1949
+ label={pushed ? undefined : label}
1950
+ aria-label={pushed ? rowLabel : undefined}
1890
1951
  labelProps={labelProps}
1891
1952
  error={error}
1892
1953
  helperText={helperText}
@@ -1921,7 +1982,8 @@ export function Select({
1921
1982
  return (
1922
1983
  <FieldLayout
1923
1984
  id={id}
1924
- label={label}
1985
+ label={pushed ? undefined : label}
1986
+ aria-label={pushed ? rowLabel : undefined}
1925
1987
  labelProps={labelProps}
1926
1988
  error={resolvedError}
1927
1989
  helperText={helperText}
@@ -1,5 +1,10 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { INLINE_CHOICES_MAX, shouldInlineChoices } from "./shouldInlineChoices";
2
+ import {
3
+ INLINE_CHOICES_MAX,
4
+ opensBesideTrigger,
5
+ shouldInlineChoices,
6
+ shouldPushChoices,
7
+ } from "./shouldInlineChoices";
3
8
 
4
9
  const glyph = "•";
5
10
  const withIcons = (n: number) => Array.from({ length: n }, () => ({ icon: glyph }));
@@ -23,3 +28,18 @@ describe("shouldInlineChoices (LC-262, FZ-7830)", () => {
23
28
  expect(shouldInlineChoices({ insideOverlay: true, options: [] })).toBe(false);
24
29
  });
25
30
  });
31
+
32
+ describe("shouldPushChoices and opensBesideTrigger (LC-262, FZ-7830 round 3)", () => {
33
+ it("pushes a single-choice list past five options inside a sheet", () => {
34
+ expect(shouldPushChoices({ inSheet: true, options: withIcons(6) })).toBe(true);
35
+ expect(shouldPushChoices({ inSheet: true, options: withIcons(5) })).toBe(false);
36
+ expect(shouldPushChoices({ inSheet: false, options: withIcons(8) })).toBe(false);
37
+ expect(shouldPushChoices({ inSheet: true, multiple: true, options: withIcons(8) })).toBe(false);
38
+ });
39
+
40
+ it("opens a list of five or fewer beside its field", () => {
41
+ expect(opensBesideTrigger(5)).toBe(true);
42
+ expect(opensBesideTrigger(6)).toBe(false);
43
+ expect(opensBesideTrigger(0)).toBe(false);
44
+ });
45
+ });
@@ -20,3 +20,29 @@ export function shouldInlineChoices({
20
20
  if (!insideOverlay || multiple || !options?.length) return false;
21
21
  return options.length <= INLINE_CHOICES_MAX && options.every((option) => option.icon != null);
22
22
  }
23
+
24
+ /**
25
+ * A single-choice Select inside a sheet with more options than the inline
26
+ * list holds pushes its checkmark list onto the sheet's navigation stack from
27
+ * a Settings row (LC-262, FZ-7830 round 3), instead of a menu over the field.
28
+ */
29
+ export function shouldPushChoices({
30
+ inSheet,
31
+ multiple,
32
+ options,
33
+ }: {
34
+ inSheet: boolean;
35
+ multiple?: boolean;
36
+ options: ReadonlyArray<unknown> | undefined;
37
+ }): boolean {
38
+ if (!inSheet || multiple || !options?.length) return false;
39
+ return options.length > INLINE_CHOICES_MAX;
40
+ }
41
+
42
+ /**
43
+ * A short list inside a sheet that is not laid out inline opens as a menu
44
+ * beside its field, under it or above it, never over it (LC-262).
45
+ */
46
+ export function opensBesideTrigger(optionCount: number): boolean {
47
+ return optionCount > 0 && optionCount <= INLINE_CHOICES_MAX;
48
+ }
@@ -0,0 +1,168 @@
1
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
2
+ import { createDefaultThemeConfig } from "@multiplatform.one/theme";
3
+ import { useForm } from "@tanstack/react-form";
4
+ import { cleanup, fireEvent, render } from "@testing-library/react";
5
+ import type { ReactElement } from "react";
6
+ import { TamaguiProvider } from "tamagui";
7
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
8
+ import { Form } from "../../Form";
9
+ import { Slider } from "./index";
10
+
11
+ const system = vi.hoisted(() => ({
12
+ os: "ios" as "ios" | "android" | null,
13
+ installed: true,
14
+ }));
15
+
16
+ // The system slider as a probe: it shows the props it got and moves through
17
+ // onValueChange, then onSlidingComplete, the way a drag on UISlider does.
18
+ const SystemSliderProbe = vi.hoisted(
19
+ () =>
20
+ function SystemSliderProbe(props: Record<string, any>) {
21
+ return (
22
+ <input
23
+ data-testid="system-slider"
24
+ data-fill={props.minimumTrackTintColor}
25
+ data-rail={props.maximumTrackTintColor}
26
+ data-thumb={props.thumbTintColor ?? ""}
27
+ data-label={props.accessibilityLabel ?? ""}
28
+ data-min={props.minimumValue}
29
+ data-max={props.maximumValue}
30
+ data-step={props.step}
31
+ disabled={props.disabled}
32
+ value={props.value}
33
+ onChange={(event) => {
34
+ props.onValueChange?.(Number(event.target.value));
35
+ props.onSlidingComplete?.(Number(event.target.value));
36
+ }}
37
+ />
38
+ );
39
+ },
40
+ );
41
+
42
+ vi.mock("../../shared/systemControls", async (importOriginal) => ({
43
+ ...(await importOriginal<typeof import("../../shared/systemControls")>()),
44
+ systemControlPlatform: () => system.os,
45
+ }));
46
+
47
+ vi.mock("../../shared/systemControlModules", () => ({
48
+ loadSystemSlider: () => (system.installed ? SystemSliderProbe : null),
49
+ loadSystemSegmentedControl: () => null,
50
+ }));
51
+
52
+ const mpoTheme = createDefaultThemeConfig();
53
+ const renderInScheme = (ui: ReactElement, scheme: "light" | "dark") =>
54
+ render(
55
+ <TamaguiProvider config={mpoTheme.tamagui} defaultTheme={scheme} disableInjectCSS>
56
+ {ui}
57
+ </TamaguiProvider>,
58
+ );
59
+
60
+ afterEach(cleanup);
61
+ beforeEach(() => {
62
+ system.os = "ios";
63
+ system.installed = true;
64
+ });
65
+
66
+ const literal = (value: string | null) => Boolean(value) && !value!.startsWith("$");
67
+ const mpoSlider = (container: HTMLElement) => container.querySelector('[role="slider"]');
68
+
69
+ function FormSlider({ onSubmit }: { onSubmit: (value: { volume: number }) => void }) {
70
+ const form = useForm({ defaultValues: { volume: 20 }, onSubmit: ({ value }) => onSubmit(value) });
71
+ return (
72
+ <Form form={form}>
73
+ <Slider name="volume" label="Volume" />
74
+ <button type="button" onClick={() => form.handleSubmit()}>
75
+ Save
76
+ </button>
77
+ </Form>
78
+ );
79
+ }
80
+
81
+ // LC-266 PLATFORM-ERGONOMICS (FZ-8118). With @react-native-community/slider in
82
+ // the binary, iOS and Android render the system slider tinted from the theme.
83
+ // Without it (One's binary today, an OTA on an older build) the MPO slider.
84
+ describe("Slider on iOS and Android", () => {
85
+ it.each(["light", "dark"] as const)(
86
+ "renders the system slider with the %s theme's fill and rail as literals",
87
+ (scheme) => {
88
+ const result = renderInScheme(
89
+ <Slider label="Volume" defaultValue={[30]} min={10} max={90} step={5} />,
90
+ scheme,
91
+ );
92
+ const view = result.getByTestId("system-slider") as HTMLInputElement;
93
+ expect(view.value).toBe("30");
94
+ expect(view.dataset).toMatchObject({ min: "10", max: "90", step: "5", label: "Volume" });
95
+ expect(literal(view.getAttribute("data-fill"))).toBe(true);
96
+ expect(literal(view.getAttribute("data-rail"))).toBe(true);
97
+ expect(view.getAttribute("data-fill")).not.toBe(view.getAttribute("data-rail"));
98
+ expect(view.dataset.thumb).toBe("");
99
+ expect(mpoSlider(result.container)).toBeNull();
100
+ expect(result.getByText("30")).toBeTruthy();
101
+ },
102
+ );
103
+
104
+ it("tints the Android thumb with the fill", () => {
105
+ system.os = "android";
106
+ const result = renderInScheme(<Slider label="Volume" />, "light");
107
+ const view = result.getByTestId("system-slider");
108
+ expect(literal(view.getAttribute("data-thumb"))).toBe(true);
109
+ expect(view.dataset.thumb).toBe(view.dataset.fill);
110
+ });
111
+
112
+ it("reports a move as a one-value array and updates the readout", () => {
113
+ const onChange = vi.fn();
114
+ const result = renderWithProviders(<Slider label="Volume" onChange={onChange} />);
115
+ fireEvent.change(result.getByTestId("system-slider"), { target: { value: "40" } });
116
+ expect(onChange).toHaveBeenCalledWith([40]);
117
+ expect(result.getByText("40")).toBeTruthy();
118
+ });
119
+
120
+ it("writes the form field it is bound to", async () => {
121
+ const onSubmit = vi.fn();
122
+ const result = renderWithProviders(<FormSlider onSubmit={onSubmit} />);
123
+ expect((result.getByTestId("system-slider") as HTMLInputElement).value).toBe("20");
124
+ fireEvent.change(result.getByTestId("system-slider"), { target: { value: "55" } });
125
+ fireEvent.click(result.getByText("Save"));
126
+ await vi.waitFor(() => expect(onSubmit).toHaveBeenCalledWith({ volume: 55 }));
127
+ });
128
+
129
+ it("passes disabled through and never reports a disabled or read-only move", () => {
130
+ const onChange = vi.fn();
131
+ const result = renderWithProviders(
132
+ <>
133
+ <Slider label="Off limits" onChange={onChange} disabled />
134
+ <Slider label="Look only" onChange={onChange} readOnly />
135
+ </>,
136
+ );
137
+ const [disabledView, readOnlyView] = result.getAllByTestId("system-slider");
138
+ expect((disabledView as HTMLInputElement).disabled).toBe(true);
139
+ fireEvent.change(disabledView!, { target: { value: "40" } });
140
+ fireEvent.change(readOnlyView!, { target: { value: "40" } });
141
+ expect(onChange).not.toHaveBeenCalled();
142
+ });
143
+
144
+ it("falls back to the MPO slider when the binary lacks the module", () => {
145
+ system.installed = false;
146
+ const result = renderWithProviders(<Slider label="Volume" defaultValue={[30]} />);
147
+ expect(result.queryByTestId("system-slider")).toBeNull();
148
+ expect(mpoSlider(result.container)).not.toBeNull();
149
+ });
150
+
151
+ it("keeps the MPO slider for a range, a vertical slider and native={false}", () => {
152
+ const result = renderWithProviders(
153
+ <>
154
+ <Slider label="Range" defaultValue={[10, 60]} />
155
+ <Slider label="Tall" sliderProps={{ orientation: "vertical" }} />
156
+ <Slider label="Opted out" native={false} />
157
+ </>,
158
+ );
159
+ expect(result.queryByTestId("system-slider")).toBeNull();
160
+ });
161
+
162
+ it("keeps the MPO slider on web and GNOME", () => {
163
+ system.os = null;
164
+ const result = renderWithProviders(<Slider label="Volume" />);
165
+ expect(result.queryByTestId("system-slider")).toBeNull();
166
+ expect(mpoSlider(result.container)).not.toBeNull();
167
+ });
168
+ });