@multiplatform.one/forms 7.76.0 → 7.77.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 (344) hide show
  1. package/dist/cjs/AdaptivePopup/index.native.js +2 -2
  2. package/dist/cjs/AdaptivePopup/index.native.js.map +1 -1
  3. package/dist/cjs/Button/IconButtonGroup.native.js.map +1 -1
  4. package/dist/cjs/Button/circleGlyph.native.js.map +1 -1
  5. package/dist/cjs/Button/iconButtonGroupContext.native.js.map +1 -1
  6. package/dist/cjs/Button/index.native.js +2 -2
  7. package/dist/cjs/Button/index.native.js.map +1 -1
  8. package/dist/cjs/Button/mediaPlate.native.js.map +1 -1
  9. package/dist/cjs/Button/webGlass.native.js.map +1 -1
  10. package/dist/cjs/Chip/index.native.js.map +1 -1
  11. package/dist/cjs/ContextualSaveBar/UnsavedChangesDialog.native.js +1 -1
  12. package/dist/cjs/ContextualSaveBar/UnsavedChangesDialog.native.js.map +1 -1
  13. package/dist/cjs/ContextualSaveBar/clientKnownIdentity.native.js.map +1 -1
  14. package/dist/cjs/ContextualSaveBar/commitImmediateField.native.js.map +1 -1
  15. package/dist/cjs/ContextualSaveBar/index.native.js +3 -3
  16. package/dist/cjs/ContextualSaveBar/index.native.js.map +1 -1
  17. package/dist/cjs/ContextualSaveBar/useUnsavedChangesGuard.native.js.map +1 -1
  18. package/dist/cjs/ControlGroup/index.native.js +1 -1
  19. package/dist/cjs/ControlGroup/index.native.js.map +1 -1
  20. package/dist/cjs/DescriptionList/index.cjs +60 -5
  21. package/dist/cjs/DescriptionList/index.native.js +63 -7
  22. package/dist/cjs/DescriptionList/index.native.js.map +1 -1
  23. package/dist/cjs/ErrorSummary.native.js.map +1 -1
  24. package/dist/cjs/FieldDisplay/editMode.native.js.map +1 -1
  25. package/dist/cjs/FieldDisplay/formatters.native.js.map +1 -1
  26. package/dist/cjs/FieldDisplay/index.native.js +1 -1
  27. package/dist/cjs/FieldDisplay/index.native.js.map +1 -1
  28. package/dist/cjs/FieldGroup.native.js.map +1 -1
  29. package/dist/cjs/FloatingPanel/PanelHeader.native.js.map +1 -1
  30. package/dist/cjs/FloatingPanel/RegularPanel.native.js.map +1 -1
  31. package/dist/cjs/FloatingPanel/compactFace.native.js.map +1 -1
  32. package/dist/cjs/FloatingPanel/containingBlock.native.js.map +1 -1
  33. package/dist/cjs/FloatingPanel/echoContext.native.js.map +1 -1
  34. package/dist/cjs/FloatingPanel/iconEcho.native.js.map +1 -1
  35. package/dist/cjs/FloatingPanel/nestedAction.native.js.map +1 -1
  36. package/dist/cjs/FloatingPanel/paintedBox.native.js.map +1 -1
  37. package/dist/cjs/FloatingPanel/panelWidth.native.js.map +1 -1
  38. package/dist/cjs/FloatingPanel/pointAnchor.native.js.map +1 -1
  39. package/dist/cjs/FloatingPanel/regularPlacement.native.js.map +1 -1
  40. package/dist/cjs/FloatingPanel/trailingAction.native.js +1 -1
  41. package/dist/cjs/FloatingPanel/trailingAction.native.js.map +1 -1
  42. package/dist/cjs/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  43. package/dist/cjs/Form/index.native.js +1 -1
  44. package/dist/cjs/Form/index.native.js.map +1 -1
  45. package/dist/cjs/Form/submissionController.native.js.map +1 -1
  46. package/dist/cjs/Form/validateOn.native.js.map +1 -1
  47. package/dist/cjs/FormGrid.native.js.map +1 -1
  48. package/dist/cjs/FormSection.native.js.map +1 -1
  49. package/dist/cjs/InputParts/PasswordReveal.native.js.map +1 -1
  50. package/dist/cjs/InputParts/index.cjs +5 -3
  51. package/dist/cjs/InputParts/index.native.js +5 -3
  52. package/dist/cjs/InputParts/index.native.js.map +1 -1
  53. package/dist/cjs/Label/index.cjs +6 -4
  54. package/dist/cjs/Label/index.native.js +6 -4
  55. package/dist/cjs/Label/index.native.js.map +1 -1
  56. package/dist/cjs/MediaPicker/base64.native.js.map +1 -1
  57. package/dist/cjs/MediaPicker/payload.native.js.map +1 -1
  58. package/dist/cjs/MediaPicker/types.native.js.map +1 -1
  59. package/dist/cjs/MediaPicker/useMediaPicker.native.js.map +1 -1
  60. package/dist/cjs/Skeleton/index.native.js.map +1 -1
  61. package/dist/cjs/Spinner/SpinnerGlyph.native.js.map +1 -1
  62. package/dist/cjs/Spinner/spinnerSize.native.js.map +1 -1
  63. package/dist/cjs/Table/index.native.js.map +1 -1
  64. package/dist/cjs/Wheel/haptic.native.js.map +1 -1
  65. package/dist/cjs/Wheel/index.native.js.map +1 -1
  66. package/dist/cjs/Wheel/wheelShared.native.js.map +1 -1
  67. package/dist/cjs/actionLabel.native.js.map +1 -1
  68. package/dist/cjs/fieldDefaults.native.js.map +1 -1
  69. package/dist/cjs/fieldLayout.cjs +39 -4
  70. package/dist/cjs/fieldLayout.native.js +40 -5
  71. package/dist/cjs/fieldLayout.native.js.map +1 -1
  72. package/dist/cjs/fields/Barcode/BarcodePreview.native.js.map +1 -1
  73. package/dist/cjs/fields/Barcode/BarcodeScanner.native.js +1 -1
  74. package/dist/cjs/fields/Barcode/code128.native.js.map +1 -1
  75. package/dist/cjs/fields/Barcode/index.native.js +2 -2
  76. package/dist/cjs/fields/Barcode/index.native.js.map +1 -1
  77. package/dist/cjs/fields/Barcode/qr.native.js.map +1 -1
  78. package/dist/cjs/fields/Barcode/scannerTypes.native.js.map +1 -1
  79. package/dist/cjs/fields/Checkbox/Checkbox.native.js +1 -1
  80. package/dist/cjs/fields/Checkbox/Checkbox.native.js.map +1 -1
  81. package/dist/cjs/fields/Checkbox/CheckboxBox.native.js.map +1 -1
  82. package/dist/cjs/fields/Checkbox/CheckboxCard.native.js.map +1 -1
  83. package/dist/cjs/fields/Checkbox/CheckboxFieldFrame.native.js.map +1 -1
  84. package/dist/cjs/fields/Checkbox/CheckboxGroup.native.js +1 -1
  85. package/dist/cjs/fields/Checkbox/CheckboxGroup.native.js.map +1 -1
  86. package/dist/cjs/fields/Checkbox/checkboxGroupContext.native.js.map +1 -1
  87. package/dist/cjs/fields/Checkbox/index.native.js.map +1 -1
  88. package/dist/cjs/fields/ColorPicker/SystemColorField.native.js.map +1 -1
  89. package/dist/cjs/fields/ColorPicker/index.native.js +2 -2
  90. package/dist/cjs/fields/ColorPicker/index.native.js.map +1 -1
  91. package/dist/cjs/fields/ColorPicker/parts.native.js +2 -2
  92. package/dist/cjs/fields/ColorPicker/parts.native.js.map +1 -1
  93. package/dist/cjs/fields/ColorPicker/systemColorWell.gnome.native.js.map +1 -1
  94. package/dist/cjs/fields/Combobox/authorOptions.native.js.map +1 -1
  95. package/dist/cjs/fields/Combobox/index.native.js +5 -5
  96. package/dist/cjs/fields/Combobox/index.native.js.map +1 -1
  97. package/dist/cjs/fields/DatePicker/Calendar.native.js.map +1 -1
  98. package/dist/cjs/fields/DatePicker/DateRangePicker.native.js +2 -2
  99. package/dist/cjs/fields/DatePicker/DateRangePicker.native.js.map +1 -1
  100. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js +3 -3
  101. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  102. package/dist/cjs/fields/DatePicker/DatetimePicker.native.js +2 -2
  103. package/dist/cjs/fields/DatePicker/MonthPicker.native.js +2 -2
  104. package/dist/cjs/fields/DatePicker/MonthPicker.native.js.map +1 -1
  105. package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js +2 -2
  106. package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
  107. package/dist/cjs/fields/DatePicker/catalog.native.js +2 -2
  108. package/dist/cjs/fields/DatePicker/catalog.native.js.map +1 -1
  109. package/dist/cjs/fields/DatePicker/fieldValueSync.native.js.map +1 -1
  110. package/dist/cjs/fields/DatePicker/index.native.js +2 -2
  111. package/dist/cjs/fields/DatePicker/parts.native.js +2 -2
  112. package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
  113. package/dist/cjs/fields/DatePicker/utils.native.js.map +1 -1
  114. package/dist/cjs/fields/Duration/durationMath.native.js.map +1 -1
  115. package/dist/cjs/fields/Duration/index.native.js +3 -3
  116. package/dist/cjs/fields/Duration/index.native.js.map +1 -1
  117. package/dist/cjs/fields/FileUpload/filePickerTypes.native.js.map +1 -1
  118. package/dist/cjs/fields/FileUpload/index.native.js +2 -2
  119. package/dist/cjs/fields/FileUpload/index.native.js.map +1 -1
  120. package/dist/cjs/fields/FileUpload/nativePickerLoader.native.js.map +1 -1
  121. package/dist/cjs/fields/Geolocation/expoMapsLoader.native.js.map +1 -1
  122. package/dist/cjs/fields/Geolocation/geoValue.native.js.map +1 -1
  123. package/dist/cjs/fields/Geolocation/geocoder.native.js.map +1 -1
  124. package/dist/cjs/fields/Geolocation/geocoderContext.native.js.map +1 -1
  125. package/dist/cjs/fields/Geolocation/index.native.js +5 -5
  126. package/dist/cjs/fields/Geolocation/index.native.js.map +1 -1
  127. package/dist/cjs/fields/Geolocation/mapMath.native.js.map +1 -1
  128. package/dist/cjs/fields/Geolocation/mapTypes.native.js.map +1 -1
  129. package/dist/cjs/fields/Geolocation/nativeSearchFocus.native.js.map +1 -1
  130. package/dist/cjs/fields/Geolocation/photonGeocoder.native.js.map +1 -1
  131. package/dist/cjs/fields/Icon/drawnSet.native.js.map +1 -1
  132. package/dist/cjs/fields/Icon/index.native.js +3 -3
  133. package/dist/cjs/fields/Icon/index.native.js.map +1 -1
  134. package/dist/cjs/fields/ImageField/index.native.js +2 -2
  135. package/dist/cjs/fields/ImageField/index.native.js.map +1 -1
  136. package/dist/cjs/fields/Input/index.cjs +7 -1
  137. package/dist/cjs/fields/Input/index.native.js +10 -4
  138. package/dist/cjs/fields/Input/index.native.js.map +1 -1
  139. package/dist/cjs/fields/MentionInput/index.native.js +3 -3
  140. package/dist/cjs/fields/MentionInput/index.native.js.map +1 -1
  141. package/dist/cjs/fields/OTPInput/index.native.js +3 -3
  142. package/dist/cjs/fields/OTPInput/index.native.js.map +1 -1
  143. package/dist/cjs/fields/OTPInput/otpCellCss.native.js.map +1 -1
  144. package/dist/cjs/fields/PhoneInput/dialCodes.native.js.map +1 -1
  145. package/dist/cjs/fields/PhoneInput/index.native.js +3 -3
  146. package/dist/cjs/fields/PhoneInput/index.native.js.map +1 -1
  147. package/dist/cjs/fields/PhoneInput/phone.native.js.map +1 -1
  148. package/dist/cjs/fields/PhoneInput/regionNames.native.js.map +1 -1
  149. package/dist/cjs/fields/Progress/index.native.js +1 -1
  150. package/dist/cjs/fields/Progress/index.native.js.map +1 -1
  151. package/dist/cjs/fields/RadioGroup/index.native.js +1 -1
  152. package/dist/cjs/fields/RadioGroup/index.native.js.map +1 -1
  153. package/dist/cjs/fields/Rating/index.native.js +1 -1
  154. package/dist/cjs/fields/Rating/index.native.js.map +1 -1
  155. package/dist/cjs/fields/Rating/ratingMath.native.js.map +1 -1
  156. package/dist/cjs/fields/Rating/types.native.js.map +1 -1
  157. package/dist/cjs/fields/SearchInput/index.native.js +2 -2
  158. package/dist/cjs/fields/SearchInput/index.native.js.map +1 -1
  159. package/dist/cjs/fields/Select/InlineChoices.native.js.map +1 -1
  160. package/dist/cjs/fields/Select/SheetPickerRow.native.js.map +1 -1
  161. package/dist/cjs/fields/Select/index.cjs +39 -22
  162. package/dist/cjs/fields/Select/index.native.js +44 -26
  163. package/dist/cjs/fields/Select/index.native.js.map +1 -1
  164. package/dist/cjs/fields/Select/shouldInlineChoices.native.js.map +1 -1
  165. package/dist/cjs/fields/Signature/index.native.js +2 -2
  166. package/dist/cjs/fields/Signature/index.native.js.map +1 -1
  167. package/dist/cjs/fields/Slider/index.native.js +1 -1
  168. package/dist/cjs/fields/Slider/index.native.js.map +1 -1
  169. package/dist/cjs/fields/Slider/jumpMotion.native.js.map +1 -1
  170. package/dist/cjs/fields/Stepper/index.native.js +2 -2
  171. package/dist/cjs/fields/Stepper/index.native.js.map +1 -1
  172. package/dist/cjs/fields/Stepper/resolveStepperPlacement.native.js.map +1 -1
  173. package/dist/cjs/fields/Switch/footprint.native.js.map +1 -1
  174. package/dist/cjs/fields/Switch/index.cjs +6 -2
  175. package/dist/cjs/fields/Switch/index.native.js +8 -4
  176. package/dist/cjs/fields/Switch/index.native.js.map +1 -1
  177. package/dist/cjs/fields/TextArea/index.native.js +2 -2
  178. package/dist/cjs/fields/TextArea/index.native.js.map +1 -1
  179. package/dist/cjs/fields/TimePicker/catalog.native.js +3 -3
  180. package/dist/cjs/fields/TimePicker/catalog.native.js.map +1 -1
  181. package/dist/cjs/fields/TimePicker/index.native.js +2 -2
  182. package/dist/cjs/fields/TimePicker/parts.native.js +1 -1
  183. package/dist/cjs/fields/TimePicker/parts.native.js.map +1 -1
  184. package/dist/cjs/fields/TimeRangeScrubber/geometry.native.js.map +1 -1
  185. package/dist/cjs/fields/TimeRangeScrubber/index.native.js +2 -2
  186. package/dist/cjs/fields/TimeRangeScrubber/index.native.js.map +1 -1
  187. package/dist/cjs/fields/ToggleGroup/index.native.js +1 -1
  188. package/dist/cjs/fields/ToggleGroup/index.native.js.map +1 -1
  189. package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js +1 -1
  190. package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
  191. package/dist/cjs/fields/nativeDateTime/dateTimePickerLoader.native.js.map +1 -1
  192. package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -1
  193. package/dist/cjs/fields/nativeDateTime/sheetKeyboard.native.js.map +1 -1
  194. package/dist/cjs/fields/nativeSelect.native.js.map +1 -1
  195. package/dist/cjs/fields/selectedOrder.native.js.map +1 -1
  196. package/dist/cjs/formSpacing.native.js.map +1 -1
  197. package/dist/cjs/hooks/useFocusManagement.native.js.map +1 -1
  198. package/dist/cjs/index.cjs +4 -1
  199. package/dist/cjs/index.native.js +48 -45
  200. package/dist/cjs/index.native.js.map +1 -1
  201. package/dist/cjs/keyboard/KeyboardDoneBar.native.js +1 -1
  202. package/dist/cjs/keyboard/chain.native.js.map +1 -1
  203. package/dist/cjs/keyboard/index.native.js.map +1 -1
  204. package/dist/cjs/keyboard/types.native.js.map +1 -1
  205. package/dist/cjs/requiredMarking.native.js.map +1 -1
  206. package/dist/cjs/shared/TimeColumn.native.js +1 -1
  207. package/dist/cjs/shared/TimeColumn.native.js.map +1 -1
  208. package/dist/cjs/shared/capabilityPreflight.native.js.map +1 -1
  209. package/dist/cjs/shared/choiceRowPress.native.js.map +1 -1
  210. package/dist/cjs/shared/colorRamps.native.js.map +1 -1
  211. package/dist/cjs/shared/devWarn.native.js.map +1 -1
  212. package/dist/cjs/shared/fieldRowContext.cjs +42 -0
  213. package/dist/cjs/shared/fieldRowContext.native.js +46 -0
  214. package/dist/cjs/shared/fieldRowContext.native.js.map +1 -0
  215. package/dist/cjs/shared/floatingList.native.js +1 -1
  216. package/dist/cjs/shared/floatingList.native.js.map +1 -1
  217. package/dist/cjs/shared/focusManagement.native.js.map +1 -1
  218. package/dist/cjs/shared/groupContext.native.js.map +1 -1
  219. package/dist/cjs/shared/i18n.native.js.map +1 -1
  220. package/dist/cjs/shared/instantMotion.native.js.map +1 -1
  221. package/dist/cjs/shared/nativeA11y.native.js.map +1 -1
  222. package/dist/cjs/shared/nativeAutoFocus.native.js.map +1 -1
  223. package/dist/cjs/shared/overlaySizeClass.native.js.map +1 -1
  224. package/dist/cjs/shared/overlayStack.native.js.map +1 -1
  225. package/dist/cjs/shared/panelPortal.native.js.map +1 -1
  226. package/dist/cjs/shared/pickerTriggerContract.native.js.map +1 -1
  227. package/dist/cjs/shared/pressSlopProps.native.js.map +1 -1
  228. package/dist/cjs/shared/restoreRetries.native.js.map +1 -1
  229. package/dist/cjs/shared/sheetNavigation.native.js.map +1 -1
  230. package/dist/cjs/shared/systemControlModules.gnome.native.js.map +1 -1
  231. package/dist/cjs/shared/systemControls.native.js.map +1 -1
  232. package/dist/cjs/shared/t.native.js.map +1 -1
  233. package/dist/cjs/shared/tableCellContext.native.js.map +1 -1
  234. package/dist/cjs/shared/useDragScroll.native.js.map +1 -1
  235. package/dist/cjs/shared/useKeyboardInset.native.js.map +1 -1
  236. package/dist/cjs/shared/useSheetEscapeDismiss.native.js.map +1 -1
  237. package/dist/cjs/shared/useWheelIncrement.native.js.map +1 -1
  238. package/dist/cjs/shared/utils.native.js +1 -1
  239. package/dist/cjs/shared/utils.native.js.map +1 -1
  240. package/dist/cjs/shared/zIndex.native.js.map +1 -1
  241. package/dist/cjs/standardActions.native.js.map +1 -1
  242. package/dist/cjs/themedPrimitives.native.js.map +1 -1
  243. package/dist/cjs/types.native.js.map +1 -1
  244. package/dist/cjs/utils/LinkCell.native.js.map +1 -1
  245. package/dist/esm/DescriptionList/index.mjs +60 -5
  246. package/dist/esm/DescriptionList/index.mjs.map +1 -1
  247. package/dist/esm/DescriptionList/index.native.js +61 -5
  248. package/dist/esm/DescriptionList/index.native.js.map +1 -1
  249. package/dist/esm/InputParts/index.mjs +5 -3
  250. package/dist/esm/InputParts/index.mjs.map +1 -1
  251. package/dist/esm/InputParts/index.native.js +5 -3
  252. package/dist/esm/InputParts/index.native.js.map +1 -1
  253. package/dist/esm/Label/index.mjs +6 -4
  254. package/dist/esm/Label/index.mjs.map +1 -1
  255. package/dist/esm/Label/index.native.js +6 -4
  256. package/dist/esm/Label/index.native.js.map +1 -1
  257. package/dist/esm/fieldLayout.mjs +41 -6
  258. package/dist/esm/fieldLayout.mjs.map +1 -1
  259. package/dist/esm/fieldLayout.native.js +41 -6
  260. package/dist/esm/fieldLayout.native.js.map +1 -1
  261. package/dist/esm/fields/Input/index.mjs +7 -1
  262. package/dist/esm/fields/Input/index.mjs.map +1 -1
  263. package/dist/esm/fields/Input/index.native.js +7 -1
  264. package/dist/esm/fields/Input/index.native.js.map +1 -1
  265. package/dist/esm/fields/Select/index.mjs +40 -23
  266. package/dist/esm/fields/Select/index.mjs.map +1 -1
  267. package/dist/esm/fields/Select/index.native.js +41 -23
  268. package/dist/esm/fields/Select/index.native.js.map +1 -1
  269. package/dist/esm/fields/Switch/index.mjs +7 -3
  270. package/dist/esm/fields/Switch/index.mjs.map +1 -1
  271. package/dist/esm/fields/Switch/index.native.js +7 -3
  272. package/dist/esm/fields/Switch/index.native.js.map +1 -1
  273. package/dist/esm/index.mjs +2 -1
  274. package/dist/esm/index.mjs.map +1 -1
  275. package/dist/esm/index.native.js +2 -1
  276. package/dist/esm/index.native.js.map +1 -1
  277. package/dist/esm/shared/fieldRowContext.mjs +16 -0
  278. package/dist/esm/shared/fieldRowContext.mjs.map +1 -0
  279. package/dist/esm/shared/fieldRowContext.native.js +17 -0
  280. package/dist/esm/shared/fieldRowContext.native.js.map +1 -0
  281. package/dist/jsx/DescriptionList/index.mjs +60 -5
  282. package/dist/jsx/DescriptionList/index.mjs.map +1 -1
  283. package/dist/jsx/DescriptionList/index.native.js +61 -5
  284. package/dist/jsx/DescriptionList/index.native.js.map +1 -1
  285. package/dist/jsx/InputParts/index.mjs +5 -3
  286. package/dist/jsx/InputParts/index.mjs.map +1 -1
  287. package/dist/jsx/InputParts/index.native.js +5 -3
  288. package/dist/jsx/InputParts/index.native.js.map +1 -1
  289. package/dist/jsx/Label/index.mjs +6 -4
  290. package/dist/jsx/Label/index.mjs.map +1 -1
  291. package/dist/jsx/Label/index.native.js +6 -4
  292. package/dist/jsx/Label/index.native.js.map +1 -1
  293. package/dist/jsx/fieldLayout.mjs +41 -6
  294. package/dist/jsx/fieldLayout.mjs.map +1 -1
  295. package/dist/jsx/fieldLayout.native.js +39 -4
  296. package/dist/jsx/fieldLayout.native.js.map +1 -1
  297. package/dist/jsx/fields/Input/index.mjs +7 -1
  298. package/dist/jsx/fields/Input/index.mjs.map +1 -1
  299. package/dist/jsx/fields/Input/index.native.js +7 -1
  300. package/dist/jsx/fields/Input/index.native.js.map +1 -1
  301. package/dist/jsx/fields/Select/index.mjs +40 -23
  302. package/dist/jsx/fields/Select/index.mjs.map +1 -1
  303. package/dist/jsx/fields/Select/index.native.js +40 -22
  304. package/dist/jsx/fields/Select/index.native.js.map +1 -1
  305. package/dist/jsx/fields/Switch/index.mjs +7 -3
  306. package/dist/jsx/fields/Switch/index.mjs.map +1 -1
  307. package/dist/jsx/fields/Switch/index.native.js +6 -2
  308. package/dist/jsx/fields/Switch/index.native.js.map +1 -1
  309. package/dist/jsx/index.js +2 -1
  310. package/dist/jsx/index.js.map +1 -1
  311. package/dist/jsx/index.mjs +2 -1
  312. package/dist/jsx/index.mjs.map +1 -1
  313. package/dist/jsx/index.native.js +3 -0
  314. package/dist/jsx/index.native.js.map +1 -1
  315. package/dist/jsx/shared/fieldRowContext.mjs +16 -0
  316. package/dist/jsx/shared/fieldRowContext.mjs.map +1 -0
  317. package/dist/jsx/shared/fieldRowContext.native.js +46 -0
  318. package/dist/jsx/shared/fieldRowContext.native.js.map +1 -0
  319. package/package.json +8 -8
  320. package/src/DescriptionList/DescriptionList.spec.tsx +81 -0
  321. package/src/DescriptionList/index.tsx +78 -12
  322. package/src/InputParts/index.tsx +5 -3
  323. package/src/Label/index.tsx +9 -4
  324. package/src/fieldLayout.tsx +46 -6
  325. package/src/fieldRows.spec.tsx +150 -0
  326. package/src/fields/Input/index.tsx +5 -0
  327. package/src/fields/Select/Select.spec.tsx +10 -0
  328. package/src/fields/Select/index.tsx +47 -19
  329. package/src/fields/Switch/index.tsx +9 -4
  330. package/src/index.ts +2 -0
  331. package/src/shared/fieldRowContext.tsx +19 -0
  332. package/types/DescriptionList/index.d.ts +18 -0
  333. package/types/DescriptionList/index.d.ts.map +1 -1
  334. package/types/InputParts/index.d.ts.map +1 -1
  335. package/types/Label/index.d.ts.map +1 -1
  336. package/types/fieldLayout.d.ts.map +1 -1
  337. package/types/fields/Input/index.d.ts.map +1 -1
  338. package/types/fields/Select/index.d.ts.map +1 -1
  339. package/types/fields/Switch/index.d.ts +1 -1
  340. package/types/fields/Switch/index.d.ts.map +1 -1
  341. package/types/index.d.ts +1 -0
  342. package/types/index.d.ts.map +1 -1
  343. package/types/shared/fieldRowContext.d.ts +14 -0
  344. package/types/shared/fieldRowContext.d.ts.map +1 -0
@@ -69,6 +69,24 @@ export interface DescriptionListItem {
69
69
  * middle so both ends stay legible, with a copy action (LC-169).
70
70
  */
71
71
  identifier?: boolean;
72
+ /**
73
+ * An icon-only fact (LC-169): no visible label. The term becomes the
74
+ * glyph's accessible name and its tooltip, and the value leads after the
75
+ * glyph. Only for a glyph that is unambiguous in context (a realm's shield
76
+ * beside "master", a link beside a URL).
77
+ */
78
+ labelHidden?: boolean;
79
+ /**
80
+ * Replaces an identifier row's copy button, e.g. a menu that also copies
81
+ * values derived from this one.
82
+ */
83
+ copyAction?: ReactNode;
84
+ /**
85
+ * Trailing content that belongs to this fact, on its line (iOS's detail
86
+ * accessory): a realm's checks as a glyph cluster beside the realm, so a
87
+ * card needs no row of its own for them (LC-169).
88
+ */
89
+ accessory?: ReactNode;
72
90
  /** Optional per-row Change action. */
73
91
  onChange?: () => void;
74
92
  changeLabel?: string;
@@ -219,6 +237,29 @@ function TermLead({ row, inline = false }: { row: DescriptionListItem; inline?:
219
237
  );
220
238
  }
221
239
 
240
+ function termText(term: ReactNode): string | undefined {
241
+ return typeof term === "string" || typeof term === "number" ? String(term) : undefined;
242
+ }
243
+
244
+ /** The glyph of an icon-only fact: it carries the term as its name and tooltip. */
245
+ function FactGlyph({ row }: { row: DescriptionListItem }) {
246
+ const ink = useGlyphColor("$textMuted");
247
+ const name = termText(row.term);
248
+ return (
249
+ <View
250
+ data-mpo-description-list-glyph=""
251
+ role="img"
252
+ aria-label={name}
253
+ flexShrink={0}
254
+ {...(isWeb
255
+ ? ({ title: name, style: ink ? { color: ink } : undefined } as Record<string, unknown>)
256
+ : { accessible: true })}
257
+ >
258
+ <IconSlot icon={row.icon} color={ink} size={16} />
259
+ </View>
260
+ );
261
+ }
262
+
222
263
  function ChangeAction({ onChange, label }: { onChange: () => void; label?: string }) {
223
264
  return (
224
265
  <View data-mpo-description-list-action="" alignItems="flex-start" justifyContent="flex-end">
@@ -254,7 +295,7 @@ export function middleSplit(text: string): [string, string] {
254
295
  */
255
296
  const CopyResultContext = createContext<((ok: boolean) => void) | undefined>(undefined);
256
297
 
257
- function IdentifierValue({ text }: { text: string }) {
298
+ function IdentifierValue({ text, action }: { text: string; action?: ReactNode }) {
258
299
  const onCopyResult = useContext(CopyResultContext);
259
300
  const { knobProps } = useResolvedKnobs();
260
301
  const [head, tail] = middleSplit(text);
@@ -287,14 +328,16 @@ function IdentifierValue({ text }: { text: string }) {
287
328
  {text}
288
329
  </Text>
289
330
  )}
290
- <Button
291
- nested
292
- chromeless
293
- flexShrink={0}
294
- icon={<CopyIcon />}
295
- aria-label={t("Copy to clipboard")}
296
- onPress={() => void copy()}
297
- />
331
+ {action ?? (
332
+ <Button
333
+ nested
334
+ chromeless
335
+ flexShrink={0}
336
+ icon={<CopyIcon />}
337
+ aria-label={t("Copy to clipboard")}
338
+ onPress={() => void copy()}
339
+ />
340
+ )}
298
341
  </XStack>
299
342
  );
300
343
  }
@@ -311,7 +354,7 @@ function ValueSlot({ row, trailing = false }: { row: DescriptionListItem; traili
311
354
  row.children
312
355
  )
313
356
  ) : row.identifier && row.value != null && row.value !== "" ? (
314
- <IdentifierValue text={String(row.value)} />
357
+ <IdentifierValue text={String(row.value)} action={row.copyAction} />
315
358
  ) : (
316
359
  // FieldDisplay grows on flex 1 from a zero basis; in a column with no
317
360
  // height of its own that collapsed it to 0 and the next rule ran
@@ -355,7 +398,8 @@ function DescriptionListRowView({
355
398
  <View data-mpo-description-list-action="" />
356
399
  ) : null;
357
400
 
358
- const oneLine = inline && !row.identifier;
401
+ const iconOnly = row.labelHidden === true;
402
+ const oneLine = (inline && !row.identifier) || iconOnly;
359
403
  const rowStyle: CSSProperties | undefined = oneLine
360
404
  ? undefined
361
405
  : isWeb
@@ -389,7 +433,29 @@ function DescriptionListRowView({
389
433
  : { justifySelf: "end" }
390
434
  : undefined;
391
435
 
392
- const body = oneLine ? (
436
+ const body = iconOnly ? (
437
+ <XStack
438
+ data-mpo-description-list-icon-only=""
439
+ {...(row.icon == null && termText(row.term)
440
+ ? { role: "group" as const, "aria-label": termText(row.term) }
441
+ : null)}
442
+ alignItems="center"
443
+ width="100%"
444
+ minWidth={0}
445
+ gap={rhythm.columnGap as never}
446
+ >
447
+ {row.icon != null ? <FactGlyph row={row} /> : null}
448
+ <View flex={1} minWidth={0}>
449
+ <ValueSlot row={row} />
450
+ </View>
451
+ {row.accessory != null ? (
452
+ <View data-mpo-description-list-accessory="" flexShrink={0} maxWidth="70%">
453
+ {row.accessory}
454
+ </View>
455
+ ) : null}
456
+ {action}
457
+ </XStack>
458
+ ) : oneLine ? (
393
459
  <XStack alignItems="center" width="100%" minWidth={0} gap={rhythm.columnGap as never}>
394
460
  <View flexShrink={0} maxWidth="60%" minWidth={0}>
395
461
  <TermLead row={row} inline />
@@ -61,6 +61,7 @@ import { formButtonColors, formCommonColors, formInputColors } from "../shared/c
61
61
  import { useControlGroupVertical, useControlGrouped } from "../shared/groupContext";
62
62
  import { nativeAutoFocus } from "../shared/nativeAutoFocus";
63
63
  import { pressSlopProps } from "../shared/pressSlopProps";
64
+ import { useFieldRow } from "../shared/fieldRowContext";
64
65
  import { useIsInTableCell } from "../shared/tableCellContext";
65
66
  import { useInPanelEcho, usePanelTriggerRadiusReporter } from "../FloatingPanel/echoContext";
66
67
  import { useFieldA11y, useFieldDescribedBy } from "../fieldLayout";
@@ -387,8 +388,9 @@ const InputGroupImpl = InputGroupFrame.styleable<{
387
388
  const hydrationTouch = useTouchSurface();
388
389
  const { knobProps, control, disabledState } = useResolvedKnobs();
389
390
  const grouped = useControlGrouped();
391
+ const inFieldRow = useFieldRow();
390
392
  const groupedVertical = useControlGroupVertical();
391
- const inTableCell = useIsInTableCell();
393
+ const inTableCell = useIsInTableCell() || inFieldRow;
392
394
  // Native Pressable owns onPress; do not also pass it to XGroup (double-fire).
393
395
  const framePressProps = isWeb ? { onPress, disabled } : { disabled };
394
396
  // Standalone Box clips children to radius. Grouped / in-cell frames leave
@@ -471,16 +473,16 @@ const InputGroupImpl = InputGroupFrame.styleable<{
471
473
  // LC-84: text-field inset, not the button ladder padX. Multiline Box
472
474
  // (height="auto") leaves padX to ThemedTextArea so padX === padY.
473
475
  const fieldInset = heightProp === "auto" ? 0 : textFieldInset(familyRecipe);
476
+ const trailingOverlay = useContext(FieldTrailingOverlayContext);
474
477
  const insetPadProps =
475
478
  sizeRecipeEscaped ||
476
479
  (typeof paddingHorizontalProp !== "number" && paddingHorizontalProp != null)
477
480
  ? undefined
478
- : { paddingHorizontal: fieldInset };
481
+ : { paddingHorizontal: inFieldRow && !trailingOverlay ? 0 : fieldInset };
479
482
  // LC-149: an action overlaid from outside the Box (a picker's native clear,
480
483
  // which must sit outside the trigger's press capture) still lands in the
481
484
  // trailing slot. The Box reserves the end cap and drops its end inset so
482
485
  // the overlay reads as part of the field.
483
- const trailingOverlay = useContext(FieldTrailingOverlayContext);
484
486
  const trailingSlot = trailingOverlay ? (
485
487
  <View
486
488
  aria-hidden
@@ -8,6 +8,7 @@ import {
8
8
  useRequiredMarking,
9
9
  type RequiredMarkMode,
10
10
  } from "../requiredMarking";
11
+ import { useFieldRow } from "../shared/fieldRowContext";
11
12
  import { formCommonColors } from "../shared/colorRamps";
12
13
 
13
14
  export interface LabelProps extends Omit<TamaguiLabelProps, "size"> {
@@ -56,15 +57,19 @@ export function Label({
56
57
  const keepLabelReadable = Boolean(disabled) && disabledState.style !== "dimWhole";
57
58
  // LC-152 label role: 13/500 web, 15/500 native at the default size token.
58
59
  const role = knobProps.textRole.label;
59
- const typeSize = (size as FontSizeTokens | undefined) ?? role.fontSize;
60
- const color = error ? formCommonColors.error : role.color;
60
+ const inRow = useFieldRow();
61
+ // A field row's label is the row's title: body size and weight, primary ink.
62
+ const typeSize = (size as FontSizeTokens | undefined) ?? (inRow ? undefined : role.fontSize);
63
+ const color = error ? formCommonColors.error : inRow ? "$color" : role.color;
61
64
  const type = asPageHeading
62
65
  ? {
63
66
  ...knobProps.heading,
64
67
  fontSize: knobProps.pageTitle.size,
65
68
  lineHeight: knobProps.pageTitle.size,
66
69
  }
67
- : { fontSize: typeSize, lineHeight: typeSize, fontWeight: role.fontWeight };
70
+ : inRow && typeSize === undefined
71
+ ? {}
72
+ : { fontSize: typeSize, lineHeight: typeSize, fontWeight: role.fontWeight };
68
73
 
69
74
  const node = (
70
75
  <TamaguiLabel
@@ -78,7 +83,7 @@ export function Label({
78
83
  color={color}
79
84
  cursor={disabled ? "not-allowed" : htmlFor ? "pointer" : "default"}
80
85
  display="flex"
81
- alignItems="flex-start"
86
+ alignItems={inRow ? "center" : "flex-start"}
82
87
  flexWrap="wrap"
83
88
  opacity={keepLabelReadable ? 1 : undefined}
84
89
  {...(disabled && !keepLabelReadable ? disabledState.assemblyKnobProps : undefined)}
@@ -1,12 +1,17 @@
1
1
  import type { FieldApi } from "@tanstack/form-core";
2
2
  import { Field as TanstackField } from "@tanstack/react-form";
3
- import { useResolvedKnobs } from "@multiplatform.one/theme";
3
+ import {
4
+ MIN_PRESS_TARGET,
5
+ hairline,
6
+ useGroupPosition,
7
+ useResolvedKnobs,
8
+ } from "@multiplatform.one/theme";
4
9
  import type { KnobProps } from "@multiplatform.one/theme";
5
10
  import type { ReactNode } from "react";
6
11
  import { createContext, useContext, useEffect, useMemo } from "react";
7
12
  import { AccessibilityInfo } from "react-native";
8
13
  import type { FontSizeTokens, LabelProps, SizeTokens, YStackProps } from "tamagui";
9
- import { isWeb, Paragraph, XStack, YStack } from "tamagui";
14
+ import { isWeb, Paragraph, View, XStack, YStack } from "tamagui";
10
15
  import { fieldWidthStyle } from "./fieldDefaults";
11
16
  import { Label } from "./Label";
12
17
  import {
@@ -16,6 +21,7 @@ import {
16
21
  useRequiredMarking,
17
22
  } from "./requiredMarking";
18
23
  import { formCommonColors } from "./shared/colorRamps";
24
+ import { useFieldRow } from "./shared/fieldRowContext";
19
25
  import { warnBannedErrorWords, warnPlaceholderAsLabel } from "./shared/devWarn";
20
26
  import type { AnyFormApi, SimpleFieldApi } from "./types";
21
27
 
@@ -203,7 +209,14 @@ export function FieldLayout({
203
209
  const dimWhole = Boolean(_disabled) && disabledStyle === "dimWhole";
204
210
  const keepLabelReadable = Boolean(_disabled) && disabledStyle !== "dimWhole";
205
211
  const labelPlacement = knobProps.fieldLabelPlacement ?? "top";
206
- const sideLabel = labelPlacement === "side" && Boolean(label);
212
+ const inRow = useFieldRow();
213
+ // In an inset group the row carries the group's inset and the hairline
214
+ // above it, as a ListGroupItem does: the separator runs the group's full
215
+ // width (FZ-8003) and the row keeps the 44pt height.
216
+ const groupPosition = useGroupPosition();
217
+ const inGroup = groupPosition != null;
218
+ const ruledAbove = inGroup && groupPosition !== "first" && groupPosition !== "only";
219
+ const sideLabel = !inRow && labelPlacement === "side" && Boolean(label);
207
220
 
208
221
  warnPlaceholderAsLabel({
209
222
  placeholder,
@@ -272,7 +285,8 @@ export function FieldLayout({
272
285
  required={required}
273
286
  requiredMarking={requiredMarkingProp}
274
287
  error={hasErrorState}
275
- flexShrink={sideLabel ? 0 : undefined}
288
+ flexShrink={sideLabel || inRow ? 0 : undefined}
289
+ {...(inRow ? { maxWidth: "50%" } : null)}
276
290
  {...labelProps}
277
291
  >
278
292
  {label}
@@ -328,11 +342,37 @@ export function FieldLayout({
328
342
  onBlur={onBlur}
329
343
  data-disabled-style={disabledStyle}
330
344
  data-disabled-dimmed={dimWhole ? "true" : "false"}
331
- data-label-placement={sideLabel ? "side" : "top"}
345
+ data-label-placement={inRow ? "row" : sideLabel ? "side" : "top"}
332
346
  data-label-gap={String(gapProps.gap)}
333
347
  data-field-hug={hug ? "true" : undefined}
334
348
  >
335
- {sideLabel ? (
349
+ {inRow ? (
350
+ <>
351
+ <YStack width="100%">
352
+ {ruledAbove ? (
353
+ <View
354
+ aria-hidden
355
+ {...hairline.line}
356
+ {...({ "data-list-hairline": "full" } as Record<string, unknown>)}
357
+ />
358
+ ) : null}
359
+ <XStack
360
+ alignItems="center"
361
+ minHeight={MIN_PRESS_TARGET}
362
+ width="100%"
363
+ {...knobProps.gap}
364
+ {...(inGroup ? { paddingHorizontal: knobProps.panelPadding.padding } : null)}
365
+ data-field-row=""
366
+ >
367
+ {labelNode}
368
+ <XStack flex={1} minWidth={0} justifyContent="flex-end" alignItems="center">
369
+ {children}
370
+ </XStack>
371
+ </XStack>
372
+ </YStack>
373
+ {errorNode ?? helperNode}
374
+ </>
375
+ ) : sideLabel ? (
336
376
  <XStack
337
377
  alignItems="flex-start"
338
378
  {...knobProps.gap}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * @vitest-environment jsdom
3
+ *
4
+ * LC-169 field rows: the iOS inset-grouped form. Inside `FieldRows` a field is
5
+ * one row: label leading at the body size, the control trailing without its
6
+ * own box, at least 44pt tall.
7
+ */
8
+
9
+ import { createDefaultThemeConfig, GroupPositionContext } from "@multiplatform.one/theme";
10
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
11
+ import { describe, expect, it } from "vitest";
12
+ import { Input } from "./fields/Input";
13
+ import { Select } from "./fields/Select";
14
+ import { Switch } from "./fields/Switch";
15
+ import { FieldRows } from "./shared/fieldRowContext";
16
+
17
+ // `$textMuted` is a key only MPO's themes define; under the stock test themes
18
+ // tamagui drops it to `$color`, so this file renders under MPO's own config.
19
+ createDefaultThemeConfig({});
20
+
21
+ const classes = (el: Element | null) => String(el?.className ?? "");
22
+
23
+ describe("FieldRows (LC-169)", () => {
24
+ it("a text field is a row: label leading, value trailing and editable in place", () => {
25
+ const result = renderWithProviders(
26
+ <FieldRows>
27
+ <Input id="name" label="Display name" value="Ada Lovelace" onChange={() => {}} />
28
+ </FieldRows>,
29
+ );
30
+ const layout = result.container.querySelector("[data-label-placement]") as HTMLElement;
31
+ expect(layout.getAttribute("data-label-placement")).toBe("row");
32
+ const row = result.container.querySelector("[data-field-row]") as HTMLElement;
33
+ expect(classes(row)).toMatch(/_fd-row/);
34
+ expect(classes(row)).toMatch(/_mih-44px/);
35
+ const input = result.container.querySelector("input") as HTMLInputElement;
36
+ expect(row.contains(input)).toBe(true);
37
+ expect(row.textContent).toContain("Display name");
38
+ expect(classes(input)).toMatch(/_ta-right/);
39
+ expect(input.readOnly).toBe(false);
40
+ });
41
+
42
+ it("the row's label is the body title in the primary ink, not the footnote label role", () => {
43
+ const outside = renderWithProviders(
44
+ <Input id="a" label="Email" value="" onChange={() => {}} />,
45
+ );
46
+ const inside = renderWithProviders(
47
+ <FieldRows>
48
+ <Input id="b" label="Email" value="" onChange={() => {}} />
49
+ </FieldRows>,
50
+ );
51
+ const label = (r: typeof outside) => r.container.querySelector("label") as HTMLElement;
52
+ expect(classes(label(inside))).toMatch(/_col-color(\s|$)/);
53
+ expect(classes(label(inside))).not.toEqual(classes(label(outside)));
54
+ });
55
+
56
+ it("a picker is a value row: the value in the secondary ink, no box", () => {
57
+ const result = renderWithProviders(
58
+ <FieldRows>
59
+ <Select
60
+ id="tz"
61
+ label="Timezone"
62
+ options={[{ value: "chi", label: "America/Chicago" }]}
63
+ value="chi"
64
+ onChange={() => {}}
65
+ />
66
+ </FieldRows>,
67
+ );
68
+ const trigger = result.getByTestId("select-trigger");
69
+ expect(classes(trigger)).not.toMatch(/_bw-1px/);
70
+ const value = result.getByText("America/Chicago");
71
+ expect(classes(value)).toMatch(/_col-textMuted/);
72
+ });
73
+
74
+ it("a switch row leads with its label by default", () => {
75
+ const result = renderWithProviders(
76
+ <FieldRows>
77
+ <Switch id="push" label="Push notifications" />
78
+ </FieldRows>,
79
+ );
80
+ const row = result.getByText("Push notifications").parentElement as HTMLElement;
81
+ expect(classes(row)).toMatch(/_fd-row-reverse/);
82
+ });
83
+
84
+ it("in an inset group the row carries the group's inset, outside it none", () => {
85
+ const loose = renderWithProviders(
86
+ <FieldRows>
87
+ <Input id="loose" label="Display name" value="Ada" onChange={() => {}} />
88
+ </FieldRows>,
89
+ );
90
+ const looseRow = loose.container.querySelector("[data-field-row]");
91
+ expect(classes(looseRow)).not.toMatch(/_pl-/);
92
+ const grouped = renderWithProviders(
93
+ <GroupPositionContext.Provider value="first">
94
+ <FieldRows>
95
+ <Input id="grouped" label="Display name" value="Ada" onChange={() => {}} />
96
+ </FieldRows>
97
+ </GroupPositionContext.Provider>,
98
+ );
99
+ const groupedRow = grouped.container.querySelector("[data-field-row]");
100
+ expect(classes(groupedRow)).toMatch(/_pl-/);
101
+ expect(classes(groupedRow)).toMatch(/_pr-/);
102
+ });
103
+
104
+ it("a value and a picker's chevron end on the row's trailing inset, as a switch does", () => {
105
+ const result = renderWithProviders(
106
+ <FieldRows>
107
+ <Input id="name" label="Display name" value="Ada Lovelace" onChange={() => {}} />
108
+ <Select
109
+ id="tz"
110
+ label="Timezone"
111
+ options={[{ value: "chi", label: "America/Chicago" }]}
112
+ value="chi"
113
+ onChange={() => {}}
114
+ />
115
+ </FieldRows>,
116
+ );
117
+ const endPads = (from: Element) => {
118
+ const pads: string[] = [];
119
+ let node: Element | null = from;
120
+ while (node && !node.hasAttribute("data-field-row")) {
121
+ pads.push(...(classes(node).match(/_pr-\S+/g) ?? []));
122
+ node = node.parentElement;
123
+ }
124
+ return pads;
125
+ };
126
+ const input = result.container.querySelector("input") as HTMLInputElement;
127
+ const trigger = result.getByTestId("select-trigger");
128
+ for (const pad of [...endPads(input), ...endPads(trigger)]) expect(pad).toBe("_pr-0px");
129
+ });
130
+
131
+ it("in an inset group every row but the first draws the group's full-width hairline (FZ-8003)", () => {
132
+ const result = renderWithProviders(
133
+ <FieldRows>
134
+ <GroupPositionContext.Provider value="first">
135
+ <Input id="first" label="Display name" value="" onChange={() => {}} />
136
+ </GroupPositionContext.Provider>
137
+ <GroupPositionContext.Provider value="last">
138
+ <Input id="last" label="Email" value="" onChange={() => {}} />
139
+ </GroupPositionContext.Provider>
140
+ </FieldRows>,
141
+ );
142
+ const layouts = result.container.querySelectorAll("[data-label-placement='row']");
143
+ expect(layouts).toHaveLength(2);
144
+ expect(layouts[0]!.querySelector("[data-list-hairline]")).toBeNull();
145
+ const rule = layouts[1]!.querySelector("[data-list-hairline]");
146
+ expect(rule?.getAttribute("data-list-hairline")).toBe("full");
147
+ const row = layouts[1]!.querySelector("[data-field-row]")!;
148
+ expect(rule!.compareDocumentPosition(row) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
149
+ });
150
+ });
@@ -29,6 +29,7 @@ import type { AnyFormApi, LinkResolver } from "../../types";
29
29
  import { LinkCell } from "../../utils/LinkCell";
30
30
  import { getFormSubmission } from "../../Form/submissionController";
31
31
  import { useKeyboardChain } from "../../keyboard";
32
+ import { useFieldRow } from "../../shared/fieldRowContext";
32
33
 
33
34
  type InputAreaProps = ComponentProps<typeof InputParts.Area>;
34
35
 
@@ -173,6 +174,8 @@ export function Input({
173
174
  const submitForm = useCallback(() => {
174
175
  if (resolvedForm && name) void getFormSubmission(resolvedForm).submit();
175
176
  }, [resolvedForm, name]);
177
+ // A field row (LC-169) edits the value in place at the row's trailing edge.
178
+ const rowAlign = useFieldRow() ? ({ textAlign: "right" } as const) : undefined;
176
179
  const keyboard = useKeyboardChain({
177
180
  inputRef,
178
181
  enabled: !disabled && !readOnly && !skeleton && !children,
@@ -341,6 +344,7 @@ export function Input({
341
344
  aria-invalid={hasError || undefined}
342
345
  {...identifierProps}
343
346
  {...areaName}
347
+ {...rowAlign}
344
348
  />
345
349
  )
346
350
  ) : (
@@ -375,6 +379,7 @@ export function Input({
375
379
  aria-required={required || undefined}
376
380
  aria-invalid={hasError || undefined}
377
381
  {...areaName}
382
+ {...rowAlign}
378
383
  onChange={onChange}
379
384
  onChangeText={(text) => {
380
385
  if (disabled) return;
@@ -9,6 +9,7 @@ import { sizeRecipeForToken } from "@multiplatform.one/theme";
9
9
  import { Button } from "../../Button";
10
10
  import { Form } from "../../Form";
11
11
  import { __resetDevWarnSeen } from "../../shared/devWarn";
12
+ import { FieldRows } from "../../shared/fieldRowContext";
12
13
  import { Select, type SelectOption } from "./index";
13
14
 
14
15
  /** Web panels portal out to the page (MPO-335, LC-90), so panel text is read from the page. */
@@ -281,6 +282,15 @@ describe("Select Components", () => {
281
282
  expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("select-too-few-options"));
282
283
  });
283
284
 
285
+ it("does not warn in a field row, where a short list is the pop-up row (LC-169)", () => {
286
+ renderWithProviders(
287
+ <FieldRows>
288
+ <Select options={mockOptions} id="row" label="Digest frequency" />
289
+ </FieldRows>,
290
+ );
291
+ expect(warnSpy).not.toHaveBeenCalledWith(expect.stringContaining("select-too-few-options"));
292
+ });
293
+
284
294
  it("does not warn when options.length > 5", () => {
285
295
  const many: SelectOption[] = Array.from({ length: 6 }, (_, i) => ({
286
296
  value: `v${i}`,