@multiplatform.one/forms 7.77.0 → 7.77.2

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 (429) hide show
  1. package/dist/cjs/Button/IconButtonGroup.cjs +45 -18
  2. package/dist/cjs/Button/IconButtonGroup.native.js +51 -20
  3. package/dist/cjs/Button/IconButtonGroup.native.js.map +1 -1
  4. package/dist/cjs/Button/circleGlyph.cjs +12 -2
  5. package/dist/cjs/Button/circleGlyph.native.js +12 -1
  6. package/dist/cjs/Button/circleGlyph.native.js.map +1 -1
  7. package/dist/cjs/Button/iconButtonGroupContext.cjs +7 -1
  8. package/dist/cjs/Button/iconButtonGroupContext.native.js +7 -1
  9. package/dist/cjs/Button/iconButtonGroupContext.native.js.map +1 -1
  10. package/dist/cjs/Button/index.cjs +3 -11
  11. package/dist/cjs/Button/index.native.js +3 -12
  12. package/dist/cjs/Button/index.native.js.map +1 -1
  13. package/dist/cjs/FloatingPanel/coverRect.cjs +50 -0
  14. package/dist/cjs/FloatingPanel/coverRect.native.js +54 -0
  15. package/dist/cjs/FloatingPanel/coverRect.native.js.map +1 -0
  16. package/dist/cjs/FloatingPanel/iconEcho.cjs +3 -1
  17. package/dist/cjs/FloatingPanel/iconEcho.native.js +3 -1
  18. package/dist/cjs/FloatingPanel/iconEcho.native.js.map +1 -1
  19. package/dist/cjs/FloatingPanel/index.cjs +27 -29
  20. package/dist/cjs/FloatingPanel/index.native.js +41 -15
  21. package/dist/cjs/FloatingPanel/index.native.js.map +1 -1
  22. package/dist/cjs/InputParts/index.cjs +5 -0
  23. package/dist/cjs/InputParts/index.native.js +7 -0
  24. package/dist/cjs/InputParts/index.native.js.map +1 -1
  25. package/dist/cjs/InputParts/keyboardAppearance.cjs +30 -0
  26. package/dist/cjs/InputParts/keyboardAppearance.native.js +33 -0
  27. package/dist/cjs/InputParts/keyboardAppearance.native.js.map +1 -0
  28. package/dist/cjs/fields/Duration/DurationWheel.cjs +34 -0
  29. package/dist/cjs/fields/Duration/DurationWheel.native.js +132 -0
  30. package/dist/cjs/fields/Duration/DurationWheel.native.js.map +1 -0
  31. package/dist/cjs/fields/Duration/durationMath.cjs +19 -1
  32. package/dist/cjs/fields/Duration/durationMath.native.js +19 -1
  33. package/dist/cjs/fields/Duration/durationMath.native.js.map +1 -1
  34. package/dist/cjs/fields/Duration/durationWheelTypes.cjs +18 -0
  35. package/dist/cjs/fields/Duration/durationWheelTypes.native.js +21 -0
  36. package/dist/cjs/fields/Duration/durationWheelTypes.native.js.map +1 -0
  37. package/dist/cjs/fields/Duration/index.cjs +30 -10
  38. package/dist/cjs/fields/Duration/index.native.js +32 -12
  39. package/dist/cjs/fields/Duration/index.native.js.map +1 -1
  40. package/dist/cjs/fields/Select/index.cjs +98 -30
  41. package/dist/cjs/fields/Select/index.native.js +122 -54
  42. package/dist/cjs/fields/Select/index.native.js.map +1 -1
  43. package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.cjs +8 -7
  44. package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js +8 -7
  45. package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
  46. package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.cjs +1 -1
  47. package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.native.js +1 -1
  48. package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -1
  49. package/dist/cjs/fields/nativeDateTime/sheetKeyboard.cjs +1 -1
  50. package/dist/cjs/fields/nativeDateTime/sheetKeyboard.native.js +1 -1
  51. package/dist/cjs/fields/nativeDateTime/sheetKeyboard.native.js.map +1 -1
  52. package/dist/cjs/shared/floatingList.cjs +2 -3
  53. package/dist/cjs/shared/floatingList.native.js +2 -3
  54. package/dist/cjs/shared/floatingList.native.js.map +1 -1
  55. package/dist/esm/AdaptivePopup/index.native.js.map +1 -1
  56. package/dist/esm/Button/IconButtonGroup.mjs +47 -20
  57. package/dist/esm/Button/IconButtonGroup.mjs.map +1 -1
  58. package/dist/esm/Button/IconButtonGroup.native.js +53 -22
  59. package/dist/esm/Button/IconButtonGroup.native.js.map +1 -1
  60. package/dist/esm/Button/circleGlyph.mjs +9 -1
  61. package/dist/esm/Button/circleGlyph.mjs.map +1 -1
  62. package/dist/esm/Button/circleGlyph.native.js +10 -1
  63. package/dist/esm/Button/circleGlyph.native.js.map +1 -1
  64. package/dist/esm/Button/iconButtonGroupContext.mjs +5 -1
  65. package/dist/esm/Button/iconButtonGroupContext.mjs.map +1 -1
  66. package/dist/esm/Button/iconButtonGroupContext.native.js +5 -1
  67. package/dist/esm/Button/iconButtonGroupContext.native.js.map +1 -1
  68. package/dist/esm/Button/index.mjs +1 -9
  69. package/dist/esm/Button/index.mjs.map +1 -1
  70. package/dist/esm/Button/index.native.js +1 -10
  71. package/dist/esm/Button/index.native.js.map +1 -1
  72. package/dist/esm/Button/mediaPlate.native.js.map +1 -1
  73. package/dist/esm/Button/webGlass.native.js.map +1 -1
  74. package/dist/esm/Chip/index.native.js.map +1 -1
  75. package/dist/esm/ContextualSaveBar/UnsavedChangesDialog.native.js.map +1 -1
  76. package/dist/esm/ContextualSaveBar/clientKnownIdentity.native.js.map +1 -1
  77. package/dist/esm/ContextualSaveBar/commitImmediateField.native.js.map +1 -1
  78. package/dist/esm/ContextualSaveBar/index.native.js.map +1 -1
  79. package/dist/esm/ContextualSaveBar/useUnsavedChangesGuard.native.js.map +1 -1
  80. package/dist/esm/ControlGroup/index.native.js.map +1 -1
  81. package/dist/esm/DescriptionList/index.native.js.map +1 -1
  82. package/dist/esm/ErrorSummary.native.js.map +1 -1
  83. package/dist/esm/FieldDisplay/editMode.native.js.map +1 -1
  84. package/dist/esm/FieldDisplay/formatters.native.js.map +1 -1
  85. package/dist/esm/FieldDisplay/index.native.js.map +1 -1
  86. package/dist/esm/FieldGroup.native.js.map +1 -1
  87. package/dist/esm/FloatingPanel/PanelHeader.native.js.map +1 -1
  88. package/dist/esm/FloatingPanel/RegularPanel.native.js.map +1 -1
  89. package/dist/esm/FloatingPanel/compactFace.native.js.map +1 -1
  90. package/dist/esm/FloatingPanel/containingBlock.native.js.map +1 -1
  91. package/dist/esm/FloatingPanel/coverRect.mjs +23 -0
  92. package/dist/esm/FloatingPanel/coverRect.mjs.map +1 -0
  93. package/dist/esm/FloatingPanel/coverRect.native.js +24 -0
  94. package/dist/esm/FloatingPanel/coverRect.native.js.map +1 -0
  95. package/dist/esm/FloatingPanel/echoContext.native.js.map +1 -1
  96. package/dist/esm/FloatingPanel/iconEcho.mjs +4 -2
  97. package/dist/esm/FloatingPanel/iconEcho.mjs.map +1 -1
  98. package/dist/esm/FloatingPanel/iconEcho.native.js +4 -2
  99. package/dist/esm/FloatingPanel/iconEcho.native.js.map +1 -1
  100. package/dist/esm/FloatingPanel/index.mjs +27 -29
  101. package/dist/esm/FloatingPanel/index.mjs.map +1 -1
  102. package/dist/esm/FloatingPanel/index.native.js +41 -15
  103. package/dist/esm/FloatingPanel/index.native.js.map +1 -1
  104. package/dist/esm/FloatingPanel/nestedAction.native.js.map +1 -1
  105. package/dist/esm/FloatingPanel/paintedBox.native.js.map +1 -1
  106. package/dist/esm/FloatingPanel/panelWidth.native.js.map +1 -1
  107. package/dist/esm/FloatingPanel/pointAnchor.native.js.map +1 -1
  108. package/dist/esm/FloatingPanel/regularPlacement.native.js.map +1 -1
  109. package/dist/esm/FloatingPanel/trailingAction.native.js.map +1 -1
  110. package/dist/esm/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  111. package/dist/esm/Form/index.native.js.map +1 -1
  112. package/dist/esm/Form/submissionController.native.js.map +1 -1
  113. package/dist/esm/Form/validateOn.native.js.map +1 -1
  114. package/dist/esm/FormGrid.native.js.map +1 -1
  115. package/dist/esm/FormSection.native.js.map +1 -1
  116. package/dist/esm/InputParts/PasswordReveal.native.js.map +1 -1
  117. package/dist/esm/InputParts/index.mjs +6 -1
  118. package/dist/esm/InputParts/index.mjs.map +1 -1
  119. package/dist/esm/InputParts/index.native.js +8 -1
  120. package/dist/esm/InputParts/index.native.js.map +1 -1
  121. package/dist/esm/InputParts/keyboardAppearance.mjs +5 -0
  122. package/dist/esm/InputParts/keyboardAppearance.mjs.map +1 -0
  123. package/dist/esm/InputParts/keyboardAppearance.native.js +5 -0
  124. package/dist/esm/InputParts/keyboardAppearance.native.js.map +1 -0
  125. package/dist/esm/Label/index.native.js.map +1 -1
  126. package/dist/esm/MediaPicker/base64.native.js.map +1 -1
  127. package/dist/esm/MediaPicker/payload.native.js.map +1 -1
  128. package/dist/esm/MediaPicker/types.native.js.map +1 -1
  129. package/dist/esm/MediaPicker/useMediaPicker.native.js.map +1 -1
  130. package/dist/esm/Skeleton/index.native.js.map +1 -1
  131. package/dist/esm/Spinner/SpinnerGlyph.native.js.map +1 -1
  132. package/dist/esm/Spinner/spinnerSize.native.js.map +1 -1
  133. package/dist/esm/Table/index.native.js.map +1 -1
  134. package/dist/esm/Wheel/haptic.native.js.map +1 -1
  135. package/dist/esm/Wheel/index.native.js.map +1 -1
  136. package/dist/esm/Wheel/wheelShared.native.js.map +1 -1
  137. package/dist/esm/actionLabel.native.js.map +1 -1
  138. package/dist/esm/fieldDefaults.native.js.map +1 -1
  139. package/dist/esm/fieldLayout.native.js.map +1 -1
  140. package/dist/esm/fields/Barcode/BarcodePreview.native.js.map +1 -1
  141. package/dist/esm/fields/Barcode/code128.native.js.map +1 -1
  142. package/dist/esm/fields/Barcode/index.native.js.map +1 -1
  143. package/dist/esm/fields/Barcode/qr.native.js.map +1 -1
  144. package/dist/esm/fields/Barcode/scannerTypes.native.js.map +1 -1
  145. package/dist/esm/fields/Checkbox/Checkbox.native.js.map +1 -1
  146. package/dist/esm/fields/Checkbox/CheckboxBox.native.js.map +1 -1
  147. package/dist/esm/fields/Checkbox/CheckboxCard.native.js.map +1 -1
  148. package/dist/esm/fields/Checkbox/CheckboxFieldFrame.native.js.map +1 -1
  149. package/dist/esm/fields/Checkbox/CheckboxGroup.native.js.map +1 -1
  150. package/dist/esm/fields/Checkbox/checkboxGroupContext.native.js.map +1 -1
  151. package/dist/esm/fields/Checkbox/index.native.js.map +1 -1
  152. package/dist/esm/fields/ColorPicker/SystemColorField.native.js.map +1 -1
  153. package/dist/esm/fields/ColorPicker/index.native.js.map +1 -1
  154. package/dist/esm/fields/ColorPicker/parts.native.js.map +1 -1
  155. package/dist/esm/fields/ColorPicker/systemColorWell.gnome.native.js.map +1 -1
  156. package/dist/esm/fields/Combobox/authorOptions.native.js.map +1 -1
  157. package/dist/esm/fields/Combobox/index.native.js.map +1 -1
  158. package/dist/esm/fields/DatePicker/Calendar.native.js.map +1 -1
  159. package/dist/esm/fields/DatePicker/DateRangePicker.native.js.map +1 -1
  160. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  161. package/dist/esm/fields/DatePicker/MonthPicker.native.js.map +1 -1
  162. package/dist/esm/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
  163. package/dist/esm/fields/DatePicker/catalog.native.js.map +1 -1
  164. package/dist/esm/fields/DatePicker/fieldValueSync.native.js.map +1 -1
  165. package/dist/esm/fields/DatePicker/parts.native.js.map +1 -1
  166. package/dist/esm/fields/DatePicker/utils.native.js.map +1 -1
  167. package/dist/esm/fields/Duration/DurationWheel.mjs +8 -0
  168. package/dist/esm/fields/Duration/DurationWheel.mjs.map +1 -0
  169. package/dist/esm/fields/Duration/DurationWheel.native.js +103 -0
  170. package/dist/esm/fields/Duration/DurationWheel.native.js.map +1 -0
  171. package/dist/esm/fields/Duration/durationMath.mjs +16 -1
  172. package/dist/esm/fields/Duration/durationMath.mjs.map +1 -1
  173. package/dist/esm/fields/Duration/durationMath.native.js +16 -1
  174. package/dist/esm/fields/Duration/durationMath.native.js.map +1 -1
  175. package/dist/esm/fields/Duration/durationWheelTypes.mjs +2 -0
  176. package/dist/esm/fields/Duration/durationWheelTypes.mjs.map +1 -0
  177. package/dist/esm/fields/Duration/durationWheelTypes.native.js +2 -0
  178. package/dist/esm/fields/Duration/durationWheelTypes.native.js.map +1 -0
  179. package/dist/esm/fields/Duration/index.mjs +28 -11
  180. package/dist/esm/fields/Duration/index.mjs.map +1 -1
  181. package/dist/esm/fields/Duration/index.native.js +30 -13
  182. package/dist/esm/fields/Duration/index.native.js.map +1 -1
  183. package/dist/esm/fields/FileUpload/filePickerTypes.native.js.map +1 -1
  184. package/dist/esm/fields/FileUpload/index.native.js.map +1 -1
  185. package/dist/esm/fields/FileUpload/nativePickerLoader.native.js.map +1 -1
  186. package/dist/esm/fields/Geolocation/expoMapsLoader.native.js.map +1 -1
  187. package/dist/esm/fields/Geolocation/geoValue.native.js.map +1 -1
  188. package/dist/esm/fields/Geolocation/geocoder.native.js.map +1 -1
  189. package/dist/esm/fields/Geolocation/geocoderContext.native.js.map +1 -1
  190. package/dist/esm/fields/Geolocation/index.native.js.map +1 -1
  191. package/dist/esm/fields/Geolocation/mapMath.native.js.map +1 -1
  192. package/dist/esm/fields/Geolocation/mapTypes.native.js.map +1 -1
  193. package/dist/esm/fields/Geolocation/nativeSearchFocus.native.js.map +1 -1
  194. package/dist/esm/fields/Geolocation/photonGeocoder.native.js.map +1 -1
  195. package/dist/esm/fields/Icon/drawnSet.native.js.map +1 -1
  196. package/dist/esm/fields/Icon/index.native.js.map +1 -1
  197. package/dist/esm/fields/ImageField/index.native.js.map +1 -1
  198. package/dist/esm/fields/Input/index.native.js.map +1 -1
  199. package/dist/esm/fields/MentionInput/index.native.js.map +1 -1
  200. package/dist/esm/fields/OTPInput/index.native.js.map +1 -1
  201. package/dist/esm/fields/OTPInput/otpCellCss.native.js.map +1 -1
  202. package/dist/esm/fields/PhoneInput/dialCodes.native.js.map +1 -1
  203. package/dist/esm/fields/PhoneInput/index.native.js.map +1 -1
  204. package/dist/esm/fields/PhoneInput/phone.native.js.map +1 -1
  205. package/dist/esm/fields/PhoneInput/regionNames.native.js.map +1 -1
  206. package/dist/esm/fields/Progress/index.native.js.map +1 -1
  207. package/dist/esm/fields/RadioGroup/index.native.js.map +1 -1
  208. package/dist/esm/fields/Rating/index.native.js.map +1 -1
  209. package/dist/esm/fields/Rating/ratingMath.native.js.map +1 -1
  210. package/dist/esm/fields/Rating/types.native.js.map +1 -1
  211. package/dist/esm/fields/SearchInput/index.native.js.map +1 -1
  212. package/dist/esm/fields/Select/InlineChoices.native.js.map +1 -1
  213. package/dist/esm/fields/Select/SheetPickerRow.native.js.map +1 -1
  214. package/dist/esm/fields/Select/index.mjs +98 -30
  215. package/dist/esm/fields/Select/index.mjs.map +1 -1
  216. package/dist/esm/fields/Select/index.native.js +122 -54
  217. package/dist/esm/fields/Select/index.native.js.map +1 -1
  218. package/dist/esm/fields/Select/shouldInlineChoices.native.js.map +1 -1
  219. package/dist/esm/fields/Signature/index.native.js.map +1 -1
  220. package/dist/esm/fields/Slider/index.native.js.map +1 -1
  221. package/dist/esm/fields/Slider/jumpMotion.native.js.map +1 -1
  222. package/dist/esm/fields/Stepper/index.native.js.map +1 -1
  223. package/dist/esm/fields/Stepper/resolveStepperPlacement.native.js.map +1 -1
  224. package/dist/esm/fields/Switch/footprint.native.js.map +1 -1
  225. package/dist/esm/fields/Switch/index.native.js.map +1 -1
  226. package/dist/esm/fields/TextArea/index.native.js.map +1 -1
  227. package/dist/esm/fields/TimePicker/catalog.native.js.map +1 -1
  228. package/dist/esm/fields/TimePicker/parts.native.js.map +1 -1
  229. package/dist/esm/fields/TimeRangeScrubber/geometry.native.js.map +1 -1
  230. package/dist/esm/fields/TimeRangeScrubber/index.native.js.map +1 -1
  231. package/dist/esm/fields/ToggleGroup/index.native.js.map +1 -1
  232. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs +8 -7
  233. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -1
  234. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js +8 -7
  235. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
  236. package/dist/esm/fields/nativeDateTime/dateTimePickerLoader.native.js.map +1 -1
  237. package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.mjs +1 -1
  238. package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.mjs.map +1 -1
  239. package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.native.js +1 -1
  240. package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -1
  241. package/dist/esm/fields/nativeDateTime/sheetKeyboard.mjs +1 -1
  242. package/dist/esm/fields/nativeDateTime/sheetKeyboard.mjs.map +1 -1
  243. package/dist/esm/fields/nativeDateTime/sheetKeyboard.native.js +1 -1
  244. package/dist/esm/fields/nativeDateTime/sheetKeyboard.native.js.map +1 -1
  245. package/dist/esm/fields/nativeSelect.native.js.map +1 -1
  246. package/dist/esm/fields/selectedOrder.native.js.map +1 -1
  247. package/dist/esm/formSpacing.native.js.map +1 -1
  248. package/dist/esm/hooks/useFocusManagement.native.js.map +1 -1
  249. package/dist/esm/index.native.js.map +1 -1
  250. package/dist/esm/keyboard/chain.native.js.map +1 -1
  251. package/dist/esm/keyboard/index.native.js.map +1 -1
  252. package/dist/esm/keyboard/types.native.js.map +1 -1
  253. package/dist/esm/requiredMarking.native.js.map +1 -1
  254. package/dist/esm/shared/TimeColumn.native.js.map +1 -1
  255. package/dist/esm/shared/capabilityPreflight.native.js.map +1 -1
  256. package/dist/esm/shared/choiceRowPress.native.js.map +1 -1
  257. package/dist/esm/shared/colorRamps.native.js.map +1 -1
  258. package/dist/esm/shared/devWarn.native.js.map +1 -1
  259. package/dist/esm/shared/fieldRowContext.native.js.map +1 -1
  260. package/dist/esm/shared/floatingList.mjs +2 -3
  261. package/dist/esm/shared/floatingList.mjs.map +1 -1
  262. package/dist/esm/shared/floatingList.native.js +2 -3
  263. package/dist/esm/shared/floatingList.native.js.map +1 -1
  264. package/dist/esm/shared/focusManagement.native.js.map +1 -1
  265. package/dist/esm/shared/groupContext.native.js.map +1 -1
  266. package/dist/esm/shared/i18n.native.js.map +1 -1
  267. package/dist/esm/shared/instantMotion.native.js.map +1 -1
  268. package/dist/esm/shared/nativeA11y.native.js.map +1 -1
  269. package/dist/esm/shared/nativeAutoFocus.native.js.map +1 -1
  270. package/dist/esm/shared/overlaySizeClass.native.js.map +1 -1
  271. package/dist/esm/shared/overlayStack.native.js.map +1 -1
  272. package/dist/esm/shared/panelPortal.native.js.map +1 -1
  273. package/dist/esm/shared/pickerTriggerContract.native.js.map +1 -1
  274. package/dist/esm/shared/pressSlopProps.native.js.map +1 -1
  275. package/dist/esm/shared/restoreRetries.native.js.map +1 -1
  276. package/dist/esm/shared/sheetNavigation.native.js.map +1 -1
  277. package/dist/esm/shared/systemControlModules.gnome.native.js.map +1 -1
  278. package/dist/esm/shared/systemControls.native.js.map +1 -1
  279. package/dist/esm/shared/t.native.js.map +1 -1
  280. package/dist/esm/shared/tableCellContext.native.js.map +1 -1
  281. package/dist/esm/shared/useDragScroll.native.js.map +1 -1
  282. package/dist/esm/shared/useKeyboardInset.native.js.map +1 -1
  283. package/dist/esm/shared/useSheetEscapeDismiss.native.js.map +1 -1
  284. package/dist/esm/shared/useWheelIncrement.native.js.map +1 -1
  285. package/dist/esm/shared/utils.native.js.map +1 -1
  286. package/dist/esm/shared/zIndex.native.js.map +1 -1
  287. package/dist/esm/standardActions.native.js.map +1 -1
  288. package/dist/esm/themedPrimitives.native.js.map +1 -1
  289. package/dist/esm/types.native.js.map +1 -1
  290. package/dist/esm/utils/LinkCell.native.js.map +1 -1
  291. package/dist/jsx/Button/IconButtonGroup.mjs +47 -20
  292. package/dist/jsx/Button/IconButtonGroup.mjs.map +1 -1
  293. package/dist/jsx/Button/IconButtonGroup.native.js +51 -20
  294. package/dist/jsx/Button/IconButtonGroup.native.js.map +1 -1
  295. package/dist/jsx/Button/circleGlyph.mjs +9 -1
  296. package/dist/jsx/Button/circleGlyph.mjs.map +1 -1
  297. package/dist/jsx/Button/circleGlyph.native.js +12 -1
  298. package/dist/jsx/Button/circleGlyph.native.js.map +1 -1
  299. package/dist/jsx/Button/iconButtonGroupContext.mjs +5 -1
  300. package/dist/jsx/Button/iconButtonGroupContext.mjs.map +1 -1
  301. package/dist/jsx/Button/iconButtonGroupContext.native.js +7 -1
  302. package/dist/jsx/Button/iconButtonGroupContext.native.js.map +1 -1
  303. package/dist/jsx/Button/index.mjs +1 -9
  304. package/dist/jsx/Button/index.mjs.map +1 -1
  305. package/dist/jsx/Button/index.native.js +3 -12
  306. package/dist/jsx/Button/index.native.js.map +1 -1
  307. package/dist/jsx/FloatingPanel/coverRect.mjs +23 -0
  308. package/dist/jsx/FloatingPanel/coverRect.mjs.map +1 -0
  309. package/dist/jsx/FloatingPanel/coverRect.native.js +54 -0
  310. package/dist/jsx/FloatingPanel/coverRect.native.js.map +1 -0
  311. package/dist/jsx/FloatingPanel/iconEcho.mjs +4 -2
  312. package/dist/jsx/FloatingPanel/iconEcho.mjs.map +1 -1
  313. package/dist/jsx/FloatingPanel/iconEcho.native.js +3 -1
  314. package/dist/jsx/FloatingPanel/iconEcho.native.js.map +1 -1
  315. package/dist/jsx/FloatingPanel/index.mjs +27 -29
  316. package/dist/jsx/FloatingPanel/index.mjs.map +1 -1
  317. package/dist/jsx/FloatingPanel/index.native.js +41 -15
  318. package/dist/jsx/FloatingPanel/index.native.js.map +1 -1
  319. package/dist/jsx/InputParts/index.mjs +6 -1
  320. package/dist/jsx/InputParts/index.mjs.map +1 -1
  321. package/dist/jsx/InputParts/index.native.js +7 -0
  322. package/dist/jsx/InputParts/index.native.js.map +1 -1
  323. package/dist/jsx/InputParts/keyboardAppearance.mjs +5 -0
  324. package/dist/jsx/InputParts/keyboardAppearance.mjs.map +1 -0
  325. package/dist/jsx/InputParts/keyboardAppearance.native.js +33 -0
  326. package/dist/jsx/InputParts/keyboardAppearance.native.js.map +1 -0
  327. package/dist/jsx/fields/Duration/DurationWheel.mjs +8 -0
  328. package/dist/jsx/fields/Duration/DurationWheel.mjs.map +1 -0
  329. package/dist/jsx/fields/Duration/DurationWheel.native.js +132 -0
  330. package/dist/jsx/fields/Duration/DurationWheel.native.js.map +1 -0
  331. package/dist/jsx/fields/Duration/durationMath.mjs +16 -1
  332. package/dist/jsx/fields/Duration/durationMath.mjs.map +1 -1
  333. package/dist/jsx/fields/Duration/durationMath.native.js +19 -1
  334. package/dist/jsx/fields/Duration/durationMath.native.js.map +1 -1
  335. package/dist/jsx/fields/Duration/durationWheelTypes.mjs +2 -0
  336. package/dist/jsx/fields/Duration/durationWheelTypes.mjs.map +1 -0
  337. package/dist/jsx/fields/Duration/durationWheelTypes.native.js +21 -0
  338. package/dist/jsx/fields/Duration/durationWheelTypes.native.js.map +1 -0
  339. package/dist/jsx/fields/Duration/index.mjs +28 -11
  340. package/dist/jsx/fields/Duration/index.mjs.map +1 -1
  341. package/dist/jsx/fields/Duration/index.native.js +32 -12
  342. package/dist/jsx/fields/Duration/index.native.js.map +1 -1
  343. package/dist/jsx/fields/Select/index.mjs +98 -30
  344. package/dist/jsx/fields/Select/index.mjs.map +1 -1
  345. package/dist/jsx/fields/Select/index.native.js +122 -54
  346. package/dist/jsx/fields/Select/index.native.js.map +1 -1
  347. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs +8 -7
  348. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -1
  349. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js +8 -7
  350. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
  351. package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.mjs +1 -1
  352. package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.mjs.map +1 -1
  353. package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.native.js +1 -1
  354. package/dist/jsx/fields/nativeDateTime/sheetKeyboard.mjs +1 -1
  355. package/dist/jsx/fields/nativeDateTime/sheetKeyboard.mjs.map +1 -1
  356. package/dist/jsx/fields/nativeDateTime/sheetKeyboard.native.js +1 -1
  357. package/dist/jsx/fields/nativeDateTime/sheetKeyboard.native.js.map +1 -1
  358. package/dist/jsx/index.native.js.map +1 -1
  359. package/dist/jsx/shared/floatingList.mjs +2 -3
  360. package/dist/jsx/shared/floatingList.mjs.map +1 -1
  361. package/dist/jsx/shared/floatingList.native.js +2 -3
  362. package/dist/jsx/shared/floatingList.native.js.map +1 -1
  363. package/package.json +8 -8
  364. package/src/Button/IconButtonGroup.spec.tsx +20 -0
  365. package/src/Button/IconButtonGroup.tsx +60 -25
  366. package/src/Button/circleGlyph.ts +18 -0
  367. package/src/Button/iconButtonGroupContext.ts +25 -0
  368. package/src/Button/index.tsx +2 -20
  369. package/src/FloatingPanel/FloatingPanel.iconEcho.spec.tsx +61 -1
  370. package/src/FloatingPanel/FloatingPanel.menuFace.native.spec.tsx +104 -0
  371. package/src/FloatingPanel/coverRect.ts +42 -0
  372. package/src/FloatingPanel/iconEcho.tsx +9 -3
  373. package/src/FloatingPanel/index.native.tsx +63 -18
  374. package/src/FloatingPanel/index.tsx +38 -35
  375. package/src/InputParts/index.tsx +6 -0
  376. package/src/InputParts/keyboardAppearance.spec.ts +12 -0
  377. package/src/InputParts/keyboardAppearance.ts +4 -0
  378. package/src/fields/Duration/Duration.spec.tsx +19 -10
  379. package/src/fields/Duration/Duration.wheel.native.spec.tsx +98 -0
  380. package/src/fields/Duration/DurationWheel.native.tsx +101 -0
  381. package/src/fields/Duration/DurationWheel.tsx +11 -0
  382. package/src/fields/Duration/durationMath.spec.ts +43 -0
  383. package/src/fields/Duration/durationMath.ts +31 -0
  384. package/src/fields/Duration/durationWheelTypes.ts +18 -0
  385. package/src/fields/Duration/index.tsx +58 -27
  386. package/src/fields/Select/Select.containingBlock.spec.tsx +88 -12
  387. package/src/fields/Select/Select.stories.tsx +74 -0
  388. package/src/fields/Select/index.tsx +113 -44
  389. package/src/fields/nativeDateTime/OsDateTimeSheet.countdown.native.spec.tsx +121 -0
  390. package/src/fields/nativeDateTime/OsDateTimeSheet.spec.ts +4 -0
  391. package/src/fields/nativeDateTime/OsDateTimeSheet.tsx +14 -9
  392. package/src/fields/nativeDateTime/osDateTimeIosDisplay.ts +3 -3
  393. package/src/fields/nativeDateTime/sheetKeyboard.spec.ts +6 -0
  394. package/src/fields/nativeDateTime/sheetKeyboard.ts +2 -2
  395. package/src/shared/floatingList.tsx +1 -4
  396. package/types/Button/IconButtonGroup.d.ts.map +1 -1
  397. package/types/Button/circleGlyph.d.ts +12 -0
  398. package/types/Button/circleGlyph.d.ts.map +1 -1
  399. package/types/Button/iconButtonGroupContext.d.ts +19 -0
  400. package/types/Button/iconButtonGroupContext.d.ts.map +1 -1
  401. package/types/Button/index.d.ts +2 -9
  402. package/types/Button/index.d.ts.map +1 -1
  403. package/types/FloatingPanel/coverRect.d.ts +25 -0
  404. package/types/FloatingPanel/coverRect.d.ts.map +1 -0
  405. package/types/FloatingPanel/index.d.ts +1 -6
  406. package/types/FloatingPanel/index.d.ts.map +1 -1
  407. package/types/FloatingPanel/index.native.d.ts.map +1 -1
  408. package/types/InputParts/index.d.ts.map +1 -1
  409. package/types/InputParts/keyboardAppearance.d.ts +3 -0
  410. package/types/InputParts/keyboardAppearance.d.ts.map +1 -0
  411. package/types/fields/Duration/DurationWheel.d.ts +5 -0
  412. package/types/fields/Duration/DurationWheel.d.ts.map +1 -0
  413. package/types/fields/Duration/DurationWheel.native.d.ts +5 -0
  414. package/types/fields/Duration/DurationWheel.native.d.ts.map +1 -0
  415. package/types/fields/Duration/durationMath.d.ts +13 -0
  416. package/types/fields/Duration/durationMath.d.ts.map +1 -1
  417. package/types/fields/Duration/durationWheelTypes.d.ts +18 -0
  418. package/types/fields/Duration/durationWheelTypes.d.ts.map +1 -0
  419. package/types/fields/Duration/index.d.ts +8 -3
  420. package/types/fields/Duration/index.d.ts.map +1 -1
  421. package/types/fields/Select/index.d.ts.map +1 -1
  422. package/types/fields/nativeDateTime/OsDateTimeSheet.d.ts +1 -1
  423. package/types/fields/nativeDateTime/OsDateTimeSheet.d.ts.map +1 -1
  424. package/types/fields/nativeDateTime/osDateTimeIosDisplay.d.ts +2 -2
  425. package/types/fields/nativeDateTime/osDateTimeIosDisplay.d.ts.map +1 -1
  426. package/types/fields/nativeDateTime/sheetKeyboard.d.ts +1 -1
  427. package/types/fields/nativeDateTime/sheetKeyboard.d.ts.map +1 -1
  428. package/types/shared/floatingList.d.ts +1 -3
  429. package/types/shared/floatingList.d.ts.map +1 -1
@@ -9,7 +9,13 @@ import { PlusIcon, XIcon } from "@phosphor-icons/react";
9
9
  import { useState, type ReactNode } from "react";
10
10
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
11
11
  import { Text, XStack, YStack, getTokens, getVariableValue } from "tamagui";
12
+ import { MIN_PRESS_TARGET } from "@multiplatform.one/theme";
12
13
  import { Button } from "../Button";
14
+ import {
15
+ IconButtonGroupSurfaceContext,
16
+ type IconButtonGroupPlate,
17
+ type IconButtonGroupSurface,
18
+ } from "../Button/iconButtonGroupContext";
13
19
  import { Input as InputParts } from "../InputParts";
14
20
  import { OverlayStackProvider } from "../shared/overlayStack";
15
21
  import { MENU_MIN_WIDTH, type CompactFace } from "./compactFace";
@@ -540,3 +546,101 @@ describe("LC-112 icon trigger echo on native (FZ-8480, rv_2607879d3bdaf9bf)", ()
540
546
  expect(glyphPath(iconEcho())).not.toBe(glyphPath(live));
541
547
  });
542
548
  });
549
+
550
+ const capsuleSurface = (plate: IconButtonGroupPlate): IconButtonGroupSurface => ({
551
+ measure: (callback) => callback(plate),
552
+ });
553
+
554
+ describe("LC-112 grouped glass capsule on native (FZ-8550, Round 37 second pass)", () => {
555
+ const plate = { x: 200, y: 50, width: 174, height: 44, radius: 22 };
556
+
557
+ it("covers the whole capsule from context at its radius; only the pressed trigger shows, as its X", () => {
558
+ trigger = { x: 330, y: 50, width: 44, height: 44 };
559
+ const result = renderWithProviders(
560
+ <IconButtonGroupSurfaceContext.Provider value={capsuleSurface(plate)}>
561
+ <EchoMenu kind="menu" />
562
+ </IconButtonGroupSurfaceContext.Provider>,
563
+ );
564
+ tap(result.getAllByRole("button", { name: "Add node" })[0]!);
565
+ const panel = menu() as HTMLElement;
566
+ layout(panel, MENU_MIN_WIDTH, 180);
567
+ const left = parseFloat(panel.style.left);
568
+ const top = parseFloat(panel.style.top);
569
+ // The panel's frame contains the capsule's: same top edge, both ends.
570
+ expect(top).toBe(plate.y);
571
+ expect(left).toBeLessThanOrEqual(plate.x);
572
+ expect(left + MENU_MIN_WIDTH).toBeGreaterThanOrEqual(plate.x + plate.width);
573
+ expect(left).toBe(plate.x + plate.width - MENU_MIN_WIDTH);
574
+ expect(corners(frame() as HTMLElement)).toEqual(radiusAtoms(plate.radius));
575
+ // The band is the capsule's height; the X keeps the trigger's own box.
576
+ const band = iconBand() as HTMLElement;
577
+ expect(band.style.height).toBe(`${plate.height}px`);
578
+ const box = band.firstElementChild as HTMLElement;
579
+ expect(box.style.width).toBe(`${trigger.width}px`);
580
+ expect(box.style.marginInlineStart || box.style.marginLeft).toBe(`${trigger.x - left}px`);
581
+ // Only the pressed trigger shows, as an X; nothing else of the group.
582
+ const echoes = document.querySelectorAll('[data-testid="floating-panel-icon-echo"]');
583
+ expect(echoes).toHaveLength(1);
584
+ const x = render(<XIcon weight="bold" />);
585
+ expect(glyphPath(echoes[0] as Element)).toBe(glyphPath(x.container));
586
+ x.unmount();
587
+ });
588
+
589
+ it("takes the capsule's width as its floor when the capsule is wider than the menu", () => {
590
+ const wide = { x: 16, y: 50, width: 358, height: 44, radius: 22 };
591
+ trigger = { x: 330, y: 50, width: 44, height: 44 };
592
+ const result = renderWithProviders(
593
+ <IconButtonGroupSurfaceContext.Provider value={capsuleSurface(wide)}>
594
+ <EchoMenu kind="menu" />
595
+ </IconButtonGroupSurfaceContext.Provider>,
596
+ );
597
+ tap(result.getAllByRole("button", { name: "Add node" })[0]!);
598
+ const panel = menu() as HTMLElement;
599
+ expect(panel.style.minWidth).toBe(`${wide.width}px`);
600
+ layout(panel, wide.width, 180);
601
+ expect(panel.style.left).toBe(`${wide.x}px`);
602
+ });
603
+
604
+ it("waits for the capsule before it shows, so it never opens on the button alone first", () => {
605
+ trigger = { x: 330, y: 50, width: 44, height: 44 };
606
+ let answer: ((plate: IconButtonGroupPlate | null) => void) | null = null;
607
+ const result = renderWithProviders(
608
+ <IconButtonGroupSurfaceContext.Provider
609
+ value={{
610
+ measure: (callback) => {
611
+ answer = callback;
612
+ },
613
+ }}
614
+ >
615
+ <EchoMenu kind="menu" />
616
+ </IconButtonGroupSurfaceContext.Provider>,
617
+ );
618
+ tap(result.getAllByRole("button", { name: "Add node" })[0]!);
619
+ const panel = menu() as HTMLElement;
620
+ layout(panel, MENU_MIN_WIDTH, 180);
621
+ expect(panel.style.opacity).toBe("0");
622
+ act(() => answer?.(plate));
623
+ expect(panel.style.opacity).toBe("1");
624
+ expect(panel.style.left).toBe(`${plate.x + plate.width - MENU_MIN_WIDTH}px`);
625
+ });
626
+
627
+ it("covers a 32pt avatar at its 44pt target, so its centre is the row-icon column (FZ-8480)", () => {
628
+ trigger = { x: 100, y: 56, width: 32, height: 32 };
629
+ const result = renderWithProviders(<EchoMenu kind="menu" />);
630
+ tap(result.getAllByRole("button", { name: "Add node" })[0]!);
631
+ const panel = menu() as HTMLElement;
632
+ layout(panel, MENU_MIN_WIDTH, 180);
633
+ const inset = (MIN_PRESS_TARGET - trigger.width) / 2;
634
+ expect(panel.style.left).toBe(`${trigger.x - inset}px`);
635
+ expect(panel.style.top).toBe(`${trigger.y - inset}px`);
636
+ const band = iconBand() as HTMLElement;
637
+ expect(band.style.height).toBe(`${MIN_PRESS_TARGET}px`);
638
+ const box = band.firstElementChild as HTMLElement;
639
+ expect(box.style.width).toBe(`${trigger.width}px`);
640
+ expect(box.style.marginTop).toBe(`${inset}px`);
641
+ expect(box.style.marginInlineStart || box.style.marginLeft).toBe(`${inset}px`);
642
+ // The X's centre sits half a press target in: where a row's leading
643
+ // glyph centres (row inset plus half its 18pt slot, on the frame border).
644
+ expect(inset + trigger.width / 2).toBe(MIN_PRESS_TARGET / 2);
645
+ });
646
+ });
@@ -0,0 +1,42 @@
1
+ import { MIN_PRESS_TARGET } from "@multiplatform.one/theme";
2
+ import type { TriggerRect } from "./pointAnchor";
3
+
4
+ /**
5
+ * The 44pt box an icon trigger is pressed in, centred on the trigger
6
+ * (LC-176). A trigger drawn smaller than its target (a 32pt avatar) is
7
+ * covered at its target, so the panel's row-icon column, which centres on
8
+ * a 44pt start inset, runs through the trigger's centre and the X drawn on
9
+ * it (FZ-8480 GAP). A trigger at least that big is its own box.
10
+ */
11
+ export function pressTargetRect(trigger: TriggerRect, min = MIN_PRESS_TARGET): TriggerRect {
12
+ const width = Math.max(trigger.width, min);
13
+ const height = Math.max(trigger.height, min);
14
+ return {
15
+ x: trigger.x - (width - trigger.width) / 2,
16
+ y: trigger.y - (height - trigger.height) / 2,
17
+ width,
18
+ height,
19
+ };
20
+ }
21
+
22
+ /**
23
+ * The surface a panel opened from an icon trigger covers (LC-112, FZ-8480
24
+ * Round 37): the plate the trigger sits on. In a grouped glass capsule
25
+ * that is the capsule (FZ-8550); a lone trigger is its press target.
26
+ */
27
+ export function iconCoverRect(trigger: TriggerRect, group: TriggerRect | null): TriggerRect {
28
+ return group ?? pressTargetRect(trigger);
29
+ }
30
+
31
+ /**
32
+ * Where the trigger sits inside the surface its panel covers: the X box's
33
+ * offset from the cover's top, and the band's height. Null when the cover is
34
+ * the trigger itself.
35
+ */
36
+ export function echoInCover(
37
+ trigger: TriggerRect,
38
+ cover: TriggerRect,
39
+ ): { top: number; height: number } | null {
40
+ if (cover.y === trigger.y && cover.height === trigger.height) return null;
41
+ return { top: trigger.y - cover.y, height: cover.height };
42
+ }
@@ -2,7 +2,7 @@ import { XIcon } from "@phosphor-icons/react";
2
2
  import { cloneElement, isValidElement, useState, type ReactElement, type ReactNode } from "react";
3
3
  import { SurfaceContext, type SurfaceIntent } from "@multiplatform.one/theme";
4
4
  import { useTheme, View } from "tamagui";
5
- import { ICON_BUTTON_CIRCLE_GLYPH_RATIO } from "../Button/circleGlyph";
5
+ import { boldStroke, ICON_BUTTON_CIRCLE_GLYPH_RATIO } from "../Button/circleGlyph";
6
6
  import { t } from "../shared/t";
7
7
 
8
8
  /**
@@ -83,10 +83,15 @@ const avatarPlate = "$selectedBackground";
83
83
  /** The X's glyph before the circle has measured itself. */
84
84
  const circleGlyphFallbackPx = 18;
85
85
 
86
- /** An X in a circle the size of the trigger it replaces (an avatar). */
86
+ /**
87
+ * An X in a circle the size of the trigger it replaces (an avatar), stroked
88
+ * to the bar buttons' bold stem (LC-176), so every X in a bar reads at one
89
+ * weight.
90
+ */
87
91
  function CircleClose({ label, onClose }: { label: string; onClose: () => void }) {
88
92
  const theme = useTheme();
89
93
  const [side, setSide] = useState(0);
94
+ const ink = theme.selectedColor?.val ?? theme.color?.val;
90
95
  return (
91
96
  <View
92
97
  role="button"
@@ -110,7 +115,8 @@ function CircleClose({ label, onClose }: { label: string; onClose: () => void })
110
115
  <XIcon
111
116
  size={side > 0 ? Math.round(side * ICON_BUTTON_CIRCLE_GLYPH_RATIO) : circleGlyphFallbackPx}
112
117
  weight="bold"
113
- color={theme.selectedColor?.val ?? theme.color?.val}
118
+ color={ink}
119
+ {...boldStroke("bold", typeof ink === "string" ? ink : undefined)}
114
120
  aria-hidden
115
121
  />
116
122
  </View>
@@ -123,6 +123,12 @@ import { RegularPanel } from "./RegularPanel";
123
123
  import { panelViewportPadding, resolvePanelWidthMode } from "./useFloatingPanel";
124
124
  import { TrailingActionOverlay, TriggerWithTrailingAction } from "./trailingAction";
125
125
  import { iconEchoElement, iconEchoOverlays, type IconEcho } from "./iconEcho";
126
+ import { echoInCover, iconCoverRect } from "./coverRect";
127
+ import {
128
+ IconButtonGroupSurfaceContext,
129
+ useIconButtonGroupSurface,
130
+ type IconButtonGroupPlate,
131
+ } from "../Button/iconButtonGroupContext";
126
132
 
127
133
  // Re-export the hook + constants so Select / Combobox / external consumers
128
134
  // resolve identically on native (their floating cores are unused on device
@@ -189,10 +195,13 @@ export function FloatingPanel({
189
195
  const { knobProps } = useResolvedKnobs({ painted: false });
190
196
  const triggerSurface = SurfaceContext.useStyledContext();
191
197
  const windowZIndex = useOverlayZIndex("sheet");
198
+ // A grouped trigger's capsule is its own: a panel's content never covers it.
192
199
  const children = (
193
- <Surface painted={false} elevated>
194
- {panelChildren}
195
- </Surface>
200
+ <IconButtonGroupSurfaceContext.Provider value={null}>
201
+ <Surface painted={false} elevated>
202
+ {panelChildren}
203
+ </Surface>
204
+ </IconButtonGroupSurfaceContext.Provider>
196
205
  );
197
206
  const slideTransition = knobProps.motion.slide.transition;
198
207
  const enterDuration = motionDurationMs(slideTransition);
@@ -250,6 +259,10 @@ export function FloatingPanel({
250
259
  const anchored = asMenu || asRegular;
251
260
  const triggerRef = useRef<RNView | null>(null);
252
261
  const [anchor, setAnchor] = useState<TriggerRect | null>(null);
262
+ // LC-112 (FZ-8550): a trigger in a bar's glass capsule covers the capsule.
263
+ const groupSurface = useIconButtonGroupSurface();
264
+ // `undefined` while the capsule is still being measured.
265
+ const [group, setGroup] = useState<IconButtonGroupPlate | null | undefined>(null);
253
266
  const [triggerRadius, setTriggerRadius] = useState<number | null>(null);
254
267
  const reportTriggerRadius = useCallback(
255
268
  (radius: number) => setTriggerRadius((prev) => (prev === radius ? prev : radius)),
@@ -260,6 +273,7 @@ export function FloatingPanel({
260
273
  useLayoutEffect(() => {
261
274
  if (!anchored || !open) {
262
275
  setAnchor(null);
276
+ setGroup(null);
263
277
  return;
264
278
  }
265
279
  const node = triggerRef.current as
@@ -275,10 +289,18 @@ export function FloatingPanel({
275
289
  } else {
276
290
  setAnchor({ x: 0, y: 0, width: 0, height: 0 });
277
291
  }
292
+ if (iconEcho && anchorMode !== "attach" && groupSurface) {
293
+ setGroup(undefined);
294
+ groupSurface.measure((plate) => {
295
+ if (live) setGroup(plate);
296
+ });
297
+ } else {
298
+ setGroup(null);
299
+ }
278
300
  return () => {
279
301
  live = false;
280
302
  };
281
- }, [anchored, open]);
303
+ }, [anchored, open, iconEcho, anchorMode, groupSurface]);
282
304
 
283
305
  const handleBackdropPress = useCallback(() => {
284
306
  if (backdropArmed) close();
@@ -636,7 +658,7 @@ export function FloatingPanel({
636
658
  <TriggerMenuPanel
637
659
  // An attached menu (an icon trigger, no echo row) opens under its
638
660
  // trigger instead of over it, the UIMenu pull-down (LC-262).
639
- anchor={anchor}
661
+ anchor={group === undefined ? null : anchor}
640
662
  attach={anchorMode === "attach"}
641
663
  bounds={{
642
664
  top: topInset + panelViewportPadding,
@@ -655,6 +677,7 @@ export function FloatingPanel({
655
677
  : null
656
678
  }
657
679
  iconEchoKind={iconEcho}
680
+ group={iconEcho && anchorMode !== "attach" ? (group ?? null) : null}
658
681
  coverAlign={anchorMode === "attach" ? "start" : coverAlign}
659
682
  panelWidth={panelWidth}
660
683
  triggerRadius={anchorMode === "attach" ? null : triggerRadius}
@@ -824,6 +847,7 @@ function TriggerMenuPanel({
824
847
  echoLabel,
825
848
  iconEcho = null,
826
849
  iconEchoKind = "menu",
850
+ group = null,
827
851
  coverAlign,
828
852
  panelWidth,
829
853
  triggerRadius,
@@ -841,6 +865,8 @@ function TriggerMenuPanel({
841
865
  /** LC-112 (FZ-8480): the icon trigger drawn again in its own spot. */
842
866
  iconEcho?: ReactNode;
843
867
  iconEchoKind?: IconEcho;
868
+ /** LC-112 (FZ-8550): the glass capsule a grouped trigger sits on. */
869
+ group?: IconButtonGroupPlate | null;
844
870
  coverAlign: "start" | "auto";
845
871
  panelWidth: PanelWidth;
846
872
  triggerRadius: number | null;
@@ -852,6 +878,11 @@ function TriggerMenuPanel({
852
878
  }) {
853
879
  const { knobProps } = useResolvedKnobs({ painted: false });
854
880
  const [size, setSize] = useState<{ width: number; height: number } | null>(null);
881
+ // LC-112 (FZ-8480, FZ-8550): an icon trigger's panel covers the surface the
882
+ // trigger is seen on, its capsule or its press target, and the X keeps the
883
+ // trigger's own box inside it.
884
+ const cover = anchor && iconEcho != null ? iconCoverRect(anchor, group) : anchor;
885
+ const echoCover = anchor && cover && iconEcho != null ? echoInCover(anchor, cover) : null;
855
886
  // LC-112 width setting (FZ-8438 r3). `"content"` keeps the iOS pull-down's
856
887
  // 250pt floor (FZ-8220), and no panel is narrower than an echoed trigger.
857
888
  const room =
@@ -871,12 +902,12 @@ function TriggerMenuPanel({
871
902
  trigger: anchor?.width ?? 0,
872
903
  available: room.right - room.left,
873
904
  echo: echo != null,
874
- floor: Math.max(MENU_MIN_WIDTH, anchor?.width ?? 0),
905
+ floor: Math.max(MENU_MIN_WIDTH, cover?.width ?? 0),
875
906
  });
876
907
  const placement =
877
- anchor && size
908
+ anchor && cover && size
878
909
  ? (attach ? placeBesideTrigger : placeOverTrigger)({
879
- trigger: anchor,
910
+ trigger: cover,
880
911
  width: size.width,
881
912
  height: size.height,
882
913
  bounds: room,
@@ -888,13 +919,15 @@ function TriggerMenuPanel({
888
919
  // LC-112 seamless cover: the menu is its trigger grown open, so it draws the
889
920
  // trigger's corner arc at all four corners. A field reports its radius; an
890
921
  // icon button with no echo is a circle or capsule, half its short side.
891
- const half = anchor ? Math.min(anchor.width, anchor.height) / 2 : undefined;
922
+ const half = cover ? Math.min(cover.width, cover.height) / 2 : undefined;
892
923
  const radius =
893
- triggerRadius != null && half != null
894
- ? Math.min(triggerRadius, half)
895
- : coverAlign === "auto" && half
896
- ? half
897
- : undefined;
924
+ group != null && iconEcho != null
925
+ ? Math.min(group.radius, half ?? group.radius)
926
+ : triggerRadius != null && half != null
927
+ ? Math.min(triggerRadius, half)
928
+ : coverAlign === "auto" && half
929
+ ? half
930
+ : undefined;
898
931
  const borderWidth = Number(knobProps.elevatedSurface.borderWidth) || 0;
899
932
  const echoOverlays = iconEchoOverlays(iconEchoKind, placement?.up ?? false);
900
933
  // A panel slid along the screen (a `"screen"` or fixed width, or one pushed
@@ -981,7 +1014,13 @@ function TriggerMenuPanel({
981
1014
  </Pressable>
982
1015
  ) : null}
983
1016
  {iconEcho != null && anchor && !placement?.up && !echoOverlays ? (
984
- <IconEchoBand anchor={anchor} shift={echoShift} borderWidth={borderWidth} edge="top">
1017
+ <IconEchoBand
1018
+ anchor={anchor}
1019
+ cover={echoCover}
1020
+ shift={echoShift}
1021
+ borderWidth={borderWidth}
1022
+ edge="top"
1023
+ >
985
1024
  {iconEcho}
986
1025
  </IconEchoBand>
987
1026
  ) : null}
@@ -996,7 +1035,7 @@ function TriggerMenuPanel({
996
1035
  ? echoHeaderBox({
997
1036
  offset: echoShift,
998
1037
  width: anchor.width,
999
- height: anchor.height,
1038
+ height: echoCover?.height ?? anchor.height,
1000
1039
  panelWidth: placement.width,
1001
1040
  rtl: I18nManager.isRTL,
1002
1041
  })
@@ -1010,6 +1049,7 @@ function TriggerMenuPanel({
1010
1049
  {iconEcho != null && anchor && (placement?.up || echoOverlays) ? (
1011
1050
  <IconEchoBand
1012
1051
  anchor={anchor}
1052
+ cover={echoCover}
1013
1053
  shift={echoShift}
1014
1054
  borderWidth={borderWidth}
1015
1055
  edge={echoOverlays ? "overlay" : "bottom"}
@@ -1032,18 +1072,22 @@ function TriggerMenuPanel({
1032
1072
  */
1033
1073
  function IconEchoBand({
1034
1074
  anchor,
1075
+ cover = null,
1035
1076
  shift,
1036
1077
  borderWidth,
1037
1078
  edge,
1038
1079
  children,
1039
1080
  }: {
1040
1081
  anchor: TriggerRect;
1082
+ /** The trigger's offset in, and the height of, a cover larger than it. */
1083
+ cover?: { top: number; height: number } | null;
1041
1084
  shift: number;
1042
1085
  borderWidth: number;
1043
1086
  /** `"overlay"`: an X laid over the content's top corner, taking no height. */
1044
1087
  edge: "top" | "bottom" | "overlay";
1045
1088
  children: ReactNode;
1046
1089
  }) {
1090
+ const bandHeight = cover?.height ?? anchor.height;
1047
1091
  return (
1048
1092
  <RNView
1049
1093
  testID="floating-panel-icon-echo-band"
@@ -1055,11 +1099,11 @@ function IconEchoBand({
1055
1099
  top: -borderWidth,
1056
1100
  start: -borderWidth,
1057
1101
  end: -borderWidth,
1058
- height: anchor.height,
1102
+ height: bandHeight,
1059
1103
  zIndex: 1,
1060
1104
  }
1061
1105
  : {
1062
- height: anchor.height,
1106
+ height: bandHeight,
1063
1107
  marginHorizontal: -borderWidth,
1064
1108
  ...(edge === "top" ? { marginTop: -borderWidth } : { marginBottom: -borderWidth }),
1065
1109
  }
@@ -1070,6 +1114,7 @@ function IconEchoBand({
1070
1114
  width: anchor.width,
1071
1115
  height: anchor.height,
1072
1116
  marginStart: shift,
1117
+ marginTop: cover?.top ?? 0,
1073
1118
  alignItems: "flex-start",
1074
1119
  justifyContent: "flex-start",
1075
1120
  }}
@@ -48,6 +48,7 @@ import {
48
48
  type PanelIconEchoBox,
49
49
  } from "./echoContext";
50
50
  import { iconEchoElement, iconEchoOverlays, type IconEcho } from "./iconEcho";
51
+ import { echoInCover, iconCoverRect } from "./coverRect";
51
52
  import { groupSurfaceOf, paintedTriggerBox } from "./paintedBox";
52
53
  import {
53
54
  contextMenuPoint,
@@ -370,7 +371,6 @@ export function TriggerEchoRow({
370
371
  anchorRect,
371
372
  source,
372
373
  presentational = false,
373
- edge = "top",
374
374
  }: {
375
375
  sourceRef: RefObject<HTMLElement | null>;
376
376
  onPress: () => void;
@@ -382,11 +382,6 @@ export function TriggerEchoRow({
382
382
  * assistive tech: a button child would break the listbox's role contract.
383
383
  */
384
384
  presentational?: boolean;
385
- /**
386
- * `"bottom"` for a drop-up (FZ-8438 r3): the panel's bottom edge is the
387
- * trigger's, so the echo is its last row, sticky at the bottom.
388
- */
389
- edge?: "top" | "bottom";
390
385
  }) {
391
386
  const rowRef = useRef<HTMLElement | null>(null);
392
387
  const visualRef = useRef<HTMLElement | null>(null);
@@ -419,19 +414,8 @@ export function TriggerEchoRow({
419
414
  row.style.alignSelf = "flex-start";
420
415
  const frameStyle = row.parentElement ? getComputedStyle(row.parentElement) : null;
421
416
  row.style.position = "sticky";
422
- if (edge === "bottom") {
423
- row.style.order = "1";
424
- row.style.marginTop = "";
425
- row.style.top = "";
426
- row.style.marginBottom = `${-(parseFloat(frameStyle?.borderBottomWidth || "0") || 0)}px`;
427
- row.style.bottom = row.style.marginBottom;
428
- } else {
429
- row.style.order = "";
430
- row.style.marginBottom = "";
431
- row.style.bottom = "";
432
- row.style.marginTop = `${-(parseFloat(frameStyle?.borderTopWidth || "0") || 0)}px`;
433
- row.style.top = row.style.marginTop;
434
- }
417
+ row.style.marginTop = `${-(parseFloat(frameStyle?.borderTopWidth || "0") || 0)}px`;
418
+ row.style.top = row.style.marginTop;
435
419
  row.style.zIndex = "1";
436
420
  // Sticky over a scrolled body, so it carries the panel's own fill.
437
421
  row.style.backgroundColor = frameStyle?.backgroundColor ?? "";
@@ -468,7 +452,7 @@ export function TriggerEchoRow({
468
452
  const observer = new ResizeObserver(sync);
469
453
  observer.observe(source);
470
454
  return () => observer.disconnect();
471
- }, [sourceRef, source, children, anchorRect, presentational, edge]);
455
+ }, [sourceRef, source, children, anchorRect, presentational]);
472
456
  return (
473
457
  <View
474
458
  ref={rowRef as any}
@@ -583,8 +567,9 @@ function placeIconEcho(
583
567
  up: boolean;
584
568
  overlay: boolean;
585
569
  /**
586
- * The shared plate a grouped trigger sits on: the band covers all of it,
587
- * and only the pressed trigger shows, at its own spot in the plate.
570
+ * The surface the panel covers when it is not the trigger's own box (a
571
+ * grouped capsule, or the 44pt target of a smaller avatar): the band
572
+ * covers all of it, and only the pressed trigger shows, at its own spot.
588
573
  */
589
574
  group?: { top: number; height: number } | null;
590
575
  },
@@ -1082,22 +1067,43 @@ function FloatingPanelWeb({
1082
1067
  measured.height + firstActionHeight + bodyPadding + totalBorderWidth,
1083
1068
  )
1084
1069
  : totalContentHeight;
1070
+ // LC-112 (FZ-8480): an icon trigger's panel covers the surface the
1071
+ // trigger is seen on: its capsule, else its press target.
1072
+ const painted = showIconEcho ? paintedTriggerBox(trig, measured) : null;
1073
+ const coverRect = painted
1074
+ ? iconCoverRect(
1075
+ {
1076
+ x: painted.left,
1077
+ y: measured.top,
1078
+ width: painted.right - painted.left,
1079
+ height: measured.height,
1080
+ },
1081
+ groupBox
1082
+ ? {
1083
+ x: groupBox.left,
1084
+ y: groupBox.top,
1085
+ width: groupBox.width,
1086
+ height: groupBox.height,
1087
+ }
1088
+ : null,
1089
+ )
1090
+ : null;
1091
+ const echoCover = coverRect
1092
+ ? echoInCover(
1093
+ { x: measured.left, y: measured.top, width: measured.width, height: measured.height },
1094
+ coverRect,
1095
+ )
1096
+ : null;
1085
1097
  let placedTop = measured.top;
1086
1098
  let placedLeft = left;
1087
1099
  let placedMaxHeight: number | null = null;
1088
1100
  iconEchoUpRef.current = false;
1089
- if (showIconEcho && measured.width > 0 && measured.height > 0) {
1101
+ if (coverRect && measured.width > 0 && measured.height > 0) {
1090
1102
  // LC-112 (FZ-8480): the button is drawn again in its own spot, so the
1091
1103
  // panel stays on it: down from its top edge, or up from its bottom.
1092
1104
  // It covers the button's plate and press box where they reach past it.
1093
- const painted = groupBox ?? paintedTriggerBox(trig, measured);
1094
1105
  const placed = placeOverTrigger({
1095
- trigger: {
1096
- x: painted.left,
1097
- y: groupBox ? groupBox.top : measured.top,
1098
- width: painted.right - painted.left,
1099
- height: groupBox ? groupBox.height : measured.height,
1100
- },
1106
+ trigger: coverRect,
1101
1107
  width,
1102
1108
  height: totalContentHeight,
1103
1109
  bounds: {
@@ -1169,7 +1175,7 @@ function FloatingPanelWeb({
1169
1175
  ? echoHeaderBox({
1170
1176
  offset: echoStart,
1171
1177
  width: measured.width,
1172
- height: measured.height,
1178
+ height: echoCover?.height ?? measured.height,
1173
1179
  panelWidth: width,
1174
1180
  rtl,
1175
1181
  })
@@ -1181,10 +1187,7 @@ function FloatingPanelWeb({
1181
1187
  height: measured.height,
1182
1188
  up: iconEchoUpRef.current,
1183
1189
  overlay: echoOverlays,
1184
- group:
1185
- groupSurface && groupBox
1186
- ? { top: measured.top - groupBox.top, height: groupBox.height }
1187
- : null,
1190
+ group: echoCover,
1188
1191
  });
1189
1192
  placeEchoOverTrigger(
1190
1193
  dropdownElement,
@@ -34,6 +34,7 @@ import {
34
34
  styled,
35
35
  useGetThemedIcon,
36
36
  useTheme,
37
+ useThemeName,
37
38
  } from "tamagui";
38
39
  import {
39
40
  ensureCompositeFocusRing,
@@ -64,6 +65,7 @@ import { pressSlopProps } from "../shared/pressSlopProps";
64
65
  import { useFieldRow } from "../shared/fieldRowContext";
65
66
  import { useIsInTableCell } from "../shared/tableCellContext";
66
67
  import { useInPanelEcho, usePanelTriggerRadiusReporter } from "../FloatingPanel/echoContext";
68
+ import { keyboardAppearanceFor } from "./keyboardAppearance";
67
69
  import { useFieldA11y, useFieldDescribedBy } from "../fieldLayout";
68
70
 
69
71
  /**
@@ -857,6 +859,7 @@ const InputAreaImpl = InputFrame.styleable<{
857
859
  // field given `size` directly (no Surface) must not type at `$true`.
858
860
  const { knobProps } = useResolvedKnobs({ size: size as SizeTokens });
859
861
  const theme = useTheme();
862
+ const themeName = useThemeName();
860
863
  const fieldDescribedBy = useFieldDescribedBy();
861
864
  const nativeA11yProps = useNativeFieldA11yProps();
862
865
  const inTableCell = useIsInTableCell();
@@ -929,6 +932,9 @@ const InputAreaImpl = InputFrame.styleable<{
929
932
  paddingHorizontal={0}
930
933
  paddingVertical={0}
931
934
  keyboardType={resolvedKeyboardType}
935
+ // The iOS keyboard follows the OS appearance unless told, so a light
936
+ // app on a dark phone typed on a dark keypad (FZ-8111).
937
+ {...(isWeb ? undefined : { keyboardAppearance: keyboardAppearanceFor(themeName) })}
932
938
  textContentType={textContentType}
933
939
  inputMode={resolvedInputMode}
934
940
  autoComplete={
@@ -0,0 +1,12 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { keyboardAppearanceFor } from "./keyboardAppearance";
3
+
4
+ describe("keyboardAppearanceFor (FZ-8111)", () => {
5
+ it("follows the app's scheme, not the phone's", () => {
6
+ expect(keyboardAppearanceFor("light")).toBe("light");
7
+ expect(keyboardAppearanceFor("light_accent")).toBe("light");
8
+ expect(keyboardAppearanceFor("dark")).toBe("dark");
9
+ expect(keyboardAppearanceFor("dark_accent")).toBe("dark");
10
+ expect(keyboardAppearanceFor(undefined)).toBe("light");
11
+ });
12
+ });
@@ -0,0 +1,4 @@
1
+ /** The iOS keypad's appearance from the active theme, not the phone's setting. */
2
+ export function keyboardAppearanceFor(themeName: string | undefined): "light" | "dark" {
3
+ return String(themeName ?? "").startsWith("dark") ? "dark" : "light";
4
+ }