@multiplatform.one/forms 7.7.0 → 7.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/AdaptivePopup/index.native.js +1 -1
- package/dist/cjs/AdaptivePopup/index.native.js.map +1 -1
- package/dist/cjs/Button/index.cjs +1 -1
- package/dist/cjs/Button/index.native.js +7 -2
- package/dist/cjs/Button/index.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 +3 -1
- package/dist/cjs/DescriptionList/index.native.js +7 -3
- 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/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/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/index.native.js.map +1 -1
- package/dist/cjs/Label/index.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/actionLabel.native.js.map +1 -1
- package/dist/cjs/fieldDefaults.native.js.map +1 -1
- package/dist/cjs/fieldLayout.native.js +1 -1
- 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/index.native.js +3 -3
- 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/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.cjs +0 -1
- package/dist/cjs/fields/FileUpload/index.native.js +7 -3
- 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/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/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.native.js +2 -2
- 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/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/index.cjs +14 -1
- package/dist/cjs/fields/Select/index.native.js +18 -5
- package/dist/cjs/fields/Select/index.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/index.native.js +2 -2
- 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.map +1 -1
- package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.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.native.js +43 -43
- package/dist/cjs/index.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/colorRamps.native.js.map +1 -1
- package/dist/cjs/shared/devWarn.native.js.map +1 -1
- 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/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/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/AdaptivePopup/index.native.js.map +1 -1
- package/dist/esm/Button/index.mjs +1 -1
- package/dist/esm/Button/index.mjs.map +1 -1
- package/dist/esm/Button/index.native.js +6 -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.mjs +3 -1
- package/dist/esm/DescriptionList/index.mjs.map +1 -1
- package/dist/esm/DescriptionList/index.native.js +5 -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/useFloatingPanel.native.js.map +1 -1
- package/dist/esm/Form/index.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/index.native.js.map +1 -1
- package/dist/esm/Label/index.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/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.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/MonthPicker.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
- 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/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.mjs +0 -1
- package/dist/esm/fields/FileUpload/index.mjs.map +1 -1
- package/dist/esm/fields/FileUpload/index.native.js +5 -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/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/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/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/index.mjs +4 -2
- package/dist/esm/fields/Select/index.mjs.map +1 -1
- package/dist/esm/fields/Select/index.native.js +4 -2
- package/dist/esm/fields/Select/index.native.js.map +1 -1
- 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/index.native.js.map +1 -1
- package/dist/esm/fields/TextArea/index.native.js.map +1 -1
- package/dist/esm/fields/TimePicker/catalog.native.js.map +1 -1
- 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/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/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/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/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/Button/index.mjs +1 -1
- package/dist/jsx/Button/index.mjs.map +1 -1
- package/dist/jsx/Button/index.native.js +6 -1
- package/dist/jsx/Button/index.native.js.map +1 -1
- package/dist/jsx/DescriptionList/index.mjs +3 -1
- package/dist/jsx/DescriptionList/index.mjs.map +1 -1
- package/dist/jsx/DescriptionList/index.native.js +5 -1
- package/dist/jsx/DescriptionList/index.native.js.map +1 -1
- package/dist/jsx/fields/FileUpload/index.mjs +0 -1
- package/dist/jsx/fields/FileUpload/index.mjs.map +1 -1
- package/dist/jsx/fields/FileUpload/index.native.js +5 -1
- package/dist/jsx/fields/FileUpload/index.native.js.map +1 -1
- package/dist/jsx/fields/Select/index.mjs +4 -2
- package/dist/jsx/fields/Select/index.mjs.map +1 -1
- package/dist/jsx/fields/Select/index.native.js +14 -1
- package/dist/jsx/fields/Select/index.native.js.map +1 -1
- package/package.json +7 -7
- package/src/Button/index.tsx +6 -1
- package/src/ContextualSaveBar/UnsavedChangesDialog.stories.tsx +118 -0
- package/src/ContextualSaveBar/unsavedChangesDialogStories.spec.tsx +79 -0
- package/src/DescriptionList/index.tsx +3 -1
- package/src/ErrorSummary.stories.tsx +197 -0
- package/src/InputParts/InputParts.stories.tsx +230 -0
- package/src/InputParts/inputPartsStories.spec.tsx +122 -0
- package/src/Skeleton/Skeleton.stories.tsx +148 -0
- package/src/Skeleton/skeletonStories.spec.tsx +107 -0
- package/src/Wheel/Wheel.stories.tsx +232 -0
- package/src/Wheel/wheelStories.spec.tsx +143 -0
- package/src/errorSummaryStories.spec.tsx +114 -0
- package/src/fields/FileUpload/index.tsx +5 -1
- package/src/fields/Input/Input.spec.tsx +3 -1
- package/src/fields/Select/index.tsx +12 -2
- package/types/Button/index.d.ts.map +1 -1
- package/types/DescriptionList/index.d.ts.map +1 -1
- package/types/fields/FileUpload/index.d.ts.map +1 -1
- package/types/fields/Select/index.d.ts.map +1 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mounts every Forms/InputParts story and asserts the anatomy each one exists
|
|
3
|
+
* to show. The theme hooks stay REAL: Box height, icon size and the caption
|
|
4
|
+
* type ramp all come out of useResolvedKnobs, so a stub would measure a
|
|
5
|
+
* geometry the gallery never paints.
|
|
6
|
+
*/
|
|
7
|
+
import { cleanup } from "@testing-library/react";
|
|
8
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
9
|
+
import type { ReactElement } from "react";
|
|
10
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
11
|
+
|
|
12
|
+
import * as storiesModule from "./InputParts.stories";
|
|
13
|
+
|
|
14
|
+
afterEach(cleanup);
|
|
15
|
+
|
|
16
|
+
const meta = storiesModule.default;
|
|
17
|
+
const {
|
|
18
|
+
Anatomy,
|
|
19
|
+
FocusRingOnTheBox,
|
|
20
|
+
Adornments,
|
|
21
|
+
TrailingButton,
|
|
22
|
+
MetaReservesOneLine,
|
|
23
|
+
Info,
|
|
24
|
+
MultiLine,
|
|
25
|
+
DisabledBox,
|
|
26
|
+
} = storiesModule;
|
|
27
|
+
|
|
28
|
+
type StoryLike = { render?: (args: Record<string, unknown>) => ReactElement };
|
|
29
|
+
|
|
30
|
+
function mount(story: StoryLike) {
|
|
31
|
+
if (!story.render) throw new Error("story has no render");
|
|
32
|
+
return renderWithProviders(story.render({ ...(meta.args as Record<string, unknown>) }));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const stories = {
|
|
36
|
+
Anatomy,
|
|
37
|
+
FocusRingOnTheBox,
|
|
38
|
+
Adornments,
|
|
39
|
+
TrailingButton,
|
|
40
|
+
MetaReservesOneLine,
|
|
41
|
+
Info,
|
|
42
|
+
MultiLine,
|
|
43
|
+
DisabledBox,
|
|
44
|
+
} as const;
|
|
45
|
+
|
|
46
|
+
describe("Forms/InputParts stories", () => {
|
|
47
|
+
it("files under its own Forms title, separate from the Input field's", () => {
|
|
48
|
+
expect(meta.title).toBe("Forms/InputParts");
|
|
49
|
+
expect(meta.title).not.toBe("Forms/Input");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it.each(Object.entries(stories))("%s mounts something interactive", (_name, story) => {
|
|
53
|
+
const { container } = mount(story as StoryLike);
|
|
54
|
+
expect(container.querySelector("input, textarea, button")).toBeTruthy();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("Anatomy wires the label to the control it names", () => {
|
|
58
|
+
const { container } = mount(Anatomy as StoryLike);
|
|
59
|
+
const input = container.querySelector("#anatomy-email");
|
|
60
|
+
expect(input).toBeTruthy();
|
|
61
|
+
expect(container.querySelector("label[for='anatomy-email']")).toBeTruthy();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("Anatomy renders the helper caption under the box", () => {
|
|
65
|
+
const { getByText } = mount(Anatomy as StoryLike);
|
|
66
|
+
expect(getByText("We only use this to send receipts.")).toBeTruthy();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("Adornments builds three boxes: leading, trailing, and both", () => {
|
|
70
|
+
const { container } = mount(Adornments as StoryLike);
|
|
71
|
+
const placeholders = Array.from(container.querySelectorAll("input")).map((i) =>
|
|
72
|
+
i.getAttribute("placeholder"),
|
|
73
|
+
);
|
|
74
|
+
expect(placeholders).toEqual(["Leading icon", "Trailing icon", "Both ends"]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("TrailingButton exposes a labelled reveal toggle beside the field", () => {
|
|
78
|
+
const { container } = mount(TrailingButton as StoryLike);
|
|
79
|
+
const button = container.querySelector("[aria-label='Show password']");
|
|
80
|
+
expect(button).toBeTruthy();
|
|
81
|
+
expect(container.querySelector("#parts-password")).toBeTruthy();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* STABLE GROUND: Meta owns ONE caption slot. The error REPLACES the helper,
|
|
86
|
+
* so the errored field must not show both lines.
|
|
87
|
+
*/
|
|
88
|
+
it("MetaReservesOneLine replaces the helper with the error rather than stacking them", () => {
|
|
89
|
+
const { container, getByText } = mount(MetaReservesOneLine as StoryLike);
|
|
90
|
+
expect(getByText("Enter at least two characters.")).toBeTruthy();
|
|
91
|
+
// The valid field keeps its helper; the errored one does not add a second line.
|
|
92
|
+
const helpers = Array.from(container.querySelectorAll("*")).filter(
|
|
93
|
+
(el) => el.children.length === 0 && el.textContent === "Two to thirty characters.",
|
|
94
|
+
);
|
|
95
|
+
expect(helpers.length).toBe(1);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("MetaReservesOneLine announces the error to assistive tech", () => {
|
|
99
|
+
const { container } = mount(MetaReservesOneLine as StoryLike);
|
|
100
|
+
const alert = container.querySelector("[role='alert']");
|
|
101
|
+
expect(alert?.textContent).toBe("Enter at least two characters.");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("Info renders caption prose without a Meta wrapper", () => {
|
|
105
|
+
const { getByText, container } = mount(Info as StoryLike);
|
|
106
|
+
expect(getByText("Shown next to everything you publish.")).toBeTruthy();
|
|
107
|
+
expect(container.querySelector("[role='alert']")).toBeNull();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("MultiLine mounts a textarea, not a single-line input", () => {
|
|
111
|
+
const { container } = mount(MultiLine as StoryLike);
|
|
112
|
+
expect(container.querySelector("textarea#parts-notes")).toBeTruthy();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("DisabledBox disables only the second field, leaving the first usable", () => {
|
|
116
|
+
const { container } = mount(DisabledBox as StoryLike);
|
|
117
|
+
const inputs = Array.from(container.querySelectorAll("input"));
|
|
118
|
+
expect(inputs.length).toBe(2);
|
|
119
|
+
expect(inputs[0].disabled).toBe(false);
|
|
120
|
+
expect(inputs[1].disabled).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
|
|
2
|
+
import { Preset } from "@multiplatform.one/theme";
|
|
3
|
+
import { Paragraph, XStack, YStack } from "tamagui";
|
|
4
|
+
import { Skeleton, SkeletonCircle, SkeletonText } from "./index";
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Skeleton> = {
|
|
7
|
+
title: "Forms/Skeleton",
|
|
8
|
+
component: Skeleton,
|
|
9
|
+
parameters: {
|
|
10
|
+
status: { type: "beta" },
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component:
|
|
14
|
+
"The placeholder every field paints while its value is loading — what `skeleton` on Input, Select, CheckboxGroup and the rest resolves to. A flat blob: it takes the radius knob but never draws a border, and its pulse rides the animation knob, so `animation: none` stops it dead rather than dimming it.",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
argTypes: {
|
|
19
|
+
variant: { control: "select", options: ["text", "circular", "rectangular", "rounded"] },
|
|
20
|
+
animate: { control: "boolean" },
|
|
21
|
+
width: { control: "text" },
|
|
22
|
+
height: { control: "text" },
|
|
23
|
+
},
|
|
24
|
+
args: {
|
|
25
|
+
variant: "text",
|
|
26
|
+
animate: true,
|
|
27
|
+
width: 240,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default meta;
|
|
32
|
+
type Story = StoryObj<typeof Skeleton>;
|
|
33
|
+
|
|
34
|
+
export const Basic: Story = {
|
|
35
|
+
render: (args) => <Skeleton {...args} />,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** The four variants. `text` and `circular` carry their own height. */
|
|
39
|
+
export const Variants: Story = {
|
|
40
|
+
render: () => (
|
|
41
|
+
<YStack gap="$4">
|
|
42
|
+
{(["text", "rounded", "rectangular"] as const).map((variant) => (
|
|
43
|
+
<YStack key={variant} gap="$2">
|
|
44
|
+
<Paragraph size="$2">{variant}</Paragraph>
|
|
45
|
+
<Skeleton variant={variant} width={240} height={variant === "text" ? undefined : 48} />
|
|
46
|
+
</YStack>
|
|
47
|
+
))}
|
|
48
|
+
<YStack gap="$2">
|
|
49
|
+
<Paragraph size="$2">circular</Paragraph>
|
|
50
|
+
<Skeleton variant="circular" width={48} height={48} />
|
|
51
|
+
</YStack>
|
|
52
|
+
</YStack>
|
|
53
|
+
),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* `SkeletonText` stacks lines and shortens the last one, which is what makes a
|
|
58
|
+
* block of bones read as a paragraph rather than a table.
|
|
59
|
+
*/
|
|
60
|
+
export const TextLines: Story = {
|
|
61
|
+
render: () => (
|
|
62
|
+
<YStack gap="$6" width={320}>
|
|
63
|
+
<YStack gap="$2">
|
|
64
|
+
<Paragraph size="$2">one line</Paragraph>
|
|
65
|
+
<SkeletonText />
|
|
66
|
+
</YStack>
|
|
67
|
+
<YStack gap="$2">
|
|
68
|
+
<Paragraph size="$2">three lines, last at 70%</Paragraph>
|
|
69
|
+
<SkeletonText lines={3} />
|
|
70
|
+
</YStack>
|
|
71
|
+
<YStack gap="$2">
|
|
72
|
+
<Paragraph size="$2">five lines, last at 40%</Paragraph>
|
|
73
|
+
<SkeletonText lines={5} lastLineWidth="40%" />
|
|
74
|
+
</YStack>
|
|
75
|
+
</YStack>
|
|
76
|
+
),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** `SkeletonCircle` sizes itself one height step above the control ramp. */
|
|
80
|
+
export const Circles: Story = {
|
|
81
|
+
render: () => (
|
|
82
|
+
<XStack gap="$4" alignItems="center">
|
|
83
|
+
<SkeletonCircle />
|
|
84
|
+
<SkeletonCircle size={32} />
|
|
85
|
+
<SkeletonCircle size={64} />
|
|
86
|
+
</XStack>
|
|
87
|
+
),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/** What a loading row actually looks like: avatar, name, two lines of body. */
|
|
91
|
+
export const LoadingRow: Story = {
|
|
92
|
+
render: () => (
|
|
93
|
+
<XStack gap="$3" width={360} alignItems="flex-start">
|
|
94
|
+
<SkeletonCircle size={40} />
|
|
95
|
+
<YStack flex={1} gap="$2">
|
|
96
|
+
<Skeleton variant="text" width="45%" />
|
|
97
|
+
<SkeletonText lines={2} />
|
|
98
|
+
</YStack>
|
|
99
|
+
</XStack>
|
|
100
|
+
),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* MOTION-RIDES-KNOB (LC-68 / LC-20): the pulse is on when the animation knob
|
|
105
|
+
* is, and gone when it is not. `animate={false}` is the same off switch at the
|
|
106
|
+
* call site — neither leaves a half-speed shimmer behind.
|
|
107
|
+
*/
|
|
108
|
+
export const MotionOff: Story = {
|
|
109
|
+
render: () => (
|
|
110
|
+
<XStack gap="$6" alignItems="flex-start">
|
|
111
|
+
<YStack gap="$2" width={180}>
|
|
112
|
+
<Paragraph size="$2">animation knob on</Paragraph>
|
|
113
|
+
<SkeletonText lines={3} />
|
|
114
|
+
</YStack>
|
|
115
|
+
<YStack gap="$2" width={180}>
|
|
116
|
+
<Paragraph size="$2">animate={"{false}"}</Paragraph>
|
|
117
|
+
<SkeletonText lines={3} animate={false} />
|
|
118
|
+
</YStack>
|
|
119
|
+
<Preset overrides={{ animation: "none" }}>
|
|
120
|
+
<YStack gap="$2" width={180}>
|
|
121
|
+
<Paragraph size="$2">animation: none</Paragraph>
|
|
122
|
+
<SkeletonText lines={3} />
|
|
123
|
+
</YStack>
|
|
124
|
+
</Preset>
|
|
125
|
+
</XStack>
|
|
126
|
+
),
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* R-CLAMP / R-PILL: a text bone squares off at `borderRadius: none`, and a
|
|
131
|
+
* circle stays round at every radius value because it declares R-PILL.
|
|
132
|
+
*/
|
|
133
|
+
export const RadiusKnob: Story = {
|
|
134
|
+
render: () => (
|
|
135
|
+
<XStack gap="$6" alignItems="flex-start">
|
|
136
|
+
{(["none", "medium", "large"] as const).map((borderRadius) => (
|
|
137
|
+
<Preset key={borderRadius} overrides={{ borderRadius }}>
|
|
138
|
+
<YStack gap="$2" width={160}>
|
|
139
|
+
<Paragraph size="$2">borderRadius: {borderRadius}</Paragraph>
|
|
140
|
+
<Skeleton variant="text" width={140} />
|
|
141
|
+
<Skeleton variant="rounded" width={140} height={40} />
|
|
142
|
+
<SkeletonCircle size={40} />
|
|
143
|
+
</YStack>
|
|
144
|
+
</Preset>
|
|
145
|
+
))}
|
|
146
|
+
</XStack>
|
|
147
|
+
),
|
|
148
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mounts every Forms/Skeleton story and asserts what it exists to show. The
|
|
3
|
+
* theme hooks stay REAL: text-bone height comes from the size recipe and the
|
|
4
|
+
* pulse gates on knobProps.transition, so a stub would measure neither.
|
|
5
|
+
*
|
|
6
|
+
* Geometry is read off the CLASS, not `style`. Tamagui compiles width, height
|
|
7
|
+
* and radius into atomic classes (`_w-240px`, `_w-7037` for 70%), and only the
|
|
8
|
+
* pulse stays an inline style — so `el.style.width` is empty on every bone.
|
|
9
|
+
*/
|
|
10
|
+
import { cleanup } from "@testing-library/react";
|
|
11
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
12
|
+
import type { ReactElement } from "react";
|
|
13
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
14
|
+
|
|
15
|
+
import * as storiesModule from "./Skeleton.stories";
|
|
16
|
+
|
|
17
|
+
afterEach(cleanup);
|
|
18
|
+
|
|
19
|
+
const meta = storiesModule.default;
|
|
20
|
+
const { Basic, Variants, TextLines, Circles, LoadingRow, MotionOff, RadiusKnob } = storiesModule;
|
|
21
|
+
|
|
22
|
+
type StoryLike = { render?: (args: Record<string, unknown>) => ReactElement };
|
|
23
|
+
|
|
24
|
+
function mount(story: StoryLike) {
|
|
25
|
+
if (!story.render) throw new Error("story has no render");
|
|
26
|
+
return renderWithProviders(story.render({ ...(meta.args as Record<string, unknown>) }));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Every bone is a SkeletonFrame, which Tamagui names `is_Skeleton`. */
|
|
30
|
+
function bones(container: HTMLElement): HTMLElement[] {
|
|
31
|
+
return Array.from(container.querySelectorAll<HTMLElement>(".is_Skeleton"));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const classesOf = (el: HTMLElement) => (el.getAttribute("class") ?? "").split(" ");
|
|
35
|
+
const widthClass = (el: HTMLElement) => classesOf(el).find((c) => c.startsWith("_w-"));
|
|
36
|
+
const heightClass = (el: HTMLElement) => classesOf(el).find((c) => c.startsWith("_h-"));
|
|
37
|
+
|
|
38
|
+
const stories = { Basic, Variants, TextLines, Circles, LoadingRow, MotionOff, RadiusKnob } as const;
|
|
39
|
+
|
|
40
|
+
describe("Forms/Skeleton stories", () => {
|
|
41
|
+
it("files under a Forms title so the gallery buckets it with the fields it fills in for", () => {
|
|
42
|
+
expect(meta.title).toBe("Forms/Skeleton");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it.each(Object.entries(stories))("%s mounts at least one bone", (_name, story) => {
|
|
46
|
+
expect(bones(mount(story as StoryLike).container).length).toBeGreaterThan(0);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("Basic paints one bone at the meta width", () => {
|
|
50
|
+
const painted = bones(mount(Basic as StoryLike).container);
|
|
51
|
+
expect(painted.length).toBe(1);
|
|
52
|
+
expect(widthClass(painted[0])).toBe("_w-240px");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("Variants shows all four, and the circular one is 1:1", () => {
|
|
56
|
+
const { container, getByText } = mount(Variants as StoryLike);
|
|
57
|
+
for (const name of ["text", "rounded", "rectangular", "circular"]) {
|
|
58
|
+
expect(getByText(name)).toBeTruthy();
|
|
59
|
+
}
|
|
60
|
+
const square = bones(container).find(
|
|
61
|
+
(el) => widthClass(el) === "_w-48px" && heightClass(el) === "_h-48px",
|
|
62
|
+
);
|
|
63
|
+
expect(square).toBeTruthy();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("TextLines shortens the last line so a block reads as prose", () => {
|
|
67
|
+
const { container, getByText } = mount(TextLines as StoryLike);
|
|
68
|
+
expect(getByText("three lines, last at 70%")).toBeTruthy();
|
|
69
|
+
const widths = bones(container).map(widthClass);
|
|
70
|
+
// 70% and 40% are the two lastLineWidth values; 100% is every other line.
|
|
71
|
+
expect(widths).toContain("_w-7037");
|
|
72
|
+
expect(widths).toContain("_w-4037");
|
|
73
|
+
expect(widths).toContain("_w-10037");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("Circles renders three, two of them at explicit sizes", () => {
|
|
77
|
+
const painted = bones(mount(Circles as StoryLike).container);
|
|
78
|
+
expect(painted.length).toBe(3);
|
|
79
|
+
const widths = painted.map(widthClass);
|
|
80
|
+
expect(widths).toContain("_w-32px");
|
|
81
|
+
expect(widths).toContain("_w-64px");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("LoadingRow composes an avatar plus a name and two body lines", () => {
|
|
85
|
+
const painted = bones(mount(LoadingRow as StoryLike).container);
|
|
86
|
+
// 1 circle + 1 name bone + 2 body bones.
|
|
87
|
+
expect(painted.length).toBe(4);
|
|
88
|
+
expect(painted.map(widthClass)).toContain("_w-40px");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/** LC-20 / LC-68: the pulse is on with the knob and gone without it. */
|
|
92
|
+
it("MotionOff pulses the animated column and not the animate={false} one", () => {
|
|
93
|
+
const { container, getByText } = mount(MotionOff as StoryLike);
|
|
94
|
+
expect(getByText("animation knob on")).toBeTruthy();
|
|
95
|
+
expect(getByText("animation: none")).toBeTruthy();
|
|
96
|
+
const animations = bones(container).map((el) => el.style.animation);
|
|
97
|
+
expect(animations.some((a) => a.includes("skeleton-pulse"))).toBe(true);
|
|
98
|
+
expect(animations.some((a) => !a)).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("RadiusKnob renders one column per radius stop", () => {
|
|
102
|
+
const { getByText } = mount(RadiusKnob as StoryLike);
|
|
103
|
+
for (const stop of ["none", "medium", "large"]) {
|
|
104
|
+
expect(getByText(`borderRadius: ${stop}`)).toBeTruthy();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { action } from "@multiplatform.one/storybook";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
|
|
3
|
+
import { Preset } from "@multiplatform.one/theme";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { Paragraph, XStack, YStack } from "tamagui";
|
|
6
|
+
import { Wheel } from "./index";
|
|
7
|
+
|
|
8
|
+
const numbers = Array.from({ length: 24 }, (_, i) => i);
|
|
9
|
+
const pad2 = (n: number) => String(n).padStart(2, "0");
|
|
10
|
+
const hours = numbers.map((h) => ({ value: h, label: pad2(h) }));
|
|
11
|
+
const minutes = Array.from({ length: 60 }, (_, m) => ({ value: m, label: pad2(m) }));
|
|
12
|
+
const periods = [
|
|
13
|
+
{ value: "AM", label: "AM" },
|
|
14
|
+
{ value: "PM", label: "PM" },
|
|
15
|
+
];
|
|
16
|
+
const currencyNames = { usd: "US Dollar", eur: "Euro", inr: "Rupee", jpy: "Yen" } as const;
|
|
17
|
+
|
|
18
|
+
const meta: Meta<typeof Wheel> = {
|
|
19
|
+
title: "Forms/Wheel",
|
|
20
|
+
component: Wheel,
|
|
21
|
+
parameters: {
|
|
22
|
+
status: { type: "beta" },
|
|
23
|
+
docs: {
|
|
24
|
+
description: {
|
|
25
|
+
component:
|
|
26
|
+
"iOS UIPickerView / Flutter CupertinoPicker cylinder. Drag, flick or arrow-key to bring an item through the centre band. TimePicker and DatePicker paint columns of these; `highlightEdges` joins adjacent columns into one selection band.",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
argTypes: {
|
|
31
|
+
visibleItems: { control: "select", options: [3, 5, 7] },
|
|
32
|
+
loop: { control: "boolean" },
|
|
33
|
+
disabled: { control: "boolean" },
|
|
34
|
+
width: { control: "number" },
|
|
35
|
+
itemHeight: { control: "number" },
|
|
36
|
+
highlightEdges: { control: "select", options: ["all", "start", "end", "none"] },
|
|
37
|
+
size: { control: "select", options: ["$6", "$8", "$9"] },
|
|
38
|
+
},
|
|
39
|
+
args: {
|
|
40
|
+
items: hours,
|
|
41
|
+
value: 9,
|
|
42
|
+
visibleItems: 5,
|
|
43
|
+
loop: false,
|
|
44
|
+
disabled: false,
|
|
45
|
+
highlightEdges: "all",
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default meta;
|
|
50
|
+
type Story = StoryObj<typeof Wheel>;
|
|
51
|
+
|
|
52
|
+
export const Basic: Story = {
|
|
53
|
+
render: (args) => {
|
|
54
|
+
const Example = () => {
|
|
55
|
+
const [value, setValue] = useState<number>((args.value as number) ?? 9);
|
|
56
|
+
return (
|
|
57
|
+
<YStack gap="$3" alignItems="flex-start">
|
|
58
|
+
<Wheel
|
|
59
|
+
{...args}
|
|
60
|
+
value={value}
|
|
61
|
+
onChange={(next) => {
|
|
62
|
+
setValue(next as number);
|
|
63
|
+
action("onChange")(next);
|
|
64
|
+
}}
|
|
65
|
+
aria-label="Hour"
|
|
66
|
+
/>
|
|
67
|
+
<Paragraph size="$3">selected: {pad2(value)}</Paragraph>
|
|
68
|
+
</YStack>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
return <Example />;
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/** Plain values (no `{ value, label }` wrapper) render their own `String(item)`. */
|
|
76
|
+
export const PlainValues: Story = {
|
|
77
|
+
render: () => (
|
|
78
|
+
<Wheel
|
|
79
|
+
items={["Small", "Medium", "Large", "Extra large"]}
|
|
80
|
+
value="Medium"
|
|
81
|
+
width={140}
|
|
82
|
+
onChange={action("onChange")}
|
|
83
|
+
aria-label="Size"
|
|
84
|
+
/>
|
|
85
|
+
),
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* `visibleItems` is the cylinder depth. 3 is a compact inline row, 7 the full
|
|
90
|
+
* iOS sheet; the value must stay odd so an item sits dead centre.
|
|
91
|
+
*/
|
|
92
|
+
export const VisibleItems: Story = {
|
|
93
|
+
render: () => (
|
|
94
|
+
<XStack gap="$6" alignItems="flex-start">
|
|
95
|
+
{([3, 5, 7] as const).map((count) => (
|
|
96
|
+
<YStack key={count} gap="$2" alignItems="center">
|
|
97
|
+
<Paragraph size="$2">visibleItems={count}</Paragraph>
|
|
98
|
+
<Wheel items={hours} value={9} visibleItems={count} aria-label={`Hour ${count}`} />
|
|
99
|
+
</YStack>
|
|
100
|
+
))}
|
|
101
|
+
</XStack>
|
|
102
|
+
),
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* `loop` wraps at the ends instead of rubber-banding. Hours and minutes loop;
|
|
107
|
+
* a bounded set like AM/PM must not, or the user can scroll past the last row
|
|
108
|
+
* into a value that was never offered.
|
|
109
|
+
*/
|
|
110
|
+
export const LoopingAndBounded: Story = {
|
|
111
|
+
render: () => (
|
|
112
|
+
<XStack gap="$6" alignItems="flex-start">
|
|
113
|
+
<YStack gap="$2" alignItems="center">
|
|
114
|
+
<Paragraph size="$2">loop</Paragraph>
|
|
115
|
+
<Wheel items={minutes} value={30} loop onChange={action("onChange")} aria-label="Minute" />
|
|
116
|
+
</YStack>
|
|
117
|
+
<YStack gap="$2" alignItems="center">
|
|
118
|
+
<Paragraph size="$2">bounded</Paragraph>
|
|
119
|
+
<Wheel
|
|
120
|
+
items={periods}
|
|
121
|
+
value="AM"
|
|
122
|
+
loop={false}
|
|
123
|
+
width={72}
|
|
124
|
+
onChange={action("onChange")}
|
|
125
|
+
aria-label="AM/PM"
|
|
126
|
+
/>
|
|
127
|
+
</YStack>
|
|
128
|
+
</XStack>
|
|
129
|
+
),
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Multi-column joining: the row of columns reads as ONE selection band because
|
|
134
|
+
* the first declares `start`, the last `end`, and everything between `none`.
|
|
135
|
+
* This is the arrangement TimePicker ships.
|
|
136
|
+
*/
|
|
137
|
+
export const JoinedColumns: Story = {
|
|
138
|
+
render: () => (
|
|
139
|
+
<XStack width={320}>
|
|
140
|
+
<Wheel items={hours} value={9} loop flex={1} highlightEdges="start" aria-label="Hour" />
|
|
141
|
+
<Wheel items={minutes} value={30} loop flex={1} highlightEdges="none" aria-label="Minute" />
|
|
142
|
+
<Wheel items={periods} value="AM" flex={1} highlightEdges="end" aria-label="AM/PM" />
|
|
143
|
+
</XStack>
|
|
144
|
+
),
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export const Disabled: Story = {
|
|
148
|
+
render: () => (
|
|
149
|
+
<XStack gap="$6" alignItems="flex-start">
|
|
150
|
+
<YStack gap="$2" alignItems="center">
|
|
151
|
+
<Paragraph size="$2">enabled</Paragraph>
|
|
152
|
+
<Wheel items={hours} value={9} aria-label="Hour enabled" />
|
|
153
|
+
</YStack>
|
|
154
|
+
<YStack gap="$2" alignItems="center">
|
|
155
|
+
<Paragraph size="$2">disabled</Paragraph>
|
|
156
|
+
<Wheel items={hours} value={9} disabled aria-label="Hour disabled" />
|
|
157
|
+
</YStack>
|
|
158
|
+
</XStack>
|
|
159
|
+
),
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* `renderItem` owns the row; the wheel keeps geometry and selection. It is
|
|
164
|
+
* handed the VALUE, not the `{ value, label }` wrapper — so a story that wants
|
|
165
|
+
* a display string looks it up itself.
|
|
166
|
+
*/
|
|
167
|
+
export const CustomRenderItem: Story = {
|
|
168
|
+
render: () => (
|
|
169
|
+
<Wheel
|
|
170
|
+
items={Object.keys(currencyNames)}
|
|
171
|
+
value="inr"
|
|
172
|
+
width={200}
|
|
173
|
+
onChange={action("onChange")}
|
|
174
|
+
aria-label="Currency"
|
|
175
|
+
renderItem={(item, _index, isSelected) => (
|
|
176
|
+
<Paragraph size="$5" fontWeight={isSelected ? "700" : "400"}>
|
|
177
|
+
{currencyNames[item as keyof typeof currencyNames]}
|
|
178
|
+
</Paragraph>
|
|
179
|
+
)}
|
|
180
|
+
/>
|
|
181
|
+
),
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/** Edge cases the geometry has to survive: empty, single item, very long labels. */
|
|
185
|
+
export const EdgeCases: Story = {
|
|
186
|
+
render: () => (
|
|
187
|
+
<XStack gap="$6" alignItems="flex-start" flexWrap="wrap">
|
|
188
|
+
<YStack gap="$2" alignItems="center">
|
|
189
|
+
<Paragraph size="$2">empty</Paragraph>
|
|
190
|
+
<Wheel items={[]} aria-label="Empty" />
|
|
191
|
+
</YStack>
|
|
192
|
+
<YStack gap="$2" alignItems="center">
|
|
193
|
+
<Paragraph size="$2">single item</Paragraph>
|
|
194
|
+
<Wheel items={[{ value: "only", label: "Only" }]} value="only" aria-label="Single" />
|
|
195
|
+
</YStack>
|
|
196
|
+
<YStack gap="$2" alignItems="center">
|
|
197
|
+
<Paragraph size="$2">long labels</Paragraph>
|
|
198
|
+
<Wheel
|
|
199
|
+
items={[
|
|
200
|
+
{ value: "a", label: "Asia/Kolkata" },
|
|
201
|
+
{ value: "b", label: "America/Argentina/Buenos_Aires" },
|
|
202
|
+
{ value: "c", label: "Europe/Isle_of_Man" },
|
|
203
|
+
]}
|
|
204
|
+
value="b"
|
|
205
|
+
width={200}
|
|
206
|
+
aria-label="Timezone"
|
|
207
|
+
/>
|
|
208
|
+
</YStack>
|
|
209
|
+
</XStack>
|
|
210
|
+
),
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* MOTION-RIDES-KNOB (LC-68): at `animation: "none"` a keyboard snap must JUMP,
|
|
215
|
+
* not tween. Arrow-key both wheels and watch the difference.
|
|
216
|
+
*/
|
|
217
|
+
export const MotionOff: Story = {
|
|
218
|
+
render: () => (
|
|
219
|
+
<XStack gap="$6" alignItems="flex-start">
|
|
220
|
+
<YStack gap="$2" alignItems="center">
|
|
221
|
+
<Paragraph size="$2">animation: medium</Paragraph>
|
|
222
|
+
<Wheel items={hours} value={9} loop aria-label="Hour animated" />
|
|
223
|
+
</YStack>
|
|
224
|
+
<Preset overrides={{ animation: "none" }}>
|
|
225
|
+
<YStack gap="$2" alignItems="center">
|
|
226
|
+
<Paragraph size="$2">animation: none</Paragraph>
|
|
227
|
+
<Wheel items={hours} value={9} loop aria-label="Hour static" />
|
|
228
|
+
</YStack>
|
|
229
|
+
</Preset>
|
|
230
|
+
</XStack>
|
|
231
|
+
),
|
|
232
|
+
};
|