@multiplatform.one/forms 7.7.4 → 7.8.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 (579) hide show
  1. package/dist/cjs/Button/index.cjs +3 -36
  2. package/dist/cjs/Button/index.native.js +3 -36
  3. package/dist/cjs/Button/index.native.js.map +1 -1
  4. package/dist/cjs/Chip/index.cjs +1 -1
  5. package/dist/cjs/Chip/index.native.js +1 -1
  6. package/dist/cjs/Chip/index.native.js.map +1 -1
  7. package/dist/cjs/ContextualSaveBar/index.cjs +17 -10
  8. package/dist/cjs/ContextualSaveBar/index.native.js +18 -10
  9. package/dist/cjs/ContextualSaveBar/index.native.js.map +1 -1
  10. package/dist/cjs/FloatingPanel/index.cjs +59 -14
  11. package/dist/cjs/FloatingPanel/index.native.js +16 -7
  12. package/dist/cjs/FloatingPanel/index.native.js.map +1 -1
  13. package/dist/cjs/FloatingPanel/nestedAction.cjs +37 -0
  14. package/dist/cjs/FloatingPanel/nestedAction.native.js +42 -0
  15. package/dist/cjs/FloatingPanel/nestedAction.native.js.map +1 -0
  16. package/dist/cjs/InputParts/index.cjs +11 -7
  17. package/dist/cjs/InputParts/index.native.js +12 -10
  18. package/dist/cjs/InputParts/index.native.js.map +1 -1
  19. package/dist/cjs/Wheel/index.cjs +3 -3
  20. package/dist/cjs/Wheel/index.native.js +3 -3
  21. package/dist/cjs/Wheel/index.native.js.map +1 -1
  22. package/dist/cjs/fieldLayout.cjs +3 -3
  23. package/dist/cjs/fieldLayout.native.js +3 -3
  24. package/dist/cjs/fieldLayout.native.js.map +1 -1
  25. package/dist/cjs/fields/Checkbox/CheckboxFieldFrame.cjs +9 -2
  26. package/dist/cjs/fields/Checkbox/CheckboxFieldFrame.native.js +10 -1
  27. package/dist/cjs/fields/Checkbox/CheckboxFieldFrame.native.js.map +1 -1
  28. package/dist/cjs/fields/Combobox/index.cjs +74 -76
  29. package/dist/cjs/fields/Combobox/index.native.js +78 -86
  30. package/dist/cjs/fields/Combobox/index.native.js.map +1 -1
  31. package/dist/cjs/fields/DatePicker/Calendar.cjs +6 -1
  32. package/dist/cjs/fields/DatePicker/Calendar.native.js +6 -1
  33. package/dist/cjs/fields/DatePicker/Calendar.native.js.map +1 -1
  34. package/dist/cjs/fields/DatePicker/DateRangePicker.cjs +2 -2
  35. package/dist/cjs/fields/DatePicker/DateRangePicker.native.js +2 -2
  36. package/dist/cjs/fields/DatePicker/DateRangePicker.native.js.map +1 -1
  37. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.cjs +15 -12
  38. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js +16 -12
  39. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  40. package/dist/cjs/fields/DatePicker/DatetimePicker.native.js +39 -14
  41. package/dist/cjs/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  42. package/dist/cjs/fields/DatePicker/MonthPicker.cjs +10 -3
  43. package/dist/cjs/fields/DatePicker/MonthPicker.native.js +10 -3
  44. package/dist/cjs/fields/DatePicker/MonthPicker.native.js.map +1 -1
  45. package/dist/cjs/fields/DatePicker/MultiDatePicker.cjs +2 -2
  46. package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js +2 -2
  47. package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
  48. package/dist/cjs/fields/DatePicker/index.native.js +3 -0
  49. package/dist/cjs/fields/DatePicker/index.native.js.map +1 -1
  50. package/dist/cjs/fields/DatePicker/parts.cjs +26 -14
  51. package/dist/cjs/fields/DatePicker/parts.native.js +28 -14
  52. package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
  53. package/dist/cjs/fields/Geolocation/GeolocationMap.native.js.map +1 -1
  54. package/dist/cjs/fields/Geolocation/geocoder.cjs +63 -0
  55. package/dist/cjs/fields/Geolocation/geocoder.native.js +68 -0
  56. package/dist/cjs/fields/Geolocation/geocoder.native.js.map +1 -0
  57. package/dist/cjs/fields/Geolocation/index.cjs +801 -152
  58. package/dist/cjs/fields/Geolocation/index.native.js +856 -167
  59. package/dist/cjs/fields/Geolocation/index.native.js.map +1 -1
  60. package/dist/cjs/fields/Geolocation/nativeSearchFocus.cjs +44 -0
  61. package/dist/cjs/fields/Geolocation/nativeSearchFocus.native.js +47 -0
  62. package/dist/cjs/fields/Geolocation/nativeSearchFocus.native.js.map +1 -0
  63. package/dist/cjs/fields/Icon/drawnSet.cjs +14 -3
  64. package/dist/cjs/fields/Icon/drawnSet.native.js +18 -3
  65. package/dist/cjs/fields/Icon/drawnSet.native.js.map +1 -1
  66. package/dist/cjs/fields/Icon/index.cjs +102 -27
  67. package/dist/cjs/fields/Icon/index.native.js +115 -31
  68. package/dist/cjs/fields/Icon/index.native.js.map +1 -1
  69. package/dist/cjs/fields/Input/index.cjs +5 -12
  70. package/dist/cjs/fields/Input/index.native.js +5 -13
  71. package/dist/cjs/fields/Input/index.native.js.map +1 -1
  72. package/dist/cjs/fields/PhoneInput/index.cjs +2 -2
  73. package/dist/cjs/fields/PhoneInput/index.native.js +2 -2
  74. package/dist/cjs/fields/PhoneInput/index.native.js.map +1 -1
  75. package/dist/cjs/fields/SearchInput/index.cjs +8 -9
  76. package/dist/cjs/fields/SearchInput/index.native.js +8 -9
  77. package/dist/cjs/fields/SearchInput/index.native.js.map +1 -1
  78. package/dist/cjs/fields/Select/index.cjs +53 -18
  79. package/dist/cjs/fields/Select/index.native.js +63 -37
  80. package/dist/cjs/fields/Select/index.native.js.map +1 -1
  81. package/dist/cjs/fields/Stepper/index.cjs +144 -31
  82. package/dist/cjs/fields/Stepper/index.native.js +151 -31
  83. package/dist/cjs/fields/Stepper/index.native.js.map +1 -1
  84. package/dist/cjs/index.cjs +2 -0
  85. package/dist/cjs/index.native.js +2 -0
  86. package/dist/cjs/index.native.js.map +1 -1
  87. package/dist/cjs/shared/focusManagement.cjs +24 -11
  88. package/dist/cjs/shared/focusManagement.native.js +24 -11
  89. package/dist/cjs/shared/focusManagement.native.js.map +1 -1
  90. package/dist/cjs/shared/pickerTriggerContract.cjs +38 -0
  91. package/dist/cjs/shared/pickerTriggerContract.native.js +41 -0
  92. package/dist/cjs/shared/pickerTriggerContract.native.js.map +1 -0
  93. package/dist/cjs/shared/pressSlopProps.cjs +68 -0
  94. package/dist/cjs/shared/pressSlopProps.native.js +72 -0
  95. package/dist/cjs/shared/pressSlopProps.native.js.map +1 -0
  96. package/dist/esm/AdaptivePopup/index.native.js.map +1 -1
  97. package/dist/esm/Button/index.mjs +1 -34
  98. package/dist/esm/Button/index.mjs.map +1 -1
  99. package/dist/esm/Button/index.native.js +1 -34
  100. package/dist/esm/Button/index.native.js.map +1 -1
  101. package/dist/esm/Chip/index.mjs +1 -1
  102. package/dist/esm/Chip/index.mjs.map +1 -1
  103. package/dist/esm/Chip/index.native.js +1 -1
  104. package/dist/esm/Chip/index.native.js.map +1 -1
  105. package/dist/esm/ContextualSaveBar/UnsavedChangesDialog.native.js.map +1 -1
  106. package/dist/esm/ContextualSaveBar/clientKnownIdentity.native.js.map +1 -1
  107. package/dist/esm/ContextualSaveBar/commitImmediateField.native.js.map +1 -1
  108. package/dist/esm/ContextualSaveBar/index.mjs +17 -10
  109. package/dist/esm/ContextualSaveBar/index.mjs.map +1 -1
  110. package/dist/esm/ContextualSaveBar/index.native.js +18 -10
  111. package/dist/esm/ContextualSaveBar/index.native.js.map +1 -1
  112. package/dist/esm/ContextualSaveBar/useUnsavedChangesGuard.native.js.map +1 -1
  113. package/dist/esm/ControlGroup/index.native.js.map +1 -1
  114. package/dist/esm/DescriptionList/index.native.js.map +1 -1
  115. package/dist/esm/ErrorSummary.native.js.map +1 -1
  116. package/dist/esm/FieldDisplay/editMode.native.js.map +1 -1
  117. package/dist/esm/FieldDisplay/formatters.native.js.map +1 -1
  118. package/dist/esm/FieldDisplay/index.native.js.map +1 -1
  119. package/dist/esm/FieldGroup.native.js.map +1 -1
  120. package/dist/esm/FloatingPanel/index.mjs +58 -15
  121. package/dist/esm/FloatingPanel/index.mjs.map +1 -1
  122. package/dist/esm/FloatingPanel/index.native.js +15 -8
  123. package/dist/esm/FloatingPanel/index.native.js.map +1 -1
  124. package/dist/esm/FloatingPanel/nestedAction.mjs +11 -0
  125. package/dist/esm/FloatingPanel/nestedAction.mjs.map +1 -0
  126. package/dist/esm/FloatingPanel/nestedAction.native.js +13 -0
  127. package/dist/esm/FloatingPanel/nestedAction.native.js.map +1 -0
  128. package/dist/esm/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  129. package/dist/esm/Form/index.native.js.map +1 -1
  130. package/dist/esm/Form/validateOn.native.js.map +1 -1
  131. package/dist/esm/FormGrid.native.js.map +1 -1
  132. package/dist/esm/FormSection.native.js.map +1 -1
  133. package/dist/esm/InputParts/index.mjs +12 -8
  134. package/dist/esm/InputParts/index.mjs.map +1 -1
  135. package/dist/esm/InputParts/index.native.js +13 -11
  136. package/dist/esm/InputParts/index.native.js.map +1 -1
  137. package/dist/esm/Label/index.native.js.map +1 -1
  138. package/dist/esm/Skeleton/index.native.js.map +1 -1
  139. package/dist/esm/Spinner/SpinnerGlyph.native.js.map +1 -1
  140. package/dist/esm/Spinner/spinnerSize.native.js.map +1 -1
  141. package/dist/esm/Table/index.native.js.map +1 -1
  142. package/dist/esm/Wheel/haptic.native.js.map +1 -1
  143. package/dist/esm/Wheel/index.mjs +3 -3
  144. package/dist/esm/Wheel/index.mjs.map +1 -1
  145. package/dist/esm/Wheel/index.native.js +3 -3
  146. package/dist/esm/Wheel/index.native.js.map +1 -1
  147. package/dist/esm/actionLabel.native.js.map +1 -1
  148. package/dist/esm/fieldDefaults.native.js.map +1 -1
  149. package/dist/esm/fieldLayout.mjs +3 -3
  150. package/dist/esm/fieldLayout.mjs.map +1 -1
  151. package/dist/esm/fieldLayout.native.js +3 -3
  152. package/dist/esm/fieldLayout.native.js.map +1 -1
  153. package/dist/esm/fields/Barcode/BarcodePreview.native.js.map +1 -1
  154. package/dist/esm/fields/Barcode/code128.native.js.map +1 -1
  155. package/dist/esm/fields/Barcode/index.native.js.map +1 -1
  156. package/dist/esm/fields/Barcode/qr.native.js.map +1 -1
  157. package/dist/esm/fields/Barcode/scannerTypes.native.js.map +1 -1
  158. package/dist/esm/fields/Checkbox/Checkbox.native.js.map +1 -1
  159. package/dist/esm/fields/Checkbox/CheckboxBox.native.js.map +1 -1
  160. package/dist/esm/fields/Checkbox/CheckboxCard.native.js.map +1 -1
  161. package/dist/esm/fields/Checkbox/CheckboxFieldFrame.mjs +8 -1
  162. package/dist/esm/fields/Checkbox/CheckboxFieldFrame.mjs.map +1 -1
  163. package/dist/esm/fields/Checkbox/CheckboxFieldFrame.native.js +10 -1
  164. package/dist/esm/fields/Checkbox/CheckboxFieldFrame.native.js.map +1 -1
  165. package/dist/esm/fields/Checkbox/CheckboxGroup.native.js.map +1 -1
  166. package/dist/esm/fields/Checkbox/checkboxGroupContext.native.js.map +1 -1
  167. package/dist/esm/fields/Checkbox/index.native.js.map +1 -1
  168. package/dist/esm/fields/ColorPicker/index.native.js.map +1 -1
  169. package/dist/esm/fields/ColorPicker/parts.native.js.map +1 -1
  170. package/dist/esm/fields/Combobox/authorOptions.native.js.map +1 -1
  171. package/dist/esm/fields/Combobox/index.mjs +74 -76
  172. package/dist/esm/fields/Combobox/index.mjs.map +1 -1
  173. package/dist/esm/fields/Combobox/index.native.js +78 -86
  174. package/dist/esm/fields/Combobox/index.native.js.map +1 -1
  175. package/dist/esm/fields/DatePicker/Calendar.mjs +6 -1
  176. package/dist/esm/fields/DatePicker/Calendar.mjs.map +1 -1
  177. package/dist/esm/fields/DatePicker/Calendar.native.js +6 -1
  178. package/dist/esm/fields/DatePicker/Calendar.native.js.map +1 -1
  179. package/dist/esm/fields/DatePicker/DateRangePicker.mjs +2 -2
  180. package/dist/esm/fields/DatePicker/DateRangePicker.mjs.map +1 -1
  181. package/dist/esm/fields/DatePicker/DateRangePicker.native.js +2 -2
  182. package/dist/esm/fields/DatePicker/DateRangePicker.native.js.map +1 -1
  183. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs +15 -12
  184. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -1
  185. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js +16 -12
  186. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  187. package/dist/esm/fields/DatePicker/DatetimePicker.native.js +39 -14
  188. package/dist/esm/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  189. package/dist/esm/fields/DatePicker/MonthPicker.mjs +10 -3
  190. package/dist/esm/fields/DatePicker/MonthPicker.mjs.map +1 -1
  191. package/dist/esm/fields/DatePicker/MonthPicker.native.js +10 -3
  192. package/dist/esm/fields/DatePicker/MonthPicker.native.js.map +1 -1
  193. package/dist/esm/fields/DatePicker/MultiDatePicker.mjs +2 -2
  194. package/dist/esm/fields/DatePicker/MultiDatePicker.mjs.map +1 -1
  195. package/dist/esm/fields/DatePicker/MultiDatePicker.native.js +2 -2
  196. package/dist/esm/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
  197. package/dist/esm/fields/DatePicker/catalog.native.js.map +1 -1
  198. package/dist/esm/fields/DatePicker/fieldValueSync.native.js.map +1 -1
  199. package/dist/esm/fields/DatePicker/index.native.js +3 -0
  200. package/dist/esm/fields/DatePicker/index.native.js.map +1 -1
  201. package/dist/esm/fields/DatePicker/parts.mjs +28 -16
  202. package/dist/esm/fields/DatePicker/parts.mjs.map +1 -1
  203. package/dist/esm/fields/DatePicker/parts.native.js +30 -16
  204. package/dist/esm/fields/DatePicker/parts.native.js.map +1 -1
  205. package/dist/esm/fields/DatePicker/utils.native.js.map +1 -1
  206. package/dist/esm/fields/Duration/durationMath.native.js.map +1 -1
  207. package/dist/esm/fields/Duration/index.native.js.map +1 -1
  208. package/dist/esm/fields/FileUpload/filePickerTypes.native.js.map +1 -1
  209. package/dist/esm/fields/FileUpload/index.native.js.map +1 -1
  210. package/dist/esm/fields/FileUpload/nativePickerLoader.native.js.map +1 -1
  211. package/dist/esm/fields/Geolocation/GeolocationMap.mjs.map +1 -1
  212. package/dist/esm/fields/Geolocation/GeolocationMap.native.js.map +1 -1
  213. package/dist/esm/fields/Geolocation/expoMapsLoader.native.js.map +1 -1
  214. package/dist/esm/fields/Geolocation/geoValue.native.js.map +1 -1
  215. package/dist/esm/fields/Geolocation/geocoder.mjs +33 -0
  216. package/dist/esm/fields/Geolocation/geocoder.mjs.map +1 -0
  217. package/dist/esm/fields/Geolocation/geocoder.native.js +35 -0
  218. package/dist/esm/fields/Geolocation/geocoder.native.js.map +1 -0
  219. package/dist/esm/fields/Geolocation/index.mjs +792 -149
  220. package/dist/esm/fields/Geolocation/index.mjs.map +1 -1
  221. package/dist/esm/fields/Geolocation/index.native.js +847 -164
  222. package/dist/esm/fields/Geolocation/index.native.js.map +1 -1
  223. package/dist/esm/fields/Geolocation/mapMath.native.js.map +1 -1
  224. package/dist/esm/fields/Geolocation/mapTypes.native.js.map +1 -1
  225. package/dist/esm/fields/Geolocation/nativeSearchFocus.mjs +19 -0
  226. package/dist/esm/fields/Geolocation/nativeSearchFocus.mjs.map +1 -0
  227. package/dist/esm/fields/Geolocation/nativeSearchFocus.native.js +19 -0
  228. package/dist/esm/fields/Geolocation/nativeSearchFocus.native.js.map +1 -0
  229. package/dist/esm/fields/Icon/drawnSet.mjs +14 -3
  230. package/dist/esm/fields/Icon/drawnSet.mjs.map +1 -1
  231. package/dist/esm/fields/Icon/drawnSet.native.js +18 -3
  232. package/dist/esm/fields/Icon/drawnSet.native.js.map +1 -1
  233. package/dist/esm/fields/Icon/index.mjs +93 -18
  234. package/dist/esm/fields/Icon/index.mjs.map +1 -1
  235. package/dist/esm/fields/Icon/index.native.js +106 -22
  236. package/dist/esm/fields/Icon/index.native.js.map +1 -1
  237. package/dist/esm/fields/ImageField/index.native.js.map +1 -1
  238. package/dist/esm/fields/Input/index.mjs +5 -12
  239. package/dist/esm/fields/Input/index.mjs.map +1 -1
  240. package/dist/esm/fields/Input/index.native.js +5 -13
  241. package/dist/esm/fields/Input/index.native.js.map +1 -1
  242. package/dist/esm/fields/MentionInput/index.native.js.map +1 -1
  243. package/dist/esm/fields/OTPInput/index.native.js.map +1 -1
  244. package/dist/esm/fields/OTPInput/otpCellCss.native.js.map +1 -1
  245. package/dist/esm/fields/PhoneInput/dialCodes.native.js.map +1 -1
  246. package/dist/esm/fields/PhoneInput/index.mjs +2 -2
  247. package/dist/esm/fields/PhoneInput/index.mjs.map +1 -1
  248. package/dist/esm/fields/PhoneInput/index.native.js +2 -2
  249. package/dist/esm/fields/PhoneInput/index.native.js.map +1 -1
  250. package/dist/esm/fields/PhoneInput/phone.native.js.map +1 -1
  251. package/dist/esm/fields/Progress/index.native.js.map +1 -1
  252. package/dist/esm/fields/RadioGroup/index.native.js.map +1 -1
  253. package/dist/esm/fields/Rating/index.native.js.map +1 -1
  254. package/dist/esm/fields/Rating/ratingMath.native.js.map +1 -1
  255. package/dist/esm/fields/Rating/types.native.js.map +1 -1
  256. package/dist/esm/fields/SearchInput/index.mjs +9 -10
  257. package/dist/esm/fields/SearchInput/index.mjs.map +1 -1
  258. package/dist/esm/fields/SearchInput/index.native.js +9 -10
  259. package/dist/esm/fields/SearchInput/index.native.js.map +1 -1
  260. package/dist/esm/fields/Select/index.mjs +54 -19
  261. package/dist/esm/fields/Select/index.mjs.map +1 -1
  262. package/dist/esm/fields/Select/index.native.js +64 -38
  263. package/dist/esm/fields/Select/index.native.js.map +1 -1
  264. package/dist/esm/fields/Signature/index.native.js.map +1 -1
  265. package/dist/esm/fields/Slider/index.native.js.map +1 -1
  266. package/dist/esm/fields/Slider/jumpMotion.native.js.map +1 -1
  267. package/dist/esm/fields/Stepper/index.mjs +145 -32
  268. package/dist/esm/fields/Stepper/index.mjs.map +1 -1
  269. package/dist/esm/fields/Stepper/index.native.js +152 -32
  270. package/dist/esm/fields/Stepper/index.native.js.map +1 -1
  271. package/dist/esm/fields/Stepper/resolveStepperPlacement.native.js.map +1 -1
  272. package/dist/esm/fields/Switch/index.native.js.map +1 -1
  273. package/dist/esm/fields/TextArea/index.native.js.map +1 -1
  274. package/dist/esm/fields/TimePicker/catalog.native.js.map +1 -1
  275. package/dist/esm/fields/TimePicker/parts.native.js.map +1 -1
  276. package/dist/esm/fields/TimeRangeScrubber/geometry.native.js.map +1 -1
  277. package/dist/esm/fields/TimeRangeScrubber/index.native.js.map +1 -1
  278. package/dist/esm/fields/ToggleGroup/index.native.js.map +1 -1
  279. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
  280. package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -1
  281. package/dist/esm/fields/nativeSelect.native.js.map +1 -1
  282. package/dist/esm/fields/selectedOrder.native.js.map +1 -1
  283. package/dist/esm/formSpacing.native.js.map +1 -1
  284. package/dist/esm/hooks/useFocusManagement.native.js.map +1 -1
  285. package/dist/esm/index.mjs +2 -1
  286. package/dist/esm/index.mjs.map +1 -1
  287. package/dist/esm/index.native.js +2 -1
  288. package/dist/esm/index.native.js.map +1 -1
  289. package/dist/esm/requiredMarking.native.js.map +1 -1
  290. package/dist/esm/shared/TimeColumn.native.js.map +1 -1
  291. package/dist/esm/shared/capabilityPreflight.native.js.map +1 -1
  292. package/dist/esm/shared/colorRamps.native.js.map +1 -1
  293. package/dist/esm/shared/devWarn.native.js.map +1 -1
  294. package/dist/esm/shared/floatingList.native.js.map +1 -1
  295. package/dist/esm/shared/focusManagement.mjs +24 -11
  296. package/dist/esm/shared/focusManagement.mjs.map +1 -1
  297. package/dist/esm/shared/focusManagement.native.js +24 -11
  298. package/dist/esm/shared/focusManagement.native.js.map +1 -1
  299. package/dist/esm/shared/groupContext.native.js.map +1 -1
  300. package/dist/esm/shared/i18n.native.js.map +1 -1
  301. package/dist/esm/shared/pickerTriggerContract.mjs +13 -0
  302. package/dist/esm/shared/pickerTriggerContract.mjs.map +1 -0
  303. package/dist/esm/shared/pickerTriggerContract.native.js +13 -0
  304. package/dist/esm/shared/pickerTriggerContract.native.js.map +1 -0
  305. package/dist/esm/shared/pressSlopProps.mjs +43 -0
  306. package/dist/esm/shared/pressSlopProps.mjs.map +1 -0
  307. package/dist/esm/shared/pressSlopProps.native.js +44 -0
  308. package/dist/esm/shared/pressSlopProps.native.js.map +1 -0
  309. package/dist/esm/shared/t.native.js.map +1 -1
  310. package/dist/esm/shared/tableCellContext.native.js.map +1 -1
  311. package/dist/esm/shared/useDragScroll.native.js.map +1 -1
  312. package/dist/esm/shared/useKeyboardInset.native.js.map +1 -1
  313. package/dist/esm/shared/useSheetEscapeDismiss.native.js.map +1 -1
  314. package/dist/esm/shared/useWheelIncrement.native.js.map +1 -1
  315. package/dist/esm/shared/utils.native.js.map +1 -1
  316. package/dist/esm/shared/zIndex.native.js.map +1 -1
  317. package/dist/esm/themedPrimitives.native.js.map +1 -1
  318. package/dist/esm/types.native.js.map +1 -1
  319. package/dist/esm/utils/LinkCell.native.js.map +1 -1
  320. package/dist/jsx/Button/index.mjs +1 -34
  321. package/dist/jsx/Button/index.mjs.map +1 -1
  322. package/dist/jsx/Button/index.native.js +3 -36
  323. package/dist/jsx/Button/index.native.js.map +1 -1
  324. package/dist/jsx/Chip/index.mjs +1 -1
  325. package/dist/jsx/Chip/index.mjs.map +1 -1
  326. package/dist/jsx/Chip/index.native.js +1 -1
  327. package/dist/jsx/Chip/index.native.js.map +1 -1
  328. package/dist/jsx/ContextualSaveBar/index.mjs +17 -10
  329. package/dist/jsx/ContextualSaveBar/index.mjs.map +1 -1
  330. package/dist/jsx/ContextualSaveBar/index.native.js +18 -10
  331. package/dist/jsx/ContextualSaveBar/index.native.js.map +1 -1
  332. package/dist/jsx/FloatingPanel/index.mjs +58 -15
  333. package/dist/jsx/FloatingPanel/index.mjs.map +1 -1
  334. package/dist/jsx/FloatingPanel/index.native.js +16 -7
  335. package/dist/jsx/FloatingPanel/index.native.js.map +1 -1
  336. package/dist/jsx/FloatingPanel/nestedAction.mjs +11 -0
  337. package/dist/jsx/FloatingPanel/nestedAction.mjs.map +1 -0
  338. package/dist/jsx/FloatingPanel/nestedAction.native.js +42 -0
  339. package/dist/jsx/FloatingPanel/nestedAction.native.js.map +1 -0
  340. package/dist/jsx/InputParts/index.mjs +12 -8
  341. package/dist/jsx/InputParts/index.mjs.map +1 -1
  342. package/dist/jsx/InputParts/index.native.js +12 -10
  343. package/dist/jsx/InputParts/index.native.js.map +1 -1
  344. package/dist/jsx/Wheel/index.mjs +3 -3
  345. package/dist/jsx/Wheel/index.mjs.map +1 -1
  346. package/dist/jsx/Wheel/index.native.js +3 -3
  347. package/dist/jsx/Wheel/index.native.js.map +1 -1
  348. package/dist/jsx/fieldLayout.mjs +3 -3
  349. package/dist/jsx/fieldLayout.mjs.map +1 -1
  350. package/dist/jsx/fieldLayout.native.js +3 -3
  351. package/dist/jsx/fieldLayout.native.js.map +1 -1
  352. package/dist/jsx/fields/Checkbox/CheckboxFieldFrame.mjs +8 -1
  353. package/dist/jsx/fields/Checkbox/CheckboxFieldFrame.mjs.map +1 -1
  354. package/dist/jsx/fields/Checkbox/CheckboxFieldFrame.native.js +10 -1
  355. package/dist/jsx/fields/Checkbox/CheckboxFieldFrame.native.js.map +1 -1
  356. package/dist/jsx/fields/Combobox/index.mjs +74 -76
  357. package/dist/jsx/fields/Combobox/index.mjs.map +1 -1
  358. package/dist/jsx/fields/Combobox/index.native.js +78 -86
  359. package/dist/jsx/fields/Combobox/index.native.js.map +1 -1
  360. package/dist/jsx/fields/DatePicker/Calendar.mjs +6 -1
  361. package/dist/jsx/fields/DatePicker/Calendar.mjs.map +1 -1
  362. package/dist/jsx/fields/DatePicker/Calendar.native.js +6 -1
  363. package/dist/jsx/fields/DatePicker/Calendar.native.js.map +1 -1
  364. package/dist/jsx/fields/DatePicker/DateRangePicker.mjs +2 -2
  365. package/dist/jsx/fields/DatePicker/DateRangePicker.mjs.map +1 -1
  366. package/dist/jsx/fields/DatePicker/DateRangePicker.native.js +2 -2
  367. package/dist/jsx/fields/DatePicker/DateRangePicker.native.js.map +1 -1
  368. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs +15 -12
  369. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -1
  370. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js +16 -12
  371. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  372. package/dist/jsx/fields/DatePicker/DatetimePicker.native.js +39 -14
  373. package/dist/jsx/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  374. package/dist/jsx/fields/DatePicker/MonthPicker.mjs +10 -3
  375. package/dist/jsx/fields/DatePicker/MonthPicker.mjs.map +1 -1
  376. package/dist/jsx/fields/DatePicker/MonthPicker.native.js +10 -3
  377. package/dist/jsx/fields/DatePicker/MonthPicker.native.js.map +1 -1
  378. package/dist/jsx/fields/DatePicker/MultiDatePicker.mjs +2 -2
  379. package/dist/jsx/fields/DatePicker/MultiDatePicker.mjs.map +1 -1
  380. package/dist/jsx/fields/DatePicker/MultiDatePicker.native.js +2 -2
  381. package/dist/jsx/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
  382. package/dist/jsx/fields/DatePicker/index.native.js +3 -0
  383. package/dist/jsx/fields/DatePicker/index.native.js.map +1 -1
  384. package/dist/jsx/fields/DatePicker/parts.mjs +28 -16
  385. package/dist/jsx/fields/DatePicker/parts.mjs.map +1 -1
  386. package/dist/jsx/fields/DatePicker/parts.native.js +28 -14
  387. package/dist/jsx/fields/DatePicker/parts.native.js.map +1 -1
  388. package/dist/jsx/fields/Geolocation/GeolocationMap.mjs.map +1 -1
  389. package/dist/jsx/fields/Geolocation/GeolocationMap.native.js.map +1 -1
  390. package/dist/jsx/fields/Geolocation/geocoder.mjs +33 -0
  391. package/dist/jsx/fields/Geolocation/geocoder.mjs.map +1 -0
  392. package/dist/jsx/fields/Geolocation/geocoder.native.js +68 -0
  393. package/dist/jsx/fields/Geolocation/geocoder.native.js.map +1 -0
  394. package/dist/jsx/fields/Geolocation/index.mjs +792 -149
  395. package/dist/jsx/fields/Geolocation/index.mjs.map +1 -1
  396. package/dist/jsx/fields/Geolocation/index.native.js +856 -167
  397. package/dist/jsx/fields/Geolocation/index.native.js.map +1 -1
  398. package/dist/jsx/fields/Geolocation/nativeSearchFocus.mjs +19 -0
  399. package/dist/jsx/fields/Geolocation/nativeSearchFocus.mjs.map +1 -0
  400. package/dist/jsx/fields/Geolocation/nativeSearchFocus.native.js +47 -0
  401. package/dist/jsx/fields/Geolocation/nativeSearchFocus.native.js.map +1 -0
  402. package/dist/jsx/fields/Icon/drawnSet.mjs +14 -3
  403. package/dist/jsx/fields/Icon/drawnSet.mjs.map +1 -1
  404. package/dist/jsx/fields/Icon/drawnSet.native.js +18 -3
  405. package/dist/jsx/fields/Icon/drawnSet.native.js.map +1 -1
  406. package/dist/jsx/fields/Icon/index.mjs +93 -18
  407. package/dist/jsx/fields/Icon/index.mjs.map +1 -1
  408. package/dist/jsx/fields/Icon/index.native.js +115 -31
  409. package/dist/jsx/fields/Icon/index.native.js.map +1 -1
  410. package/dist/jsx/fields/Input/index.mjs +5 -12
  411. package/dist/jsx/fields/Input/index.mjs.map +1 -1
  412. package/dist/jsx/fields/Input/index.native.js +5 -13
  413. package/dist/jsx/fields/Input/index.native.js.map +1 -1
  414. package/dist/jsx/fields/PhoneInput/index.mjs +2 -2
  415. package/dist/jsx/fields/PhoneInput/index.mjs.map +1 -1
  416. package/dist/jsx/fields/PhoneInput/index.native.js +2 -2
  417. package/dist/jsx/fields/PhoneInput/index.native.js.map +1 -1
  418. package/dist/jsx/fields/SearchInput/index.mjs +9 -10
  419. package/dist/jsx/fields/SearchInput/index.mjs.map +1 -1
  420. package/dist/jsx/fields/SearchInput/index.native.js +8 -9
  421. package/dist/jsx/fields/SearchInput/index.native.js.map +1 -1
  422. package/dist/jsx/fields/Select/index.mjs +54 -19
  423. package/dist/jsx/fields/Select/index.mjs.map +1 -1
  424. package/dist/jsx/fields/Select/index.native.js +63 -37
  425. package/dist/jsx/fields/Select/index.native.js.map +1 -1
  426. package/dist/jsx/fields/Stepper/index.mjs +145 -32
  427. package/dist/jsx/fields/Stepper/index.mjs.map +1 -1
  428. package/dist/jsx/fields/Stepper/index.native.js +151 -31
  429. package/dist/jsx/fields/Stepper/index.native.js.map +1 -1
  430. package/dist/jsx/index.js +2 -1
  431. package/dist/jsx/index.js.map +1 -1
  432. package/dist/jsx/index.mjs +2 -1
  433. package/dist/jsx/index.mjs.map +1 -1
  434. package/dist/jsx/index.native.js +2 -0
  435. package/dist/jsx/index.native.js.map +1 -1
  436. package/dist/jsx/shared/focusManagement.mjs +24 -11
  437. package/dist/jsx/shared/focusManagement.mjs.map +1 -1
  438. package/dist/jsx/shared/focusManagement.native.js +24 -11
  439. package/dist/jsx/shared/focusManagement.native.js.map +1 -1
  440. package/dist/jsx/shared/pickerTriggerContract.mjs +13 -0
  441. package/dist/jsx/shared/pickerTriggerContract.mjs.map +1 -0
  442. package/dist/jsx/shared/pickerTriggerContract.native.js +41 -0
  443. package/dist/jsx/shared/pickerTriggerContract.native.js.map +1 -0
  444. package/dist/jsx/shared/pressSlopProps.mjs +43 -0
  445. package/dist/jsx/shared/pressSlopProps.mjs.map +1 -0
  446. package/dist/jsx/shared/pressSlopProps.native.js +72 -0
  447. package/dist/jsx/shared/pressSlopProps.native.js.map +1 -0
  448. package/package.json +8 -7
  449. package/src/Button/Button.labelInk.spec.tsx +21 -18
  450. package/src/Button/Button.outlinedChannel.spec.tsx +46 -0
  451. package/src/Button/Button.sizeLadder.spec.tsx +4 -3
  452. package/src/Button/Button.stories.tsx +20 -0
  453. package/src/Button/index.tsx +26 -39
  454. package/src/Chip/Chip.spec.tsx +3 -1
  455. package/src/Chip/index.tsx +1 -5
  456. package/src/ContextualSaveBar/ContextualSaveBar.spec.tsx +33 -0
  457. package/src/ContextualSaveBar/ContextualSaveBar.stories.tsx +19 -0
  458. package/src/ContextualSaveBar/index.tsx +12 -8
  459. package/src/ContextualSaveBar/nativeFlow.spec.tsx +20 -0
  460. package/src/FieldLayout.stories.tsx +41 -0
  461. package/src/FloatingPanel/FloatingPanel.native.spec.tsx +54 -0
  462. package/src/FloatingPanel/FloatingPanel.spec.tsx +109 -0
  463. package/src/FloatingPanel/index.native.tsx +24 -7
  464. package/src/FloatingPanel/index.tsx +80 -13
  465. package/src/FloatingPanel/nestedAction.spec.ts +36 -0
  466. package/src/FloatingPanel/nestedAction.ts +15 -0
  467. package/src/FloatingPanel/panelWidthContract.spec.ts +12 -15
  468. package/src/FormsIntegration.stories.tsx +2 -6
  469. package/src/InputParts/index.tsx +20 -6
  470. package/src/InputParts/layoutGap.spec.tsx +43 -0
  471. package/src/Wheel/Wheel.spec.tsx +16 -0
  472. package/src/Wheel/index.tsx +3 -3
  473. package/src/fieldLayout.spec.tsx +42 -1
  474. package/src/fieldLayout.tsx +9 -9
  475. package/src/fields/Checkbox/CheckboxFieldFrame.spec.tsx +18 -0
  476. package/src/fields/Checkbox/CheckboxFieldFrame.tsx +6 -1
  477. package/src/fields/Combobox/Combobox.spec.tsx +48 -1
  478. package/src/fields/Combobox/index.tsx +90 -80
  479. package/src/fields/DatePicker/Calendar.tsx +4 -1
  480. package/src/fields/DatePicker/DatePicker.stories.tsx +22 -1
  481. package/src/fields/DatePicker/DateRangePicker.tsx +4 -2
  482. package/src/fields/DatePicker/DatetimePicker.catalog.tsx +21 -19
  483. package/src/fields/DatePicker/DatetimePicker.native.spec.tsx +144 -0
  484. package/src/fields/DatePicker/DatetimePicker.native.tsx +57 -32
  485. package/src/fields/DatePicker/DatetimePicker.stories.tsx +22 -0
  486. package/src/fields/DatePicker/MonthPicker.tsx +8 -3
  487. package/src/fields/DatePicker/MultiDatePicker.tsx +4 -2
  488. package/src/fields/DatePicker/index.native.tsx +1 -0
  489. package/src/fields/DatePicker/parts.spec.tsx +56 -0
  490. package/src/fields/DatePicker/parts.tsx +33 -12
  491. package/src/fields/Geolocation/Geolocation.spec.tsx +287 -35
  492. package/src/fields/Geolocation/Geolocation.stories.tsx +141 -7
  493. package/src/fields/Geolocation/GeolocationMap.native.tsx +3 -0
  494. package/src/fields/Geolocation/GeolocationMap.tsx +4 -0
  495. package/src/fields/Geolocation/geocoder.spec.ts +59 -0
  496. package/src/fields/Geolocation/geocoder.ts +84 -0
  497. package/src/fields/Geolocation/geocoderSeam.spec.tsx +203 -0
  498. package/src/fields/Geolocation/index.tsx +912 -180
  499. package/src/fields/Geolocation/nativeSearchFocus.spec.ts +34 -0
  500. package/src/fields/Geolocation/nativeSearchFocus.ts +19 -0
  501. package/src/fields/Geolocation/salvage.spec.tsx +398 -0
  502. package/src/fields/Geolocation/storyProviders.spec.tsx +44 -0
  503. package/src/fields/Icon/Icon.spec.tsx +215 -1
  504. package/src/fields/Icon/Icon.stories.tsx +9 -1
  505. package/src/fields/Icon/drawnSet.tsx +39 -4
  506. package/src/fields/Icon/index.tsx +118 -18
  507. package/src/fields/Input/Input.copy.spec.tsx +1 -0
  508. package/src/fields/Input/Input.spec.tsx +50 -0
  509. package/src/fields/Input/Input.stories.tsx +1 -1
  510. package/src/fields/Input/index.tsx +7 -13
  511. package/src/fields/PhoneInput/index.tsx +2 -2
  512. package/src/fields/SearchInput/SearchInput.spec.tsx +14 -0
  513. package/src/fields/SearchInput/index.tsx +5 -6
  514. package/src/fields/Select/Select.spec.tsx +54 -0
  515. package/src/fields/Select/index.tsx +63 -33
  516. package/src/fields/Stepper/Stepper.spec.tsx +34 -1
  517. package/src/fields/Stepper/index.tsx +193 -33
  518. package/src/fields/pickerActivation.spec.tsx +105 -0
  519. package/src/fields/pickerDensity.spec.tsx +86 -0
  520. package/src/fields/pickerRows.spec.tsx +66 -0
  521. package/src/index.ts +9 -0
  522. package/src/shared/focusManagement.spec.tsx +68 -0
  523. package/src/shared/focusManagement.ts +20 -10
  524. package/src/shared/pickerTriggerContract.ts +13 -0
  525. package/src/shared/pressSlopProps.spec.ts +39 -0
  526. package/src/shared/pressSlopProps.ts +52 -0
  527. package/types/Button/index.d.ts +22 -3
  528. package/types/Button/index.d.ts.map +1 -1
  529. package/types/Chip/index.d.ts.map +1 -1
  530. package/types/ContextualSaveBar/index.d.ts +1 -1
  531. package/types/ContextualSaveBar/index.d.ts.map +1 -1
  532. package/types/FloatingPanel/index.d.ts +11 -1
  533. package/types/FloatingPanel/index.d.ts.map +1 -1
  534. package/types/FloatingPanel/index.native.d.ts +2 -1
  535. package/types/FloatingPanel/index.native.d.ts.map +1 -1
  536. package/types/FloatingPanel/nestedAction.d.ts +13 -0
  537. package/types/FloatingPanel/nestedAction.d.ts.map +1 -0
  538. package/types/InputParts/index.d.ts.map +1 -1
  539. package/types/fields/Checkbox/CheckboxFieldFrame.d.ts +46 -2
  540. package/types/fields/Checkbox/CheckboxFieldFrame.d.ts.map +1 -1
  541. package/types/fields/Checkbox/index.d.ts +3 -3
  542. package/types/fields/Combobox/index.d.ts +3 -1
  543. package/types/fields/Combobox/index.d.ts.map +1 -1
  544. package/types/fields/DatePicker/Calendar.d.ts.map +1 -1
  545. package/types/fields/DatePicker/DateRangePicker.d.ts.map +1 -1
  546. package/types/fields/DatePicker/DatetimePicker.catalog.d.ts.map +1 -1
  547. package/types/fields/DatePicker/DatetimePicker.native.d.ts.map +1 -1
  548. package/types/fields/DatePicker/MonthPicker.d.ts.map +1 -1
  549. package/types/fields/DatePicker/MultiDatePicker.d.ts.map +1 -1
  550. package/types/fields/DatePicker/index.native.d.ts.map +1 -1
  551. package/types/fields/DatePicker/parts.d.ts.map +1 -1
  552. package/types/fields/Geolocation/GeolocationMap.d.ts +4 -0
  553. package/types/fields/Geolocation/GeolocationMap.d.ts.map +1 -1
  554. package/types/fields/Geolocation/GeolocationMap.native.d.ts +3 -0
  555. package/types/fields/Geolocation/GeolocationMap.native.d.ts.map +1 -1
  556. package/types/fields/Geolocation/geocoder.d.ts +53 -0
  557. package/types/fields/Geolocation/geocoder.d.ts.map +1 -0
  558. package/types/fields/Geolocation/index.d.ts +18 -2
  559. package/types/fields/Geolocation/index.d.ts.map +1 -1
  560. package/types/fields/Geolocation/nativeSearchFocus.d.ts +8 -0
  561. package/types/fields/Geolocation/nativeSearchFocus.d.ts.map +1 -0
  562. package/types/fields/Icon/drawnSet.d.ts.map +1 -1
  563. package/types/fields/Icon/index.d.ts.map +1 -1
  564. package/types/fields/Input/index.d.ts.map +1 -1
  565. package/types/fields/PhoneInput/index.d.ts.map +1 -1
  566. package/types/fields/RadioGroup/index.d.ts +2 -2
  567. package/types/fields/SearchInput/index.d.ts.map +1 -1
  568. package/types/fields/Select/index.d.ts.map +1 -1
  569. package/types/fields/Stepper/index.d.ts.map +1 -1
  570. package/types/fields/ToggleGroup/index.d.ts +1 -1
  571. package/types/index.d.ts +2 -0
  572. package/types/index.d.ts.map +1 -1
  573. package/types/shared/focusManagement.d.ts +2 -2
  574. package/types/shared/focusManagement.d.ts.map +1 -1
  575. package/types/shared/pickerTriggerContract.d.ts +18 -0
  576. package/types/shared/pickerTriggerContract.d.ts.map +1 -0
  577. package/types/shared/pressSlopProps.d.ts +8 -0
  578. package/types/shared/pressSlopProps.d.ts.map +1 -0
  579. package/src/router.d.ts +0 -16
@@ -1,4 +1,4 @@
1
- import type { ReactNode, UIEvent } from "react";
1
+ import type { ReactNode, RefObject, UIEvent } from "react";
2
2
  import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
3
3
  import {
4
4
  OVERLAY_ATTACH_GAP,
@@ -15,6 +15,9 @@ import { useSheetEscapeDismiss } from "../shared/useSheetEscapeDismiss";
15
15
  import { useFloatingPanel, panelTransition, panelViewportPadding } from "./useFloatingPanel";
16
16
  import type { PanelWidthMode } from "./useFloatingPanel";
17
17
  import { zIndex } from "../shared/zIndex";
18
+ import { isNestedPanelAction, type PanelActionEvent } from "./nestedAction";
19
+
20
+ export { floatingPanelActionProps, isNestedPanelAction } from "./nestedAction";
18
21
 
19
22
  // Re-export hook for Select / Combobox / external consumers
20
23
  export {
@@ -37,6 +40,10 @@ export type {
37
40
  export interface FloatingPanelProps {
38
41
  open: boolean;
39
42
  onOpenChange: (open: boolean) => void;
43
+ /** Native only: the modal is presented and its inputs can receive focus. */
44
+ onNativeShow?: () => void;
45
+ /** Suppress delayed trigger focus when a consumer handles close focus itself. Web only. */
46
+ skipReturnFocusRef?: RefObject<boolean>;
40
47
  trigger: ReactNode;
41
48
  /**
42
49
  * Optional control rendered beside the trigger but OUTSIDE the native press
@@ -120,6 +127,11 @@ export interface FloatingPanelProps {
120
127
  * the strings must live on the Pressable itself. Web ignores this.
121
128
  */
122
129
  triggerA11y?: { label?: string; value?: string; hint?: string };
130
+ /**
131
+ * Stretch keeps the whole field pressable by default. Content lets an
132
+ * adornment trigger hug its content inside a composite field.
133
+ */
134
+ triggerSizing?: "stretch" | "content";
123
135
  /**
124
136
  * How the trigger opens the panel. `"press"` (default) is click/tap.
125
137
  * `"contextmenu"` is the ContextMenu gesture: web `contextmenu` (right-click
@@ -231,8 +243,10 @@ export function FloatingPanel(props: FloatingPanelProps) {
231
243
  function FloatingPanelSheet({
232
244
  open,
233
245
  onOpenChange,
246
+ skipReturnFocusRef,
234
247
  trigger,
235
248
  triggerEnd,
249
+ triggerSizing = "stretch",
236
250
  children,
237
251
  scrollable,
238
252
  disabled,
@@ -260,7 +274,7 @@ function FloatingPanelSheet({
260
274
  // DG-A11Y-04 / Axiom 7: Sheet has no focus scope — move focus into the
261
275
  // frame on open, trap Tab while open, return focus to the trigger on close.
262
276
  const sheetFrameRef = useRef<HTMLElement | null>(null);
263
- useSheetModalFocus(open, sheetFrameRef);
277
+ useSheetModalFocus(open, sheetFrameRef, skipReturnFocusRef);
264
278
  useKeyboardFocusIntoPanel(open, sheetFrameRef);
265
279
 
266
280
  const framePadding = contentPadding === "none" ? "$0" : knobProps.panelPadding.padding;
@@ -283,7 +297,10 @@ function FloatingPanelSheet({
283
297
  // shut the sheet it had just opened. Nothing is lost: the sheet is modal, so
284
298
  // its overlay covers the trigger and the toggle could only ever have run
285
299
  // with `open === false`.
286
- const openFromTrigger = () => handleOpenChange(true);
300
+ const openFromTrigger = (event?: PanelActionEvent) => {
301
+ if (event && isNestedPanelAction(event)) return;
302
+ handleOpenChange(true);
303
+ };
287
304
  const isContextMenu = openOn === "contextmenu";
288
305
  const triggerActivation = isWeb
289
306
  ? isContextMenu
@@ -298,9 +315,13 @@ function FloatingPanelSheet({
298
315
 
299
316
  return (
300
317
  <>
301
- <View flexDirection="row" alignItems="center" width="100%">
318
+ <View
319
+ flexDirection="row"
320
+ alignItems="center"
321
+ width={triggerSizing === "content" ? "auto" : "100%"}
322
+ >
302
323
  <View
303
- flex={1}
324
+ flex={triggerSizing === "content" ? undefined : 1}
304
325
  minWidth={0}
305
326
  {...(isContextMenu ? undefined : { onPress: openFromTrigger })}
306
327
  cursor={disabled ? "not-allowed" : "pointer"}
@@ -361,6 +382,7 @@ function FloatingPanelSheet({
361
382
  function FloatingPanelWeb({
362
383
  open,
363
384
  onOpenChange,
385
+ skipReturnFocusRef,
364
386
  trigger,
365
387
  children,
366
388
  disabled,
@@ -379,6 +401,7 @@ function FloatingPanelWeb({
379
401
  const growDownOnlyRef = useRef(false);
380
402
  const lastScrollTopRef = useRef(0);
381
403
  const keyboardOpenRef = useRef(false);
404
+ const contentRef = useRef<HTMLElement | null>(null);
382
405
 
383
406
  const repositionFnRef = useRef<(() => void) | null>(null);
384
407
 
@@ -405,7 +428,7 @@ function FloatingPanelWeb({
405
428
  // Escape (or outside click on empty space) closes the panel while focus is
406
429
  // inside it, focus falls to <body>. Return it to the pre-open element
407
430
  // (the trigger's focusable). Deliberate focus moves elsewhere are kept.
408
- useReturnFocusOnClose(open);
431
+ useReturnFocusOnClose(open, skipReturnFocusRef);
409
432
  useKeyboardFocusIntoPanel(open && panel.mounted, panel.refs.floating, keyboardOpenRef);
410
433
  useEffect(() => {
411
434
  if (!open) keyboardOpenRef.current = false;
@@ -573,6 +596,29 @@ function FloatingPanelWeb({
573
596
  repositionOverlay();
574
597
  }, [open, panel.mounted, repositionOverlay, panel.y]);
575
598
 
599
+ useLayoutEffect(() => {
600
+ const content = contentRef.current;
601
+ if (!open || !panel.mounted || !isFill || !content || typeof ResizeObserver === "undefined") {
602
+ return;
603
+ }
604
+ // Matching the trigger width can rewrap content after the panel's first
605
+ // height measurement. Observe the content, not the clamped viewport, so a
606
+ // later open or result change uses its actual height without a resize loop.
607
+ let frame = 0;
608
+ const observer = new ResizeObserver(() => {
609
+ if (frame) cancelAnimationFrame(frame);
610
+ frame = requestAnimationFrame(() => {
611
+ frame = 0;
612
+ repositionOverlay();
613
+ });
614
+ });
615
+ observer.observe(content);
616
+ return () => {
617
+ observer.disconnect();
618
+ if (frame) cancelAnimationFrame(frame);
619
+ };
620
+ }, [open, panel.mounted, isFill, repositionOverlay]);
621
+
576
622
  const handleScroll = useCallback(
577
623
  (event: UIEvent) => {
578
624
  const el = event.target as HTMLElement;
@@ -633,8 +679,8 @@ function FloatingPanelWeb({
633
679
  // (`p-results/trigger-slot/detail.json`): a pointer click carries the click
634
680
  // count, `element.click()` and native keyboard activation carry 0.
635
681
  const openFromSynthesizedClick = useCallback(
636
- (event: { detail: number }) => {
637
- if (disabled || isContextMenu || event.detail !== 0) return;
682
+ (event: { detail: number } & PanelActionEvent) => {
683
+ if (disabled || isContextMenu || event.detail !== 0 || isNestedPanelAction(event)) return;
638
684
  // detail 0 is keyboard/programmatic activation (LC-50). Count it as
639
685
  // keyboard-origin so the first inner control rings (LC-71).
640
686
  keyboardOpenRef.current = true;
@@ -652,14 +698,26 @@ function FloatingPanelWeb({
652
698
  [disabled, onOpenChange],
653
699
  );
654
700
 
701
+ const referenceProps = getReferenceProps({
702
+ ref: panel.setReferenceRef,
703
+ onClickCapture: openFromSynthesizedClick,
704
+ ...(isContextMenu ? { onContextMenu: openFromContextMenu } : {}),
705
+ });
706
+ // Guard the composed floating-ui openers, after hook handlers are merged.
707
+ // Declining leaves each action's own events and focus defaults intact.
708
+ for (const name of ["onMouseDown", "onClick", "onKeyDown", "onKeyUp"] as const) {
709
+ const handler = referenceProps[name];
710
+ if (typeof handler === "function") {
711
+ referenceProps[name] = (event: PanelActionEvent) => {
712
+ if (!isNestedPanelAction(event)) handler(event);
713
+ };
714
+ }
715
+ }
716
+
655
717
  return (
656
718
  <>
657
719
  <View
658
- {...getReferenceProps({
659
- ref: panel.setReferenceRef,
660
- onClickCapture: openFromSynthesizedClick,
661
- ...(isContextMenu ? { onContextMenu: openFromContextMenu } : {}),
662
- })}
720
+ {...referenceProps}
663
721
  cursor={disabled ? "not-allowed" : "pointer"}
664
722
  opacity={disabled ? 0.5 : 1}
665
723
  // The cover contract is stated as a relation between the panel's top
@@ -693,6 +751,14 @@ function FloatingPanelWeb({
693
751
  transformOrigin: panelTransformOrigin(panel.placement),
694
752
  },
695
753
  onScroll: handleScroll,
754
+ onTransitionEnd: (event) => {
755
+ if (
756
+ event.target === event.currentTarget &&
757
+ (event.propertyName === "height" || event.propertyName === "max-height")
758
+ ) {
759
+ panel.updateArrows(event.currentTarget);
760
+ }
761
+ },
696
762
  })}
697
763
  data-testid="floating-panel-viewport"
698
764
  data-fp-panel=""
@@ -714,6 +780,7 @@ function FloatingPanelWeb({
714
780
  arrowScrollDirRef={panel.arrowScrollDirRef}
715
781
  />
716
782
  <View
783
+ ref={contentRef as any}
717
784
  {...(contentPadding !== "none" ? panel.knobProps.panelPadding : undefined)}
718
785
  {...(contentPadding !== "none" ? panel.knobProps.gapLg : undefined)}
719
786
  >
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { isNestedPanelAction } from "./nestedAction";
3
+
4
+ describe("independent actions inside panel triggers", () => {
5
+ it("recognizes an SVG descendant of a marked action", () => {
6
+ const wrapper = document.createElement("div");
7
+ wrapper.innerHTML = '<button data-floating-panel-action="true"><svg><path /></svg></button>';
8
+ expect(
9
+ isNestedPanelAction({
10
+ nativeEvent: { target: wrapper.querySelector("path") },
11
+ currentTarget: wrapper,
12
+ }),
13
+ ).toBe(true);
14
+ });
15
+
16
+ it("does not let a marker above the wrapper suppress an ordinary opener", () => {
17
+ const outer = document.createElement("div");
18
+ outer.setAttribute("data-floating-panel-action", "true");
19
+ outer.innerHTML = "<div><button>Open</button></div>";
20
+ expect(
21
+ isNestedPanelAction({
22
+ target: outer.querySelector("button"),
23
+ currentTarget: outer.firstElementChild,
24
+ }),
25
+ ).toBe(false);
26
+ });
27
+
28
+ it("keeps unmarked buttons and non-DOM events as ordinary trigger presses", () => {
29
+ const wrapper = document.createElement("div");
30
+ wrapper.innerHTML = "<button>Open</button>";
31
+ expect(isNestedPanelAction({ target: wrapper.firstElementChild, currentTarget: wrapper })).toBe(
32
+ false,
33
+ );
34
+ expect(isNestedPanelAction({})).toBe(false);
35
+ });
36
+ });
@@ -0,0 +1,15 @@
1
+ export const floatingPanelActionProps = { "data-floating-panel-action": "true" } as const;
2
+
3
+ export interface PanelActionEvent {
4
+ target?: unknown;
5
+ currentTarget?: unknown;
6
+ nativeEvent?: { target?: unknown };
7
+ }
8
+
9
+ /** Marked actions inside a trigger keep their own press and keyboard behavior. */
10
+ export function isNestedPanelAction(event: PanelActionEvent): boolean {
11
+ const target = (event.nativeEvent?.target ?? event.target) as Element | null;
12
+ const wrapper = event.currentTarget as Element | null;
13
+ const action = target?.closest?.('[data-floating-panel-action="true"]');
14
+ return !!action && !!wrapper?.contains?.(action);
15
+ }
@@ -1,6 +1,5 @@
1
1
  /**
2
- * MPO-38 AC: no FloatingPanel consumer renders a panel narrower than its
3
- * trigger.
2
+ * Text listboxes match the field width; intrinsic editors size to their content.
4
3
  *
5
4
  * The runtime half is covered in `useFloatingPanel.spec.tsx`. This is the
6
5
  * source half, and it exists because the defect it guards is invisible at
@@ -8,11 +7,9 @@
8
7
  * DatePicker measured 262px of calendar under a 289px trigger purely because
9
8
  * the call site said `fitContent`.
10
9
  *
11
- * A FIELD-shaped trigger (a control the user reads a value out of) takes
12
- * `at-least-trigger`. A BUTTON-shaped trigger — an icon, a toolbar chip —
13
- * legitimately keeps `fit-content`, because matching a 32px button would be
14
- * absurd; those are listed explicitly so the exemption is a decision rather
15
- * than an oversight.
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.
16
13
  */
17
14
  import { readFileSync } from "node:fs";
18
15
  import { dirname, resolve } from "node:path";
@@ -22,13 +19,9 @@ import { describe, expect, it } from "vitest";
22
19
  const fieldsDir = resolve(dirname(fileURLToPath(import.meta.url)), "..", "fields");
23
20
 
24
21
  /** Pickers whose trigger is a form FIELD: the panel may never be narrower. */
25
- const fieldShapedPickers = [
26
- "DatePicker/parts.tsx",
27
- "DatePicker/DatetimePicker.catalog.tsx",
28
- "TimePicker/catalog.tsx",
29
- "ColorPicker/parts.tsx",
30
- "Icon/index.tsx",
31
- ];
22
+ const fieldShapedPickers = ["TimePicker/catalog.tsx", "ColorPicker/parts.tsx", "Icon/index.tsx"];
23
+
24
+ const intrinsicGridPickers = ["DatePicker/parts.tsx", "DatePicker/DatetimePicker.catalog.tsx"];
32
25
 
33
26
  /** Panels opened from an icon/button trigger, where fit-content is correct. */
34
27
  const buttonShapedPickers = ["Geolocation/index.tsx"];
@@ -37,7 +30,11 @@ function read(relative: string): string {
37
30
  return readFileSync(resolve(fieldsDir, relative), "utf8");
38
31
  }
39
32
 
40
- describe("FloatingPanel width contract (MPO-38)", () => {
33
+ describe("FloatingPanel width contract (MPO-38, MPO-262)", () => {
34
+ it.each(intrinsicGridPickers)("%s hugs its calendar grid", (relative) => {
35
+ expect(read(relative)).toContain('widthMode="fit-content"');
36
+ });
37
+
41
38
  it.each(fieldShapedPickers)("%s declares at-least-trigger", (relative) => {
42
39
  const source = read(relative);
43
40
  expect(source).toContain('widthMode="at-least-trigger"');
@@ -212,9 +212,7 @@ export const multiStepRegistration: Story = {
212
212
  />
213
213
 
214
214
  <FormActions>
215
- <Button variant="outlined" onPress={() => setStep("personal")}>
216
- Back
217
- </Button>
215
+ <Button onPress={() => setStep("personal")}>Back</Button>
218
216
  <Button onPress={() => setStep("verification")}>Next: Verification</Button>
219
217
  </FormActions>
220
218
  </FormSection>
@@ -240,9 +238,7 @@ export const multiStepRegistration: Story = {
240
238
  />
241
239
 
242
240
  <FormActions>
243
- <Button variant="outlined" onPress={() => setStep("contact")}>
244
- Back
245
- </Button>
241
+ <Button onPress={() => setStep("contact")}>Back</Button>
246
242
  <form.Subscribe selector={(state) => [state.canSubmit, state.isSubmitting]}>
247
243
  {([canSubmit, isSubmitting]) => (
248
244
  <Button action="submit" disabled={!canSubmit || isSubmitting}>
@@ -1,6 +1,6 @@
1
1
  import { getFontSized } from "@tamagui/get-font-sized";
2
2
  import type { SizeVariantSpreadFunction } from "@tamagui/web";
3
- import { useEffect, useState, type ReactNode } from "react";
3
+ import { useCallback, useEffect, useState, type ReactNode } from "react";
4
4
  import type { ComponentProps, Ref, RefObject } from "react";
5
5
  import { Pressable as RNPressable } from "react-native";
6
6
  import type {
@@ -443,7 +443,10 @@ const InputGroupImpl = InputGroupFrame.styleable<{
443
443
  // paints outside overflow:hidden, so the ring clip can stay.
444
444
  const paintsRing = !grouped && !inTableCell;
445
445
  const hasElevation = knobProps.elevation != null;
446
- const boxOverflow: BoxOverflow = paintsRing && !(hasElevation && !isWeb) ? "hidden" : "visible";
446
+ const hasWebPressSlop =
447
+ isWeb && (props as { "data-mp-press-slop"?: string })["data-mp-press-slop"] != null;
448
+ const boxOverflow: BoxOverflow =
449
+ paintsRing && !(hasElevation && !isWeb) && !hasWebPressSlop ? "hidden" : "visible";
447
450
  const ringMode = boxRingMode(boxOverflow, paintsRing);
448
451
  if (isWeb) {
449
452
  ensureCompositeFocusRing();
@@ -536,6 +539,7 @@ const InputGroupImpl = InputGroupFrame.styleable<{
536
539
  minWidth={0}
537
540
  elevation={undefined}
538
541
  {...props}
542
+ {...controlFragment}
539
543
  {...passthroughSizeProps}
540
544
  {...insetPadProps}
541
545
  {...sizeRecipeEscapeOverride}
@@ -1031,6 +1035,16 @@ const InputButtonImpl = InputButton.styleable<{ glyphRing?: boolean }>((buttonPr
1031
1035
  size: knobProps.controlIcon.width,
1032
1036
  color: paint as never,
1033
1037
  });
1038
+ const decorativeRef = useCallback(
1039
+ (node: TamaguiElement | null) => {
1040
+ // Tamagui 2.7.6 Button stamps tabIndex=0 after consumer props.
1041
+ // Correct the host at hydration; decorative caps share the field stop.
1042
+ if (isWeb && node && "tabIndex" in node) node.tabIndex = tabIndexProp ?? -1;
1043
+ if (typeof ref === "function") ref(node);
1044
+ else if (ref) ref.current = node;
1045
+ },
1046
+ [ref, tabIndexProp],
1047
+ );
1034
1048
 
1035
1049
  if (glyphRing) {
1036
1050
  // A real tab stop must square off the token it was HANDED, not the
@@ -1073,7 +1087,7 @@ const InputButtonImpl = InputButton.styleable<{ glyphRing?: boolean }>((buttonPr
1073
1087
  const square = knobProps.control.height;
1074
1088
  return (
1075
1089
  <InputButton
1076
- ref={ref}
1090
+ ref={decorativeRef}
1077
1091
  size={size}
1078
1092
  height={square}
1079
1093
  width={square}
@@ -1162,9 +1176,8 @@ export const InputContainerFrame = styled(View, {
1162
1176
 
1163
1177
  variants: {
1164
1178
  size: {
1165
- "...size": (val, { tokens }) => ({
1166
- gap: (tokens.space[val] as any)?.val * 0.3,
1167
- }),
1179
+ // Size is shared with the child parts; layout spacing belongs to space.
1180
+ "...size": () => ({}),
1168
1181
  },
1169
1182
  color: {
1170
1183
  "...color": () => ({}),
@@ -1190,6 +1203,7 @@ const InputContainerImpl = InputContainerFrame.styleable((props, forwardedRef) =
1190
1203
  ref={forwardedRef}
1191
1204
  size={knobProps.sizeToken}
1192
1205
  {...knobProps.body}
1206
+ {...knobProps.gap}
1193
1207
  // LC-06/LC-10 (UX-P12): inside a table cell the field root must adopt
1194
1208
  // the cell width — the raw web <input> carries an intrinsic width that
1195
1209
  // otherwise floors the whole column at ~200px and lays out past the
@@ -0,0 +1,43 @@
1
+ import { createRef } from "react";
2
+ import { Preset } from "@multiplatform.one/theme";
3
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
4
+ import { cleanup } from "@testing-library/react";
5
+ import { afterEach, expect, it } from "vitest";
6
+ import { Input } from "./index";
7
+
8
+ afterEach(cleanup);
9
+
10
+ function measure(size: "small" | "medium", space: "small" | "large", gap?: number) {
11
+ const ref = createRef<HTMLElement>();
12
+ const { getByTestId } = renderWithProviders(
13
+ <Preset overrides={{ size, space }}>
14
+ <Input testID="root" ref={ref as never} {...(gap === undefined ? {} : { gap })}>
15
+ <Input.Label>Location</Input.Label>
16
+ <Input.Box>
17
+ <Input.Area placeholder="Location" />
18
+ </Input.Box>
19
+ </Input>
20
+ </Preset>,
21
+ );
22
+ const root = getByTestId("root");
23
+ expect(ref.current).toBe(root);
24
+ // Happy DOM leaves token-backed CSS gaps unresolved; browser coverage reads
25
+ // the actual pixels. Keep this test checking the rendered gap owner.
26
+ const value =
27
+ getComputedStyle(root).gap || [...root.classList].find((name) => name.startsWith("_gap-"));
28
+ expect(value).toBeTruthy();
29
+ cleanup();
30
+ return value;
31
+ }
32
+
33
+ it("lets space change the multi-slot layout gap at a fixed control size", () => {
34
+ expect(measure("medium", "small")).not.toBe(measure("medium", "large"));
35
+ });
36
+
37
+ it("keeps the layout gap stable when only control size changes", () => {
38
+ expect(measure("small", "small")).toBe(measure("medium", "small"));
39
+ });
40
+
41
+ it("preserves the consumer gap eject and forwarded root ref", () => {
42
+ expect(measure("medium", "large", 11)).toBe("11px");
43
+ });
@@ -7,6 +7,22 @@ import { Wheel } from "./index";
7
7
  afterEach(cleanup);
8
8
 
9
9
  describe("Wheel contrast contract", () => {
10
+ it("uses the selected-text weight and keeps rounding on the selection band", () => {
11
+ renderWithProviders(
12
+ <Preset overrides={{ borderRadius: "full" }}>
13
+ <Wheel items={[1, 2, 3, 4, 5]} value={3} visibleItems={5} />
14
+ </Preset>,
15
+ );
16
+ const wheel = screen.getByTestId("wheel");
17
+ const selected = wheel.querySelector('[data-selected="true"]')?.firstElementChild;
18
+ expect(selected).toBeTruthy();
19
+ expect(["700", "bold"]).toContain(getComputedStyle(selected as Element).fontWeight);
20
+ expect(["", "0px"]).toContain(getComputedStyle(wheel).borderTopLeftRadius);
21
+ expect(getComputedStyle(screen.getByTestId("wheel-selection")).borderStartStartRadius).not.toBe(
22
+ "0px",
23
+ );
24
+ });
25
+
10
26
  it("keeps inactive item text on the AA $color11 ramp (not opacity-dimmed)", () => {
11
27
  renderWithProviders(
12
28
  <Wheel items={[1, 2, 3, 4, 5]} value={3} onChange={() => {}} visibleItems={5} />,
@@ -112,7 +112,7 @@ function ensureWheelFocusStyles() {
112
112
  const style = document.createElement("style");
113
113
  style.id = WHEEL_FOCUS_STYLE_ID;
114
114
  style.textContent =
115
- ".mp1-wheel-wrapper:focus-visible { outline: 2px solid var(--outlineColor, rgba(0,0,0,0.5)); outline-offset: -2px; }";
115
+ ".mp1-wheel-wrapper:focus-visible { outline: 2px solid var(--outlineColor, var(--c-outlineColor, CanvasText)); outline-offset: -2px; }";
116
116
  document.head.appendChild(style);
117
117
  }
118
118
 
@@ -847,7 +847,7 @@ export function Wheel<T = string | number>({
847
847
  <Text
848
848
  {...knobProps.body}
849
849
  fontSize={size}
850
- fontWeight={isSelected && !disabled ? "600" : "400"}
850
+ fontWeight={isSelected && !disabled ? "bold" : knobProps.textWeight.fontWeight}
851
851
  color={itemColor(isSelected)}
852
852
  numberOfLines={1}
853
853
  ellipsizeMode="tail"
@@ -865,7 +865,7 @@ export function Wheel<T = string | number>({
865
865
  width={flex ? "100%" : columnWidth}
866
866
  minWidth={flex ? 0 : columnWidth}
867
867
  flex={flex}
868
- {...knobProps.containerRadius}
868
+ borderRadius={0}
869
869
  data-testid="wheel"
870
870
  data-disabled={disabled ? true : undefined}
871
871
  aria-disabled={disabled || undefined}
@@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
2
  import { renderWithProviders } from "@multiplatform.one/test-utils";
3
3
  import { Preset } from "@multiplatform.one/theme";
4
4
  import { Input as TamaguiInput } from "tamagui";
5
- import { FieldLayout } from "./fieldLayout";
5
+ import { FieldLayout, useFieldDescribedBy } from "./fieldLayout";
6
6
  import { __resetDevWarnSeen } from "./shared/devWarn";
7
7
 
8
8
  function classIncludes(container: HTMLElement, fragment: string): boolean {
@@ -188,6 +188,47 @@ describe("FieldLayout error precedence (Axiom 5 eject-last)", () => {
188
188
  });
189
189
  });
190
190
 
191
+ describe("FieldLayout message-before-control anatomy", () => {
192
+ function Control() {
193
+ return <input id="ordered" aria-describedby={useFieldDescribedBy()} />;
194
+ }
195
+
196
+ for (const mode of ["top", "side", "compact"] as const) {
197
+ it(`${mode}: helper and replacement error precede the control and describe only the rendered slot`, () => {
198
+ const renderField = (error?: string) => (
199
+ <Preset overrides={{ fieldLabelPlacement: mode === "side" ? "side" : "top" }}>
200
+ <FieldLayout
201
+ id="ordered"
202
+ label="Email"
203
+ compactSpacing={mode === "compact"}
204
+ helperText="Use your work address"
205
+ error={error}
206
+ >
207
+ <Control />
208
+ </FieldLayout>
209
+ </Preset>
210
+ );
211
+ const result = renderWithProviders(renderField());
212
+ const assertSlot = (id: string, text: string) => {
213
+ const input = result.container.querySelector("input")!;
214
+ const message = result.container.querySelector(`#${id}`)!;
215
+ expect(message.textContent).toBe(text);
216
+ expect(
217
+ message.compareDocumentPosition(input) & Node.DOCUMENT_POSITION_FOLLOWING,
218
+ ).toBeTruthy();
219
+ expect(input.getAttribute("aria-describedby")).toBe(id);
220
+ expect(
221
+ result.container.querySelectorAll("[id$='-description'], [id$='-error']"),
222
+ ).toHaveLength(1);
223
+ };
224
+ assertSlot("ordered-description", "Use your work address");
225
+ result.rerender(renderField("Enter an email address"));
226
+ assertSlot("ordered-error", "Enter an email address");
227
+ expect(result.container.textContent).not.toContain("Use your work address");
228
+ });
229
+ }
230
+ });
231
+
191
232
  describe("FieldLayout banned-error-word DEV warn", () => {
192
233
  let warnSpy: ReturnType<typeof vi.spyOn>;
193
234
 
@@ -218,13 +218,13 @@ export function FieldLayout({
218
218
  ...(width !== undefined ? { width } : {}),
219
219
  };
220
220
 
221
- const describedBy =
222
- [
223
- helperText && id ? `${id}-description` : undefined,
224
- errorMessage && id ? `${id}-error` : undefined,
225
- ]
226
- .filter(Boolean)
227
- .join(" ") || undefined;
221
+ const describedBy = id
222
+ ? errorMessage
223
+ ? `${id}-error`
224
+ : helperText
225
+ ? `${id}-description`
226
+ : undefined
227
+ : undefined;
228
228
 
229
229
  const a11yLabel = typeof label === "string" ? `${label}${requiredSuffix}` : ariaLabel;
230
230
  const a11yDescription = errorMessage ?? helperText;
@@ -293,9 +293,9 @@ export function FieldLayout({
293
293
 
294
294
  const controlColumn = (
295
295
  <YStack flex={sideLabel ? 1 : undefined} {...gapProps}>
296
- {children}
297
296
  {helperNode}
298
297
  {errorNode}
298
+ {children}
299
299
  </YStack>
300
300
  );
301
301
 
@@ -330,9 +330,9 @@ export function FieldLayout({
330
330
  ) : (
331
331
  <>
332
332
  {labelNode}
333
- {children}
334
333
  {helperNode}
335
334
  {errorNode}
335
+ {children}
336
336
  </>
337
337
  )}
338
338
  </YStack>
@@ -11,6 +11,8 @@
11
11
  import { cleanup } from "@testing-library/react";
12
12
  import { renderWithProviders } from "@multiplatform.one/test-utils";
13
13
  import { afterEach, describe, expect, it } from "vitest";
14
+ import { createRef } from "react";
15
+ import { FieldLayout } from "../../fieldLayout";
14
16
  import { CheckboxFieldFrame } from "./CheckboxFieldFrame";
15
17
 
16
18
  afterEach(cleanup);
@@ -31,6 +33,22 @@ function frame(container: HTMLElement): HTMLElement {
31
33
  }
32
34
 
33
35
  describe("CheckboxFieldFrame", () => {
36
+ it("links the rendered field message, forwards its ref, and preserves an explicit description", () => {
37
+ const ref = createRef<any>();
38
+ const fixture = (override?: { "aria-describedby": string | undefined }) => (
39
+ <FieldLayout id="consent" helperText="Read the terms" error="Accept the terms">
40
+ <CheckboxFieldFrame id="consent" checked={false} ref={ref} {...override} />
41
+ </FieldLayout>
42
+ );
43
+ const result = renderWithProviders(fixture());
44
+ expect(frame(result.container).getAttribute("aria-describedby")).toBe("consent-error");
45
+ expect(ref.current).toBe(frame(result.container));
46
+ result.rerender(fixture({ "aria-describedby": "consumer-description" }));
47
+ expect(frame(result.container).getAttribute("aria-describedby")).toBe("consumer-description");
48
+ result.rerender(fixture({ "aria-describedby": undefined }));
49
+ expect(frame(result.container).getAttribute("aria-describedby")).toBeNull();
50
+ });
51
+
34
52
  it("still renders a real role=checkbox, so the styling never costs the semantics", () => {
35
53
  const { container } = renderWithProviders(<CheckboxFieldFrame checked={false} />);
36
54
  expect(frame(container)).toBeTruthy();
@@ -1,7 +1,8 @@
1
1
  import { Checkbox as TamaguiCheckbox, styled } from "tamagui";
2
+ import { useFieldDescribedBy } from "../../fieldLayout";
2
3
 
3
4
  /** Hit-target frame. The painted box is CheckboxGlyphBox (LC-71). */
4
- export const CheckboxFieldFrame = styled(TamaguiCheckbox, {
5
+ const CheckboxFieldFrameBase = styled(TamaguiCheckbox, {
5
6
  backgroundColor: "transparent",
6
7
  borderColor: "transparent",
7
8
  borderWidth: 0,
@@ -21,3 +22,7 @@ export const CheckboxFieldFrame = styled(TamaguiCheckbox, {
21
22
  },
22
23
  activeStyle: { backgroundColor: "transparent", borderColor: "transparent" },
23
24
  });
25
+
26
+ export const CheckboxFieldFrame = CheckboxFieldFrameBase.styleable((props, ref) => (
27
+ <CheckboxFieldFrameBase aria-describedby={useFieldDescribedBy()} {...props} ref={ref} />
28
+ ));