@multiplatform.one/forms 7.71.0 → 7.73.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 (381) hide show
  1. package/dist/cjs/Button/circleGlyph.cjs +28 -0
  2. package/dist/cjs/Button/circleGlyph.native.js +31 -0
  3. package/dist/cjs/Button/circleGlyph.native.js.map +1 -0
  4. package/dist/cjs/Button/index.cjs +19 -3
  5. package/dist/cjs/Button/index.native.js +19 -3
  6. package/dist/cjs/Button/index.native.js.map +1 -1
  7. package/dist/cjs/FloatingPanel/PanelHeader.cjs +49 -0
  8. package/dist/cjs/FloatingPanel/PanelHeader.native.js +53 -0
  9. package/dist/cjs/FloatingPanel/PanelHeader.native.js.map +1 -0
  10. package/dist/cjs/FloatingPanel/echoContext.cjs +67 -0
  11. package/dist/cjs/FloatingPanel/echoContext.native.js +71 -0
  12. package/dist/cjs/FloatingPanel/echoContext.native.js.map +1 -0
  13. package/dist/cjs/FloatingPanel/iconEcho.cjs +104 -0
  14. package/dist/cjs/FloatingPanel/iconEcho.native.js +114 -0
  15. package/dist/cjs/FloatingPanel/iconEcho.native.js.map +1 -0
  16. package/dist/cjs/FloatingPanel/index.cjs +371 -86
  17. package/dist/cjs/FloatingPanel/index.native.js +164 -20
  18. package/dist/cjs/FloatingPanel/index.native.js.map +1 -1
  19. package/dist/cjs/FloatingPanel/paintedBox.cjs +80 -0
  20. package/dist/cjs/FloatingPanel/paintedBox.native.js +85 -0
  21. package/dist/cjs/FloatingPanel/paintedBox.native.js.map +1 -0
  22. package/dist/cjs/FloatingPanel/panelWidth.cjs +92 -0
  23. package/dist/cjs/FloatingPanel/panelWidth.native.js +99 -0
  24. package/dist/cjs/FloatingPanel/panelWidth.native.js.map +1 -0
  25. package/dist/cjs/FloatingPanel/pointAnchor.cjs +34 -5
  26. package/dist/cjs/FloatingPanel/pointAnchor.native.js +37 -8
  27. package/dist/cjs/FloatingPanel/pointAnchor.native.js.map +1 -1
  28. package/dist/cjs/FloatingPanel/useFloatingPanel.cjs +60 -5
  29. package/dist/cjs/FloatingPanel/useFloatingPanel.native.js +72 -5
  30. package/dist/cjs/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  31. package/dist/cjs/InputParts/index.cjs +18 -0
  32. package/dist/cjs/InputParts/index.native.js +19 -1
  33. package/dist/cjs/InputParts/index.native.js.map +1 -1
  34. package/dist/cjs/Wheel/NativeWheel.native.js +1 -1
  35. package/dist/cjs/Wheel/NativeWheel.native.js.map +1 -1
  36. package/dist/cjs/Wheel/index.cjs +2 -1
  37. package/dist/cjs/Wheel/index.native.js +4 -1
  38. package/dist/cjs/Wheel/index.native.js.map +1 -1
  39. package/dist/cjs/Wheel/wheelShared.cjs +6 -1
  40. package/dist/cjs/Wheel/wheelShared.native.js +7 -1
  41. package/dist/cjs/Wheel/wheelShared.native.js.map +1 -1
  42. package/dist/cjs/fields/Combobox/index.cjs +1 -0
  43. package/dist/cjs/fields/Combobox/index.native.js +1 -0
  44. package/dist/cjs/fields/Combobox/index.native.js.map +1 -1
  45. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.cjs +6 -30
  46. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js +11 -34
  47. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  48. package/dist/cjs/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  49. package/dist/cjs/fields/DatePicker/catalog.cjs +2 -28
  50. package/dist/cjs/fields/DatePicker/catalog.native.js +2 -30
  51. package/dist/cjs/fields/DatePicker/catalog.native.js.map +1 -1
  52. package/dist/cjs/fields/DatePicker/index.native.js.map +1 -1
  53. package/dist/cjs/fields/DatePicker/parts.cjs +11 -20
  54. package/dist/cjs/fields/DatePicker/parts.native.js +13 -22
  55. package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
  56. package/dist/cjs/fields/Select/index.cjs +65 -44
  57. package/dist/cjs/fields/Select/index.native.js +69 -47
  58. package/dist/cjs/fields/Select/index.native.js.map +1 -1
  59. package/dist/cjs/fields/Select/shouldInlineChoices.cjs +3 -1
  60. package/dist/cjs/fields/Select/shouldInlineChoices.native.js +3 -1
  61. package/dist/cjs/fields/Select/shouldInlineChoices.native.js.map +1 -1
  62. package/dist/cjs/fields/TimePicker/catalog.cjs +4 -25
  63. package/dist/cjs/fields/TimePicker/catalog.native.js +7 -29
  64. package/dist/cjs/fields/TimePicker/catalog.native.js.map +1 -1
  65. package/dist/cjs/fields/TimePicker/index.native.js.map +1 -1
  66. package/dist/cjs/fields/TimePicker/parts.cjs +8 -21
  67. package/dist/cjs/fields/TimePicker/parts.native.js +8 -21
  68. package/dist/cjs/fields/TimePicker/parts.native.js.map +1 -1
  69. package/dist/cjs/index.cjs +2 -0
  70. package/dist/cjs/index.native.js +2 -0
  71. package/dist/cjs/index.native.js.map +1 -1
  72. package/dist/cjs/shared/floatingList.cjs +13 -4
  73. package/dist/cjs/shared/floatingList.native.js +15 -4
  74. package/dist/cjs/shared/floatingList.native.js.map +1 -1
  75. package/dist/esm/Button/circleGlyph.mjs +3 -0
  76. package/dist/esm/Button/circleGlyph.mjs.map +1 -0
  77. package/dist/esm/Button/circleGlyph.native.js +3 -0
  78. package/dist/esm/Button/circleGlyph.native.js.map +1 -0
  79. package/dist/esm/Button/index.mjs +18 -2
  80. package/dist/esm/Button/index.mjs.map +1 -1
  81. package/dist/esm/Button/index.native.js +18 -2
  82. package/dist/esm/Button/index.native.js.map +1 -1
  83. package/dist/esm/FloatingPanel/PanelHeader.mjs +24 -0
  84. package/dist/esm/FloatingPanel/PanelHeader.mjs.map +1 -0
  85. package/dist/esm/FloatingPanel/PanelHeader.native.js +25 -0
  86. package/dist/esm/FloatingPanel/PanelHeader.native.js.map +1 -0
  87. package/dist/esm/FloatingPanel/echoContext.mjs +35 -0
  88. package/dist/esm/FloatingPanel/echoContext.mjs.map +1 -0
  89. package/dist/esm/FloatingPanel/echoContext.native.js +36 -0
  90. package/dist/esm/FloatingPanel/echoContext.native.js.map +1 -0
  91. package/dist/esm/FloatingPanel/iconEcho.mjs +77 -0
  92. package/dist/esm/FloatingPanel/iconEcho.mjs.map +1 -0
  93. package/dist/esm/FloatingPanel/iconEcho.native.js +84 -0
  94. package/dist/esm/FloatingPanel/iconEcho.native.js.map +1 -0
  95. package/dist/esm/FloatingPanel/index.mjs +373 -90
  96. package/dist/esm/FloatingPanel/index.mjs.map +1 -1
  97. package/dist/esm/FloatingPanel/index.native.js +165 -23
  98. package/dist/esm/FloatingPanel/index.native.js.map +1 -1
  99. package/dist/esm/FloatingPanel/paintedBox.mjs +52 -0
  100. package/dist/esm/FloatingPanel/paintedBox.mjs.map +1 -0
  101. package/dist/esm/FloatingPanel/paintedBox.native.js +54 -0
  102. package/dist/esm/FloatingPanel/paintedBox.native.js.map +1 -0
  103. package/dist/esm/FloatingPanel/panelWidth.mjs +63 -0
  104. package/dist/esm/FloatingPanel/panelWidth.mjs.map +1 -0
  105. package/dist/esm/FloatingPanel/panelWidth.native.js +67 -0
  106. package/dist/esm/FloatingPanel/panelWidth.native.js.map +1 -0
  107. package/dist/esm/FloatingPanel/pointAnchor.mjs +34 -5
  108. package/dist/esm/FloatingPanel/pointAnchor.mjs.map +1 -1
  109. package/dist/esm/FloatingPanel/pointAnchor.native.js +37 -8
  110. package/dist/esm/FloatingPanel/pointAnchor.native.js.map +1 -1
  111. package/dist/esm/FloatingPanel/useFloatingPanel.mjs +60 -6
  112. package/dist/esm/FloatingPanel/useFloatingPanel.mjs.map +1 -1
  113. package/dist/esm/FloatingPanel/useFloatingPanel.native.js +72 -6
  114. package/dist/esm/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  115. package/dist/esm/InputParts/index.mjs +19 -1
  116. package/dist/esm/InputParts/index.mjs.map +1 -1
  117. package/dist/esm/InputParts/index.native.js +20 -2
  118. package/dist/esm/InputParts/index.native.js.map +1 -1
  119. package/dist/esm/Wheel/NativeWheel.native.js +1 -1
  120. package/dist/esm/Wheel/NativeWheel.native.js.map +1 -1
  121. package/dist/esm/Wheel/index.mjs +2 -1
  122. package/dist/esm/Wheel/index.mjs.map +1 -1
  123. package/dist/esm/Wheel/index.native.js +4 -1
  124. package/dist/esm/Wheel/index.native.js.map +1 -1
  125. package/dist/esm/Wheel/wheelShared.mjs +6 -1
  126. package/dist/esm/Wheel/wheelShared.mjs.map +1 -1
  127. package/dist/esm/Wheel/wheelShared.native.js +7 -1
  128. package/dist/esm/Wheel/wheelShared.native.js.map +1 -1
  129. package/dist/esm/fields/Combobox/index.mjs +1 -0
  130. package/dist/esm/fields/Combobox/index.mjs.map +1 -1
  131. package/dist/esm/fields/Combobox/index.native.js +1 -0
  132. package/dist/esm/fields/Combobox/index.native.js.map +1 -1
  133. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs +6 -30
  134. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -1
  135. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js +11 -34
  136. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  137. package/dist/esm/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  138. package/dist/esm/fields/DatePicker/catalog.mjs +2 -28
  139. package/dist/esm/fields/DatePicker/catalog.mjs.map +1 -1
  140. package/dist/esm/fields/DatePicker/catalog.native.js +2 -30
  141. package/dist/esm/fields/DatePicker/catalog.native.js.map +1 -1
  142. package/dist/esm/fields/DatePicker/index.native.js.map +1 -1
  143. package/dist/esm/fields/DatePicker/parts.mjs +11 -20
  144. package/dist/esm/fields/DatePicker/parts.mjs.map +1 -1
  145. package/dist/esm/fields/DatePicker/parts.native.js +13 -22
  146. package/dist/esm/fields/DatePicker/parts.native.js.map +1 -1
  147. package/dist/esm/fields/Select/index.mjs +66 -45
  148. package/dist/esm/fields/Select/index.mjs.map +1 -1
  149. package/dist/esm/fields/Select/index.native.js +70 -48
  150. package/dist/esm/fields/Select/index.native.js.map +1 -1
  151. package/dist/esm/fields/Select/shouldInlineChoices.mjs +3 -1
  152. package/dist/esm/fields/Select/shouldInlineChoices.mjs.map +1 -1
  153. package/dist/esm/fields/Select/shouldInlineChoices.native.js +3 -1
  154. package/dist/esm/fields/Select/shouldInlineChoices.native.js.map +1 -1
  155. package/dist/esm/fields/TimePicker/catalog.mjs +4 -25
  156. package/dist/esm/fields/TimePicker/catalog.mjs.map +1 -1
  157. package/dist/esm/fields/TimePicker/catalog.native.js +7 -29
  158. package/dist/esm/fields/TimePicker/catalog.native.js.map +1 -1
  159. package/dist/esm/fields/TimePicker/index.native.js.map +1 -1
  160. package/dist/esm/fields/TimePicker/parts.mjs +8 -21
  161. package/dist/esm/fields/TimePicker/parts.mjs.map +1 -1
  162. package/dist/esm/fields/TimePicker/parts.native.js +8 -21
  163. package/dist/esm/fields/TimePicker/parts.native.js.map +1 -1
  164. package/dist/esm/index.mjs +2 -1
  165. package/dist/esm/index.mjs.map +1 -1
  166. package/dist/esm/index.native.js +2 -1
  167. package/dist/esm/index.native.js.map +1 -1
  168. package/dist/esm/shared/floatingList.mjs +14 -5
  169. package/dist/esm/shared/floatingList.mjs.map +1 -1
  170. package/dist/esm/shared/floatingList.native.js +16 -5
  171. package/dist/esm/shared/floatingList.native.js.map +1 -1
  172. package/dist/jsx/Button/circleGlyph.mjs +3 -0
  173. package/dist/jsx/Button/circleGlyph.mjs.map +1 -0
  174. package/dist/jsx/Button/circleGlyph.native.js +31 -0
  175. package/dist/jsx/Button/circleGlyph.native.js.map +1 -0
  176. package/dist/jsx/Button/index.mjs +18 -2
  177. package/dist/jsx/Button/index.mjs.map +1 -1
  178. package/dist/jsx/Button/index.native.js +19 -3
  179. package/dist/jsx/Button/index.native.js.map +1 -1
  180. package/dist/jsx/FloatingPanel/PanelHeader.mjs +24 -0
  181. package/dist/jsx/FloatingPanel/PanelHeader.mjs.map +1 -0
  182. package/dist/jsx/FloatingPanel/PanelHeader.native.js +53 -0
  183. package/dist/jsx/FloatingPanel/PanelHeader.native.js.map +1 -0
  184. package/dist/jsx/FloatingPanel/echoContext.mjs +35 -0
  185. package/dist/jsx/FloatingPanel/echoContext.mjs.map +1 -0
  186. package/dist/jsx/FloatingPanel/echoContext.native.js +71 -0
  187. package/dist/jsx/FloatingPanel/echoContext.native.js.map +1 -0
  188. package/dist/jsx/FloatingPanel/iconEcho.mjs +77 -0
  189. package/dist/jsx/FloatingPanel/iconEcho.mjs.map +1 -0
  190. package/dist/jsx/FloatingPanel/iconEcho.native.js +114 -0
  191. package/dist/jsx/FloatingPanel/iconEcho.native.js.map +1 -0
  192. package/dist/jsx/FloatingPanel/index.mjs +373 -90
  193. package/dist/jsx/FloatingPanel/index.mjs.map +1 -1
  194. package/dist/jsx/FloatingPanel/index.native.js +164 -20
  195. package/dist/jsx/FloatingPanel/index.native.js.map +1 -1
  196. package/dist/jsx/FloatingPanel/paintedBox.mjs +52 -0
  197. package/dist/jsx/FloatingPanel/paintedBox.mjs.map +1 -0
  198. package/dist/jsx/FloatingPanel/paintedBox.native.js +85 -0
  199. package/dist/jsx/FloatingPanel/paintedBox.native.js.map +1 -0
  200. package/dist/jsx/FloatingPanel/panelWidth.mjs +63 -0
  201. package/dist/jsx/FloatingPanel/panelWidth.mjs.map +1 -0
  202. package/dist/jsx/FloatingPanel/panelWidth.native.js +99 -0
  203. package/dist/jsx/FloatingPanel/panelWidth.native.js.map +1 -0
  204. package/dist/jsx/FloatingPanel/pointAnchor.mjs +34 -5
  205. package/dist/jsx/FloatingPanel/pointAnchor.mjs.map +1 -1
  206. package/dist/jsx/FloatingPanel/pointAnchor.native.js +37 -8
  207. package/dist/jsx/FloatingPanel/pointAnchor.native.js.map +1 -1
  208. package/dist/jsx/FloatingPanel/useFloatingPanel.mjs +60 -6
  209. package/dist/jsx/FloatingPanel/useFloatingPanel.mjs.map +1 -1
  210. package/dist/jsx/FloatingPanel/useFloatingPanel.native.js +72 -5
  211. package/dist/jsx/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  212. package/dist/jsx/InputParts/index.mjs +19 -1
  213. package/dist/jsx/InputParts/index.mjs.map +1 -1
  214. package/dist/jsx/InputParts/index.native.js +19 -1
  215. package/dist/jsx/InputParts/index.native.js.map +1 -1
  216. package/dist/jsx/Wheel/NativeWheel.native.js +1 -1
  217. package/dist/jsx/Wheel/NativeWheel.native.js.map +1 -1
  218. package/dist/jsx/Wheel/index.mjs +2 -1
  219. package/dist/jsx/Wheel/index.mjs.map +1 -1
  220. package/dist/jsx/Wheel/index.native.js +4 -1
  221. package/dist/jsx/Wheel/index.native.js.map +1 -1
  222. package/dist/jsx/Wheel/wheelShared.mjs +6 -1
  223. package/dist/jsx/Wheel/wheelShared.mjs.map +1 -1
  224. package/dist/jsx/Wheel/wheelShared.native.js +7 -1
  225. package/dist/jsx/Wheel/wheelShared.native.js.map +1 -1
  226. package/dist/jsx/fields/Combobox/index.mjs +1 -0
  227. package/dist/jsx/fields/Combobox/index.mjs.map +1 -1
  228. package/dist/jsx/fields/Combobox/index.native.js +1 -0
  229. package/dist/jsx/fields/Combobox/index.native.js.map +1 -1
  230. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs +6 -30
  231. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -1
  232. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js +11 -34
  233. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  234. package/dist/jsx/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  235. package/dist/jsx/fields/DatePicker/catalog.mjs +2 -28
  236. package/dist/jsx/fields/DatePicker/catalog.mjs.map +1 -1
  237. package/dist/jsx/fields/DatePicker/catalog.native.js +2 -30
  238. package/dist/jsx/fields/DatePicker/catalog.native.js.map +1 -1
  239. package/dist/jsx/fields/DatePicker/index.native.js.map +1 -1
  240. package/dist/jsx/fields/DatePicker/parts.mjs +11 -20
  241. package/dist/jsx/fields/DatePicker/parts.mjs.map +1 -1
  242. package/dist/jsx/fields/DatePicker/parts.native.js +13 -22
  243. package/dist/jsx/fields/DatePicker/parts.native.js.map +1 -1
  244. package/dist/jsx/fields/Select/index.mjs +66 -45
  245. package/dist/jsx/fields/Select/index.mjs.map +1 -1
  246. package/dist/jsx/fields/Select/index.native.js +69 -47
  247. package/dist/jsx/fields/Select/index.native.js.map +1 -1
  248. package/dist/jsx/fields/Select/shouldInlineChoices.mjs +3 -1
  249. package/dist/jsx/fields/Select/shouldInlineChoices.mjs.map +1 -1
  250. package/dist/jsx/fields/Select/shouldInlineChoices.native.js +3 -1
  251. package/dist/jsx/fields/Select/shouldInlineChoices.native.js.map +1 -1
  252. package/dist/jsx/fields/TimePicker/catalog.mjs +4 -25
  253. package/dist/jsx/fields/TimePicker/catalog.mjs.map +1 -1
  254. package/dist/jsx/fields/TimePicker/catalog.native.js +7 -29
  255. package/dist/jsx/fields/TimePicker/catalog.native.js.map +1 -1
  256. package/dist/jsx/fields/TimePicker/index.native.js.map +1 -1
  257. package/dist/jsx/fields/TimePicker/parts.mjs +8 -21
  258. package/dist/jsx/fields/TimePicker/parts.mjs.map +1 -1
  259. package/dist/jsx/fields/TimePicker/parts.native.js +8 -21
  260. package/dist/jsx/fields/TimePicker/parts.native.js.map +1 -1
  261. package/dist/jsx/index.js +2 -1
  262. package/dist/jsx/index.js.map +1 -1
  263. package/dist/jsx/index.mjs +2 -1
  264. package/dist/jsx/index.mjs.map +1 -1
  265. package/dist/jsx/index.native.js +2 -0
  266. package/dist/jsx/index.native.js.map +1 -1
  267. package/dist/jsx/shared/floatingList.mjs +14 -5
  268. package/dist/jsx/shared/floatingList.mjs.map +1 -1
  269. package/dist/jsx/shared/floatingList.native.js +15 -4
  270. package/dist/jsx/shared/floatingList.native.js.map +1 -1
  271. package/package.json +8 -8
  272. package/src/Button/circleGlyph.ts +6 -0
  273. package/src/Button/index.tsx +25 -7
  274. package/src/FloatingPanel/FloatingPanel.echoStretch.spec.tsx +67 -0
  275. package/src/FloatingPanel/FloatingPanel.iconEcho.spec.tsx +297 -0
  276. package/src/FloatingPanel/FloatingPanel.menuFace.native.spec.tsx +280 -3
  277. package/src/FloatingPanel/FloatingPanel.seamlessCover.spec.tsx +237 -0
  278. package/src/FloatingPanel/PanelHeader.tsx +26 -0
  279. package/src/FloatingPanel/echoContext.ts +67 -0
  280. package/src/FloatingPanel/iconEcho.tsx +118 -0
  281. package/src/FloatingPanel/index.native.tsx +220 -12
  282. package/src/FloatingPanel/index.tsx +539 -109
  283. package/src/FloatingPanel/paintedBox.spec.ts +95 -0
  284. package/src/FloatingPanel/paintedBox.ts +75 -0
  285. package/src/FloatingPanel/panelWidth.spec.ts +158 -0
  286. package/src/FloatingPanel/panelWidth.ts +122 -0
  287. package/src/FloatingPanel/panelWidthContract.spec.ts +10 -10
  288. package/src/FloatingPanel/pointAnchor.spec.ts +111 -2
  289. package/src/FloatingPanel/pointAnchor.ts +56 -3
  290. package/src/FloatingPanel/useFloatingPanel.spec.tsx +124 -4
  291. package/src/FloatingPanel/useFloatingPanel.ts +112 -34
  292. package/src/InputParts/index.tsx +26 -0
  293. package/src/Wheel/NativeWheel.native.tsx +1 -1
  294. package/src/Wheel/Wheel.spec.tsx +9 -0
  295. package/src/Wheel/index.tsx +12 -3
  296. package/src/Wheel/wheelShared.ts +6 -2
  297. package/src/fields/Combobox/index.tsx +1 -0
  298. package/src/fields/DatePicker/DatePicker.stories.tsx +3 -3
  299. package/src/fields/DatePicker/DatetimePicker.catalog.tsx +17 -45
  300. package/src/fields/DatePicker/DatetimePicker.native.tsx +3 -0
  301. package/src/fields/DatePicker/catalog.tsx +8 -39
  302. package/src/fields/DatePicker/index.native.tsx +3 -0
  303. package/src/fields/DatePicker/parts.tsx +13 -23
  304. package/src/fields/DatePicker/seamlessCover.spec.tsx +180 -0
  305. package/src/fields/DatePicker/touchWeb.spec.tsx +109 -167
  306. package/src/fields/Select/Select.containingBlock.spec.tsx +191 -0
  307. package/src/fields/Select/Select.stories.tsx +43 -0
  308. package/src/fields/Select/index.tsx +109 -45
  309. package/src/fields/Select/shouldInlineChoices.spec.ts +13 -0
  310. package/src/fields/Select/shouldInlineChoices.ts +6 -0
  311. package/src/fields/TimePicker/catalog.tsx +12 -33
  312. package/src/fields/TimePicker/index.native.tsx +2 -0
  313. package/src/fields/TimePicker/parts.tsx +6 -20
  314. package/src/index.ts +3 -0
  315. package/src/shared/floatingList.tsx +21 -5
  316. package/types/Button/circleGlyph.d.ts +7 -0
  317. package/types/Button/circleGlyph.d.ts.map +1 -0
  318. package/types/Button/index.d.ts +2 -6
  319. package/types/Button/index.d.ts.map +1 -1
  320. package/types/FloatingPanel/PanelHeader.d.ts +9 -0
  321. package/types/FloatingPanel/PanelHeader.d.ts.map +1 -0
  322. package/types/FloatingPanel/echoContext.d.ts +38 -0
  323. package/types/FloatingPanel/echoContext.d.ts.map +1 -0
  324. package/types/FloatingPanel/iconEcho.d.ts +29 -0
  325. package/types/FloatingPanel/iconEcho.d.ts.map +1 -0
  326. package/types/FloatingPanel/index.d.ts +69 -0
  327. package/types/FloatingPanel/index.d.ts.map +1 -1
  328. package/types/FloatingPanel/index.native.d.ts +9 -1
  329. package/types/FloatingPanel/index.native.d.ts.map +1 -1
  330. package/types/FloatingPanel/paintedBox.d.ts +29 -0
  331. package/types/FloatingPanel/paintedBox.d.ts.map +1 -0
  332. package/types/FloatingPanel/panelWidth.d.ts +71 -0
  333. package/types/FloatingPanel/panelWidth.d.ts.map +1 -0
  334. package/types/FloatingPanel/pointAnchor.d.ts +17 -1
  335. package/types/FloatingPanel/pointAnchor.d.ts.map +1 -1
  336. package/types/FloatingPanel/useFloatingPanel.d.ts +33 -2
  337. package/types/FloatingPanel/useFloatingPanel.d.ts.map +1 -1
  338. package/types/InputParts/index.d.ts.map +1 -1
  339. package/types/Wheel/index.d.ts.map +1 -1
  340. package/types/Wheel/wheelShared.d.ts +2 -1
  341. package/types/Wheel/wheelShared.d.ts.map +1 -1
  342. package/types/fields/Combobox/index.d.ts.map +1 -1
  343. package/types/fields/DatePicker/DatetimePicker.catalog.d.ts +6 -0
  344. package/types/fields/DatePicker/DatetimePicker.catalog.d.ts.map +1 -1
  345. package/types/fields/DatePicker/DatetimePicker.native.d.ts +3 -0
  346. package/types/fields/DatePicker/DatetimePicker.native.d.ts.map +1 -1
  347. package/types/fields/DatePicker/catalog.d.ts +6 -0
  348. package/types/fields/DatePicker/catalog.d.ts.map +1 -1
  349. package/types/fields/DatePicker/index.native.d.ts +3 -0
  350. package/types/fields/DatePicker/index.native.d.ts.map +1 -1
  351. package/types/fields/DatePicker/parts.d.ts +5 -7
  352. package/types/fields/DatePicker/parts.d.ts.map +1 -1
  353. package/types/fields/Select/index.d.ts +8 -1
  354. package/types/fields/Select/index.d.ts.map +1 -1
  355. package/types/fields/Select/shouldInlineChoices.d.ts +5 -1
  356. package/types/fields/Select/shouldInlineChoices.d.ts.map +1 -1
  357. package/types/fields/TimePicker/catalog.d.ts +7 -1
  358. package/types/fields/TimePicker/catalog.d.ts.map +1 -1
  359. package/types/fields/TimePicker/index.native.d.ts +2 -0
  360. package/types/fields/TimePicker/index.native.d.ts.map +1 -1
  361. package/types/fields/TimePicker/parts.d.ts +1 -3
  362. package/types/fields/TimePicker/parts.d.ts.map +1 -1
  363. package/types/index.d.ts +3 -0
  364. package/types/index.d.ts.map +1 -1
  365. package/types/shared/floatingList.d.ts +5 -1
  366. package/types/shared/floatingList.d.ts.map +1 -1
  367. package/dist/cjs/shared/touchWebPicker.cjs +0 -136
  368. package/dist/cjs/shared/touchWebPicker.native.js +0 -154
  369. package/dist/cjs/shared/touchWebPicker.native.js.map +0 -1
  370. package/dist/esm/shared/touchWebPicker.mjs +0 -105
  371. package/dist/esm/shared/touchWebPicker.mjs.map +0 -1
  372. package/dist/esm/shared/touchWebPicker.native.js +0 -120
  373. package/dist/esm/shared/touchWebPicker.native.js.map +0 -1
  374. package/dist/jsx/shared/touchWebPicker.mjs +0 -105
  375. package/dist/jsx/shared/touchWebPicker.mjs.map +0 -1
  376. package/dist/jsx/shared/touchWebPicker.native.js +0 -154
  377. package/dist/jsx/shared/touchWebPicker.native.js.map +0 -1
  378. package/src/shared/touchWebPicker.spec.ts +0 -98
  379. package/src/shared/touchWebPicker.tsx +0 -158
  380. package/types/shared/touchWebPicker.d.ts +0 -40
  381. package/types/shared/touchWebPicker.d.ts.map +0 -1
@@ -136,6 +136,10 @@ export interface TriggerPlacement {
136
136
  maxHeight: number;
137
137
  /** The menu moved off the trigger's top start corner to stay on screen. */
138
138
  shifted: boolean;
139
+ /** An `"auto"` menu took the trigger's top trailing corner instead. */
140
+ end: boolean;
141
+ /** A pinned menu opened upward: its bottom edge is the trigger's. */
142
+ up: boolean;
139
143
  }
140
144
 
141
145
  /**
@@ -151,17 +155,56 @@ export function placeOverTrigger({
151
155
  height,
152
156
  bounds,
153
157
  rtl = false,
158
+ align = "start",
159
+ pinned = false,
154
160
  }: {
155
161
  trigger: TriggerRect;
156
162
  width: number;
157
163
  height: number;
158
164
  bounds: PanelBounds;
159
165
  rtl?: boolean;
166
+ /**
167
+ * `"auto"` (a menu with no echo row) opens from the trailing top corner when
168
+ * the leading one would run off the end edge and the trailing one fits.
169
+ */
170
+ align?: "start" | "auto";
171
+ /**
172
+ * The menu echoes its icon trigger in the button's own spot (LC-112,
173
+ * FZ-8480), so it never slides off the button vertically: it opens down
174
+ * from the button's top edge, or up from its bottom edge where there is
175
+ * more room above, and scrolls what still does not fit.
176
+ */
177
+ pinned?: boolean;
160
178
  }): TriggerPlacement {
161
179
  const room = Math.max(0, bounds.right - bounds.left);
162
180
  const w = Math.min(Math.max(width, trigger.width), room);
163
- const wantLeft = rtl ? trigger.x + trigger.width - w : trigger.x;
164
- const left = Math.max(bounds.left, Math.min(wantLeft, bounds.right - w));
181
+ const startLeft = rtl ? trigger.x + trigger.width - w : trigger.x;
182
+ const endLeft = rtl ? trigger.x : trigger.x + trigger.width - w;
183
+ // Only the free edge has to clear the padded bounds: the aligned edge is
184
+ // the trigger's own, so a bar button inside the margin keeps its corner.
185
+ const startFree = rtl ? startLeft >= bounds.left : startLeft + w <= bounds.right;
186
+ const endFree = rtl ? endLeft + w <= bounds.right : endLeft >= bounds.left;
187
+ const auto = align === "auto";
188
+ const end = auto && !startFree && endFree;
189
+ const wantLeft = end ? endLeft : startLeft;
190
+ const keep = auto && (end || startFree);
191
+ const left = keep ? wantLeft : Math.max(bounds.left, Math.min(wantLeft, bounds.right - w));
192
+ if (pinned) {
193
+ const triggerBottom = trigger.y + trigger.height;
194
+ const below = Math.max(0, bounds.bottom - trigger.y);
195
+ const above = Math.max(0, triggerBottom - bounds.top);
196
+ const up = height > below && above > below;
197
+ const top = up ? Math.max(bounds.top, triggerBottom - height) : trigger.y;
198
+ return {
199
+ left,
200
+ top,
201
+ width: w,
202
+ maxHeight: up ? above : below,
203
+ shifted: left !== wantLeft,
204
+ end,
205
+ up,
206
+ };
207
+ }
165
208
  const h = Math.min(height, Math.max(0, bounds.bottom - bounds.top));
166
209
  const top = Math.max(bounds.top, Math.min(trigger.y, bounds.bottom - h));
167
210
  return {
@@ -170,6 +213,8 @@ export function placeOverTrigger({
170
213
  width: w,
171
214
  maxHeight: Math.max(0, bounds.bottom - top),
172
215
  shifted: left !== wantLeft || top !== trigger.y,
216
+ end,
217
+ up: false,
173
218
  };
174
219
  }
175
220
 
@@ -200,5 +245,13 @@ export function placeBesideTrigger({
200
245
  const opensUp = height > below && above > below;
201
246
  const maxHeight = Math.min(height, opensUp ? above : below);
202
247
  const top = opensUp ? trigger.y - maxHeight : trigger.y + trigger.height;
203
- return { left, top, width: w, maxHeight, shifted: left !== wantLeft || opensUp };
248
+ return {
249
+ left,
250
+ top,
251
+ width: w,
252
+ maxHeight,
253
+ shifted: left !== wantLeft || opensUp,
254
+ end: false,
255
+ up: opensUp,
256
+ };
204
257
  }
@@ -22,11 +22,13 @@ import { FloatingPanel } from "./index";
22
22
  import { renderWithProviders } from "@multiplatform.one/test-utils";
23
23
  import { Preset, type Knobs } from "@multiplatform.one/theme";
24
24
  import { afterEach, describe, expect, it } from "vitest";
25
+ import { type PanelWidth } from "./panelWidth";
25
26
  import {
26
27
  type PanelWidthMode,
27
28
  measureDrawnRadius,
28
29
  panelAnchorOffset,
29
30
  panelCoverOffset,
31
+ panelViewportPadding,
30
32
  resolvePanelWidthMode,
31
33
  useFloatingPanel,
32
34
  } from "./useFloatingPanel";
@@ -35,10 +37,14 @@ function Harness({
35
37
  open,
36
38
  fitContent,
37
39
  widthMode,
40
+ panelWidth,
41
+ echo,
38
42
  }: {
39
43
  open: boolean;
40
44
  fitContent?: boolean;
41
45
  widthMode?: PanelWidthMode;
46
+ panelWidth?: PanelWidth;
47
+ echo?: boolean;
42
48
  }) {
43
49
  const panel = useFloatingPanel({
44
50
  open,
@@ -46,6 +52,8 @@ function Harness({
46
52
  useAutoUpdate: false,
47
53
  fitContent,
48
54
  widthMode,
55
+ panelWidth,
56
+ echo,
49
57
  });
50
58
  return (
51
59
  <>
@@ -76,7 +84,12 @@ function mockRect(el: HTMLElement, rect: { x: number; y: number; width: number;
76
84
  /** Open the panel against a trigger of a known geometry and hand back the
77
85
  * floating element once floating-ui has positioned it. */
78
86
  async function openAgainstTrigger(
79
- props: { fitContent?: boolean; widthMode?: PanelWidthMode },
87
+ props: {
88
+ fitContent?: boolean;
89
+ widthMode?: PanelWidthMode;
90
+ panelWidth?: PanelWidth;
91
+ echo?: boolean;
92
+ },
80
93
  triggerRect: { x: number; y: number; width: number; height: number },
81
94
  ) {
82
95
  const result = renderWithProviders(<Harness open={false} {...props} />);
@@ -173,6 +186,90 @@ describe("FloatingPanel and DropdownMenu open over their trigger (MPO-38 cover r
173
186
  });
174
187
  });
175
188
 
189
+ describe("FloatingPanel panelWidth setting (LC-112, FZ-8438 r3)", () => {
190
+ const room = () =>
191
+ (document.documentElement.clientWidth || window.innerWidth) - 2 * panelViewportPadding;
192
+
193
+ it('"content" hugs its rows but never drops below an echoed trigger', async () => {
194
+ const result = await openAgainstTrigger({ panelWidth: "content" }, trigger);
195
+ await waitFor(() => {
196
+ const el = result.getByTestId("anchor-floating");
197
+ expect(el.style.minWidth).toBe("240px");
198
+ expect(el.style.width).toBe("");
199
+ expect(el.style.maxWidth).toBe(`${room()}px`);
200
+ });
201
+ });
202
+
203
+ it('"content" without an echo (an icon trigger) has no trigger floor', async () => {
204
+ const result = await openAgainstTrigger({ panelWidth: "content", echo: false }, trigger);
205
+ await waitFor(() => {
206
+ const el = result.getByTestId("anchor-floating");
207
+ expect(el.style.minWidth).toBe("0px");
208
+ expect(el.style.width).toBe("");
209
+ });
210
+ });
211
+
212
+ it('"trigger" is exactly the trigger\'s width', async () => {
213
+ const result = await openAgainstTrigger({ panelWidth: "trigger" }, trigger);
214
+ await waitFor(() => {
215
+ const el = result.getByTestId("anchor-floating");
216
+ expect([el.style.minWidth, el.style.width, el.style.maxWidth]).toEqual([
217
+ "240px",
218
+ "240px",
219
+ "240px",
220
+ ]);
221
+ });
222
+ });
223
+
224
+ it('"screen" from a trigger in the page gutter spans the gutters, both sides', async () => {
225
+ // The trigger sits 16 in: the panel takes that inset at each end, so its
226
+ // start edge is the trigger's and its rows line up under the echo.
227
+ const result = await openAgainstTrigger({ panelWidth: "screen" }, trigger);
228
+ const gutters = (document.documentElement.clientWidth || window.innerWidth) - 2 * trigger.x;
229
+ await waitFor(() => {
230
+ const el = result.getByTestId("anchor-floating");
231
+ expect(el.style.width).toBe(`${gutters}px`);
232
+ expect(el.style.maxWidth).toBe(`${gutters}px`);
233
+ });
234
+ });
235
+
236
+ it('"screen" from a trigger deep in the page is the room between the safe margins', async () => {
237
+ const result = await openAgainstTrigger({ panelWidth: "screen" }, { ...trigger, x: 400 });
238
+ await waitFor(() => {
239
+ const el = result.getByTestId("anchor-floating");
240
+ expect(el.style.width).toBe(`${room()}px`);
241
+ expect(el.style.maxWidth).toBe(`${room()}px`);
242
+ });
243
+ });
244
+
245
+ it("a number is a fixed width, wider or narrower than the trigger", async () => {
246
+ const wide = await openAgainstTrigger({ panelWidth: 320 }, trigger);
247
+ await waitFor(() => {
248
+ expect(wide.getByTestId("anchor-floating").style.width).toBe("320px");
249
+ });
250
+ wide.unmount();
251
+ const narrow = await openAgainstTrigger({ panelWidth: 180 }, trigger);
252
+ await waitFor(() => {
253
+ const el = narrow.getByTestId("anchor-floating");
254
+ expect([el.style.minWidth, el.style.width, el.style.maxWidth]).toEqual([
255
+ "180px",
256
+ "180px",
257
+ "180px",
258
+ ]);
259
+ });
260
+ });
261
+
262
+ it("wins over widthMode when both are set", async () => {
263
+ const result = await openAgainstTrigger(
264
+ { widthMode: "fit-content", panelWidth: "trigger" },
265
+ trigger,
266
+ );
267
+ await waitFor(() => {
268
+ expect(result.getByTestId("anchor-floating").style.width).toBe("240px");
269
+ });
270
+ });
271
+ });
272
+
176
273
  describe("FloatingPanel width modes (MPO-38)", () => {
177
274
  it("resolves the deprecated boolean onto the three modes", () => {
178
275
  expect(resolvePanelWidthMode(undefined, undefined)).toBe("at-least-trigger");
@@ -441,14 +538,15 @@ describe("FloatingPanel drawn radius (MPO-129)", () => {
441
538
  /**
442
539
  * CONTAINER-CAP (MPO-56). Part 2b files floating panels under DG-RAD-04: the
443
540
  * class value on the panel's own padding is the ceiling, and the trigger's
444
- * drawn arc (above) only ever lowers it.
541
+ * drawn arc (above) lowers it. A covering panel takes the arc whole (FZ-8438).
445
542
  */
446
- function RadiusHarness({ open }: { open: boolean }) {
447
- const panel = useFloatingPanel({ open, onOpenChange: () => {}, useAutoUpdate: false });
543
+ function RadiusHarness({ open, cover = false }: { open: boolean; cover?: boolean }) {
544
+ const panel = useFloatingPanel({ open, onOpenChange: () => {}, useAutoUpdate: false, cover });
448
545
  return (
449
546
  <div
450
547
  data-testid="radius-trigger"
451
548
  data-radius={String(panel.dropdownRadius)}
549
+ data-inset={String(panel.coverCornerInset)}
452
550
  ref={panel.setReferenceRef}
453
551
  />
454
552
  );
@@ -513,4 +611,26 @@ describe("FloatingPanel radius is CONTAINER-CAP on its padding (DG-RAD-04, MPO-5
513
611
  );
514
612
  await waitFor(() => expect(anchor.getAttribute("data-radius")).toBe("18"));
515
613
  });
614
+
615
+ it("a covering panel takes the trigger's drawn arc exactly, past the cap (LC-112, FZ-8438)", async () => {
616
+ const result = renderWithProviders(
617
+ <Preset overrides={{ borderRadius: "full" }}>
618
+ <RadiusHarness open={false} cover />
619
+ </Preset>,
620
+ );
621
+ const anchor = result.getByTestId("radius-trigger");
622
+ Object.assign(anchor.style, {
623
+ borderTopLeftRadius: "50px",
624
+ backgroundColor: "rgb(255, 255, 255)",
625
+ });
626
+ mockRect(anchor, { x: 0, y: 0, width: 240, height: 44 });
627
+ result.rerender(
628
+ <Preset overrides={{ borderRadius: "full" }}>
629
+ <RadiusHarness open cover />
630
+ </Preset>,
631
+ );
632
+ await waitFor(() => expect(anchor.getAttribute("data-radius")).toBe("22"));
633
+ // Past the cap, the bottom insets by the arc's sagitta at 45° so rows clear it.
634
+ expect(anchor.getAttribute("data-inset")).toBe(String(Math.ceil(22 * (1 - Math.SQRT1_2))));
635
+ });
516
636
  });
@@ -22,6 +22,8 @@ import {
22
22
  import { getTokenValue, type TransitionProp } from "tamagui";
23
23
  import { clampDropdownRadius, scrollArrowThreshold } from "../shared/floatingList";
24
24
  import { containsAcrossPanels } from "../shared/panelPortal";
25
+ import { groupSurfaceOf } from "./paintedBox";
26
+ import { type PanelWidth, panelWidthBox, screenBounds } from "./panelWidth";
25
27
 
26
28
  // React Native defines a `window` global without DOM event methods, so a bare
27
29
  // `typeof window` check is not enough — the floating cores mount this hook on
@@ -43,6 +45,24 @@ function roundToDevicePixel(value: number): number {
43
45
  export const panelAnimationDuration = motionDurationMs(motionFor("quick").overlay.transition);
44
46
  export const panelGrowDuration = panelAnimationDuration;
45
47
  export const panelViewportPadding = 10;
48
+
49
+ /** The room a `"screen"` panel spans for this trigger (see `screenBounds`). */
50
+ function screenRoom(
51
+ reference: { getBoundingClientRect(): { left: number; right: number } },
52
+ viewportWidth: number,
53
+ ) {
54
+ const box = reference.getBoundingClientRect();
55
+ return screenBounds({
56
+ triggerLeft: box.left,
57
+ triggerRight: box.right,
58
+ boundsLeft: panelViewportPadding,
59
+ boundsRight: viewportWidth - panelViewportPadding,
60
+ rtl:
61
+ typeof Element !== "undefined" &&
62
+ reference instanceof Element &&
63
+ getComputedStyle(reference).direction === "rtl",
64
+ });
65
+ }
46
66
  /** Gap between trigger and panel: ZERO for the FloatingPanel family (one
47
67
  * source, Axiom 5); `OVERLAY_ANCHOR_GAP` still governs FREE overlays. */
48
68
  export const panelAnchorOffset = OVERLAY_ATTACH_GAP;
@@ -127,6 +147,14 @@ export interface UseFloatingPanelOptions {
127
147
  */
128
148
  freeOverlay?: boolean;
129
149
  cover?: boolean;
150
+ /**
151
+ * How wide the panel opens (LC-112, FZ-8438 r3): `"content"`, `"trigger"`,
152
+ * `"screen"` or a fixed width. Wins over `widthMode` when set; unset keeps
153
+ * `widthMode`'s behaviour, so no consumer changes until it asks.
154
+ */
155
+ panelWidth?: PanelWidth;
156
+ /** The panel's first row echoes the trigger. Default: a covering panel. */
157
+ echo?: boolean;
130
158
  }
131
159
 
132
160
  /** One place that resolves the deprecated boolean onto the three modes, so
@@ -168,9 +196,15 @@ export interface UseFloatingPanelReturn {
168
196
  /**
169
197
  * Panel corner radius: CONTAINER-CAP on the panel's own padding (DG-RAD-04),
170
198
  * lowered to the trigger's measured drawn radius once open when that arc
171
- * is smaller, so the panel never rounds past the control it hangs from.
199
+ * is smaller. A covering panel (LC-112 seamless cover) takes the trigger's
200
+ * arc whole, past the cap.
172
201
  */
173
202
  dropdownRadius: string | number;
203
+ /**
204
+ * Bottom inset a covering panel adds when its arc runs past the cap, so the
205
+ * last row's highlight and the scroll arrow clear the rounded corners.
206
+ */
207
+ coverCornerInset: number;
174
208
  hasBorder: boolean;
175
209
  panelTransition: string;
176
210
  knobProps: ReturnType<typeof useResolvedKnobs>["knobProps"];
@@ -202,7 +236,7 @@ function paintsACorner(el: HTMLElement): boolean {
202
236
  * top-left corner and actually paints one. Nothing painted → null, so the
203
237
  * caller falls back to the clamped token rather than to zero.
204
238
  */
205
- function paintedCornerBox(root: HTMLElement): HTMLElement | null {
239
+ export function paintedCornerBox(root: HTMLElement): HTMLElement | null {
206
240
  if (paintsACorner(root)) return root;
207
241
  const rootRect = root.getBoundingClientRect();
208
242
  for (const el of Array.from(root.querySelectorAll<HTMLElement>("*"))) {
@@ -250,6 +284,8 @@ export function useFloatingPanel({
250
284
  fitContent,
251
285
  freeOverlay = false,
252
286
  cover = true,
287
+ panelWidth,
288
+ echo = cover && !freeOverlay,
253
289
  }: UseFloatingPanelOptions): UseFloatingPanelReturn {
254
290
  const widthMode = resolvePanelWidthMode(widthModeProp, fitContent);
255
291
  // An overlay floats over the page: it never inherits a painted ancestor.
@@ -268,10 +304,18 @@ export function useFloatingPanel({
268
304
  radiusStop && typeof panelPadPx === "number"
269
305
  ? resolveRadiusClass("CONTAINER-CAP", radiusStop, { paddingPx: panelPadPx })
270
306
  : undefined;
271
- const dropdownRadius =
307
+ const capped =
272
308
  radiusCap === undefined
273
309
  ? (measuredRadius ?? clampDropdownRadius(knobProps.borderRadius.borderRadius))
274
310
  : Math.min(measuredRadius ?? radiusCap, radiusCap);
311
+ // LC-112 seamless cover (FZ-8438): a panel laid over its trigger IS the
312
+ // trigger grown open, so all four corners take the trigger's drawn arc.
313
+ // The container cap only governs panels that sit beside their trigger.
314
+ const dropdownRadius = cover && !freeOverlay && measuredRadius != null ? measuredRadius : capped;
315
+ const coverCornerInset =
316
+ typeof dropdownRadius === "number" && typeof capped === "number" && dropdownRadius > capped
317
+ ? Math.ceil(dropdownRadius * (1 - Math.SQRT1_2))
318
+ : 0;
275
319
 
276
320
  // ── State ─────────────────────────────────────────────────
277
321
 
@@ -321,7 +365,11 @@ export function useFloatingPanel({
321
365
  const setupForOpen = useCallback(() => {
322
366
  computePlacement();
323
367
  if (canUseDom && triggerRef.current) {
324
- setMeasuredRadius(freeOverlay ? null : measureDrawnRadius(triggerRef.current));
368
+ setMeasuredRadius(
369
+ freeOverlay
370
+ ? null
371
+ : measureDrawnRadius(groupSurfaceOf(triggerRef.current) ?? triggerRef.current),
372
+ );
325
373
  }
326
374
  onBeforeOpenRef.current?.();
327
375
  if (closingTimerRef.current) clearTimeout(closingTimerRef.current);
@@ -381,40 +429,69 @@ export function useFloatingPanel({
381
429
  // Horizontal collision handling: panels anchored near the right viewport
382
430
  // edge (e.g. a ColorPicker in the last table column) shift back into view
383
431
  // instead of clipping. Vertical placement is handled by computePlacement.
384
- shift({ padding: panelViewportPadding }),
385
- ...(widthMode === "fit-content"
386
- ? []
387
- : [
432
+ // A screen-wide panel shifts inside the same room its width was cut to,
433
+ // so a trigger in the page gutter keeps its start edge.
434
+ shift(({ elements }) => {
435
+ if (panelWidth !== "screen") return { padding: panelViewportPadding };
436
+ const viewportWidth = document.documentElement.clientWidth || window.innerWidth || 0;
437
+ const room = screenRoom(elements.reference, viewportWidth);
438
+ return { padding: { left: room.left, right: viewportWidth - room.right } };
439
+ }),
440
+ ...(panelWidth !== undefined
441
+ ? [
388
442
  size({
389
- apply({
390
- rects: {
391
- reference: { width },
392
- },
393
- elements,
394
- }) {
395
- // MPO-38 width modes. `at-least-trigger` writes only min-width,
396
- // so content may still grow the panel wider; `match-trigger`
397
- // pins all three so the panel is the trigger's width EXACTLY,
398
- // for content narrower AND wider than it.
399
- //
400
- // Any oversize on an anchored listbox breaks the flush
401
- // start-alignment (a wider panel near the viewport edge gets
402
- // shift()ed off the trigger's x) and lets trigger decorations
403
- // peek around the panel as ghosted edges.
404
- const style: Record<string, number> =
405
- widthMode === "match-trigger"
406
- ? { minWidth: width, width, maxWidth: width }
407
- : { minWidth: width };
408
- Object.assign(floatingStyleRef.current, style);
409
- elements.floating.style.minWidth = `${width}px`;
410
- if (widthMode === "match-trigger") {
411
- elements.floating.style.width = `${width}px`;
412
- elements.floating.style.maxWidth = `${width}px`;
413
- }
443
+ apply({ rects: { reference }, elements }) {
444
+ const viewportWidth = document.documentElement.clientWidth || window.innerWidth || 0;
445
+ const room =
446
+ panelWidth === "screen"
447
+ ? screenRoom(elements.reference, viewportWidth)
448
+ : { left: panelViewportPadding, right: viewportWidth - panelViewportPadding };
449
+ const box = panelWidthBox(panelWidth, {
450
+ trigger: reference.width,
451
+ available: room.right - room.left,
452
+ echo,
453
+ });
454
+ Object.assign(floatingStyleRef.current, box);
455
+ elements.floating.style.minWidth = `${box.minWidth}px`;
456
+ elements.floating.style.maxWidth = `${box.maxWidth}px`;
457
+ elements.floating.style.width = box.width === undefined ? "" : `${box.width}px`;
414
458
  },
415
459
  padding: panelViewportPadding,
416
460
  }),
417
- ]),
461
+ ]
462
+ : widthMode === "fit-content"
463
+ ? []
464
+ : [
465
+ size({
466
+ apply({
467
+ rects: {
468
+ reference: { width },
469
+ },
470
+ elements,
471
+ }) {
472
+ // MPO-38 width modes. `at-least-trigger` writes only min-width,
473
+ // so content may still grow the panel wider; `match-trigger`
474
+ // pins all three so the panel is the trigger's width EXACTLY,
475
+ // for content narrower AND wider than it.
476
+ //
477
+ // Any oversize on an anchored listbox breaks the flush
478
+ // start-alignment (a wider panel near the viewport edge gets
479
+ // shift()ed off the trigger's x) and lets trigger decorations
480
+ // peek around the panel as ghosted edges.
481
+ const style: Record<string, number> =
482
+ widthMode === "match-trigger"
483
+ ? { minWidth: width, width, maxWidth: width }
484
+ : { minWidth: width };
485
+ Object.assign(floatingStyleRef.current, style);
486
+ elements.floating.style.minWidth = `${width}px`;
487
+ if (widthMode === "match-trigger") {
488
+ elements.floating.style.width = `${width}px`;
489
+ elements.floating.style.maxWidth = `${width}px`;
490
+ }
491
+ },
492
+ padding: panelViewportPadding,
493
+ }),
494
+ ]),
418
495
  ];
419
496
 
420
497
  const { x, y, strategy, context, refs, update } = useFloating({
@@ -556,6 +633,7 @@ export function useFloatingPanel({
556
633
  setReferenceRef,
557
634
  setMounted,
558
635
  dropdownRadius,
636
+ coverCornerInset,
559
637
  hasBorder,
560
638
  panelTransition: panelTransitionFor(knobProps.motion.overlay.transition),
561
639
  knobProps,
@@ -28,6 +28,7 @@ import {
28
28
  View,
29
29
  XGroup,
30
30
  createStyledContext,
31
+ getTokenValue,
31
32
  getVariable,
32
33
  isWeb,
33
34
  styled,
@@ -61,6 +62,7 @@ import { useControlGroupVertical, useControlGrouped } from "../shared/groupConte
61
62
  import { nativeAutoFocus } from "../shared/nativeAutoFocus";
62
63
  import { pressSlopProps } from "../shared/pressSlopProps";
63
64
  import { useIsInTableCell } from "../shared/tableCellContext";
65
+ import { useInPanelEcho, usePanelTriggerRadiusReporter } from "../FloatingPanel/echoContext";
64
66
  import { useFieldA11y, useFieldDescribedBy } from "../fieldLayout";
65
67
 
66
68
  /**
@@ -498,6 +500,29 @@ const InputGroupImpl = InputGroupFrame.styleable<{
498
500
  (props as { width?: unknown }).width == null
499
501
  ? { minWidth: 0, maxWidth: "100%" as const }
500
502
  : undefined;
503
+ // LC-112 seamless cover (FZ-8438): a covering panel repeats this frame's
504
+ // arc at its corners, and an echo of this frame draws no chrome of its own.
505
+ const inPanelEcho = useInPanelEcho();
506
+ const reportRadius = usePanelTriggerRadiusReporter();
507
+ const drawnRadiusToken = sizeTableRadius?.borderRadius ?? knobProps.borderRadius.borderRadius;
508
+ const drawnRadius =
509
+ typeof drawnRadiusToken === "number"
510
+ ? drawnRadiusToken
511
+ : Number(getTokenValue(drawnRadiusToken as never, "radius"));
512
+ useEffect(() => {
513
+ if (reportRadius && !inPanelEcho && !grouped && !inTableCell && Number.isFinite(drawnRadius)) {
514
+ reportRadius(drawnRadius);
515
+ }
516
+ }, [reportRadius, inPanelEcho, grouped, inTableCell, drawnRadius]);
517
+ const echoChrome = inPanelEcho
518
+ ? {
519
+ borderColor: "transparent",
520
+ backgroundColor: "transparent",
521
+ elevation: undefined,
522
+ shadowColor: "transparent",
523
+ shadowOpacity: 0,
524
+ }
525
+ : undefined;
501
526
 
502
527
  // Chromeless rendering inside table cells
503
528
  if (inTableCell) {
@@ -615,6 +640,7 @@ const InputGroupImpl = InputGroupFrame.styleable<{
615
640
  hoverStyle: { ...disabledState.surfaceKnobProps },
616
641
  }
617
642
  : undefined)}
643
+ {...echoChrome}
618
644
  overflow={boxOverflow}
619
645
  >
620
646
  {children}
@@ -347,7 +347,7 @@ export function NativeWheel<T = string | number>(props: WheelProps<T>) {
347
347
  right={0}
348
348
  top={padTop}
349
349
  height={itemHeight}
350
- backgroundColor="$color4"
350
+ backgroundColor={layout.selectedFill}
351
351
  pointerEvents="none"
352
352
  style={layout.highlightStyle as never}
353
353
  data-testid="wheel-selection"
@@ -209,6 +209,15 @@ describe("Wheel iOS picker contract", () => {
209
209
  expect(style.borderBottomWidth === "0px" || style.borderBottomWidth === "").toBe(true);
210
210
  });
211
211
 
212
+ it("does not paint the band with the dark elevated surface colour (FZ-8438)", () => {
213
+ renderWithProviders(
214
+ <Wheel items={[1, 2, 3, 4, 5]} value={3} onChange={() => {}} visibleItems={5} />,
215
+ );
216
+ const band = screen.getByTestId("wheel-selection");
217
+ const paint = `${band.className} ${band.getAttribute("style") ?? ""}`;
218
+ expect(paint).not.toMatch(/_bg-color4|--color4/);
219
+ });
220
+
212
221
  it("seats the selected row in the highlight band", () => {
213
222
  renderWithProviders(
214
223
  <Wheel
@@ -154,8 +154,15 @@ function WebWheel<T = string | number>(props: WheelProps<T>) {
154
154
  onItemCross,
155
155
  "aria-label": ariaLabel,
156
156
  } = props;
157
- const { knobProps, size, itemHeight, snapDurationMs, highlightStyle, highlightClassName } =
158
- useWheelLayout(props);
157
+ const {
158
+ knobProps,
159
+ selectedFill,
160
+ size,
161
+ itemHeight,
162
+ snapDurationMs,
163
+ highlightStyle,
164
+ highlightClassName,
165
+ } = useWheelLayout(props);
159
166
  const columnWidth = widthProp ?? defaultColumnWidth;
160
167
  ensureWheelFocusStyles();
161
168
  const containerRef = useRef<HTMLDivElement>(null);
@@ -672,7 +679,9 @@ function WebWheel<T = string | number>(props: WheelProps<T>) {
672
679
  {/* iOS 14+ selection fill — LC-71 selected=fill, never hairline borders */}
673
680
  <SelectionIndicator
674
681
  className={highlightClassName}
675
- backgroundColor="$color4"
682
+ // FZ-8438: the selection tint (FZ-8355). $color4 is the dark elevated
683
+ // surface itself, so the band vanished on a dark cover panel.
684
+ backgroundColor={selectedFill}
676
685
  top={paddingItems * itemHeight}
677
686
  height={itemHeight}
678
687
  style={highlightStyle}
@@ -1,6 +1,6 @@
1
1
  import type { CSSProperties, ReactNode } from "react";
2
2
  import { motionDurationMs, sizeTokenStep, useResolvedKnobs } from "@multiplatform.one/theme";
3
- import type { SizeTokens } from "tamagui";
3
+ import type { ColorTokens, SizeTokens } from "tamagui";
4
4
  import { getConfig, getTokens } from "tamagui";
5
5
 
6
6
  export interface WheelItem<T = string | number> {
@@ -198,7 +198,7 @@ export function useWheelLayout<T>({
198
198
  size: sizeProp,
199
199
  highlightEdges = "all",
200
200
  }: Pick<WheelProps<T>, "visibleItems" | "itemHeight" | "size" | "highlightEdges">) {
201
- const { knobProps } = useResolvedKnobs();
201
+ const { knobProps, state } = useResolvedKnobs();
202
202
  const size = sizeProp ?? wheelFontByStep[sizeTokenStep(knobProps.sizeToken)];
203
203
  const bodyFont = (knobProps.body as { fontFamily?: unknown }).fontFamily;
204
204
  const itemHeight =
@@ -208,6 +208,10 @@ export function useWheelLayout<T>({
208
208
  const radiusToken = knobProps.borderRadius.borderRadius as string | number;
209
209
  return {
210
210
  knobProps,
211
+ // The band is the selection tint (LC-131, FZ-8355) from the state recipe;
212
+ // a preset with no accent tint falls back one step above the elevated
213
+ // dark surface, so the band still shows on a cover panel (FZ-8438).
214
+ selectedFill: (state.selected.surfaceKnobProps.backgroundColor ?? "$color5") as ColorTokens,
211
215
  size,
212
216
  itemHeight,
213
217
  frameHeight,
@@ -1502,6 +1502,7 @@ function FloatingComboboxCore({
1502
1502
  {...elevatedSurfaceProps}
1503
1503
  borderRadius={panel.dropdownRadius}
1504
1504
  data-testid="combobox-dropdown"
1505
+ paddingBottom={panel.coverCornerInset || undefined}
1505
1506
  >
1506
1507
  {!hideSearch && (
1507
1508
  <InputParts size={knobProps.sizeToken}>
@@ -131,9 +131,9 @@ export const StateContract: Story = {
131
131
  };
132
132
 
133
133
  /**
134
- * LC-266 (FZ-8114): on a coarse-pointer browser each field opens the
135
- * browser's own picker; a fine pointer keeps the MPO calendar and wheels. The
136
- * lines below show the committed value, which is the same on both paths.
134
+ * LC-112 seamless cover (FZ-8438): on a coarse-pointer browser each field
135
+ * opens the same MPO panel as on desktop, one surface over the field with
136
+ * the calendar or the wheels. The lines below show the committed value.
137
137
  */
138
138
  export const TouchWeb: Story = {
139
139
  render: () => {