@multiplatform.one/forms 7.58.0 → 7.60.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/FloatingPanel/index.native.js +8 -2
- package/dist/cjs/FloatingPanel/index.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DateRangePicker.cjs +5 -9
- package/dist/cjs/fields/DatePicker/DateRangePicker.native.js +5 -9
- package/dist/cjs/fields/DatePicker/DateRangePicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.cjs +7 -11
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js +7 -11
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DatetimePicker.native.js +5 -9
- package/dist/cjs/fields/DatePicker/DatetimePicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/MonthPicker.cjs +5 -9
- package/dist/cjs/fields/DatePicker/MonthPicker.native.js +5 -9
- package/dist/cjs/fields/DatePicker/MonthPicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/MultiDatePicker.cjs +5 -9
- package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js +5 -9
- package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/catalog.cjs +5 -9
- package/dist/cjs/fields/DatePicker/catalog.native.js +5 -9
- package/dist/cjs/fields/DatePicker/catalog.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/index.native.js +5 -9
- package/dist/cjs/fields/DatePicker/index.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/parts.cjs +46 -4
- package/dist/cjs/fields/DatePicker/parts.native.js +50 -5
- package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/cjs/fields/Select/InlineChoices.cjs +148 -0
- package/dist/cjs/fields/Select/InlineChoices.native.js +163 -0
- package/dist/cjs/fields/Select/InlineChoices.native.js.map +1 -0
- package/dist/cjs/fields/Select/index.cjs +66 -15
- package/dist/cjs/fields/Select/index.native.js +73 -17
- package/dist/cjs/fields/Select/index.native.js.map +1 -1
- package/dist/cjs/fields/Select/shouldInlineChoices.cjs +37 -0
- package/dist/cjs/fields/Select/shouldInlineChoices.native.js +43 -0
- package/dist/cjs/fields/Select/shouldInlineChoices.native.js.map +1 -0
- package/dist/cjs/fields/TimePicker/catalog.cjs +1 -1
- package/dist/cjs/fields/TimePicker/catalog.native.js +1 -1
- 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/index.native.js.map +1 -1
- package/dist/cjs/fields/TimePicker/parts.cjs +13 -11
- package/dist/cjs/fields/TimePicker/parts.native.js +16 -13
- package/dist/cjs/fields/TimePicker/parts.native.js.map +1 -1
- package/dist/esm/AdaptivePopup/index.native.js.map +1 -1
- package/dist/esm/Button/index.native.js.map +1 -1
- package/dist/esm/Chip/index.native.js.map +1 -1
- package/dist/esm/ContextualSaveBar/UnsavedChangesDialog.native.js.map +1 -1
- package/dist/esm/ContextualSaveBar/clientKnownIdentity.native.js.map +1 -1
- package/dist/esm/ContextualSaveBar/commitImmediateField.native.js.map +1 -1
- package/dist/esm/ContextualSaveBar/index.native.js.map +1 -1
- package/dist/esm/ContextualSaveBar/useUnsavedChangesGuard.native.js.map +1 -1
- package/dist/esm/ControlGroup/index.native.js.map +1 -1
- package/dist/esm/DescriptionList/index.native.js.map +1 -1
- package/dist/esm/ErrorSummary.native.js.map +1 -1
- package/dist/esm/FieldDisplay/editMode.native.js.map +1 -1
- package/dist/esm/FieldDisplay/formatters.native.js.map +1 -1
- package/dist/esm/FieldDisplay/index.native.js.map +1 -1
- package/dist/esm/FieldGroup.native.js.map +1 -1
- package/dist/esm/FloatingPanel/compactFace.native.js.map +1 -1
- package/dist/esm/FloatingPanel/index.native.js +8 -2
- package/dist/esm/FloatingPanel/index.native.js.map +1 -1
- package/dist/esm/FloatingPanel/nestedAction.native.js.map +1 -1
- package/dist/esm/FloatingPanel/pointAnchor.native.js.map +1 -1
- package/dist/esm/FloatingPanel/trailingAction.native.js.map +1 -1
- package/dist/esm/FloatingPanel/useFloatingPanel.native.js.map +1 -1
- package/dist/esm/Form/index.native.js.map +1 -1
- package/dist/esm/Form/submissionController.native.js.map +1 -1
- package/dist/esm/Form/validateOn.native.js.map +1 -1
- package/dist/esm/FormGrid.native.js.map +1 -1
- package/dist/esm/FormSection.native.js.map +1 -1
- package/dist/esm/InputParts/PasswordReveal.native.js.map +1 -1
- package/dist/esm/InputParts/index.native.js.map +1 -1
- package/dist/esm/Label/index.native.js.map +1 -1
- package/dist/esm/MediaPicker/base64.native.js.map +1 -1
- package/dist/esm/MediaPicker/payload.native.js.map +1 -1
- package/dist/esm/MediaPicker/types.native.js.map +1 -1
- package/dist/esm/MediaPicker/useMediaPicker.native.js.map +1 -1
- package/dist/esm/Skeleton/index.native.js.map +1 -1
- package/dist/esm/Spinner/SpinnerGlyph.native.js.map +1 -1
- package/dist/esm/Spinner/spinnerSize.native.js.map +1 -1
- package/dist/esm/Table/index.native.js.map +1 -1
- package/dist/esm/Wheel/haptic.native.js.map +1 -1
- package/dist/esm/Wheel/index.native.js.map +1 -1
- package/dist/esm/Wheel/wheelShared.native.js.map +1 -1
- package/dist/esm/actionLabel.native.js.map +1 -1
- package/dist/esm/fieldDefaults.native.js.map +1 -1
- package/dist/esm/fieldLayout.native.js.map +1 -1
- package/dist/esm/fields/Barcode/BarcodePreview.native.js.map +1 -1
- package/dist/esm/fields/Barcode/code128.native.js.map +1 -1
- package/dist/esm/fields/Barcode/index.native.js.map +1 -1
- package/dist/esm/fields/Barcode/qr.native.js.map +1 -1
- package/dist/esm/fields/Barcode/scannerTypes.native.js.map +1 -1
- package/dist/esm/fields/Checkbox/Checkbox.native.js.map +1 -1
- package/dist/esm/fields/Checkbox/CheckboxBox.native.js.map +1 -1
- package/dist/esm/fields/Checkbox/CheckboxCard.native.js.map +1 -1
- package/dist/esm/fields/Checkbox/CheckboxFieldFrame.native.js.map +1 -1
- package/dist/esm/fields/Checkbox/CheckboxGroup.native.js.map +1 -1
- package/dist/esm/fields/Checkbox/checkboxGroupContext.native.js.map +1 -1
- package/dist/esm/fields/Checkbox/index.native.js.map +1 -1
- package/dist/esm/fields/ColorPicker/index.native.js.map +1 -1
- package/dist/esm/fields/ColorPicker/parts.native.js.map +1 -1
- package/dist/esm/fields/Combobox/authorOptions.native.js.map +1 -1
- package/dist/esm/fields/Combobox/index.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/Calendar.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/DateRangePicker.mjs +6 -10
- package/dist/esm/fields/DatePicker/DateRangePicker.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/DateRangePicker.native.js +6 -10
- package/dist/esm/fields/DatePicker/DateRangePicker.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs +8 -12
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js +8 -12
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.native.js +6 -10
- package/dist/esm/fields/DatePicker/DatetimePicker.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/MonthPicker.mjs +6 -10
- package/dist/esm/fields/DatePicker/MonthPicker.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/MonthPicker.native.js +6 -10
- package/dist/esm/fields/DatePicker/MonthPicker.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/MultiDatePicker.mjs +6 -10
- package/dist/esm/fields/DatePicker/MultiDatePicker.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/MultiDatePicker.native.js +6 -10
- package/dist/esm/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/catalog.mjs +6 -10
- package/dist/esm/fields/DatePicker/catalog.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/catalog.native.js +6 -10
- package/dist/esm/fields/DatePicker/catalog.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/fieldValueSync.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/index.native.js +6 -10
- package/dist/esm/fields/DatePicker/index.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/parts.mjs +46 -5
- package/dist/esm/fields/DatePicker/parts.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/parts.native.js +50 -6
- package/dist/esm/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/utils.native.js.map +1 -1
- package/dist/esm/fields/Duration/durationMath.native.js.map +1 -1
- package/dist/esm/fields/Duration/index.native.js.map +1 -1
- package/dist/esm/fields/FileUpload/filePickerTypes.native.js.map +1 -1
- package/dist/esm/fields/FileUpload/index.native.js.map +1 -1
- package/dist/esm/fields/FileUpload/nativePickerLoader.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/expoMapsLoader.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/geoValue.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/geocoder.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/geocoderContext.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/index.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/mapMath.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/mapTypes.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/nativeSearchFocus.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/photonGeocoder.native.js.map +1 -1
- package/dist/esm/fields/Icon/drawnSet.native.js.map +1 -1
- package/dist/esm/fields/Icon/index.native.js.map +1 -1
- package/dist/esm/fields/ImageField/index.native.js.map +1 -1
- package/dist/esm/fields/Input/index.native.js.map +1 -1
- package/dist/esm/fields/MentionInput/index.native.js.map +1 -1
- package/dist/esm/fields/OTPInput/index.native.js.map +1 -1
- package/dist/esm/fields/OTPInput/otpCellCss.native.js.map +1 -1
- package/dist/esm/fields/PhoneInput/dialCodes.native.js.map +1 -1
- package/dist/esm/fields/PhoneInput/index.native.js.map +1 -1
- package/dist/esm/fields/PhoneInput/phone.native.js.map +1 -1
- package/dist/esm/fields/PhoneInput/regionNames.native.js.map +1 -1
- package/dist/esm/fields/Progress/index.native.js.map +1 -1
- package/dist/esm/fields/RadioGroup/index.native.js.map +1 -1
- package/dist/esm/fields/Rating/index.native.js.map +1 -1
- package/dist/esm/fields/Rating/ratingMath.native.js.map +1 -1
- package/dist/esm/fields/Rating/types.native.js.map +1 -1
- package/dist/esm/fields/SearchInput/index.native.js.map +1 -1
- package/dist/esm/fields/Select/InlineChoices.mjs +123 -0
- package/dist/esm/fields/Select/InlineChoices.mjs.map +1 -0
- package/dist/esm/fields/Select/InlineChoices.native.js +135 -0
- package/dist/esm/fields/Select/InlineChoices.native.js.map +1 -0
- package/dist/esm/fields/Select/index.mjs +67 -16
- package/dist/esm/fields/Select/index.mjs.map +1 -1
- package/dist/esm/fields/Select/index.native.js +74 -18
- package/dist/esm/fields/Select/index.native.js.map +1 -1
- package/dist/esm/fields/Select/shouldInlineChoices.mjs +11 -0
- package/dist/esm/fields/Select/shouldInlineChoices.mjs.map +1 -0
- package/dist/esm/fields/Select/shouldInlineChoices.native.js +14 -0
- package/dist/esm/fields/Select/shouldInlineChoices.native.js.map +1 -0
- package/dist/esm/fields/Signature/index.native.js.map +1 -1
- package/dist/esm/fields/Slider/index.native.js.map +1 -1
- package/dist/esm/fields/Slider/jumpMotion.native.js.map +1 -1
- package/dist/esm/fields/Stepper/index.native.js.map +1 -1
- package/dist/esm/fields/Stepper/resolveStepperPlacement.native.js.map +1 -1
- package/dist/esm/fields/Switch/footprint.native.js.map +1 -1
- package/dist/esm/fields/Switch/index.native.js.map +1 -1
- package/dist/esm/fields/TextArea/index.native.js.map +1 -1
- package/dist/esm/fields/TimePicker/catalog.mjs +1 -1
- package/dist/esm/fields/TimePicker/catalog.mjs.map +1 -1
- package/dist/esm/fields/TimePicker/catalog.native.js +1 -1
- package/dist/esm/fields/TimePicker/catalog.native.js.map +1 -1
- package/dist/esm/fields/TimePicker/index.native.js +2 -2
- package/dist/esm/fields/TimePicker/index.native.js.map +1 -1
- package/dist/esm/fields/TimePicker/parts.mjs +13 -11
- package/dist/esm/fields/TimePicker/parts.mjs.map +1 -1
- package/dist/esm/fields/TimePicker/parts.native.js +16 -13
- package/dist/esm/fields/TimePicker/parts.native.js.map +1 -1
- package/dist/esm/fields/TimeRangeScrubber/geometry.native.js.map +1 -1
- package/dist/esm/fields/TimeRangeScrubber/index.native.js.map +1 -1
- package/dist/esm/fields/ToggleGroup/index.native.js.map +1 -1
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
- package/dist/esm/fields/nativeDateTime/dateTimePickerLoader.native.js.map +1 -1
- package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -1
- package/dist/esm/fields/nativeSelect.native.js.map +1 -1
- package/dist/esm/fields/selectedOrder.native.js.map +1 -1
- package/dist/esm/formSpacing.native.js.map +1 -1
- package/dist/esm/hooks/useFocusManagement.native.js.map +1 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/keyboard/chain.native.js.map +1 -1
- package/dist/esm/keyboard/index.native.js.map +1 -1
- package/dist/esm/keyboard/types.native.js.map +1 -1
- package/dist/esm/requiredMarking.native.js.map +1 -1
- package/dist/esm/shared/TimeColumn.native.js.map +1 -1
- package/dist/esm/shared/capabilityPreflight.native.js.map +1 -1
- package/dist/esm/shared/choiceRowPress.native.js.map +1 -1
- package/dist/esm/shared/colorRamps.native.js.map +1 -1
- package/dist/esm/shared/devWarn.native.js.map +1 -1
- package/dist/esm/shared/floatingList.native.js.map +1 -1
- package/dist/esm/shared/focusManagement.native.js.map +1 -1
- package/dist/esm/shared/groupContext.native.js.map +1 -1
- package/dist/esm/shared/i18n.native.js.map +1 -1
- package/dist/esm/shared/instantMotion.native.js.map +1 -1
- package/dist/esm/shared/nativeA11y.native.js.map +1 -1
- package/dist/esm/shared/nativeAutoFocus.native.js.map +1 -1
- package/dist/esm/shared/overlayStack.native.js.map +1 -1
- package/dist/esm/shared/panelPortal.native.js.map +1 -1
- package/dist/esm/shared/pickerTriggerContract.native.js.map +1 -1
- package/dist/esm/shared/pressSlopProps.native.js.map +1 -1
- package/dist/esm/shared/restoreRetries.native.js.map +1 -1
- package/dist/esm/shared/t.native.js.map +1 -1
- package/dist/esm/shared/tableCellContext.native.js.map +1 -1
- package/dist/esm/shared/useDragScroll.native.js.map +1 -1
- package/dist/esm/shared/useKeyboardInset.native.js.map +1 -1
- package/dist/esm/shared/useSheetEscapeDismiss.native.js.map +1 -1
- package/dist/esm/shared/useWheelIncrement.native.js.map +1 -1
- package/dist/esm/shared/utils.native.js.map +1 -1
- package/dist/esm/shared/zIndex.native.js.map +1 -1
- package/dist/esm/themedPrimitives.native.js.map +1 -1
- package/dist/esm/types.native.js.map +1 -1
- package/dist/esm/utils/LinkCell.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/index.native.js +8 -2
- package/dist/jsx/FloatingPanel/index.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/DateRangePicker.mjs +6 -10
- package/dist/jsx/fields/DatePicker/DateRangePicker.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/DateRangePicker.native.js +5 -9
- package/dist/jsx/fields/DatePicker/DateRangePicker.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs +8 -12
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js +7 -11
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/DatetimePicker.native.js +5 -9
- package/dist/jsx/fields/DatePicker/DatetimePicker.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/MonthPicker.mjs +6 -10
- package/dist/jsx/fields/DatePicker/MonthPicker.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/MonthPicker.native.js +5 -9
- package/dist/jsx/fields/DatePicker/MonthPicker.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/MultiDatePicker.mjs +6 -10
- package/dist/jsx/fields/DatePicker/MultiDatePicker.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/MultiDatePicker.native.js +5 -9
- package/dist/jsx/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/catalog.mjs +6 -10
- package/dist/jsx/fields/DatePicker/catalog.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/catalog.native.js +5 -9
- package/dist/jsx/fields/DatePicker/catalog.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/index.native.js +5 -9
- package/dist/jsx/fields/DatePicker/index.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/parts.mjs +46 -5
- package/dist/jsx/fields/DatePicker/parts.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/parts.native.js +50 -5
- package/dist/jsx/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/jsx/fields/Select/InlineChoices.mjs +123 -0
- package/dist/jsx/fields/Select/InlineChoices.mjs.map +1 -0
- package/dist/jsx/fields/Select/InlineChoices.native.js +163 -0
- package/dist/jsx/fields/Select/InlineChoices.native.js.map +1 -0
- package/dist/jsx/fields/Select/index.mjs +67 -16
- package/dist/jsx/fields/Select/index.mjs.map +1 -1
- package/dist/jsx/fields/Select/index.native.js +73 -17
- package/dist/jsx/fields/Select/index.native.js.map +1 -1
- package/dist/jsx/fields/Select/shouldInlineChoices.mjs +11 -0
- package/dist/jsx/fields/Select/shouldInlineChoices.mjs.map +1 -0
- package/dist/jsx/fields/Select/shouldInlineChoices.native.js +43 -0
- package/dist/jsx/fields/Select/shouldInlineChoices.native.js.map +1 -0
- package/dist/jsx/fields/TimePicker/catalog.mjs +1 -1
- package/dist/jsx/fields/TimePicker/catalog.mjs.map +1 -1
- package/dist/jsx/fields/TimePicker/catalog.native.js +1 -1
- package/dist/jsx/fields/TimePicker/catalog.native.js.map +1 -1
- package/dist/jsx/fields/TimePicker/index.native.js +2 -2
- package/dist/jsx/fields/TimePicker/index.native.js.map +1 -1
- package/dist/jsx/fields/TimePicker/parts.mjs +13 -11
- package/dist/jsx/fields/TimePicker/parts.mjs.map +1 -1
- package/dist/jsx/fields/TimePicker/parts.native.js +16 -13
- package/dist/jsx/fields/TimePicker/parts.native.js.map +1 -1
- package/dist/jsx/index.native.js.map +1 -1
- package/package.json +8 -8
- package/src/FloatingPanel/FloatingPanel.menuFace.native.spec.tsx +13 -1
- package/src/FloatingPanel/index.native.tsx +8 -1
- package/src/fields/DatePicker/DatePicker.stories.tsx +6 -2
- package/src/fields/DatePicker/DateRangePicker.tsx +7 -10
- package/src/fields/DatePicker/DatetimePicker.catalog.tsx +9 -12
- package/src/fields/DatePicker/DatetimePicker.native.tsx +7 -11
- package/src/fields/DatePicker/DatetimePicker.stories.tsx +11 -2
- package/src/fields/DatePicker/MonthPicker.tsx +7 -10
- package/src/fields/DatePicker/MultiDatePicker.tsx +7 -10
- package/src/fields/DatePicker/catalog.tsx +7 -10
- package/src/fields/DatePicker/fieldStates.spec.tsx +120 -0
- package/src/fields/DatePicker/index.native.tsx +7 -11
- package/src/fields/DatePicker/parts.tsx +61 -2
- package/src/fields/Select/InlineChoices.tsx +156 -0
- package/src/fields/Select/index.tsx +64 -39
- package/src/fields/Select/shouldInlineChoices.spec.ts +25 -0
- package/src/fields/Select/shouldInlineChoices.ts +22 -0
- package/src/fields/TimePicker/TimePicker.stories.tsx +3 -4
- package/src/fields/TimePicker/catalog.tsx +1 -1
- package/src/fields/TimePicker/index.native.tsx +2 -2
- package/src/fields/TimePicker/parts.tsx +15 -9
- package/types/FloatingPanel/index.native.d.ts +1 -1
- package/types/FloatingPanel/index.native.d.ts.map +1 -1
- package/types/fields/Checkbox/index.d.ts +3 -3
- package/types/fields/DatePicker/DateRangePicker.d.ts.map +1 -1
- package/types/fields/DatePicker/DatetimePicker.catalog.d.ts.map +1 -1
- package/types/fields/DatePicker/DatetimePicker.native.d.ts.map +1 -1
- package/types/fields/DatePicker/MonthPicker.d.ts.map +1 -1
- package/types/fields/DatePicker/MultiDatePicker.d.ts.map +1 -1
- package/types/fields/DatePicker/catalog.d.ts.map +1 -1
- package/types/fields/DatePicker/index.native.d.ts.map +1 -1
- package/types/fields/DatePicker/parts.d.ts +14 -0
- package/types/fields/DatePicker/parts.d.ts.map +1 -1
- package/types/fields/Select/InlineChoices.d.ts +29 -0
- package/types/fields/Select/InlineChoices.d.ts.map +1 -0
- package/types/fields/Select/index.d.ts +6 -0
- package/types/fields/Select/index.d.ts.map +1 -1
- package/types/fields/Select/shouldInlineChoices.d.ts +16 -0
- package/types/fields/Select/shouldInlineChoices.d.ts.map +1 -0
- package/types/fields/TimePicker/parts.d.ts +3 -1
- package/types/fields/TimePicker/parts.d.ts.map +1 -1
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
CalendarHeader,
|
|
24
24
|
CalendarPanelFrame,
|
|
25
25
|
DatePickerInputTrigger,
|
|
26
|
+
DatePickerReadOnlyField,
|
|
26
27
|
DatePickerPopoverShell,
|
|
27
28
|
CalendarViewSwitcher,
|
|
28
29
|
isSameDay,
|
|
@@ -424,16 +425,12 @@ export function MultiDatePicker<
|
|
|
424
425
|
) => {
|
|
425
426
|
if (readOnly) {
|
|
426
427
|
return (
|
|
427
|
-
<
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
aria-invalid={hasError || undefined}
|
|
434
|
-
placeholder={placeholder}
|
|
435
|
-
/>
|
|
436
|
-
</InputParts.Box>
|
|
428
|
+
<DatePickerReadOnlyField
|
|
429
|
+
value={formatMultiDates(selectedDates)}
|
|
430
|
+
aria-required={required || undefined}
|
|
431
|
+
aria-invalid={hasError || undefined}
|
|
432
|
+
placeholder={placeholder}
|
|
433
|
+
/>
|
|
437
434
|
);
|
|
438
435
|
}
|
|
439
436
|
const handleOpenChange = (isOpen: boolean) => {
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
CalendarHeader,
|
|
23
23
|
CalendarPanelFrame,
|
|
24
24
|
DatePickerInputTrigger,
|
|
25
|
+
DatePickerReadOnlyField,
|
|
25
26
|
DatePickerPopoverShell,
|
|
26
27
|
CalendarViewSwitcher,
|
|
27
28
|
formatDateDisplay,
|
|
@@ -283,16 +284,12 @@ export function DatePicker<
|
|
|
283
284
|
) => {
|
|
284
285
|
if (readOnly) {
|
|
285
286
|
return (
|
|
286
|
-
<
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
aria-invalid={hasError || undefined}
|
|
293
|
-
placeholder={placeholder}
|
|
294
|
-
/>
|
|
295
|
-
</InputParts.Box>
|
|
287
|
+
<DatePickerReadOnlyField
|
|
288
|
+
value={formatDateDisplay(selectedDate)}
|
|
289
|
+
aria-required={required || undefined}
|
|
290
|
+
aria-invalid={hasError || undefined}
|
|
291
|
+
placeholder={placeholder}
|
|
292
|
+
/>
|
|
296
293
|
);
|
|
297
294
|
}
|
|
298
295
|
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
4
|
+
import { YStack } from "tamagui";
|
|
5
|
+
import { TimePicker } from "../TimePicker";
|
|
6
|
+
import * as timeStories from "../TimePicker/TimePicker.stories";
|
|
7
|
+
import { DatePicker, DateRangePicker, DatetimePicker, MonthPicker, MultiDatePicker } from "./index";
|
|
8
|
+
|
|
9
|
+
vi.mock("@multiplatform.one/storybook", () => ({ action: () => () => {} }));
|
|
10
|
+
vi.mock("@multiplatform.one/table", () => ({ DataTable: () => null }));
|
|
11
|
+
|
|
12
|
+
// FZ-7853 round 2. happy-dom has no layout, so these pin the CSS and the
|
|
13
|
+
// anatomy; lc-verify LC-10 fieldEdge measures the shared right edge in the
|
|
14
|
+
// browser at 320 and 390.
|
|
15
|
+
|
|
16
|
+
function render(node: ReactElement) {
|
|
17
|
+
return renderWithProviders(<YStack width={360}>{node}</YStack>).container;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function box(root: HTMLElement) {
|
|
21
|
+
const found = root.querySelector<HTMLElement>("[data-mp-input-box]");
|
|
22
|
+
expect(found).toBeTruthy();
|
|
23
|
+
return found as HTMLElement;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const datePickers = [
|
|
27
|
+
["DatePicker", (p: object) => <DatePicker label="Date" {...p} />, "2026-10-31"],
|
|
28
|
+
[
|
|
29
|
+
"DatetimePicker",
|
|
30
|
+
(p: object) => <DatetimePicker label="When" {...p} />,
|
|
31
|
+
new Date("2026-10-31T09:30:00"),
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
"MonthPicker",
|
|
35
|
+
({ value, ...p }: { value?: unknown; readOnly?: boolean }) => (
|
|
36
|
+
<MonthPicker label="Month" defaultValue={value} {...p} />
|
|
37
|
+
),
|
|
38
|
+
{ year: 2026, month: 9 },
|
|
39
|
+
],
|
|
40
|
+
] as const;
|
|
41
|
+
|
|
42
|
+
describe("FZ-7853 one right edge: every state fills its row", () => {
|
|
43
|
+
it.each([
|
|
44
|
+
["DatePicker empty", <DatePicker key="a" label="Date" />],
|
|
45
|
+
["DatePicker filled", <DatePicker key="b" label="Date" value="2026-10-31" />],
|
|
46
|
+
["DatePicker read-only", <DatePicker key="c" label="Date" value="2026-10-31" readOnly />],
|
|
47
|
+
["DatePicker disabled", <DatePicker key="d" label="Date" value="2026-10-31" disabled />],
|
|
48
|
+
["DatetimePicker empty", <DatetimePicker key="e" label="When" />],
|
|
49
|
+
["MonthPicker empty", <MonthPicker key="f" label="Month" />],
|
|
50
|
+
["DateRangePicker empty", <DateRangePicker key="g" label="Range" />],
|
|
51
|
+
["MultiDatePicker empty", <MultiDatePicker key="h" label="Days" />],
|
|
52
|
+
["TimePicker empty", <TimePicker key="i" label="Time" />],
|
|
53
|
+
["TimePicker filled", <TimePicker key="j" label="Time" value="09:30" />],
|
|
54
|
+
["TimePicker read-only", <TimePicker key="k" label="Time" value="09:30" readOnly />],
|
|
55
|
+
["TimePicker disabled", <TimePicker key="l" label="Time" value="09:30" disabled />],
|
|
56
|
+
])("%s grows to the row, not to its content", (_name, node) => {
|
|
57
|
+
const cs = getComputedStyle(box(render(node)));
|
|
58
|
+
expect(cs.flexGrow).toBe("1");
|
|
59
|
+
expect(cs.minWidth).toBe("0px");
|
|
60
|
+
expect(cs.maxWidth).toBe("100%");
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("FZ-7853 same shell in every state", () => {
|
|
65
|
+
it.each(datePickers)(
|
|
66
|
+
"%s read-only keeps the glyph and the insets, and drops only the clear action",
|
|
67
|
+
(_name, make, value) => {
|
|
68
|
+
const editable = box(render(make({ value })));
|
|
69
|
+
const readOnly = render(make({ value, readOnly: true }));
|
|
70
|
+
const readOnlyBox = box(readOnly);
|
|
71
|
+
expect(readOnlyBox.querySelector("[data-testid='date-picker-glyph']")).toBeTruthy();
|
|
72
|
+
expect(readOnly.querySelector("[data-testid='date-picker-clear']")).toBeNull();
|
|
73
|
+
expect(readOnly.querySelector("input")?.getAttribute("aria-readonly")).toBe("true");
|
|
74
|
+
for (const side of ["paddingLeft", "paddingRight", "height", "borderTopWidth"] as const) {
|
|
75
|
+
expect(getComputedStyle(readOnlyBox)[side]).toBe(getComputedStyle(editable)[side]);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
it("TimePicker read-only keeps the clock glyph, drops the clear action and opens nothing", () => {
|
|
81
|
+
const editable = box(render(<TimePicker label="Time" value="09:30" />));
|
|
82
|
+
const root = render(<TimePicker label="Time" value="09:30" readOnly />);
|
|
83
|
+
const readOnlyBox = box(root);
|
|
84
|
+
expect(readOnlyBox.querySelector("[data-testid='time-picker-glyph']")).toBeTruthy();
|
|
85
|
+
expect(root.querySelector("[data-testid='time-picker-clear']")).toBeNull();
|
|
86
|
+
expect(root.querySelector("input")?.getAttribute("aria-readonly")).toBe("true");
|
|
87
|
+
for (const side of ["paddingLeft", "paddingRight", "height"] as const) {
|
|
88
|
+
expect(getComputedStyle(readOnlyBox)[side]).toBe(getComputedStyle(editable)[side]);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it.each([
|
|
93
|
+
["DatePicker", <DatePicker key="a" label="Date" value="2026-10-31" disabled />, "date"],
|
|
94
|
+
["TimePicker", <TimePicker key="b" label="Time" value="09:30" disabled />, "time"],
|
|
95
|
+
])("%s disabled keeps the glyph and the geometry, dimmed", (_name, node, kind) => {
|
|
96
|
+
const disabled = box(render(node));
|
|
97
|
+
expect(disabled.querySelector(`[data-testid='${kind}-picker-glyph']`)).toBeTruthy();
|
|
98
|
+
expect(getComputedStyle(disabled).opacity).toBe("0.5");
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe("FZ-7853 TimePicker stories carry one label (LC-171)", () => {
|
|
103
|
+
it.each(["Basic", "Standalone", "WithError", "Disabled"] as const)(
|
|
104
|
+
"%s: the hint is the placeholder, not a second line under the label",
|
|
105
|
+
(name) => {
|
|
106
|
+
const args = (timeStories[name] as { args: Record<string, unknown> }).args;
|
|
107
|
+
expect(args.helperText).toBeUndefined();
|
|
108
|
+
const root = render(<TimePicker {...(args as object)} />);
|
|
109
|
+
expect(root.textContent ?? "").not.toContain("Select a time");
|
|
110
|
+
if (name !== "Disabled") {
|
|
111
|
+
expect(args.placeholder).toBe("Select a time");
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
it("an empty TimePicker shows the hint inside the field", () => {
|
|
117
|
+
const root = render(<TimePicker label="Time" placeholder="Select a time" />);
|
|
118
|
+
expect(root.querySelector("input")?.getAttribute("placeholder")).toBe("Select a time");
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -24,7 +24,7 @@ import { loadDateTimePicker } from "../nativeDateTime/dateTimePickerLoader";
|
|
|
24
24
|
import { OsDateTimeSheet } from "../nativeDateTime/OsDateTimeSheet";
|
|
25
25
|
import { DatePicker as CatalogDatePicker } from "./catalog";
|
|
26
26
|
import { DatePickerFieldValueSync } from "./fieldValueSync";
|
|
27
|
-
import { DatePickerInputTrigger, formatDateDisplay } from "./parts";
|
|
27
|
+
import { DatePickerInputTrigger, DatePickerReadOnlyField, formatDateDisplay } from "./parts";
|
|
28
28
|
import { coerceToDate } from "./utils";
|
|
29
29
|
|
|
30
30
|
export type DatePickerProps<
|
|
@@ -138,16 +138,12 @@ export function DatePicker<
|
|
|
138
138
|
) => {
|
|
139
139
|
if (readOnly) {
|
|
140
140
|
return (
|
|
141
|
-
<
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
aria-invalid={hasError || undefined}
|
|
148
|
-
placeholder={placeholder}
|
|
149
|
-
/>
|
|
150
|
-
</InputParts.Box>
|
|
141
|
+
<DatePickerReadOnlyField
|
|
142
|
+
value={formatDateDisplay(selectedDate)}
|
|
143
|
+
aria-required={required || undefined}
|
|
144
|
+
aria-invalid={hasError || undefined}
|
|
145
|
+
placeholder={placeholder}
|
|
146
|
+
/>
|
|
151
147
|
);
|
|
152
148
|
}
|
|
153
149
|
return (
|
|
@@ -933,8 +933,11 @@ export function DatePickerInputTrigger({
|
|
|
933
933
|
pointerEvents={isWeb ? undefined : "none"}
|
|
934
934
|
>
|
|
935
935
|
<InputParts.Box
|
|
936
|
-
//
|
|
937
|
-
|
|
936
|
+
// FZ-7853: the box fills its row on every platform, so an empty
|
|
937
|
+
// trigger ends on the same inset as a filled one (Yoga also has no
|
|
938
|
+
// intrinsic TextInput width to size it).
|
|
939
|
+
flex={1}
|
|
940
|
+
minWidth={0}
|
|
938
941
|
// Elevation is an overlay recipe: it belongs on the open panel, never
|
|
939
942
|
// on the closed text-field trigger.
|
|
940
943
|
elevation={undefined}
|
|
@@ -984,6 +987,62 @@ export function DatePickerInputTrigger({
|
|
|
984
987
|
);
|
|
985
988
|
}
|
|
986
989
|
|
|
990
|
+
interface DatePickerReadOnlyFieldProps {
|
|
991
|
+
value: string;
|
|
992
|
+
placeholder?: string;
|
|
993
|
+
id?: string;
|
|
994
|
+
glyph?: "calendar" | "clock";
|
|
995
|
+
"aria-label"?: string;
|
|
996
|
+
"aria-required"?: boolean;
|
|
997
|
+
"aria-invalid"?: boolean;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
* FZ-7853: a read-only picker keeps the trigger's shell (glyph in the
|
|
1002
|
+
* secondary ink, same insets, full row) and leaves out only the clear action.
|
|
1003
|
+
*/
|
|
1004
|
+
export function DatePickerReadOnlyField({
|
|
1005
|
+
value,
|
|
1006
|
+
placeholder,
|
|
1007
|
+
id,
|
|
1008
|
+
glyph = "calendar",
|
|
1009
|
+
"aria-label": ariaLabel,
|
|
1010
|
+
"aria-required": ariaRequired,
|
|
1011
|
+
"aria-invalid": ariaInvalid,
|
|
1012
|
+
}: DatePickerReadOnlyFieldProps) {
|
|
1013
|
+
const Glyph = glyph === "clock" ? ClockIcon : CalendarBlankIcon;
|
|
1014
|
+
return (
|
|
1015
|
+
<View flexDirection="row" alignItems="center" width="100%">
|
|
1016
|
+
<InputParts.Box
|
|
1017
|
+
flex={1}
|
|
1018
|
+
minWidth={0}
|
|
1019
|
+
elevation={undefined}
|
|
1020
|
+
data-testid="date-picker-readonly"
|
|
1021
|
+
>
|
|
1022
|
+
<InputParts.Section data-testid="date-picker-glyph">
|
|
1023
|
+
<InputParts.Icon adornment="leading" color={formCommonColors.muted}>
|
|
1024
|
+
<Glyph />
|
|
1025
|
+
</InputParts.Icon>
|
|
1026
|
+
</InputParts.Section>
|
|
1027
|
+
<InputParts.Section>
|
|
1028
|
+
<InputParts.Area
|
|
1029
|
+
readOnly
|
|
1030
|
+
id={id}
|
|
1031
|
+
value={value}
|
|
1032
|
+
placeholder={placeholder}
|
|
1033
|
+
color={formCommonColors.text}
|
|
1034
|
+
placeholderTextColor={formCommonColors.muted}
|
|
1035
|
+
aria-label={ariaLabel}
|
|
1036
|
+
aria-readonly="true"
|
|
1037
|
+
aria-required={ariaRequired}
|
|
1038
|
+
aria-invalid={ariaInvalid}
|
|
1039
|
+
/>
|
|
1040
|
+
</InputParts.Section>
|
|
1041
|
+
</InputParts.Box>
|
|
1042
|
+
</View>
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
987
1046
|
// ---------------------------------------------------------------------------
|
|
988
1047
|
// DatePickerPopoverShell — FloatingPanel (sheet below OVERLAY_BREAKPOINT)
|
|
989
1048
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { hairline, IconSlot, keyboardFocusRingProps } from "@multiplatform.one/theme";
|
|
2
|
+
import { CheckIcon } from "@phosphor-icons/react";
|
|
3
|
+
import { useRef, type KeyboardEvent, type ReactNode } from "react";
|
|
4
|
+
import { Pressable } from "react-native";
|
|
5
|
+
import { isWeb, Text, View, XStack, YStack } from "tamagui";
|
|
6
|
+
import { useFieldDescribedBy } from "../../fieldLayout";
|
|
7
|
+
import { formCommonColors, formInputColors } from "../../shared/colorRamps";
|
|
8
|
+
import { choiceItemA11y } from "../../shared/nativeA11y";
|
|
9
|
+
|
|
10
|
+
export interface InlineChoice {
|
|
11
|
+
value: string;
|
|
12
|
+
label: string;
|
|
13
|
+
icon?: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface InlineChoicesProps {
|
|
17
|
+
options: InlineChoice[];
|
|
18
|
+
value?: string;
|
|
19
|
+
onValueChange: (value: string) => void;
|
|
20
|
+
onBlur?: (...args: any[]) => void;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
/** The field id: FieldLayout's label points at it. */
|
|
23
|
+
id?: string;
|
|
24
|
+
labelledBy?: string;
|
|
25
|
+
"aria-label"?: string;
|
|
26
|
+
knobProps: any;
|
|
27
|
+
/** The LC-05 selected mark, already resolved to a paint. */
|
|
28
|
+
markColor: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const nextKeys = new Set(["ArrowDown", "ArrowRight"]);
|
|
32
|
+
const previousKeys = new Set(["ArrowUp", "ArrowLeft"]);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A short Select inside a sheet laid out in place (LC-262 DIRECT-ACTIONS): one
|
|
36
|
+
* grouped list of radio rows, each with its glyph and label, the chosen row
|
|
37
|
+
* marked by the accent check alone. No trigger and no popover. On the web it
|
|
38
|
+
* is one tab stop, arrows move and choose, Space and Enter choose (APG radio
|
|
39
|
+
* group).
|
|
40
|
+
*/
|
|
41
|
+
export function InlineChoices({
|
|
42
|
+
options,
|
|
43
|
+
value,
|
|
44
|
+
onValueChange,
|
|
45
|
+
onBlur,
|
|
46
|
+
disabled,
|
|
47
|
+
id,
|
|
48
|
+
labelledBy,
|
|
49
|
+
"aria-label": ariaLabel,
|
|
50
|
+
knobProps,
|
|
51
|
+
markColor,
|
|
52
|
+
}: InlineChoicesProps) {
|
|
53
|
+
const describedBy = useFieldDescribedBy();
|
|
54
|
+
const rowsRef = useRef<Array<HTMLElement | null>>([]);
|
|
55
|
+
const textColor = knobProps.textAccentColor;
|
|
56
|
+
const selectedIndex = options.findIndex((option) => option.value === value);
|
|
57
|
+
const tabStop = selectedIndex >= 0 ? selectedIndex : 0;
|
|
58
|
+
|
|
59
|
+
const choose = (index: number) => {
|
|
60
|
+
const option = options[index];
|
|
61
|
+
if (disabled || !option || option.value === value) return;
|
|
62
|
+
onValueChange(option.value);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const onKeyDown = (index: number) => (event: KeyboardEvent) => {
|
|
66
|
+
const step = nextKeys.has(event.key) ? 1 : previousKeys.has(event.key) ? -1 : 0;
|
|
67
|
+
if (step) {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
const next = (index + step + options.length) % options.length;
|
|
70
|
+
rowsRef.current[next]?.focus();
|
|
71
|
+
choose(next);
|
|
72
|
+
} else if (event.key === " " || event.key === "Enter") {
|
|
73
|
+
event.preventDefault();
|
|
74
|
+
choose(index);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const rowContent = (option: InlineChoice, index: number, selected: boolean) => (
|
|
79
|
+
<XStack
|
|
80
|
+
alignItems="center"
|
|
81
|
+
height={knobProps.menuRow.minHeight}
|
|
82
|
+
paddingHorizontal={knobProps.panelPadding.padding}
|
|
83
|
+
{...knobProps.gap}
|
|
84
|
+
{...(index === 0 ? undefined : hairline.top)}
|
|
85
|
+
borderColor={formCommonColors.divider}
|
|
86
|
+
pointerEvents="none"
|
|
87
|
+
>
|
|
88
|
+
<View flexShrink={0} alignItems="center" justifyContent="center" minWidth={18}>
|
|
89
|
+
<IconSlot icon={option.icon} color={textColor} />
|
|
90
|
+
</View>
|
|
91
|
+
<Text
|
|
92
|
+
fontFamily={knobProps.body.fontFamily}
|
|
93
|
+
fontWeight={knobProps.body.fontWeight}
|
|
94
|
+
color={textColor}
|
|
95
|
+
numberOfLines={1}
|
|
96
|
+
flex={1}
|
|
97
|
+
>
|
|
98
|
+
{option.label}
|
|
99
|
+
</Text>
|
|
100
|
+
{selected ? <CheckIcon size={14} weight="bold" color={markColor} /> : null}
|
|
101
|
+
</XStack>
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<YStack
|
|
106
|
+
id={id}
|
|
107
|
+
role="radiogroup"
|
|
108
|
+
aria-labelledby={labelledBy}
|
|
109
|
+
aria-label={labelledBy ? undefined : ariaLabel}
|
|
110
|
+
aria-describedby={describedBy}
|
|
111
|
+
aria-disabled={disabled || undefined}
|
|
112
|
+
data-testid="select-inline-choices"
|
|
113
|
+
{...knobProps.borderRadius}
|
|
114
|
+
borderColor={formInputColors.border.base}
|
|
115
|
+
backgroundColor={formInputColors.background.base}
|
|
116
|
+
overflow="hidden"
|
|
117
|
+
width="100%"
|
|
118
|
+
>
|
|
119
|
+
{options.map((option, index) => {
|
|
120
|
+
const selected = option.value === value;
|
|
121
|
+
if (isWeb) {
|
|
122
|
+
return (
|
|
123
|
+
<View
|
|
124
|
+
key={option.value}
|
|
125
|
+
ref={(node: any) => {
|
|
126
|
+
rowsRef.current[index] = node;
|
|
127
|
+
}}
|
|
128
|
+
role="radio"
|
|
129
|
+
aria-checked={selected}
|
|
130
|
+
aria-disabled={disabled || undefined}
|
|
131
|
+
tabIndex={!disabled && index === tabStop ? 0 : -1}
|
|
132
|
+
cursor={disabled ? "default" : "pointer"}
|
|
133
|
+
onPress={() => choose(index)}
|
|
134
|
+
onKeyDown={onKeyDown(index)}
|
|
135
|
+
onBlur={onBlur}
|
|
136
|
+
outlineWidth={0}
|
|
137
|
+
focusVisibleStyle={keyboardFocusRingProps}
|
|
138
|
+
>
|
|
139
|
+
{rowContent(option, index, selected)}
|
|
140
|
+
</View>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
return (
|
|
144
|
+
<Pressable
|
|
145
|
+
key={option.value}
|
|
146
|
+
{...choiceItemA11y({ name: option.label, selected, disabled: !!disabled })}
|
|
147
|
+
disabled={disabled}
|
|
148
|
+
onPress={() => choose(index)}
|
|
149
|
+
>
|
|
150
|
+
{rowContent(option, index, selected)}
|
|
151
|
+
</Pressable>
|
|
152
|
+
);
|
|
153
|
+
})}
|
|
154
|
+
</YStack>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useTouchSurface } from "@multiplatform.one/theme";
|
|
1
|
+
import { IconSlot, useTouchSurface } from "@multiplatform.one/theme";
|
|
2
2
|
import type { KeyboardEvent, MouseEvent, ReactNode, RefObject, UIEvent } from "react";
|
|
3
3
|
import {
|
|
4
4
|
useCallback,
|
|
@@ -71,6 +71,8 @@ import { useIsInTableCell } from "../../shared/tableCellContext";
|
|
|
71
71
|
import { t } from "../../shared/t";
|
|
72
72
|
import { orderOptionsBySelected, type SelectedOrder } from "../selectedOrder";
|
|
73
73
|
import { shouldUseIosNativeSelect } from "../nativeSelect";
|
|
74
|
+
import { InlineChoices } from "./InlineChoices";
|
|
75
|
+
import { shouldInlineChoices } from "./shouldInlineChoices";
|
|
74
76
|
|
|
75
77
|
/** Undefined on web (RNW ships no ActionSheetIOS); real on iOS. */
|
|
76
78
|
const actionSheetIOS = (ReactNative as { ActionSheetIOS?: typeof ReactNative.ActionSheetIOS })
|
|
@@ -83,6 +85,12 @@ export interface SelectOption {
|
|
|
83
85
|
label: string;
|
|
84
86
|
/** Optional group label for grouping options */
|
|
85
87
|
group?: string;
|
|
88
|
+
/**
|
|
89
|
+
* Leading glyph on the option's row (LC-112: every menu row carries one when
|
|
90
|
+
* the options have them). Inside a sheet, five or fewer options that all
|
|
91
|
+
* carry one render inline as an icon choice list (LC-262).
|
|
92
|
+
*/
|
|
93
|
+
icon?: ReactNode;
|
|
86
94
|
}
|
|
87
95
|
|
|
88
96
|
export interface SelectProps {
|
|
@@ -160,8 +168,9 @@ const SelectRow = styled(View, {
|
|
|
160
168
|
// Do not put outlineWidth here — Tamagui maps these onto mouse :focus and
|
|
161
169
|
// they beat instance ring props. Rows paint the ring via selectItemRingProps.
|
|
162
170
|
variants: {
|
|
171
|
+
// LC-262: the accent check alone marks the chosen row, never a grey fill.
|
|
163
172
|
selected: {
|
|
164
|
-
true: { backgroundColor:
|
|
173
|
+
true: { backgroundColor: "transparent", borderRadius: 0 },
|
|
165
174
|
false: { backgroundColor: "transparent" },
|
|
166
175
|
},
|
|
167
176
|
active: {
|
|
@@ -174,6 +183,15 @@ const SelectRow = styled(View, {
|
|
|
174
183
|
} as const,
|
|
175
184
|
});
|
|
176
185
|
|
|
186
|
+
/** An option's leading glyph, in the row's ink. */
|
|
187
|
+
function OptionGlyph({ icon, color }: { icon: ReactNode; color?: string }) {
|
|
188
|
+
return (
|
|
189
|
+
<View flexShrink={0} alignItems="center" justifyContent="center" minWidth={18}>
|
|
190
|
+
<IconSlot icon={icon} color={color} />
|
|
191
|
+
</View>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
177
195
|
function GroupHeader(props: HouseTextProps) {
|
|
178
196
|
return (
|
|
179
197
|
<HouseText
|
|
@@ -442,7 +460,9 @@ const SelectItem = memo(function SelectItem({
|
|
|
442
460
|
role="option"
|
|
443
461
|
tabIndex={-1}
|
|
444
462
|
selected={isSelected}
|
|
445
|
-
|
|
463
|
+
// The open-time cursor sits on the chosen row; it only paints while the
|
|
464
|
+
// keyboard drives it, so the chosen row never opens grey (LC-262).
|
|
465
|
+
active={Boolean(isActive) && ctx.kbNav}
|
|
446
466
|
aria-selected={isSelected}
|
|
447
467
|
data-state={isSelected ? "active" : "inactive"}
|
|
448
468
|
height={ctx.knobProps.menuRow.minHeight}
|
|
@@ -456,22 +476,15 @@ const SelectItem = memo(function SelectItem({
|
|
|
456
476
|
{...(suppressHover
|
|
457
477
|
? {
|
|
458
478
|
hoverStyle: {
|
|
459
|
-
backgroundColor: isActive
|
|
460
|
-
? formInputColors.background.focus
|
|
461
|
-
: isSelected
|
|
462
|
-
? formInputColors.background.base
|
|
463
|
-
: "transparent",
|
|
479
|
+
backgroundColor: isActive ? formInputColors.background.focus : "transparent",
|
|
464
480
|
},
|
|
465
481
|
}
|
|
466
482
|
: {
|
|
467
|
-
hoverStyle: {
|
|
468
|
-
backgroundColor: isSelected
|
|
469
|
-
? formInputColors.background.base
|
|
470
|
-
: formInputColors.background.focus,
|
|
471
|
-
},
|
|
483
|
+
hoverStyle: { backgroundColor: formInputColors.background.focus },
|
|
472
484
|
})}
|
|
473
485
|
{...transitionProps(ctx.knobProps.motion.control)}
|
|
474
486
|
>
|
|
487
|
+
{option.icon ? <OptionGlyph icon={option.icon} color={ctx.textColor} /> : null}
|
|
475
488
|
<Text
|
|
476
489
|
fontFamily={ctx.knobProps.body.fontFamily}
|
|
477
490
|
fontWeight={ctx.knobProps.body.fontWeight}
|
|
@@ -1435,30 +1448,16 @@ function NativeSelectCore({
|
|
|
1435
1448
|
{...(isWeb ? selectItemRingProps(active, kbNav) : undefined)}
|
|
1436
1449
|
hoverStyle={
|
|
1437
1450
|
activeIndex != null
|
|
1438
|
-
? {
|
|
1439
|
-
|
|
1440
|
-
? formInputColors.background.focus
|
|
1441
|
-
: selected
|
|
1442
|
-
? formInputColors.background.base
|
|
1443
|
-
: "transparent",
|
|
1444
|
-
}
|
|
1445
|
-
: {
|
|
1446
|
-
backgroundColor: selected
|
|
1447
|
-
? formInputColors.background.base
|
|
1448
|
-
: formInputColors.background.focus,
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
backgroundColor={
|
|
1452
|
-
active
|
|
1453
|
-
? formInputColors.background.focus
|
|
1454
|
-
: selected
|
|
1455
|
-
? formInputColors.background.base
|
|
1456
|
-
: "transparent"
|
|
1451
|
+
? { backgroundColor: active ? formInputColors.background.focus : "transparent" }
|
|
1452
|
+
: { backgroundColor: formInputColors.background.focus }
|
|
1457
1453
|
}
|
|
1454
|
+
// LC-262: the accent check alone marks the chosen row.
|
|
1455
|
+
backgroundColor={active && kbNav ? formInputColors.background.focus : "transparent"}
|
|
1458
1456
|
pressStyle={{ backgroundColor: formInputColors.background.focus }}
|
|
1459
1457
|
// Native: parent RNPressable owns the hit; disable child stealing.
|
|
1460
1458
|
pointerEvents={isWeb ? undefined : "none"}
|
|
1461
1459
|
>
|
|
1460
|
+
{option.icon ? <OptionGlyph icon={option.icon} color={textColor} /> : null}
|
|
1462
1461
|
<Text
|
|
1463
1462
|
fontFamily={knobProps.body.fontFamily}
|
|
1464
1463
|
fontWeight={knobProps.body.fontWeight}
|
|
@@ -1747,13 +1746,6 @@ export function Select({
|
|
|
1747
1746
|
const isControlled = value !== undefined;
|
|
1748
1747
|
const hasLabel = Boolean(label);
|
|
1749
1748
|
|
|
1750
|
-
// DG-CTRL-01 / DG-CTRL-05 — short single-select lists prefer RadioGroup.
|
|
1751
|
-
warnSelectTooFewOptions({
|
|
1752
|
-
count: options?.length ?? 0,
|
|
1753
|
-
multiple,
|
|
1754
|
-
component: "Select",
|
|
1755
|
-
id,
|
|
1756
|
-
});
|
|
1757
1749
|
const viewportGtSm = useViewportGtSm();
|
|
1758
1750
|
const insideOverlay = useInsideOverlay();
|
|
1759
1751
|
// Sheet core on native and below `$sm` (viewport < 640, OVERLAY_BREAKPOINT).
|
|
@@ -1767,6 +1759,19 @@ export function Select({
|
|
|
1767
1759
|
hydrated &&
|
|
1768
1760
|
resolveCompactFace(pickerCompactFace(options?.length ?? 0), insideOverlay) === "menu";
|
|
1769
1761
|
const shouldUseSheet = !isWeb || (!effectiveGtSm && !compactMenu);
|
|
1762
|
+
const inTableCell = useIsInTableCell();
|
|
1763
|
+
const inline = !inTableCell && shouldInlineChoices({ insideOverlay, multiple, options });
|
|
1764
|
+
// DG-CTRL-01 / DG-CTRL-05 — short single-select lists prefer RadioGroup,
|
|
1765
|
+
// which the inline face already is.
|
|
1766
|
+
if (!inline) {
|
|
1767
|
+
warnSelectTooFewOptions({
|
|
1768
|
+
count: options?.length ?? 0,
|
|
1769
|
+
multiple,
|
|
1770
|
+
component: "Select",
|
|
1771
|
+
id,
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
const markColor = useSelectedMarkColor();
|
|
1770
1775
|
|
|
1771
1776
|
// Render skeleton placeholder
|
|
1772
1777
|
if (skeleton) {
|
|
@@ -1831,6 +1836,26 @@ export function Select({
|
|
|
1831
1836
|
handleBlur?: (...args: any[]) => void,
|
|
1832
1837
|
) => {
|
|
1833
1838
|
if (readOnly) return renderReadOnly(selectValue);
|
|
1839
|
+
if (inline) {
|
|
1840
|
+
return (
|
|
1841
|
+
<InlineChoices
|
|
1842
|
+
options={options}
|
|
1843
|
+
value={typeof selectValue === "string" ? selectValue : undefined}
|
|
1844
|
+
onValueChange={(val) => {
|
|
1845
|
+
if (disabled) return;
|
|
1846
|
+
handleValueChange?.(val);
|
|
1847
|
+
emitValueChange(val);
|
|
1848
|
+
}}
|
|
1849
|
+
onBlur={handleBlur}
|
|
1850
|
+
disabled={disabled}
|
|
1851
|
+
id={id}
|
|
1852
|
+
labelledBy={hasLabel ? `${id}-label` : undefined}
|
|
1853
|
+
aria-label={ariaLabel}
|
|
1854
|
+
knobProps={knobProps}
|
|
1855
|
+
markColor={markColor}
|
|
1856
|
+
/>
|
|
1857
|
+
);
|
|
1858
|
+
}
|
|
1834
1859
|
const SelectCore = shouldUseSheet ? NativeSelectCore : FloatingSelectCore;
|
|
1835
1860
|
return (
|
|
1836
1861
|
<SelectCore
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { INLINE_CHOICES_MAX, shouldInlineChoices } from "./shouldInlineChoices";
|
|
3
|
+
|
|
4
|
+
const glyph = "•";
|
|
5
|
+
const withIcons = (n: number) => Array.from({ length: n }, () => ({ icon: glyph }));
|
|
6
|
+
|
|
7
|
+
describe("shouldInlineChoices (LC-262, FZ-7830)", () => {
|
|
8
|
+
it("lays out five or fewer glyphed options inline inside an overlay", () => {
|
|
9
|
+
expect(INLINE_CHOICES_MAX).toBe(5);
|
|
10
|
+
expect(shouldInlineChoices({ insideOverlay: true, options: withIcons(2) })).toBe(true);
|
|
11
|
+
expect(shouldInlineChoices({ insideOverlay: true, options: withIcons(5) })).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("keeps the trigger past five, without a glyph on every option, at page level, or for multiple", () => {
|
|
15
|
+
expect(shouldInlineChoices({ insideOverlay: true, options: withIcons(6) })).toBe(false);
|
|
16
|
+
expect(shouldInlineChoices({ insideOverlay: true, options: [...withIcons(3), {}] })).toBe(
|
|
17
|
+
false,
|
|
18
|
+
);
|
|
19
|
+
expect(shouldInlineChoices({ insideOverlay: false, options: withIcons(3) })).toBe(false);
|
|
20
|
+
expect(
|
|
21
|
+
shouldInlineChoices({ insideOverlay: true, multiple: true, options: withIcons(3) }),
|
|
22
|
+
).toBe(false);
|
|
23
|
+
expect(shouldInlineChoices({ insideOverlay: true, options: [] })).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
});
|