@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.
- package/dist/cjs/AdaptivePopup/index.native.js +2 -2
- package/dist/cjs/AdaptivePopup/index.native.js.map +1 -1
- package/dist/cjs/Button/IconButtonGroup.native.js.map +1 -1
- package/dist/cjs/Button/circleGlyph.native.js.map +1 -1
- package/dist/cjs/Button/iconButtonGroupContext.native.js.map +1 -1
- package/dist/cjs/Button/index.native.js +2 -2
- package/dist/cjs/Button/index.native.js.map +1 -1
- package/dist/cjs/Button/mediaPlate.native.js.map +1 -1
- package/dist/cjs/Button/webGlass.native.js.map +1 -1
- package/dist/cjs/Chip/index.native.js.map +1 -1
- package/dist/cjs/ContextualSaveBar/UnsavedChangesDialog.native.js +1 -1
- package/dist/cjs/ContextualSaveBar/UnsavedChangesDialog.native.js.map +1 -1
- package/dist/cjs/ContextualSaveBar/clientKnownIdentity.native.js.map +1 -1
- package/dist/cjs/ContextualSaveBar/commitImmediateField.native.js.map +1 -1
- package/dist/cjs/ContextualSaveBar/index.native.js +3 -3
- package/dist/cjs/ContextualSaveBar/index.native.js.map +1 -1
- package/dist/cjs/ContextualSaveBar/useUnsavedChangesGuard.native.js.map +1 -1
- package/dist/cjs/ControlGroup/index.native.js +1 -1
- package/dist/cjs/ControlGroup/index.native.js.map +1 -1
- package/dist/cjs/DescriptionList/index.cjs +60 -5
- package/dist/cjs/DescriptionList/index.native.js +63 -7
- package/dist/cjs/DescriptionList/index.native.js.map +1 -1
- package/dist/cjs/ErrorSummary.native.js.map +1 -1
- package/dist/cjs/FieldDisplay/editMode.native.js.map +1 -1
- package/dist/cjs/FieldDisplay/formatters.native.js.map +1 -1
- package/dist/cjs/FieldDisplay/index.native.js +1 -1
- package/dist/cjs/FieldDisplay/index.native.js.map +1 -1
- package/dist/cjs/FieldGroup.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/PanelHeader.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/RegularPanel.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/compactFace.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/containingBlock.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/echoContext.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/iconEcho.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/nestedAction.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/paintedBox.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/panelWidth.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/pointAnchor.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/regularPlacement.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/trailingAction.native.js +1 -1
- package/dist/cjs/FloatingPanel/trailingAction.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/useFloatingPanel.native.js.map +1 -1
- package/dist/cjs/Form/index.native.js +1 -1
- package/dist/cjs/Form/index.native.js.map +1 -1
- package/dist/cjs/Form/submissionController.native.js.map +1 -1
- package/dist/cjs/Form/validateOn.native.js.map +1 -1
- package/dist/cjs/FormGrid.native.js.map +1 -1
- package/dist/cjs/FormSection.native.js.map +1 -1
- package/dist/cjs/InputParts/PasswordReveal.native.js.map +1 -1
- package/dist/cjs/InputParts/index.cjs +5 -3
- package/dist/cjs/InputParts/index.native.js +5 -3
- package/dist/cjs/InputParts/index.native.js.map +1 -1
- package/dist/cjs/Label/index.cjs +6 -4
- package/dist/cjs/Label/index.native.js +6 -4
- package/dist/cjs/Label/index.native.js.map +1 -1
- package/dist/cjs/MediaPicker/base64.native.js.map +1 -1
- package/dist/cjs/MediaPicker/payload.native.js.map +1 -1
- package/dist/cjs/MediaPicker/types.native.js.map +1 -1
- package/dist/cjs/MediaPicker/useMediaPicker.native.js.map +1 -1
- package/dist/cjs/Skeleton/index.native.js.map +1 -1
- package/dist/cjs/Spinner/SpinnerGlyph.native.js.map +1 -1
- package/dist/cjs/Spinner/spinnerSize.native.js.map +1 -1
- package/dist/cjs/Table/index.native.js.map +1 -1
- package/dist/cjs/Wheel/haptic.native.js.map +1 -1
- package/dist/cjs/Wheel/index.native.js.map +1 -1
- package/dist/cjs/Wheel/wheelShared.native.js.map +1 -1
- package/dist/cjs/actionLabel.native.js.map +1 -1
- package/dist/cjs/fieldDefaults.native.js.map +1 -1
- package/dist/cjs/fieldLayout.cjs +39 -4
- package/dist/cjs/fieldLayout.native.js +40 -5
- package/dist/cjs/fieldLayout.native.js.map +1 -1
- package/dist/cjs/fields/Barcode/BarcodePreview.native.js.map +1 -1
- package/dist/cjs/fields/Barcode/BarcodeScanner.native.js +1 -1
- package/dist/cjs/fields/Barcode/code128.native.js.map +1 -1
- package/dist/cjs/fields/Barcode/index.native.js +2 -2
- package/dist/cjs/fields/Barcode/index.native.js.map +1 -1
- package/dist/cjs/fields/Barcode/qr.native.js.map +1 -1
- package/dist/cjs/fields/Barcode/scannerTypes.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/Checkbox.native.js +1 -1
- package/dist/cjs/fields/Checkbox/Checkbox.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/CheckboxBox.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/CheckboxCard.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/CheckboxFieldFrame.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/CheckboxGroup.native.js +1 -1
- package/dist/cjs/fields/Checkbox/CheckboxGroup.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/checkboxGroupContext.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/index.native.js.map +1 -1
- package/dist/cjs/fields/ColorPicker/SystemColorField.native.js.map +1 -1
- package/dist/cjs/fields/ColorPicker/index.native.js +2 -2
- package/dist/cjs/fields/ColorPicker/index.native.js.map +1 -1
- package/dist/cjs/fields/ColorPicker/parts.native.js +2 -2
- package/dist/cjs/fields/ColorPicker/parts.native.js.map +1 -1
- package/dist/cjs/fields/ColorPicker/systemColorWell.gnome.native.js.map +1 -1
- package/dist/cjs/fields/Combobox/authorOptions.native.js.map +1 -1
- package/dist/cjs/fields/Combobox/index.native.js +5 -5
- package/dist/cjs/fields/Combobox/index.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/Calendar.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DateRangePicker.native.js +2 -2
- package/dist/cjs/fields/DatePicker/DateRangePicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js +3 -3
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DatetimePicker.native.js +2 -2
- package/dist/cjs/fields/DatePicker/MonthPicker.native.js +2 -2
- package/dist/cjs/fields/DatePicker/MonthPicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js +2 -2
- package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/catalog.native.js +2 -2
- package/dist/cjs/fields/DatePicker/catalog.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/fieldValueSync.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/index.native.js +2 -2
- package/dist/cjs/fields/DatePicker/parts.native.js +2 -2
- package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/utils.native.js.map +1 -1
- package/dist/cjs/fields/Duration/durationMath.native.js.map +1 -1
- package/dist/cjs/fields/Duration/index.native.js +3 -3
- package/dist/cjs/fields/Duration/index.native.js.map +1 -1
- package/dist/cjs/fields/FileUpload/filePickerTypes.native.js.map +1 -1
- package/dist/cjs/fields/FileUpload/index.native.js +2 -2
- package/dist/cjs/fields/FileUpload/index.native.js.map +1 -1
- package/dist/cjs/fields/FileUpload/nativePickerLoader.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/expoMapsLoader.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/geoValue.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/geocoder.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/geocoderContext.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/index.native.js +5 -5
- package/dist/cjs/fields/Geolocation/index.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/mapMath.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/mapTypes.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/nativeSearchFocus.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/photonGeocoder.native.js.map +1 -1
- package/dist/cjs/fields/Icon/drawnSet.native.js.map +1 -1
- package/dist/cjs/fields/Icon/index.native.js +3 -3
- package/dist/cjs/fields/Icon/index.native.js.map +1 -1
- package/dist/cjs/fields/ImageField/index.native.js +2 -2
- package/dist/cjs/fields/ImageField/index.native.js.map +1 -1
- package/dist/cjs/fields/Input/index.cjs +7 -1
- package/dist/cjs/fields/Input/index.native.js +10 -4
- package/dist/cjs/fields/Input/index.native.js.map +1 -1
- package/dist/cjs/fields/MentionInput/index.native.js +3 -3
- package/dist/cjs/fields/MentionInput/index.native.js.map +1 -1
- package/dist/cjs/fields/OTPInput/index.native.js +3 -3
- package/dist/cjs/fields/OTPInput/index.native.js.map +1 -1
- package/dist/cjs/fields/OTPInput/otpCellCss.native.js.map +1 -1
- package/dist/cjs/fields/PhoneInput/dialCodes.native.js.map +1 -1
- package/dist/cjs/fields/PhoneInput/index.native.js +3 -3
- package/dist/cjs/fields/PhoneInput/index.native.js.map +1 -1
- package/dist/cjs/fields/PhoneInput/phone.native.js.map +1 -1
- package/dist/cjs/fields/PhoneInput/regionNames.native.js.map +1 -1
- package/dist/cjs/fields/Progress/index.native.js +1 -1
- package/dist/cjs/fields/Progress/index.native.js.map +1 -1
- package/dist/cjs/fields/RadioGroup/index.native.js +1 -1
- package/dist/cjs/fields/RadioGroup/index.native.js.map +1 -1
- package/dist/cjs/fields/Rating/index.native.js +1 -1
- package/dist/cjs/fields/Rating/index.native.js.map +1 -1
- package/dist/cjs/fields/Rating/ratingMath.native.js.map +1 -1
- package/dist/cjs/fields/Rating/types.native.js.map +1 -1
- package/dist/cjs/fields/SearchInput/index.native.js +2 -2
- package/dist/cjs/fields/SearchInput/index.native.js.map +1 -1
- package/dist/cjs/fields/Select/InlineChoices.native.js.map +1 -1
- package/dist/cjs/fields/Select/SheetPickerRow.native.js.map +1 -1
- package/dist/cjs/fields/Select/index.cjs +39 -22
- package/dist/cjs/fields/Select/index.native.js +44 -26
- package/dist/cjs/fields/Select/index.native.js.map +1 -1
- package/dist/cjs/fields/Select/shouldInlineChoices.native.js.map +1 -1
- package/dist/cjs/fields/Signature/index.native.js +2 -2
- package/dist/cjs/fields/Signature/index.native.js.map +1 -1
- package/dist/cjs/fields/Slider/index.native.js +1 -1
- package/dist/cjs/fields/Slider/index.native.js.map +1 -1
- package/dist/cjs/fields/Slider/jumpMotion.native.js.map +1 -1
- package/dist/cjs/fields/Stepper/index.native.js +2 -2
- package/dist/cjs/fields/Stepper/index.native.js.map +1 -1
- package/dist/cjs/fields/Stepper/resolveStepperPlacement.native.js.map +1 -1
- package/dist/cjs/fields/Switch/footprint.native.js.map +1 -1
- package/dist/cjs/fields/Switch/index.cjs +6 -2
- package/dist/cjs/fields/Switch/index.native.js +8 -4
- package/dist/cjs/fields/Switch/index.native.js.map +1 -1
- package/dist/cjs/fields/TextArea/index.native.js +2 -2
- package/dist/cjs/fields/TextArea/index.native.js.map +1 -1
- package/dist/cjs/fields/TimePicker/catalog.native.js +3 -3
- package/dist/cjs/fields/TimePicker/catalog.native.js.map +1 -1
- package/dist/cjs/fields/TimePicker/index.native.js +2 -2
- package/dist/cjs/fields/TimePicker/parts.native.js +1 -1
- package/dist/cjs/fields/TimePicker/parts.native.js.map +1 -1
- package/dist/cjs/fields/TimeRangeScrubber/geometry.native.js.map +1 -1
- package/dist/cjs/fields/TimeRangeScrubber/index.native.js +2 -2
- package/dist/cjs/fields/TimeRangeScrubber/index.native.js.map +1 -1
- package/dist/cjs/fields/ToggleGroup/index.native.js +1 -1
- package/dist/cjs/fields/ToggleGroup/index.native.js.map +1 -1
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js +1 -1
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
- package/dist/cjs/fields/nativeDateTime/dateTimePickerLoader.native.js.map +1 -1
- package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -1
- package/dist/cjs/fields/nativeDateTime/sheetKeyboard.native.js.map +1 -1
- package/dist/cjs/fields/nativeSelect.native.js.map +1 -1
- package/dist/cjs/fields/selectedOrder.native.js.map +1 -1
- package/dist/cjs/formSpacing.native.js.map +1 -1
- package/dist/cjs/hooks/useFocusManagement.native.js.map +1 -1
- package/dist/cjs/index.cjs +4 -1
- package/dist/cjs/index.native.js +48 -45
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/keyboard/KeyboardDoneBar.native.js +1 -1
- package/dist/cjs/keyboard/chain.native.js.map +1 -1
- package/dist/cjs/keyboard/index.native.js.map +1 -1
- package/dist/cjs/keyboard/types.native.js.map +1 -1
- package/dist/cjs/requiredMarking.native.js.map +1 -1
- package/dist/cjs/shared/TimeColumn.native.js +1 -1
- package/dist/cjs/shared/TimeColumn.native.js.map +1 -1
- package/dist/cjs/shared/capabilityPreflight.native.js.map +1 -1
- package/dist/cjs/shared/choiceRowPress.native.js.map +1 -1
- package/dist/cjs/shared/colorRamps.native.js.map +1 -1
- package/dist/cjs/shared/devWarn.native.js.map +1 -1
- package/dist/cjs/shared/fieldRowContext.cjs +42 -0
- package/dist/cjs/shared/fieldRowContext.native.js +46 -0
- package/dist/cjs/shared/fieldRowContext.native.js.map +1 -0
- package/dist/cjs/shared/floatingList.native.js +1 -1
- package/dist/cjs/shared/floatingList.native.js.map +1 -1
- package/dist/cjs/shared/focusManagement.native.js.map +1 -1
- package/dist/cjs/shared/groupContext.native.js.map +1 -1
- package/dist/cjs/shared/i18n.native.js.map +1 -1
- package/dist/cjs/shared/instantMotion.native.js.map +1 -1
- package/dist/cjs/shared/nativeA11y.native.js.map +1 -1
- package/dist/cjs/shared/nativeAutoFocus.native.js.map +1 -1
- package/dist/cjs/shared/overlaySizeClass.native.js.map +1 -1
- package/dist/cjs/shared/overlayStack.native.js.map +1 -1
- package/dist/cjs/shared/panelPortal.native.js.map +1 -1
- package/dist/cjs/shared/pickerTriggerContract.native.js.map +1 -1
- package/dist/cjs/shared/pressSlopProps.native.js.map +1 -1
- package/dist/cjs/shared/restoreRetries.native.js.map +1 -1
- package/dist/cjs/shared/sheetNavigation.native.js.map +1 -1
- package/dist/cjs/shared/systemControlModules.gnome.native.js.map +1 -1
- package/dist/cjs/shared/systemControls.native.js.map +1 -1
- package/dist/cjs/shared/t.native.js.map +1 -1
- package/dist/cjs/shared/tableCellContext.native.js.map +1 -1
- package/dist/cjs/shared/useDragScroll.native.js.map +1 -1
- package/dist/cjs/shared/useKeyboardInset.native.js.map +1 -1
- package/dist/cjs/shared/useSheetEscapeDismiss.native.js.map +1 -1
- package/dist/cjs/shared/useWheelIncrement.native.js.map +1 -1
- package/dist/cjs/shared/utils.native.js +1 -1
- package/dist/cjs/shared/utils.native.js.map +1 -1
- package/dist/cjs/shared/zIndex.native.js.map +1 -1
- package/dist/cjs/standardActions.native.js.map +1 -1
- package/dist/cjs/themedPrimitives.native.js.map +1 -1
- package/dist/cjs/types.native.js.map +1 -1
- package/dist/cjs/utils/LinkCell.native.js.map +1 -1
- package/dist/esm/DescriptionList/index.mjs +60 -5
- package/dist/esm/DescriptionList/index.mjs.map +1 -1
- package/dist/esm/DescriptionList/index.native.js +61 -5
- package/dist/esm/DescriptionList/index.native.js.map +1 -1
- package/dist/esm/InputParts/index.mjs +5 -3
- package/dist/esm/InputParts/index.mjs.map +1 -1
- package/dist/esm/InputParts/index.native.js +5 -3
- package/dist/esm/InputParts/index.native.js.map +1 -1
- package/dist/esm/Label/index.mjs +6 -4
- package/dist/esm/Label/index.mjs.map +1 -1
- package/dist/esm/Label/index.native.js +6 -4
- package/dist/esm/Label/index.native.js.map +1 -1
- package/dist/esm/fieldLayout.mjs +41 -6
- package/dist/esm/fieldLayout.mjs.map +1 -1
- package/dist/esm/fieldLayout.native.js +41 -6
- package/dist/esm/fieldLayout.native.js.map +1 -1
- package/dist/esm/fields/Input/index.mjs +7 -1
- package/dist/esm/fields/Input/index.mjs.map +1 -1
- package/dist/esm/fields/Input/index.native.js +7 -1
- package/dist/esm/fields/Input/index.native.js.map +1 -1
- package/dist/esm/fields/Select/index.mjs +40 -23
- package/dist/esm/fields/Select/index.mjs.map +1 -1
- package/dist/esm/fields/Select/index.native.js +41 -23
- package/dist/esm/fields/Select/index.native.js.map +1 -1
- package/dist/esm/fields/Switch/index.mjs +7 -3
- package/dist/esm/fields/Switch/index.mjs.map +1 -1
- package/dist/esm/fields/Switch/index.native.js +7 -3
- package/dist/esm/fields/Switch/index.native.js.map +1 -1
- package/dist/esm/index.mjs +2 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +2 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/shared/fieldRowContext.mjs +16 -0
- package/dist/esm/shared/fieldRowContext.mjs.map +1 -0
- package/dist/esm/shared/fieldRowContext.native.js +17 -0
- package/dist/esm/shared/fieldRowContext.native.js.map +1 -0
- package/dist/jsx/DescriptionList/index.mjs +60 -5
- package/dist/jsx/DescriptionList/index.mjs.map +1 -1
- package/dist/jsx/DescriptionList/index.native.js +61 -5
- package/dist/jsx/DescriptionList/index.native.js.map +1 -1
- package/dist/jsx/InputParts/index.mjs +5 -3
- package/dist/jsx/InputParts/index.mjs.map +1 -1
- package/dist/jsx/InputParts/index.native.js +5 -3
- package/dist/jsx/InputParts/index.native.js.map +1 -1
- package/dist/jsx/Label/index.mjs +6 -4
- package/dist/jsx/Label/index.mjs.map +1 -1
- package/dist/jsx/Label/index.native.js +6 -4
- package/dist/jsx/Label/index.native.js.map +1 -1
- package/dist/jsx/fieldLayout.mjs +41 -6
- package/dist/jsx/fieldLayout.mjs.map +1 -1
- package/dist/jsx/fieldLayout.native.js +39 -4
- package/dist/jsx/fieldLayout.native.js.map +1 -1
- package/dist/jsx/fields/Input/index.mjs +7 -1
- package/dist/jsx/fields/Input/index.mjs.map +1 -1
- package/dist/jsx/fields/Input/index.native.js +7 -1
- package/dist/jsx/fields/Input/index.native.js.map +1 -1
- package/dist/jsx/fields/Select/index.mjs +40 -23
- package/dist/jsx/fields/Select/index.mjs.map +1 -1
- package/dist/jsx/fields/Select/index.native.js +40 -22
- package/dist/jsx/fields/Select/index.native.js.map +1 -1
- package/dist/jsx/fields/Switch/index.mjs +7 -3
- package/dist/jsx/fields/Switch/index.mjs.map +1 -1
- package/dist/jsx/fields/Switch/index.native.js +6 -2
- package/dist/jsx/fields/Switch/index.native.js.map +1 -1
- package/dist/jsx/index.js +2 -1
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +2 -1
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +3 -0
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/shared/fieldRowContext.mjs +16 -0
- package/dist/jsx/shared/fieldRowContext.mjs.map +1 -0
- package/dist/jsx/shared/fieldRowContext.native.js +46 -0
- package/dist/jsx/shared/fieldRowContext.native.js.map +1 -0
- package/package.json +8 -8
- package/src/DescriptionList/DescriptionList.spec.tsx +81 -0
- package/src/DescriptionList/index.tsx +78 -12
- package/src/InputParts/index.tsx +5 -3
- package/src/Label/index.tsx +9 -4
- package/src/fieldLayout.tsx +46 -6
- package/src/fieldRows.spec.tsx +150 -0
- package/src/fields/Input/index.tsx +5 -0
- package/src/fields/Select/Select.spec.tsx +10 -0
- package/src/fields/Select/index.tsx +47 -19
- package/src/fields/Switch/index.tsx +9 -4
- package/src/index.ts +2 -0
- package/src/shared/fieldRowContext.tsx +19 -0
- package/types/DescriptionList/index.d.ts +18 -0
- package/types/DescriptionList/index.d.ts.map +1 -1
- package/types/InputParts/index.d.ts.map +1 -1
- package/types/Label/index.d.ts.map +1 -1
- package/types/fieldLayout.d.ts.map +1 -1
- package/types/fields/Input/index.d.ts.map +1 -1
- package/types/fields/Select/index.d.ts.map +1 -1
- package/types/fields/Switch/index.d.ts +1 -1
- package/types/fields/Switch/index.d.ts.map +1 -1
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
- package/types/shared/fieldRowContext.d.ts +14 -0
- 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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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
|
|
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 =
|
|
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 />
|
package/src/InputParts/index.tsx
CHANGED
|
@@ -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
|
package/src/Label/index.tsx
CHANGED
|
@@ -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
|
|
60
|
-
|
|
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
|
-
:
|
|
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)}
|
package/src/fieldLayout.tsx
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
-
{
|
|
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}`,
|