@multiplatform.one/forms 7.70.0 → 7.72.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 (333) hide show
  1. package/dist/cjs/Button/index.cjs +16 -0
  2. package/dist/cjs/Button/index.native.js +16 -0
  3. package/dist/cjs/Button/index.native.js.map +1 -1
  4. package/dist/cjs/FloatingPanel/echoContext.cjs +39 -0
  5. package/dist/cjs/FloatingPanel/echoContext.native.js +42 -0
  6. package/dist/cjs/FloatingPanel/echoContext.native.js.map +1 -0
  7. package/dist/cjs/FloatingPanel/index.cjs +212 -80
  8. package/dist/cjs/FloatingPanel/index.native.js +92 -17
  9. package/dist/cjs/FloatingPanel/index.native.js.map +1 -1
  10. package/dist/cjs/FloatingPanel/panelWidth.cjs +92 -0
  11. package/dist/cjs/FloatingPanel/panelWidth.native.js +99 -0
  12. package/dist/cjs/FloatingPanel/panelWidth.native.js.map +1 -0
  13. package/dist/cjs/FloatingPanel/pointAnchor.cjs +15 -5
  14. package/dist/cjs/FloatingPanel/pointAnchor.native.js +15 -5
  15. package/dist/cjs/FloatingPanel/pointAnchor.native.js.map +1 -1
  16. package/dist/cjs/FloatingPanel/useFloatingPanel.cjs +58 -4
  17. package/dist/cjs/FloatingPanel/useFloatingPanel.native.js +69 -4
  18. package/dist/cjs/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  19. package/dist/cjs/InputParts/index.cjs +20 -1
  20. package/dist/cjs/InputParts/index.native.js +22 -2
  21. package/dist/cjs/InputParts/index.native.js.map +1 -1
  22. package/dist/cjs/Wheel/NativeWheel.native.js +1 -1
  23. package/dist/cjs/Wheel/NativeWheel.native.js.map +1 -1
  24. package/dist/cjs/Wheel/index.cjs +2 -1
  25. package/dist/cjs/Wheel/index.native.js +4 -1
  26. package/dist/cjs/Wheel/index.native.js.map +1 -1
  27. package/dist/cjs/Wheel/wheelShared.cjs +6 -1
  28. package/dist/cjs/Wheel/wheelShared.native.js +7 -1
  29. package/dist/cjs/Wheel/wheelShared.native.js.map +1 -1
  30. package/dist/cjs/fields/Combobox/index.cjs +1 -0
  31. package/dist/cjs/fields/Combobox/index.native.js +1 -0
  32. package/dist/cjs/fields/Combobox/index.native.js.map +1 -1
  33. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.cjs +6 -30
  34. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js +11 -34
  35. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  36. package/dist/cjs/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  37. package/dist/cjs/fields/DatePicker/catalog.cjs +2 -28
  38. package/dist/cjs/fields/DatePicker/catalog.native.js +2 -30
  39. package/dist/cjs/fields/DatePicker/catalog.native.js.map +1 -1
  40. package/dist/cjs/fields/DatePicker/index.native.js.map +1 -1
  41. package/dist/cjs/fields/DatePicker/parts.cjs +11 -20
  42. package/dist/cjs/fields/DatePicker/parts.native.js +13 -22
  43. package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
  44. package/dist/cjs/fields/SearchInput/index.cjs +23 -1
  45. package/dist/cjs/fields/SearchInput/index.native.js +23 -1
  46. package/dist/cjs/fields/SearchInput/index.native.js.map +1 -1
  47. package/dist/cjs/fields/Select/index.cjs +65 -44
  48. package/dist/cjs/fields/Select/index.native.js +69 -47
  49. package/dist/cjs/fields/Select/index.native.js.map +1 -1
  50. package/dist/cjs/fields/Select/shouldInlineChoices.cjs +3 -1
  51. package/dist/cjs/fields/Select/shouldInlineChoices.native.js +3 -1
  52. package/dist/cjs/fields/Select/shouldInlineChoices.native.js.map +1 -1
  53. package/dist/cjs/fields/TimePicker/catalog.cjs +4 -25
  54. package/dist/cjs/fields/TimePicker/catalog.native.js +7 -29
  55. package/dist/cjs/fields/TimePicker/catalog.native.js.map +1 -1
  56. package/dist/cjs/fields/TimePicker/index.native.js.map +1 -1
  57. package/dist/cjs/fields/TimePicker/parts.cjs +8 -21
  58. package/dist/cjs/fields/TimePicker/parts.native.js +8 -21
  59. package/dist/cjs/fields/TimePicker/parts.native.js.map +1 -1
  60. package/dist/cjs/index.native.js.map +1 -1
  61. package/dist/cjs/shared/floatingList.cjs +13 -4
  62. package/dist/cjs/shared/floatingList.native.js +15 -4
  63. package/dist/cjs/shared/floatingList.native.js.map +1 -1
  64. package/dist/esm/Button/index.mjs +17 -1
  65. package/dist/esm/Button/index.mjs.map +1 -1
  66. package/dist/esm/Button/index.native.js +17 -1
  67. package/dist/esm/Button/index.native.js.map +1 -1
  68. package/dist/esm/FloatingPanel/echoContext.mjs +11 -0
  69. package/dist/esm/FloatingPanel/echoContext.mjs.map +1 -0
  70. package/dist/esm/FloatingPanel/echoContext.native.js +11 -0
  71. package/dist/esm/FloatingPanel/echoContext.native.js.map +1 -0
  72. package/dist/esm/FloatingPanel/index.mjs +213 -83
  73. package/dist/esm/FloatingPanel/index.mjs.map +1 -1
  74. package/dist/esm/FloatingPanel/index.native.js +92 -19
  75. package/dist/esm/FloatingPanel/index.native.js.map +1 -1
  76. package/dist/esm/FloatingPanel/panelWidth.mjs +63 -0
  77. package/dist/esm/FloatingPanel/panelWidth.mjs.map +1 -0
  78. package/dist/esm/FloatingPanel/panelWidth.native.js +67 -0
  79. package/dist/esm/FloatingPanel/panelWidth.native.js.map +1 -0
  80. package/dist/esm/FloatingPanel/pointAnchor.mjs +15 -5
  81. package/dist/esm/FloatingPanel/pointAnchor.mjs.map +1 -1
  82. package/dist/esm/FloatingPanel/pointAnchor.native.js +15 -5
  83. package/dist/esm/FloatingPanel/pointAnchor.native.js.map +1 -1
  84. package/dist/esm/FloatingPanel/useFloatingPanel.mjs +58 -5
  85. package/dist/esm/FloatingPanel/useFloatingPanel.mjs.map +1 -1
  86. package/dist/esm/FloatingPanel/useFloatingPanel.native.js +69 -5
  87. package/dist/esm/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  88. package/dist/esm/InputParts/index.mjs +21 -2
  89. package/dist/esm/InputParts/index.mjs.map +1 -1
  90. package/dist/esm/InputParts/index.native.js +23 -3
  91. package/dist/esm/InputParts/index.native.js.map +1 -1
  92. package/dist/esm/Wheel/NativeWheel.native.js +1 -1
  93. package/dist/esm/Wheel/NativeWheel.native.js.map +1 -1
  94. package/dist/esm/Wheel/index.mjs +2 -1
  95. package/dist/esm/Wheel/index.mjs.map +1 -1
  96. package/dist/esm/Wheel/index.native.js +4 -1
  97. package/dist/esm/Wheel/index.native.js.map +1 -1
  98. package/dist/esm/Wheel/wheelShared.mjs +6 -1
  99. package/dist/esm/Wheel/wheelShared.mjs.map +1 -1
  100. package/dist/esm/Wheel/wheelShared.native.js +7 -1
  101. package/dist/esm/Wheel/wheelShared.native.js.map +1 -1
  102. package/dist/esm/fields/Combobox/index.mjs +1 -0
  103. package/dist/esm/fields/Combobox/index.mjs.map +1 -1
  104. package/dist/esm/fields/Combobox/index.native.js +1 -0
  105. package/dist/esm/fields/Combobox/index.native.js.map +1 -1
  106. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs +6 -30
  107. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -1
  108. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js +11 -34
  109. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  110. package/dist/esm/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  111. package/dist/esm/fields/DatePicker/catalog.mjs +2 -28
  112. package/dist/esm/fields/DatePicker/catalog.mjs.map +1 -1
  113. package/dist/esm/fields/DatePicker/catalog.native.js +2 -30
  114. package/dist/esm/fields/DatePicker/catalog.native.js.map +1 -1
  115. package/dist/esm/fields/DatePicker/index.native.js.map +1 -1
  116. package/dist/esm/fields/DatePicker/parts.mjs +11 -20
  117. package/dist/esm/fields/DatePicker/parts.mjs.map +1 -1
  118. package/dist/esm/fields/DatePicker/parts.native.js +13 -22
  119. package/dist/esm/fields/DatePicker/parts.native.js.map +1 -1
  120. package/dist/esm/fields/SearchInput/index.mjs +25 -3
  121. package/dist/esm/fields/SearchInput/index.mjs.map +1 -1
  122. package/dist/esm/fields/SearchInput/index.native.js +25 -3
  123. package/dist/esm/fields/SearchInput/index.native.js.map +1 -1
  124. package/dist/esm/fields/Select/index.mjs +66 -45
  125. package/dist/esm/fields/Select/index.mjs.map +1 -1
  126. package/dist/esm/fields/Select/index.native.js +70 -48
  127. package/dist/esm/fields/Select/index.native.js.map +1 -1
  128. package/dist/esm/fields/Select/shouldInlineChoices.mjs +3 -1
  129. package/dist/esm/fields/Select/shouldInlineChoices.mjs.map +1 -1
  130. package/dist/esm/fields/Select/shouldInlineChoices.native.js +3 -1
  131. package/dist/esm/fields/Select/shouldInlineChoices.native.js.map +1 -1
  132. package/dist/esm/fields/TimePicker/catalog.mjs +4 -25
  133. package/dist/esm/fields/TimePicker/catalog.mjs.map +1 -1
  134. package/dist/esm/fields/TimePicker/catalog.native.js +7 -29
  135. package/dist/esm/fields/TimePicker/catalog.native.js.map +1 -1
  136. package/dist/esm/fields/TimePicker/index.native.js.map +1 -1
  137. package/dist/esm/fields/TimePicker/parts.mjs +8 -21
  138. package/dist/esm/fields/TimePicker/parts.mjs.map +1 -1
  139. package/dist/esm/fields/TimePicker/parts.native.js +8 -21
  140. package/dist/esm/fields/TimePicker/parts.native.js.map +1 -1
  141. package/dist/esm/index.mjs.map +1 -1
  142. package/dist/esm/index.native.js.map +1 -1
  143. package/dist/esm/shared/floatingList.mjs +14 -5
  144. package/dist/esm/shared/floatingList.mjs.map +1 -1
  145. package/dist/esm/shared/floatingList.native.js +16 -5
  146. package/dist/esm/shared/floatingList.native.js.map +1 -1
  147. package/dist/jsx/Button/index.mjs +17 -1
  148. package/dist/jsx/Button/index.mjs.map +1 -1
  149. package/dist/jsx/Button/index.native.js +16 -0
  150. package/dist/jsx/Button/index.native.js.map +1 -1
  151. package/dist/jsx/FloatingPanel/echoContext.mjs +11 -0
  152. package/dist/jsx/FloatingPanel/echoContext.mjs.map +1 -0
  153. package/dist/jsx/FloatingPanel/echoContext.native.js +42 -0
  154. package/dist/jsx/FloatingPanel/echoContext.native.js.map +1 -0
  155. package/dist/jsx/FloatingPanel/index.mjs +213 -83
  156. package/dist/jsx/FloatingPanel/index.mjs.map +1 -1
  157. package/dist/jsx/FloatingPanel/index.native.js +92 -17
  158. package/dist/jsx/FloatingPanel/index.native.js.map +1 -1
  159. package/dist/jsx/FloatingPanel/panelWidth.mjs +63 -0
  160. package/dist/jsx/FloatingPanel/panelWidth.mjs.map +1 -0
  161. package/dist/jsx/FloatingPanel/panelWidth.native.js +99 -0
  162. package/dist/jsx/FloatingPanel/panelWidth.native.js.map +1 -0
  163. package/dist/jsx/FloatingPanel/pointAnchor.mjs +15 -5
  164. package/dist/jsx/FloatingPanel/pointAnchor.mjs.map +1 -1
  165. package/dist/jsx/FloatingPanel/pointAnchor.native.js +15 -5
  166. package/dist/jsx/FloatingPanel/pointAnchor.native.js.map +1 -1
  167. package/dist/jsx/FloatingPanel/useFloatingPanel.mjs +58 -5
  168. package/dist/jsx/FloatingPanel/useFloatingPanel.mjs.map +1 -1
  169. package/dist/jsx/FloatingPanel/useFloatingPanel.native.js +69 -4
  170. package/dist/jsx/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  171. package/dist/jsx/InputParts/index.mjs +21 -2
  172. package/dist/jsx/InputParts/index.mjs.map +1 -1
  173. package/dist/jsx/InputParts/index.native.js +22 -2
  174. package/dist/jsx/InputParts/index.native.js.map +1 -1
  175. package/dist/jsx/Wheel/NativeWheel.native.js +1 -1
  176. package/dist/jsx/Wheel/NativeWheel.native.js.map +1 -1
  177. package/dist/jsx/Wheel/index.mjs +2 -1
  178. package/dist/jsx/Wheel/index.mjs.map +1 -1
  179. package/dist/jsx/Wheel/index.native.js +4 -1
  180. package/dist/jsx/Wheel/index.native.js.map +1 -1
  181. package/dist/jsx/Wheel/wheelShared.mjs +6 -1
  182. package/dist/jsx/Wheel/wheelShared.mjs.map +1 -1
  183. package/dist/jsx/Wheel/wheelShared.native.js +7 -1
  184. package/dist/jsx/Wheel/wheelShared.native.js.map +1 -1
  185. package/dist/jsx/fields/Combobox/index.mjs +1 -0
  186. package/dist/jsx/fields/Combobox/index.mjs.map +1 -1
  187. package/dist/jsx/fields/Combobox/index.native.js +1 -0
  188. package/dist/jsx/fields/Combobox/index.native.js.map +1 -1
  189. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs +6 -30
  190. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -1
  191. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js +11 -34
  192. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  193. package/dist/jsx/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  194. package/dist/jsx/fields/DatePicker/catalog.mjs +2 -28
  195. package/dist/jsx/fields/DatePicker/catalog.mjs.map +1 -1
  196. package/dist/jsx/fields/DatePicker/catalog.native.js +2 -30
  197. package/dist/jsx/fields/DatePicker/catalog.native.js.map +1 -1
  198. package/dist/jsx/fields/DatePicker/index.native.js.map +1 -1
  199. package/dist/jsx/fields/DatePicker/parts.mjs +11 -20
  200. package/dist/jsx/fields/DatePicker/parts.mjs.map +1 -1
  201. package/dist/jsx/fields/DatePicker/parts.native.js +13 -22
  202. package/dist/jsx/fields/DatePicker/parts.native.js.map +1 -1
  203. package/dist/jsx/fields/SearchInput/index.mjs +25 -3
  204. package/dist/jsx/fields/SearchInput/index.mjs.map +1 -1
  205. package/dist/jsx/fields/SearchInput/index.native.js +23 -1
  206. package/dist/jsx/fields/SearchInput/index.native.js.map +1 -1
  207. package/dist/jsx/fields/Select/index.mjs +66 -45
  208. package/dist/jsx/fields/Select/index.mjs.map +1 -1
  209. package/dist/jsx/fields/Select/index.native.js +69 -47
  210. package/dist/jsx/fields/Select/index.native.js.map +1 -1
  211. package/dist/jsx/fields/Select/shouldInlineChoices.mjs +3 -1
  212. package/dist/jsx/fields/Select/shouldInlineChoices.mjs.map +1 -1
  213. package/dist/jsx/fields/Select/shouldInlineChoices.native.js +3 -1
  214. package/dist/jsx/fields/Select/shouldInlineChoices.native.js.map +1 -1
  215. package/dist/jsx/fields/TimePicker/catalog.mjs +4 -25
  216. package/dist/jsx/fields/TimePicker/catalog.mjs.map +1 -1
  217. package/dist/jsx/fields/TimePicker/catalog.native.js +7 -29
  218. package/dist/jsx/fields/TimePicker/catalog.native.js.map +1 -1
  219. package/dist/jsx/fields/TimePicker/index.native.js.map +1 -1
  220. package/dist/jsx/fields/TimePicker/parts.mjs +8 -21
  221. package/dist/jsx/fields/TimePicker/parts.mjs.map +1 -1
  222. package/dist/jsx/fields/TimePicker/parts.native.js +8 -21
  223. package/dist/jsx/fields/TimePicker/parts.native.js.map +1 -1
  224. package/dist/jsx/index.js.map +1 -1
  225. package/dist/jsx/index.mjs.map +1 -1
  226. package/dist/jsx/index.native.js.map +1 -1
  227. package/dist/jsx/shared/floatingList.mjs +14 -5
  228. package/dist/jsx/shared/floatingList.mjs.map +1 -1
  229. package/dist/jsx/shared/floatingList.native.js +15 -4
  230. package/dist/jsx/shared/floatingList.native.js.map +1 -1
  231. package/package.json +8 -8
  232. package/src/Button/index.tsx +22 -0
  233. package/src/FloatingPanel/FloatingPanel.echoStretch.spec.tsx +67 -0
  234. package/src/FloatingPanel/FloatingPanel.menuFace.native.spec.tsx +193 -2
  235. package/src/FloatingPanel/FloatingPanel.seamlessCover.spec.tsx +237 -0
  236. package/src/FloatingPanel/echoContext.ts +22 -0
  237. package/src/FloatingPanel/index.native.tsx +106 -9
  238. package/src/FloatingPanel/index.tsx +310 -102
  239. package/src/FloatingPanel/panelWidth.spec.ts +158 -0
  240. package/src/FloatingPanel/panelWidth.ts +122 -0
  241. package/src/FloatingPanel/panelWidthContract.spec.ts +10 -10
  242. package/src/FloatingPanel/pointAnchor.spec.ts +64 -2
  243. package/src/FloatingPanel/pointAnchor.ts +28 -3
  244. package/src/FloatingPanel/useFloatingPanel.spec.tsx +124 -4
  245. package/src/FloatingPanel/useFloatingPanel.ts +106 -33
  246. package/src/InputParts/index.tsx +30 -1
  247. package/src/Wheel/NativeWheel.native.tsx +1 -1
  248. package/src/Wheel/Wheel.spec.tsx +9 -0
  249. package/src/Wheel/index.tsx +12 -3
  250. package/src/Wheel/wheelShared.ts +6 -2
  251. package/src/fields/Combobox/index.tsx +1 -0
  252. package/src/fields/DatePicker/DatePicker.stories.tsx +3 -3
  253. package/src/fields/DatePicker/DatetimePicker.catalog.tsx +17 -45
  254. package/src/fields/DatePicker/DatetimePicker.native.tsx +3 -0
  255. package/src/fields/DatePicker/catalog.tsx +8 -39
  256. package/src/fields/DatePicker/index.native.tsx +3 -0
  257. package/src/fields/DatePicker/parts.tsx +13 -23
  258. package/src/fields/DatePicker/seamlessCover.spec.tsx +180 -0
  259. package/src/fields/DatePicker/touchWeb.spec.tsx +109 -167
  260. package/src/fields/SearchInput/SearchInput.spec.tsx +40 -0
  261. package/src/fields/SearchInput/index.tsx +47 -2
  262. package/src/fields/Select/Select.containingBlock.spec.tsx +191 -0
  263. package/src/fields/Select/Select.stories.tsx +43 -0
  264. package/src/fields/Select/index.tsx +109 -45
  265. package/src/fields/Select/shouldInlineChoices.spec.ts +13 -0
  266. package/src/fields/Select/shouldInlineChoices.ts +6 -0
  267. package/src/fields/TimePicker/catalog.tsx +12 -33
  268. package/src/fields/TimePicker/index.native.tsx +2 -0
  269. package/src/fields/TimePicker/parts.tsx +6 -20
  270. package/src/index.ts +1 -0
  271. package/src/shared/floatingList.tsx +21 -5
  272. package/types/Button/index.d.ts.map +1 -1
  273. package/types/FloatingPanel/echoContext.d.ts +14 -0
  274. package/types/FloatingPanel/echoContext.d.ts.map +1 -0
  275. package/types/FloatingPanel/index.d.ts +59 -0
  276. package/types/FloatingPanel/index.d.ts.map +1 -1
  277. package/types/FloatingPanel/index.native.d.ts +9 -1
  278. package/types/FloatingPanel/index.native.d.ts.map +1 -1
  279. package/types/FloatingPanel/panelWidth.d.ts +71 -0
  280. package/types/FloatingPanel/panelWidth.d.ts.map +1 -0
  281. package/types/FloatingPanel/pointAnchor.d.ts +8 -1
  282. package/types/FloatingPanel/pointAnchor.d.ts.map +1 -1
  283. package/types/FloatingPanel/useFloatingPanel.d.ts +33 -2
  284. package/types/FloatingPanel/useFloatingPanel.d.ts.map +1 -1
  285. package/types/InputParts/index.d.ts +5 -1
  286. package/types/InputParts/index.d.ts.map +1 -1
  287. package/types/Wheel/index.d.ts.map +1 -1
  288. package/types/Wheel/wheelShared.d.ts +2 -1
  289. package/types/Wheel/wheelShared.d.ts.map +1 -1
  290. package/types/fields/Combobox/index.d.ts.map +1 -1
  291. package/types/fields/DatePicker/DatetimePicker.catalog.d.ts +6 -0
  292. package/types/fields/DatePicker/DatetimePicker.catalog.d.ts.map +1 -1
  293. package/types/fields/DatePicker/DatetimePicker.native.d.ts +3 -0
  294. package/types/fields/DatePicker/DatetimePicker.native.d.ts.map +1 -1
  295. package/types/fields/DatePicker/catalog.d.ts +6 -0
  296. package/types/fields/DatePicker/catalog.d.ts.map +1 -1
  297. package/types/fields/DatePicker/index.native.d.ts +3 -0
  298. package/types/fields/DatePicker/index.native.d.ts.map +1 -1
  299. package/types/fields/DatePicker/parts.d.ts +5 -7
  300. package/types/fields/DatePicker/parts.d.ts.map +1 -1
  301. package/types/fields/Input/index.d.ts +3 -1
  302. package/types/fields/Input/index.d.ts.map +1 -1
  303. package/types/fields/SearchInput/index.d.ts +11 -0
  304. package/types/fields/SearchInput/index.d.ts.map +1 -1
  305. package/types/fields/Select/index.d.ts +8 -1
  306. package/types/fields/Select/index.d.ts.map +1 -1
  307. package/types/fields/Select/shouldInlineChoices.d.ts +5 -1
  308. package/types/fields/Select/shouldInlineChoices.d.ts.map +1 -1
  309. package/types/fields/TimePicker/catalog.d.ts +7 -1
  310. package/types/fields/TimePicker/catalog.d.ts.map +1 -1
  311. package/types/fields/TimePicker/index.native.d.ts +2 -0
  312. package/types/fields/TimePicker/index.native.d.ts.map +1 -1
  313. package/types/fields/TimePicker/parts.d.ts +1 -3
  314. package/types/fields/TimePicker/parts.d.ts.map +1 -1
  315. package/types/index.d.ts +1 -0
  316. package/types/index.d.ts.map +1 -1
  317. package/types/shared/floatingList.d.ts +5 -1
  318. package/types/shared/floatingList.d.ts.map +1 -1
  319. package/dist/cjs/shared/touchWebPicker.cjs +0 -136
  320. package/dist/cjs/shared/touchWebPicker.native.js +0 -154
  321. package/dist/cjs/shared/touchWebPicker.native.js.map +0 -1
  322. package/dist/esm/shared/touchWebPicker.mjs +0 -105
  323. package/dist/esm/shared/touchWebPicker.mjs.map +0 -1
  324. package/dist/esm/shared/touchWebPicker.native.js +0 -120
  325. package/dist/esm/shared/touchWebPicker.native.js.map +0 -1
  326. package/dist/jsx/shared/touchWebPicker.mjs +0 -105
  327. package/dist/jsx/shared/touchWebPicker.mjs.map +0 -1
  328. package/dist/jsx/shared/touchWebPicker.native.js +0 -154
  329. package/dist/jsx/shared/touchWebPicker.native.js.map +0 -1
  330. package/src/shared/touchWebPicker.spec.ts +0 -98
  331. package/src/shared/touchWebPicker.tsx +0 -158
  332. package/types/shared/touchWebPicker.d.ts +0 -40
  333. package/types/shared/touchWebPicker.d.ts.map +0 -1
@@ -0,0 +1,158 @@
1
+ /**
2
+ * LC-112 width setting (FZ-8438 r3, Clay on the desktop Date shot): "it can
3
+ * collapse to the content, it can be the full width of the initial input
4
+ * button, or it can open up to be the full width of the screen, or it can be
5
+ * a fixed size that it opens to."
6
+ */
7
+ import { describe, expect, it } from "vitest";
8
+ import { MENU_MIN_WIDTH } from "./compactFace";
9
+ import {
10
+ echoOffset,
11
+ panelLeftForWidth,
12
+ panelWidthBox,
13
+ screenBounds,
14
+ slidesAlongScreen,
15
+ } from "./panelWidth";
16
+
17
+ describe("panelWidthBox", () => {
18
+ it('"content" floors at an echoed trigger and leaves the width to its rows', () => {
19
+ expect(panelWidthBox("content", { trigger: 240, available: 1000, echo: true })).toEqual({
20
+ minWidth: 240,
21
+ maxWidth: 1000,
22
+ });
23
+ });
24
+
25
+ it('"content" without an echo has no trigger floor', () => {
26
+ expect(panelWidthBox("content", { trigger: 44, available: 1000, echo: false }).minWidth).toBe(
27
+ 0,
28
+ );
29
+ });
30
+
31
+ it('"content" keeps a platform floor (the native 250pt pull-down)', () => {
32
+ expect(
33
+ panelWidthBox("content", { trigger: 44, available: 370, echo: false, floor: MENU_MIN_WIDTH })
34
+ .minWidth,
35
+ ).toBe(MENU_MIN_WIDTH);
36
+ expect(
37
+ panelWidthBox("content", { trigger: 44, available: 220, echo: false, floor: MENU_MIN_WIDTH })
38
+ .minWidth,
39
+ ).toBe(220);
40
+ });
41
+
42
+ it('"trigger" pins min, width and max to the trigger', () => {
43
+ expect(panelWidthBox("trigger", { trigger: 240, available: 1000, echo: true })).toEqual({
44
+ minWidth: 240,
45
+ width: 240,
46
+ maxWidth: 240,
47
+ });
48
+ });
49
+
50
+ it('"screen" pins the box to the room between the margins', () => {
51
+ expect(panelWidthBox("screen", { trigger: 240, available: 370, echo: true })).toEqual({
52
+ minWidth: 370,
53
+ width: 370,
54
+ maxWidth: 370,
55
+ });
56
+ });
57
+
58
+ it("a number is that width, capped at the room", () => {
59
+ expect(panelWidthBox(320, { trigger: 240, available: 1000, echo: true }).width).toBe(320);
60
+ expect(panelWidthBox(180, { trigger: 240, available: 1000, echo: true }).width).toBe(180);
61
+ expect(panelWidthBox(600, { trigger: 240, available: 370, echo: true }).width).toBe(370);
62
+ expect(panelWidthBox(-5, { trigger: 240, available: 370, echo: true }).width).toBe(0);
63
+ });
64
+ });
65
+
66
+ describe("panelLeftForWidth", () => {
67
+ const bounds = { boundsLeft: 10, boundsRight: 1430 };
68
+
69
+ it("keeps the trigger's start edge while the panel fits", () => {
70
+ expect(panelLeftForWidth({ triggerLeft: 200, triggerRight: 440, width: 320, ...bounds })).toBe(
71
+ 200,
72
+ );
73
+ });
74
+
75
+ it("slides back inside the end margin", () => {
76
+ expect(
77
+ panelLeftForWidth({ triggerLeft: 1300, triggerRight: 1420, width: 320, ...bounds }),
78
+ ).toBe(1110);
79
+ });
80
+
81
+ it("a screen-wide panel sits on the start margin", () => {
82
+ expect(panelLeftForWidth({ triggerLeft: 200, triggerRight: 440, width: 1420, ...bounds })).toBe(
83
+ 10,
84
+ );
85
+ });
86
+
87
+ it("right to left keeps the trigger's right edge", () => {
88
+ expect(
89
+ panelLeftForWidth({
90
+ triggerLeft: 1000,
91
+ triggerRight: 1240,
92
+ width: 320,
93
+ rtl: true,
94
+ ...bounds,
95
+ }),
96
+ ).toBe(920);
97
+ });
98
+ });
99
+
100
+ describe("echoOffset", () => {
101
+ it("is the trigger's distance from the panel's start edge", () => {
102
+ expect(
103
+ echoOffset({ triggerLeft: 200, triggerRight: 440, panelLeft: 10, panelWidth: 1420 }),
104
+ ).toBe(190);
105
+ expect(
106
+ echoOffset({ triggerLeft: 200, triggerRight: 440, panelLeft: 200, panelWidth: 320 }),
107
+ ).toBe(0);
108
+ });
109
+
110
+ it("measures from the right edge in right to left", () => {
111
+ expect(
112
+ echoOffset({
113
+ triggerLeft: 1000,
114
+ triggerRight: 1240,
115
+ panelLeft: 10,
116
+ panelWidth: 1420,
117
+ rtl: true,
118
+ }),
119
+ ).toBe(190);
120
+ });
121
+ });
122
+
123
+ describe("slidesAlongScreen", () => {
124
+ it("is true only for the widths not tied to the trigger's start edge", () => {
125
+ expect(slidesAlongScreen("screen")).toBe(true);
126
+ expect(slidesAlongScreen(320)).toBe(true);
127
+ expect(slidesAlongScreen("content")).toBe(false);
128
+ expect(slidesAlongScreen("trigger")).toBe(false);
129
+ expect(slidesAlongScreen(undefined)).toBe(false);
130
+ });
131
+ });
132
+
133
+ describe("screenBounds", () => {
134
+ const bounds = { boundsLeft: 10, boundsRight: 1430 };
135
+
136
+ it("a trigger in the page gutter lends its inset to both sides", () => {
137
+ // Field at the 16pt gutter, margins at 10: the panel spans 16..1424, so
138
+ // its start edge is the trigger's and the rows line up under the echo.
139
+ expect(screenBounds({ triggerLeft: 16, triggerRight: 276, ...bounds })).toEqual({
140
+ left: 16,
141
+ right: 1424,
142
+ });
143
+ });
144
+
145
+ it("a trigger deep in the page keeps the safe margins", () => {
146
+ expect(screenBounds({ triggerLeft: 600, triggerRight: 860, ...bounds })).toEqual({
147
+ left: 10,
148
+ right: 1430,
149
+ });
150
+ });
151
+
152
+ it("right to left measures the gutter from the right edge", () => {
153
+ expect(screenBounds({ triggerLeft: 1164, triggerRight: 1414, rtl: true, ...bounds })).toEqual({
154
+ left: 26,
155
+ right: 1414,
156
+ });
157
+ });
158
+ });
@@ -0,0 +1,122 @@
1
+ /**
2
+ * How wide a panel opens over its trigger (LC-112, FZ-8438 r3). One setting
3
+ * for every picker and menu, on the web and native:
4
+ *
5
+ * - `"content"`: the panel hugs its content. While it echoes its trigger it
6
+ * is never narrower than the trigger, so the echo keeps its box.
7
+ * - `"trigger"`: exactly the trigger's width. Content wraps or scrolls.
8
+ * - `"screen"`: the room between the screen's safe margins. The top edge
9
+ * still sits on the trigger's top edge.
10
+ * - a number: that many points, capped at the room between the margins.
11
+ */
12
+ export type PanelWidth = "content" | "trigger" | "screen" | number;
13
+
14
+ export interface PanelWidthBox {
15
+ minWidth: number;
16
+ /** Set for every mode but `"content"`, which leaves the width to its rows. */
17
+ width?: number;
18
+ maxWidth: number;
19
+ }
20
+
21
+ export function panelWidthBox(
22
+ panelWidth: PanelWidth,
23
+ {
24
+ trigger,
25
+ available,
26
+ echo,
27
+ floor = 0,
28
+ }: {
29
+ /** The trigger's width. */
30
+ trigger: number;
31
+ /** The room between the screen's safe margins. */
32
+ available: number;
33
+ /** The panel's first row repeats the trigger. */
34
+ echo: boolean;
35
+ /** A platform minimum for `"content"` (the native pull-down's 250pt). */
36
+ floor?: number;
37
+ },
38
+ ): PanelWidthBox {
39
+ const room = Math.max(0, available);
40
+ const pinned = (width: number) => {
41
+ const w = Math.max(0, Math.min(width, room));
42
+ return { minWidth: w, width: w, maxWidth: w };
43
+ };
44
+ if (panelWidth === "trigger") return pinned(trigger);
45
+ if (panelWidth === "screen") return pinned(room);
46
+ if (typeof panelWidth === "number") return pinned(panelWidth);
47
+ return { minWidth: Math.min(Math.max(echo ? trigger : 0, floor), room), maxWidth: room };
48
+ }
49
+
50
+ /**
51
+ * A panel whose width is not tied to its trigger's box (`"screen"`, or a fixed
52
+ * width wider than the room from the trigger's start edge) slides along the
53
+ * screen to stay inside the margins. Its echo row is moved back over the
54
+ * trigger by the returned offset, so the trigger's content stays in place.
55
+ */
56
+ export function panelLeftForWidth({
57
+ triggerLeft,
58
+ triggerRight,
59
+ width,
60
+ boundsLeft,
61
+ boundsRight,
62
+ rtl = false,
63
+ }: {
64
+ triggerLeft: number;
65
+ triggerRight: number;
66
+ width: number;
67
+ boundsLeft: number;
68
+ boundsRight: number;
69
+ rtl?: boolean;
70
+ }): number {
71
+ const want = rtl ? triggerRight - width : triggerLeft;
72
+ return Math.max(boundsLeft, Math.min(want, boundsRight - width));
73
+ }
74
+
75
+ /** How far the echo row moves from the panel's start edge to sit on its trigger. */
76
+ export function echoOffset({
77
+ triggerLeft,
78
+ triggerRight,
79
+ panelLeft,
80
+ panelWidth,
81
+ rtl = false,
82
+ }: {
83
+ triggerLeft: number;
84
+ triggerRight: number;
85
+ panelLeft: number;
86
+ panelWidth: number;
87
+ rtl?: boolean;
88
+ }): number {
89
+ return rtl ? panelLeft + panelWidth - triggerRight : triggerLeft - panelLeft;
90
+ }
91
+
92
+ /** True for the widths that are not tied to the trigger's start edge. */
93
+ export function slidesAlongScreen(panelWidth: PanelWidth | undefined): boolean {
94
+ return panelWidth === "screen" || typeof panelWidth === "number";
95
+ }
96
+
97
+ /**
98
+ * The bounds a `"screen"` panel spans. A trigger that sits in the page's start
99
+ * gutter, up to `gutter` points inside the safe margins, lends that inset to
100
+ * both sides, so the panel's start edge stays on the trigger's start edge and
101
+ * the rows line up under the echo. A trigger deeper in the page gets the safe
102
+ * margins as they are.
103
+ */
104
+ export function screenBounds({
105
+ triggerLeft,
106
+ triggerRight,
107
+ boundsLeft,
108
+ boundsRight,
109
+ rtl = false,
110
+ gutter = 32,
111
+ }: {
112
+ triggerLeft: number;
113
+ triggerRight: number;
114
+ boundsLeft: number;
115
+ boundsRight: number;
116
+ rtl?: boolean;
117
+ gutter?: number;
118
+ }): { left: number; right: number } {
119
+ const inset = rtl ? boundsRight - triggerRight : triggerLeft - boundsLeft;
120
+ const margin = inset > 0 && inset <= gutter ? inset : 0;
121
+ return { left: boundsLeft + margin, right: boundsRight - margin };
122
+ }
@@ -7,9 +7,10 @@
7
7
  * DatePicker measured 262px of calendar under a 289px trigger purely because
8
8
  * the call site said `fitContent`.
9
9
  *
10
- * Calendar grids take fit-content (MPO-262): a full-width field wrapper is
11
- * unrelated to seven day columns and must not create a mostly empty popup.
12
- * Text listboxes retain at-least-trigger; icon/button launchers may also hug.
10
+ * Calendar grids opened from a field are the field grown open (LC-112
11
+ * seamless cover, FZ-8438): at least the field's width, superseding MPO-262's
12
+ * fit-content. Text listboxes retain at-least-trigger; icon/button launchers
13
+ * may hug.
13
14
  * The Icon glyph grid pins to its field exactly (match-trigger): fixed 64px
14
15
  * cells wrap into more columns on a wider field instead of stretching.
15
16
  */
@@ -21,13 +22,16 @@ import { describe, expect, it } from "vitest";
21
22
  const fieldsDir = resolve(dirname(fileURLToPath(import.meta.url)), "..", "fields");
22
23
 
23
24
  /** Pickers whose trigger is a form FIELD: the panel may never be narrower. */
24
- const fieldShapedPickers = ["TimePicker/catalog.tsx", "ColorPicker/parts.tsx"];
25
+ const fieldShapedPickers = [
26
+ "TimePicker/catalog.tsx",
27
+ "ColorPicker/parts.tsx",
28
+ "DatePicker/parts.tsx",
29
+ "DatePicker/DatetimePicker.catalog.tsx",
30
+ ];
25
31
 
26
32
  /** Field-triggered pickers pinned to the field's width exactly. */
27
33
  const matchTriggerPickers = ["Icon/index.tsx"];
28
34
 
29
- const intrinsicGridPickers = ["DatePicker/parts.tsx", "DatePicker/DatetimePicker.catalog.tsx"];
30
-
31
35
  /** Panels opened from an icon/button trigger, where fit-content is correct. */
32
36
  const buttonShapedPickers = ["Geolocation/index.tsx"];
33
37
 
@@ -36,10 +40,6 @@ function read(relative: string): string {
36
40
  }
37
41
 
38
42
  describe("FloatingPanel width contract (MPO-38, MPO-262)", () => {
39
- it.each(intrinsicGridPickers)("%s hugs its calendar grid", (relative) => {
40
- expect(read(relative)).toContain('widthMode="fit-content"');
41
- });
42
-
43
43
  it.each(fieldShapedPickers)("%s declares at-least-trigger", (relative) => {
44
44
  const source = read(relative);
45
45
  expect(source).toContain('widthMode="at-least-trigger"');
@@ -154,7 +154,14 @@ describe("placeOverTrigger (FZ-7830, LC-112 where no sheet may take it)", () =>
154
154
  height: 180,
155
155
  bounds: phone,
156
156
  });
157
- expect(placed).toEqual({ left: 20, top: 200, width: 200, maxHeight: 600, shifted: false });
157
+ expect(placed).toEqual({
158
+ left: 20,
159
+ top: 200,
160
+ width: 200,
161
+ maxHeight: 600,
162
+ shifted: false,
163
+ end: false,
164
+ });
158
165
  });
159
166
 
160
167
  it("is at least the trigger's width", () => {
@@ -209,6 +216,54 @@ describe("placeOverTrigger (FZ-7830, LC-112 where no sheet may take it)", () =>
209
216
  expect(placed.maxHeight).toBe(743);
210
217
  });
211
218
 
219
+ it("opens an icon menu from the trigger's top trailing corner at the end edge (FZ-8438)", () => {
220
+ const placed = placeOverTrigger({
221
+ trigger,
222
+ width: 200,
223
+ height: 180,
224
+ bounds: phone,
225
+ align: "auto",
226
+ });
227
+ expect(placed).toMatchObject({ left: 144, top: 200, end: true, shifted: false });
228
+ });
229
+
230
+ it("keeps a bar button's corner inside the viewport margin (FZ-8438)", () => {
231
+ const placed = placeOverTrigger({
232
+ trigger: { x: 342, y: 60, width: 44, height: 44 },
233
+ width: 250,
234
+ height: 180,
235
+ bounds: phone,
236
+ align: "auto",
237
+ });
238
+ expect(placed.left).toBe(342 + 44 - 250);
239
+ expect(placed.end).toBe(true);
240
+ expect(placed.shifted).toBe(false);
241
+ });
242
+
243
+ it("mirrors the icon menu in RTL: a leading-edge button opens from its left corner (FZ-8438)", () => {
244
+ const placed = placeOverTrigger({
245
+ trigger: { x: 4, y: 60, width: 44, height: 44 },
246
+ width: 250,
247
+ height: 180,
248
+ bounds: phone,
249
+ rtl: true,
250
+ align: "auto",
251
+ });
252
+ expect(placed.left).toBe(4);
253
+ expect(placed.end).toBe(true);
254
+ });
255
+
256
+ it("keeps the start corner for an icon menu that fits (FZ-8438)", () => {
257
+ const placed = placeOverTrigger({
258
+ trigger: { x: 20, y: 200, width: 44, height: 44 },
259
+ width: 200,
260
+ height: 180,
261
+ bounds: phone,
262
+ align: "auto",
263
+ });
264
+ expect(placed).toMatchObject({ left: 20, end: false, shifted: false });
265
+ });
266
+
212
267
  it("never grows wider than the bounds", () => {
213
268
  const placed = placeOverTrigger({
214
269
  trigger: { x: 20, y: 200, width: 120, height: 44 },
@@ -227,7 +282,14 @@ describe("placeBesideTrigger (FZ-7830 round 3, LC-262: beside the trigger, never
227
282
 
228
283
  it("opens under the trigger when it fits", () => {
229
284
  const placed = placeBesideTrigger({ trigger: field, width: 200, height: 180, bounds: phone });
230
- expect(placed).toEqual({ left: 16, top: 344, width: 358, maxHeight: 180, shifted: false });
285
+ expect(placed).toEqual({
286
+ left: 16,
287
+ top: 344,
288
+ width: 358,
289
+ maxHeight: 180,
290
+ shifted: false,
291
+ end: false,
292
+ });
231
293
  });
232
294
 
233
295
  it("opens above the trigger when there is more room there, its bottom on the trigger's top", () => {
@@ -136,6 +136,8 @@ 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;
139
141
  }
140
142
 
141
143
  /**
@@ -151,17 +153,32 @@ export function placeOverTrigger({
151
153
  height,
152
154
  bounds,
153
155
  rtl = false,
156
+ align = "start",
154
157
  }: {
155
158
  trigger: TriggerRect;
156
159
  width: number;
157
160
  height: number;
158
161
  bounds: PanelBounds;
159
162
  rtl?: boolean;
163
+ /**
164
+ * `"auto"` (a menu with no echo row) opens from the trailing top corner when
165
+ * the leading one would run off the end edge and the trailing one fits.
166
+ */
167
+ align?: "start" | "auto";
160
168
  }): TriggerPlacement {
161
169
  const room = Math.max(0, bounds.right - bounds.left);
162
170
  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));
171
+ const startLeft = rtl ? trigger.x + trigger.width - w : trigger.x;
172
+ const endLeft = rtl ? trigger.x : trigger.x + trigger.width - w;
173
+ // Only the free edge has to clear the padded bounds: the aligned edge is
174
+ // the trigger's own, so a bar button inside the margin keeps its corner.
175
+ const startFree = rtl ? startLeft >= bounds.left : startLeft + w <= bounds.right;
176
+ const endFree = rtl ? endLeft + w <= bounds.right : endLeft >= bounds.left;
177
+ const auto = align === "auto";
178
+ const end = auto && !startFree && endFree;
179
+ const wantLeft = end ? endLeft : startLeft;
180
+ const keep = auto && (end || startFree);
181
+ const left = keep ? wantLeft : Math.max(bounds.left, Math.min(wantLeft, bounds.right - w));
165
182
  const h = Math.min(height, Math.max(0, bounds.bottom - bounds.top));
166
183
  const top = Math.max(bounds.top, Math.min(trigger.y, bounds.bottom - h));
167
184
  return {
@@ -170,6 +187,7 @@ export function placeOverTrigger({
170
187
  width: w,
171
188
  maxHeight: Math.max(0, bounds.bottom - top),
172
189
  shifted: left !== wantLeft || top !== trigger.y,
190
+ end,
173
191
  };
174
192
  }
175
193
 
@@ -200,5 +218,12 @@ export function placeBesideTrigger({
200
218
  const opensUp = height > below && above > below;
201
219
  const maxHeight = Math.min(height, opensUp ? above : below);
202
220
  const top = opensUp ? trigger.y - maxHeight : trigger.y + trigger.height;
203
- return { left, top, width: w, maxHeight, shifted: left !== wantLeft || opensUp };
221
+ return {
222
+ left,
223
+ top,
224
+ width: w,
225
+ maxHeight,
226
+ shifted: left !== wantLeft || opensUp,
227
+ end: false,
228
+ };
204
229
  }
@@ -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
  });