orn-ui 0.2.10 → 0.3.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/AGENTS.md +66 -51
- package/README.md +3 -7
- package/lib/commonjs/atoms/Avatar.js +77 -5
- package/lib/commonjs/atoms/Avatar.js.map +1 -1
- package/lib/commonjs/atoms/Badge.js +17 -8
- package/lib/commonjs/atoms/Badge.js.map +1 -1
- package/lib/commonjs/atoms/Button.js +108 -39
- package/lib/commonjs/atoms/Button.js.map +1 -1
- package/lib/commonjs/atoms/Card.js +24 -2
- package/lib/commonjs/atoms/Card.js.map +1 -1
- package/lib/commonjs/atoms/Checkbox.js +41 -10
- package/lib/commonjs/atoms/Checkbox.js.map +1 -1
- package/lib/commonjs/atoms/Divider.js +30 -5
- package/lib/commonjs/atoms/Divider.js.map +1 -1
- package/lib/commonjs/atoms/EmptyState.js +58 -20
- package/lib/commonjs/atoms/EmptyState.js.map +1 -1
- package/lib/commonjs/atoms/Fab.js +32 -12
- package/lib/commonjs/atoms/Fab.js.map +1 -1
- package/lib/commonjs/atoms/Gradient.js +102 -36
- package/lib/commonjs/atoms/Gradient.js.map +1 -1
- package/lib/commonjs/atoms/IconButton.js +25 -4
- package/lib/commonjs/atoms/IconButton.js.map +1 -1
- package/lib/commonjs/atoms/Image.js +61 -25
- package/lib/commonjs/atoms/Image.js.map +1 -1
- package/lib/commonjs/atoms/Input.js +105 -17
- package/lib/commonjs/atoms/Input.js.map +1 -1
- package/lib/commonjs/atoms/KeyValueRow.js +37 -9
- package/lib/commonjs/atoms/KeyValueRow.js.map +1 -1
- package/lib/commonjs/atoms/PressableScale.js +25 -5
- package/lib/commonjs/atoms/PressableScale.js.map +1 -1
- package/lib/commonjs/atoms/ShinyText.js +29 -29
- package/lib/commonjs/atoms/Skeleton.js +28 -13
- package/lib/commonjs/atoms/Skeleton.js.map +1 -1
- package/lib/commonjs/atoms/Spinner.js +81 -22
- package/lib/commonjs/atoms/Spinner.js.map +1 -1
- package/lib/commonjs/atoms/Text.js +56 -5
- package/lib/commonjs/atoms/Text.js.map +1 -1
- package/lib/commonjs/atoms/Transition.js +46 -43
- package/lib/commonjs/atoms/Transition.js.map +1 -1
- package/lib/commonjs/atoms/index.js +6 -0
- package/lib/commonjs/atoms/index.js.map +1 -1
- package/lib/commonjs/atoms/inputValidation.js +56 -0
- package/lib/commonjs/atoms/inputValidation.js.map +1 -0
- package/lib/commonjs/icons/Icon.js +4 -4
- package/lib/commonjs/icons/defaultIcons.js +576 -24
- package/lib/commonjs/icons/defaultIcons.js.map +1 -1
- package/lib/commonjs/icons/index.js +6 -0
- package/lib/commonjs/icons/index.js.map +1 -1
- package/lib/commonjs/molecules/AvatarHeader.js +47 -14
- package/lib/commonjs/molecules/AvatarHeader.js.map +1 -1
- package/lib/commonjs/molecules/FormActions.js +25 -9
- package/lib/commonjs/molecules/FormActions.js.map +1 -1
- package/lib/commonjs/molecules/InfoRow.js +30 -14
- package/lib/commonjs/molecules/InfoRow.js.map +1 -1
- package/lib/commonjs/molecules/OptionCard.js +37 -22
- package/lib/commonjs/molecules/OptionCard.js.map +1 -1
- package/lib/commonjs/molecules/SegmentedControl.js +103 -14
- package/lib/commonjs/molecules/SegmentedControl.js.map +1 -1
- package/lib/commonjs/molecules/Stepper.js +61 -16
- package/lib/commonjs/molecules/Stepper.js.map +1 -1
- package/lib/commonjs/molecules/Steps.js +105 -41
- package/lib/commonjs/molecules/Steps.js.map +1 -1
- package/lib/commonjs/molecules/SymmetricGrid.js +26 -18
- package/lib/commonjs/molecules/SymmetricGrid.js.map +1 -1
- package/lib/commonjs/organisms/Alert.js +49 -35
- package/lib/commonjs/organisms/Alert.js.map +1 -1
- package/lib/commonjs/organisms/AlertProvider.js +10 -10
- package/lib/commonjs/organisms/BottomSheet.js +137 -85
- package/lib/commonjs/organisms/BottomSheet.js.map +1 -1
- package/lib/commonjs/organisms/Chart.js +174 -154
- package/lib/commonjs/organisms/Chart.js.map +1 -1
- package/lib/commonjs/organisms/DateField.js +36 -20
- package/lib/commonjs/organisms/DateField.js.map +1 -1
- package/lib/commonjs/organisms/DatePicker.js +56 -33
- package/lib/commonjs/organisms/DatePicker.js.map +1 -1
- package/lib/commonjs/organisms/List.js +62 -34
- package/lib/commonjs/organisms/List.js.map +1 -1
- package/lib/commonjs/organisms/Modal.js +120 -82
- package/lib/commonjs/organisms/Modal.js.map +1 -1
- package/lib/commonjs/organisms/NavigationBar.js +63 -24
- package/lib/commonjs/organisms/NavigationBar.js.map +1 -1
- package/lib/commonjs/organisms/OptionWheel.js +64 -64
- package/lib/commonjs/organisms/OptionWheel.js.map +1 -1
- package/lib/commonjs/organisms/ReorderableList.js +48 -27
- package/lib/commonjs/organisms/ReorderableList.js.map +1 -1
- package/lib/commonjs/organisms/Screen.js +68 -35
- package/lib/commonjs/organisms/Screen.js.map +1 -1
- package/lib/commonjs/organisms/SearchList.js +56 -26
- package/lib/commonjs/organisms/SearchList.js.map +1 -1
- package/lib/commonjs/organisms/Select.js +45 -24
- package/lib/commonjs/organisms/Select.js.map +1 -1
- package/lib/commonjs/organisms/Slides.js +36 -23
- package/lib/commonjs/organisms/Slides.js.map +1 -1
- package/lib/commonjs/organisms/ThemeToggle.js +10 -9
- package/lib/commonjs/organisms/ThemeToggle.js.map +1 -1
- package/lib/commonjs/organisms/Timeline.js +73 -56
- package/lib/commonjs/organisms/Timeline.js.map +1 -1
- package/lib/commonjs/organisms/Toast.js +46 -15
- package/lib/commonjs/organisms/Toast.js.map +1 -1
- package/lib/commonjs/organisms/ToastProvider.js +14 -10
- package/lib/commonjs/organisms/ToastProvider.js.map +1 -1
- package/lib/commonjs/organisms/Wizard.js +40 -15
- package/lib/commonjs/organisms/Wizard.js.map +1 -1
- package/lib/commonjs/organisms/chart/frame.js +25 -25
- package/lib/commonjs/organisms/chart/frame.js.map +1 -1
- package/lib/commonjs/organisms/chart/marks.js +42 -42
- package/lib/commonjs/organisms/chart/math.js +84 -85
- package/lib/commonjs/organisms/chart/math.js.map +1 -1
- package/lib/commonjs/organisms/chart/palette.js +28 -28
- package/lib/commonjs/organisms/imperativeBridge.js +5 -5
- package/lib/commonjs/organisms/imperativeBridge.js.map +1 -1
- package/lib/commonjs/organisms/useSlides.js +53 -21
- package/lib/commonjs/organisms/useSlides.js.map +1 -1
- package/lib/commonjs/safe-area/SafeAreaUIProvider.js +9 -66
- package/lib/commonjs/safe-area/SafeAreaUIProvider.js.map +1 -1
- package/lib/commonjs/safe-area/index.js.map +1 -1
- package/lib/commonjs/theme/KeyboardAvoidance.js +206 -0
- package/lib/commonjs/theme/KeyboardAvoidance.js.map +1 -0
- package/lib/commonjs/theme/NativeOverlay.js +263 -0
- package/lib/commonjs/theme/NativeOverlay.js.map +1 -0
- package/lib/commonjs/theme/UIProvider.js +86 -33
- package/lib/commonjs/theme/UIProvider.js.map +1 -1
- package/lib/commonjs/theme/colors.js +17 -17
- package/lib/commonjs/theme/colors.js.map +1 -1
- package/lib/commonjs/theme/createStyles.js +19 -16
- package/lib/commonjs/theme/createStyles.js.map +1 -1
- package/lib/commonjs/theme/createTheme.js +6 -6
- package/lib/commonjs/theme/index.js +6 -0
- package/lib/commonjs/theme/index.js.map +1 -1
- package/lib/commonjs/theme/insets.js +1 -1
- package/lib/commonjs/theme/labels.js +29 -1
- package/lib/commonjs/theme/labels.js.map +1 -1
- package/lib/commonjs/theme/palettes.js +25 -23
- package/lib/commonjs/theme/palettes.js.map +1 -1
- package/lib/commonjs/theme/tokens.js +28 -14
- package/lib/commonjs/theme/tokens.js.map +1 -1
- package/lib/commonjs/theme/useKeyboardField.js +36 -0
- package/lib/commonjs/theme/useKeyboardField.js.map +1 -0
- package/lib/commonjs/theme/useReduceMotion.js +31 -0
- package/lib/commonjs/theme/useReduceMotion.js.map +1 -0
- package/lib/module/atoms/Avatar.js +79 -8
- package/lib/module/atoms/Avatar.js.map +1 -1
- package/lib/module/atoms/Badge.js +17 -8
- package/lib/module/atoms/Badge.js.map +1 -1
- package/lib/module/atoms/Button.js +110 -41
- package/lib/module/atoms/Button.js.map +1 -1
- package/lib/module/atoms/Card.js +24 -2
- package/lib/module/atoms/Card.js.map +1 -1
- package/lib/module/atoms/Checkbox.js +42 -11
- package/lib/module/atoms/Checkbox.js.map +1 -1
- package/lib/module/atoms/Divider.js +31 -6
- package/lib/module/atoms/Divider.js.map +1 -1
- package/lib/module/atoms/EmptyState.js +58 -20
- package/lib/module/atoms/EmptyState.js.map +1 -1
- package/lib/module/atoms/Fab.js +33 -13
- package/lib/module/atoms/Fab.js.map +1 -1
- package/lib/module/atoms/Gradient.js +103 -37
- package/lib/module/atoms/Gradient.js.map +1 -1
- package/lib/module/atoms/IconButton.js +25 -4
- package/lib/module/atoms/IconButton.js.map +1 -1
- package/lib/module/atoms/Image.js +61 -25
- package/lib/module/atoms/Image.js.map +1 -1
- package/lib/module/atoms/Input.js +108 -20
- package/lib/module/atoms/Input.js.map +1 -1
- package/lib/module/atoms/KeyValueRow.js +37 -9
- package/lib/module/atoms/KeyValueRow.js.map +1 -1
- package/lib/module/atoms/PressableScale.js +25 -5
- package/lib/module/atoms/PressableScale.js.map +1 -1
- package/lib/module/atoms/ShinyText.js +29 -29
- package/lib/module/atoms/Skeleton.js +30 -13
- package/lib/module/atoms/Skeleton.js.map +1 -1
- package/lib/module/atoms/Spinner.js +82 -23
- package/lib/module/atoms/Spinner.js.map +1 -1
- package/lib/module/atoms/Text.js +56 -5
- package/lib/module/atoms/Text.js.map +1 -1
- package/lib/module/atoms/Transition.js +41 -42
- package/lib/module/atoms/Transition.js.map +1 -1
- package/lib/module/atoms/index.js +1 -1
- package/lib/module/atoms/index.js.map +1 -1
- package/lib/module/atoms/inputValidation.js +52 -0
- package/lib/module/atoms/inputValidation.js.map +1 -0
- package/lib/module/icons/Icon.js +4 -4
- package/lib/module/icons/defaultIcons.js +574 -24
- package/lib/module/icons/defaultIcons.js.map +1 -1
- package/lib/module/icons/index.js +1 -1
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/molecules/AvatarHeader.js +47 -14
- package/lib/module/molecules/AvatarHeader.js.map +1 -1
- package/lib/module/molecules/FormActions.js +25 -9
- package/lib/module/molecules/FormActions.js.map +1 -1
- package/lib/module/molecules/InfoRow.js +30 -14
- package/lib/module/molecules/InfoRow.js.map +1 -1
- package/lib/module/molecules/OptionCard.js +37 -22
- package/lib/module/molecules/OptionCard.js.map +1 -1
- package/lib/module/molecules/SegmentedControl.js +107 -18
- package/lib/module/molecules/SegmentedControl.js.map +1 -1
- package/lib/module/molecules/Stepper.js +63 -18
- package/lib/module/molecules/Stepper.js.map +1 -1
- package/lib/module/molecules/Steps.js +107 -43
- package/lib/module/molecules/Steps.js.map +1 -1
- package/lib/module/molecules/SymmetricGrid.js +26 -18
- package/lib/module/molecules/SymmetricGrid.js.map +1 -1
- package/lib/module/organisms/Alert.js +51 -37
- package/lib/module/organisms/Alert.js.map +1 -1
- package/lib/module/organisms/AlertProvider.js +10 -10
- package/lib/module/organisms/BottomSheet.js +142 -88
- package/lib/module/organisms/BottomSheet.js.map +1 -1
- package/lib/module/organisms/Chart.js +176 -156
- package/lib/module/organisms/Chart.js.map +1 -1
- package/lib/module/organisms/DateField.js +36 -20
- package/lib/module/organisms/DateField.js.map +1 -1
- package/lib/module/organisms/DatePicker.js +56 -33
- package/lib/module/organisms/DatePicker.js.map +1 -1
- package/lib/module/organisms/List.js +62 -34
- package/lib/module/organisms/List.js.map +1 -1
- package/lib/module/organisms/Modal.js +122 -84
- package/lib/module/organisms/Modal.js.map +1 -1
- package/lib/module/organisms/NavigationBar.js +64 -25
- package/lib/module/organisms/NavigationBar.js.map +1 -1
- package/lib/module/organisms/OptionWheel.js +64 -64
- package/lib/module/organisms/OptionWheel.js.map +1 -1
- package/lib/module/organisms/ReorderableList.js +48 -27
- package/lib/module/organisms/ReorderableList.js.map +1 -1
- package/lib/module/organisms/Screen.js +70 -37
- package/lib/module/organisms/Screen.js.map +1 -1
- package/lib/module/organisms/SearchList.js +56 -26
- package/lib/module/organisms/SearchList.js.map +1 -1
- package/lib/module/organisms/Select.js +46 -25
- package/lib/module/organisms/Select.js.map +1 -1
- package/lib/module/organisms/Slides.js +37 -24
- package/lib/module/organisms/Slides.js.map +1 -1
- package/lib/module/organisms/ThemeToggle.js +11 -10
- package/lib/module/organisms/ThemeToggle.js.map +1 -1
- package/lib/module/organisms/Timeline.js +73 -56
- package/lib/module/organisms/Timeline.js.map +1 -1
- package/lib/module/organisms/Toast.js +47 -16
- package/lib/module/organisms/Toast.js.map +1 -1
- package/lib/module/organisms/ToastProvider.js +15 -11
- package/lib/module/organisms/ToastProvider.js.map +1 -1
- package/lib/module/organisms/Wizard.js +41 -16
- package/lib/module/organisms/Wizard.js.map +1 -1
- package/lib/module/organisms/chart/frame.js +25 -25
- package/lib/module/organisms/chart/frame.js.map +1 -1
- package/lib/module/organisms/chart/marks.js +42 -42
- package/lib/module/organisms/chart/math.js +84 -85
- package/lib/module/organisms/chart/math.js.map +1 -1
- package/lib/module/organisms/chart/palette.js +28 -28
- package/lib/module/organisms/imperativeBridge.js +5 -5
- package/lib/module/organisms/imperativeBridge.js.map +1 -1
- package/lib/module/organisms/useSlides.js +53 -21
- package/lib/module/organisms/useSlides.js.map +1 -1
- package/lib/module/safe-area/SafeAreaUIProvider.js +10 -65
- package/lib/module/safe-area/SafeAreaUIProvider.js.map +1 -1
- package/lib/module/safe-area/index.js +1 -0
- package/lib/module/safe-area/index.js.map +1 -1
- package/lib/module/theme/KeyboardAvoidance.js +198 -0
- package/lib/module/theme/KeyboardAvoidance.js.map +1 -0
- package/lib/module/theme/NativeOverlay.js +257 -0
- package/lib/module/theme/NativeOverlay.js.map +1 -0
- package/lib/module/theme/UIProvider.js +88 -35
- package/lib/module/theme/UIProvider.js.map +1 -1
- package/lib/module/theme/colors.js +17 -17
- package/lib/module/theme/colors.js.map +1 -1
- package/lib/module/theme/createStyles.js +20 -16
- package/lib/module/theme/createStyles.js.map +1 -1
- package/lib/module/theme/createTheme.js +6 -6
- package/lib/module/theme/index.js +1 -1
- package/lib/module/theme/index.js.map +1 -1
- package/lib/module/theme/insets.js +1 -1
- package/lib/module/theme/labels.js +29 -1
- package/lib/module/theme/labels.js.map +1 -1
- package/lib/module/theme/palettes.js +25 -23
- package/lib/module/theme/palettes.js.map +1 -1
- package/lib/module/theme/tokens.js +27 -13
- package/lib/module/theme/tokens.js.map +1 -1
- package/lib/module/theme/useKeyboardField.js +33 -0
- package/lib/module/theme/useKeyboardField.js.map +1 -0
- package/lib/module/theme/useReduceMotion.js +28 -0
- package/lib/module/theme/useReduceMotion.js.map +1 -0
- package/lib/typescript/atoms/Avatar.d.ts +35 -5
- package/lib/typescript/atoms/Avatar.d.ts.map +1 -1
- package/lib/typescript/atoms/Badge.d.ts +14 -3
- package/lib/typescript/atoms/Badge.d.ts.map +1 -1
- package/lib/typescript/atoms/Button.d.ts +34 -3
- package/lib/typescript/atoms/Button.d.ts.map +1 -1
- package/lib/typescript/atoms/Card.d.ts +13 -2
- package/lib/typescript/atoms/Card.d.ts.map +1 -1
- package/lib/typescript/atoms/Checkbox.d.ts +26 -1
- package/lib/typescript/atoms/Checkbox.d.ts.map +1 -1
- package/lib/typescript/atoms/Divider.d.ts +18 -1
- package/lib/typescript/atoms/Divider.d.ts.map +1 -1
- package/lib/typescript/atoms/EmptyState.d.ts +30 -3
- package/lib/typescript/atoms/EmptyState.d.ts.map +1 -1
- package/lib/typescript/atoms/Fab.d.ts +24 -4
- package/lib/typescript/atoms/Fab.d.ts.map +1 -1
- package/lib/typescript/atoms/Gradient.d.ts +21 -15
- package/lib/typescript/atoms/Gradient.d.ts.map +1 -1
- package/lib/typescript/atoms/IconButton.d.ts +18 -4
- package/lib/typescript/atoms/IconButton.d.ts.map +1 -1
- package/lib/typescript/atoms/Image.d.ts +39 -17
- package/lib/typescript/atoms/Image.d.ts.map +1 -1
- package/lib/typescript/atoms/Input.d.ts +57 -7
- package/lib/typescript/atoms/Input.d.ts.map +1 -1
- package/lib/typescript/atoms/KeyValueRow.d.ts +16 -4
- package/lib/typescript/atoms/KeyValueRow.d.ts.map +1 -1
- package/lib/typescript/atoms/PressableScale.d.ts +12 -5
- package/lib/typescript/atoms/PressableScale.d.ts.map +1 -1
- package/lib/typescript/atoms/ShinyText.d.ts +17 -17
- package/lib/typescript/atoms/ShinyText.d.ts.map +1 -1
- package/lib/typescript/atoms/Skeleton.d.ts +12 -7
- package/lib/typescript/atoms/Skeleton.d.ts.map +1 -1
- package/lib/typescript/atoms/Spinner.d.ts +29 -1
- package/lib/typescript/atoms/Spinner.d.ts.map +1 -1
- package/lib/typescript/atoms/Text.d.ts +46 -0
- package/lib/typescript/atoms/Text.d.ts.map +1 -1
- package/lib/typescript/atoms/Transition.d.ts +19 -15
- package/lib/typescript/atoms/Transition.d.ts.map +1 -1
- package/lib/typescript/atoms/index.d.ts +3 -3
- package/lib/typescript/atoms/index.d.ts.map +1 -1
- package/lib/typescript/atoms/inputValidation.d.ts +29 -0
- package/lib/typescript/atoms/inputValidation.d.ts.map +1 -0
- package/lib/typescript/icons/Icon.d.ts +4 -4
- package/lib/typescript/icons/defaultIcons.d.ts +19 -2
- package/lib/typescript/icons/defaultIcons.d.ts.map +1 -1
- package/lib/typescript/icons/index.d.ts +2 -2
- package/lib/typescript/icons/index.d.ts.map +1 -1
- package/lib/typescript/icons/types.d.ts +13 -3
- package/lib/typescript/icons/types.d.ts.map +1 -1
- package/lib/typescript/molecules/AvatarHeader.d.ts +23 -4
- package/lib/typescript/molecules/AvatarHeader.d.ts.map +1 -1
- package/lib/typescript/molecules/FormActions.d.ts +21 -4
- package/lib/typescript/molecules/FormActions.d.ts.map +1 -1
- package/lib/typescript/molecules/InfoRow.d.ts +17 -3
- package/lib/typescript/molecules/InfoRow.d.ts.map +1 -1
- package/lib/typescript/molecules/OptionCard.d.ts +20 -5
- package/lib/typescript/molecules/OptionCard.d.ts.map +1 -1
- package/lib/typescript/molecules/SegmentedControl.d.ts +18 -2
- package/lib/typescript/molecules/SegmentedControl.d.ts.map +1 -1
- package/lib/typescript/molecules/Stepper.d.ts +23 -7
- package/lib/typescript/molecules/Stepper.d.ts.map +1 -1
- package/lib/typescript/molecules/Steps.d.ts +18 -4
- package/lib/typescript/molecules/Steps.d.ts.map +1 -1
- package/lib/typescript/molecules/SymmetricGrid.d.ts +15 -5
- package/lib/typescript/molecules/SymmetricGrid.d.ts.map +1 -1
- package/lib/typescript/organisms/Alert.d.ts +28 -2
- package/lib/typescript/organisms/Alert.d.ts.map +1 -1
- package/lib/typescript/organisms/AlertProvider.d.ts +10 -10
- package/lib/typescript/organisms/AlertProvider.d.ts.map +1 -1
- package/lib/typescript/organisms/BottomSheet.d.ts +20 -5
- package/lib/typescript/organisms/BottomSheet.d.ts.map +1 -1
- package/lib/typescript/organisms/Chart.d.ts +86 -75
- package/lib/typescript/organisms/Chart.d.ts.map +1 -1
- package/lib/typescript/organisms/DateField.d.ts +17 -3
- package/lib/typescript/organisms/DateField.d.ts.map +1 -1
- package/lib/typescript/organisms/DatePicker.d.ts +7 -11
- package/lib/typescript/organisms/DatePicker.d.ts.map +1 -1
- package/lib/typescript/organisms/List.d.ts +30 -3
- package/lib/typescript/organisms/List.d.ts.map +1 -1
- package/lib/typescript/organisms/Modal.d.ts +30 -3
- package/lib/typescript/organisms/Modal.d.ts.map +1 -1
- package/lib/typescript/organisms/NavigationBar.d.ts +26 -7
- package/lib/typescript/organisms/NavigationBar.d.ts.map +1 -1
- package/lib/typescript/organisms/OptionWheel.d.ts +20 -16
- package/lib/typescript/organisms/OptionWheel.d.ts.map +1 -1
- package/lib/typescript/organisms/ReorderableList.d.ts +20 -11
- package/lib/typescript/organisms/ReorderableList.d.ts.map +1 -1
- package/lib/typescript/organisms/Screen.d.ts +27 -8
- package/lib/typescript/organisms/Screen.d.ts.map +1 -1
- package/lib/typescript/organisms/SearchList.d.ts +46 -3
- package/lib/typescript/organisms/SearchList.d.ts.map +1 -1
- package/lib/typescript/organisms/Select.d.ts +26 -6
- package/lib/typescript/organisms/Select.d.ts.map +1 -1
- package/lib/typescript/organisms/Slides.d.ts +44 -26
- package/lib/typescript/organisms/Slides.d.ts.map +1 -1
- package/lib/typescript/organisms/ThemeToggle.d.ts +6 -2
- package/lib/typescript/organisms/ThemeToggle.d.ts.map +1 -1
- package/lib/typescript/organisms/Timeline.d.ts +26 -9
- package/lib/typescript/organisms/Timeline.d.ts.map +1 -1
- package/lib/typescript/organisms/Toast.d.ts +19 -4
- package/lib/typescript/organisms/Toast.d.ts.map +1 -1
- package/lib/typescript/organisms/ToastProvider.d.ts +8 -8
- package/lib/typescript/organisms/ToastProvider.d.ts.map +1 -1
- package/lib/typescript/organisms/Wizard.d.ts +24 -6
- package/lib/typescript/organisms/Wizard.d.ts.map +1 -1
- package/lib/typescript/organisms/chart/frame.d.ts +21 -21
- package/lib/typescript/organisms/chart/frame.d.ts.map +1 -1
- package/lib/typescript/organisms/chart/marks.d.ts +28 -28
- package/lib/typescript/organisms/chart/marks.d.ts.map +1 -1
- package/lib/typescript/organisms/chart/math.d.ts +69 -70
- package/lib/typescript/organisms/chart/math.d.ts.map +1 -1
- package/lib/typescript/organisms/chart/palette.d.ts +2 -2
- package/lib/typescript/organisms/chart/palette.d.ts.map +1 -1
- package/lib/typescript/organisms/imperativeBridge.d.ts +4 -4
- package/lib/typescript/organisms/useSlides.d.ts +8 -16
- package/lib/typescript/organisms/useSlides.d.ts.map +1 -1
- package/lib/typescript/safe-area/SafeAreaUIProvider.d.ts +9 -7
- package/lib/typescript/safe-area/SafeAreaUIProvider.d.ts.map +1 -1
- package/lib/typescript/safe-area/index.d.ts.map +1 -1
- package/lib/typescript/theme/KeyboardAvoidance.d.ts +62 -0
- package/lib/typescript/theme/KeyboardAvoidance.d.ts.map +1 -0
- package/lib/typescript/theme/NativeOverlay.d.ts +43 -0
- package/lib/typescript/theme/NativeOverlay.d.ts.map +1 -0
- package/lib/typescript/theme/UIProvider.d.ts +38 -20
- package/lib/typescript/theme/UIProvider.d.ts.map +1 -1
- package/lib/typescript/theme/colors.d.ts +13 -13
- package/lib/typescript/theme/colors.d.ts.map +1 -1
- package/lib/typescript/theme/createStyles.d.ts +20 -14
- package/lib/typescript/theme/createStyles.d.ts.map +1 -1
- package/lib/typescript/theme/createTheme.d.ts +5 -5
- package/lib/typescript/theme/index.d.ts +1 -1
- package/lib/typescript/theme/index.d.ts.map +1 -1
- package/lib/typescript/theme/insets.d.ts +1 -1
- package/lib/typescript/theme/insets.d.ts.map +1 -1
- package/lib/typescript/theme/labels.d.ts +57 -2
- package/lib/typescript/theme/labels.d.ts.map +1 -1
- package/lib/typescript/theme/palettes.d.ts +25 -19
- package/lib/typescript/theme/palettes.d.ts.map +1 -1
- package/lib/typescript/theme/tokens.d.ts +15 -8
- package/lib/typescript/theme/tokens.d.ts.map +1 -1
- package/lib/typescript/theme/types.d.ts +7 -7
- package/lib/typescript/theme/types.d.ts.map +1 -1
- package/lib/typescript/theme/useKeyboardField.d.ts +17 -0
- package/lib/typescript/theme/useKeyboardField.d.ts.map +1 -0
- package/lib/typescript/theme/useReduceMotion.d.ts +7 -0
- package/lib/typescript/theme/useReduceMotion.d.ts.map +1 -0
- package/llms.txt +4 -4
- package/package.json +15 -14
- package/registry/alert-provider.json +4 -3
- package/registry/alert.json +3 -2
- package/registry/avatar-header.json +1 -1
- package/registry/avatar.json +3 -2
- package/registry/badge.json +1 -1
- package/registry/bottom-sheet.json +1 -1
- package/registry/button.json +3 -2
- package/registry/card.json +1 -1
- package/registry/chart.json +5 -5
- package/registry/checkbox.json +3 -2
- package/registry/core.json +31 -15
- package/registry/date-field.json +1 -1
- package/registry/date-picker.json +3 -2
- package/registry/divider.json +1 -1
- package/registry/empty-state.json +4 -2
- package/registry/fab.json +3 -2
- package/registry/form-actions.json +3 -2
- package/registry/gradient.json +1 -1
- package/registry/icon-button.json +3 -2
- package/registry/image.json +1 -1
- package/registry/index.json +79 -61
- package/registry/info-row.json +1 -1
- package/registry/input.json +5 -1
- package/registry/key-value-row.json +1 -1
- package/registry/list.json +3 -1
- package/registry/manifest.json +3 -1
- package/registry/modal.json +1 -1
- package/registry/navigation-bar.json +1 -1
- package/registry/option-card.json +3 -2
- package/registry/option-wheel.json +1 -1
- package/registry/pressable-scale.json +3 -2
- package/registry/reorderable-list.json +1 -1
- package/registry/screen.json +1 -1
- package/registry/search-list.json +7 -1
- package/registry/segmented-control.json +1 -1
- package/registry/select.json +1 -1
- package/registry/shiny-text.json +1 -1
- package/registry/skeleton.json +3 -2
- package/registry/slides.json +2 -2
- package/registry/spinner.json +1 -1
- package/registry/stepper.json +1 -1
- package/registry/steps.json +1 -1
- package/registry/symmetric-grid.json +1 -1
- package/registry/theme-toggle.json +1 -1
- package/registry/timeline.json +1 -1
- package/registry/title.json +1 -1
- package/registry/toast-provider.json +2 -2
- package/registry/toast.json +1 -1
- package/registry/transition.json +1 -1
- package/registry/wizard.json +1 -1
- package/src/atoms/Avatar.tsx +117 -26
- package/src/atoms/Badge.tsx +20 -9
- package/src/atoms/Button.tsx +102 -30
- package/src/atoms/Card.tsx +23 -3
- package/src/atoms/Checkbox.tsx +49 -12
- package/src/atoms/Divider.tsx +34 -4
- package/src/atoms/EmptyState.tsx +63 -16
- package/src/atoms/Fab.tsx +56 -9
- package/src/atoms/Gradient.tsx +92 -41
- package/src/atoms/IconButton.tsx +27 -4
- package/src/atoms/Image.tsx +74 -28
- package/src/atoms/Input.tsx +129 -19
- package/src/atoms/KeyValueRow.tsx +72 -28
- package/src/atoms/PressableScale.tsx +31 -7
- package/src/atoms/ShinyText.tsx +31 -31
- package/src/atoms/Skeleton.tsx +37 -17
- package/src/atoms/Spinner.tsx +87 -24
- package/src/atoms/Text.tsx +66 -6
- package/src/atoms/Transition.tsx +40 -44
- package/src/atoms/index.ts +3 -3
- package/src/atoms/inputValidation.ts +59 -0
- package/src/icons/Icon.tsx +4 -4
- package/src/icons/defaultIcons.tsx +445 -22
- package/src/icons/index.ts +2 -2
- package/src/icons/types.ts +26 -4
- package/src/molecules/AvatarHeader.tsx +88 -34
- package/src/molecules/FormActions.tsx +36 -6
- package/src/molecules/InfoRow.tsx +39 -12
- package/src/molecules/OptionCard.tsx +55 -20
- package/src/molecules/SegmentedControl.tsx +135 -15
- package/src/molecules/Stepper.tsx +73 -19
- package/src/molecules/Steps.tsx +121 -39
- package/src/molecules/SymmetricGrid.tsx +28 -12
- package/src/organisms/Alert.tsx +97 -26
- package/src/organisms/AlertProvider.tsx +13 -13
- package/src/organisms/BottomSheet.tsx +115 -43
- package/src/organisms/Chart.tsx +193 -164
- package/src/organisms/DateField.tsx +40 -15
- package/src/organisms/DatePicker.tsx +53 -32
- package/src/organisms/List.tsx +64 -15
- package/src/organisms/Modal.tsx +122 -63
- package/src/organisms/NavigationBar.tsx +73 -23
- package/src/organisms/OptionWheel.tsx +70 -66
- package/src/organisms/ReorderableList.tsx +49 -27
- package/src/organisms/Screen.tsx +79 -42
- package/src/organisms/SearchList.tsx +84 -20
- package/src/organisms/Select.tsx +63 -29
- package/src/organisms/Slides.tsx +63 -44
- package/src/organisms/ThemeToggle.tsx +15 -6
- package/src/organisms/Timeline.tsx +79 -62
- package/src/organisms/Toast.tsx +50 -15
- package/src/organisms/ToastProvider.tsx +17 -13
- package/src/organisms/Wizard.tsx +58 -21
- package/src/organisms/chart/frame.tsx +35 -35
- package/src/organisms/chart/marks.tsx +53 -53
- package/src/organisms/chart/math.ts +93 -94
- package/src/organisms/chart/palette.ts +28 -28
- package/src/organisms/imperativeBridge.ts +5 -5
- package/src/organisms/useSlides.ts +66 -31
- package/src/safe-area/SafeAreaUIProvider.tsx +9 -70
- package/src/safe-area/index.ts +1 -0
- package/src/theme/KeyboardAvoidance.tsx +216 -0
- package/src/theme/NativeOverlay.tsx +269 -0
- package/src/theme/UIProvider.tsx +109 -46
- package/src/theme/colors.ts +21 -21
- package/src/theme/createStyles.ts +23 -19
- package/src/theme/createTheme.ts +6 -6
- package/src/theme/index.ts +1 -1
- package/src/theme/insets.ts +1 -1
- package/src/theme/labels.ts +85 -2
- package/src/theme/palettes.ts +33 -25
- package/src/theme/tokens.ts +27 -13
- package/src/theme/types.ts +7 -7
- package/src/theme/useKeyboardField.ts +33 -0
- package/src/theme/useReduceMotion.ts +27 -0
|
@@ -22,16 +22,26 @@ export interface SearchListProps<T> {
|
|
|
22
22
|
/** Optional chrome above the search bar (e.g. back button + title). */
|
|
23
23
|
header?: React.ReactNode;
|
|
24
24
|
|
|
25
|
+
/** Text of the search box (controlled). */
|
|
25
26
|
searchValue: string;
|
|
27
|
+
/** Called with the new text; the clear button calls it with an empty string. */
|
|
26
28
|
onSearchChange: (text: string) => void;
|
|
29
|
+
/** Placeholder of the search box. @default labels.search */
|
|
27
30
|
searchPlaceholder?: string;
|
|
31
|
+
/** Shows a spinner inside the search box. @default false */
|
|
28
32
|
searchIsLoading?: boolean;
|
|
33
|
+
/** Shows a button next to the search box and is called when it is pressed. */
|
|
29
34
|
onScanPress?: () => void;
|
|
35
|
+
/** Accessible name of the `onScanPress` button. @default labels.scan */
|
|
30
36
|
scanAccessibilityLabel?: string;
|
|
37
|
+
/** Extra nodes at the end of the search row. */
|
|
31
38
|
extraActions?: React.ReactNode;
|
|
32
39
|
|
|
40
|
+
/** Items to render. */
|
|
33
41
|
data: T[];
|
|
42
|
+
/** Stable key per item. */
|
|
34
43
|
keyExtractor: (item: T, index: number) => string;
|
|
44
|
+
/** Renders each row (same as FlatList's `renderItem`). */
|
|
35
45
|
renderItem: FlatListProps<T>['renderItem'];
|
|
36
46
|
|
|
37
47
|
/** Initial load: while true and there's no data, placeholders are shown. */
|
|
@@ -40,12 +50,23 @@ export interface SearchListProps<T> {
|
|
|
40
50
|
loadingMode?: 'replace' | 'overlay';
|
|
41
51
|
/** Reload with data already on screen (pull-to-refresh). @default false */
|
|
42
52
|
isRefreshing?: boolean;
|
|
53
|
+
/** Shows a spinner in the footer while the next page loads. @default false */
|
|
43
54
|
isLoadingMore?: boolean;
|
|
44
55
|
/** Controlled by the consumer: while false, a fullscreen spinner is shown. @default true */
|
|
45
56
|
isReady?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Text of the spinner shown while `isReady` is false and of the `overlay` loading. @default
|
|
59
|
+
* labels.loading
|
|
60
|
+
*/
|
|
46
61
|
loadingText?: string;
|
|
62
|
+
/** Text of the footer spinner shown with `isLoadingMore`. @default labels.loadingMore */
|
|
47
63
|
loadingMoreText?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Whether more pages remain. While false, `noMoreText` shows at the end of a non-empty list.
|
|
66
|
+
* @default false
|
|
67
|
+
*/
|
|
48
68
|
hasMore?: boolean;
|
|
69
|
+
/** Text shown at the end of the list when `hasMore` is false. */
|
|
49
70
|
noMoreText?: string;
|
|
50
71
|
|
|
51
72
|
/** Ghost rows for the initial load. @default 6 */
|
|
@@ -53,31 +74,43 @@ export interface SearchListProps<T> {
|
|
|
53
74
|
/** Custom placeholder, to match the real shape of the item. */
|
|
54
75
|
renderSkeletonItem?: () => React.ReactElement;
|
|
55
76
|
|
|
77
|
+
/** Called when scrolling gets close to the end. Takes precedence over `onEndReached`. */
|
|
56
78
|
onLoadMore?: () => void;
|
|
79
|
+
/** Called when scrolling gets close to the end. Ignored when `onLoadMore` is passed. */
|
|
57
80
|
onEndReached?: () => void;
|
|
81
|
+
/** How close to the end `onEndReached` fires, in visible lengths of the list. @default 0.3 */
|
|
58
82
|
onEndReachedThreshold?: number;
|
|
83
|
+
/** Enables pull-to-refresh and is called when the user pulls. */
|
|
59
84
|
onRefresh?: () => void;
|
|
60
85
|
|
|
86
|
+
/** Title of the empty state. @default labels.noResultsTitle */
|
|
61
87
|
emptyTitle?: string;
|
|
88
|
+
/** Supporting line of the empty state. @default labels.noResultsDescription */
|
|
62
89
|
emptyDescription?: string;
|
|
90
|
+
/** Icon of the empty state. @default 'search' */
|
|
63
91
|
emptyIconName?: IconName;
|
|
64
92
|
|
|
93
|
+
/** Rendered above the items, inside the scroll. */
|
|
65
94
|
ListHeaderComponent?: FlatListProps<T>['ListHeaderComponent'];
|
|
95
|
+
/** Rendered below the items, before the pagination loader and `noMoreText`. */
|
|
66
96
|
ListFooterComponent?: FlatListProps<T>['ListFooterComponent'];
|
|
67
97
|
|
|
98
|
+
/** Style of the list's content container (also used by the skeleton). */
|
|
68
99
|
contentContainerStyle?: StyleProp<ViewStyle>;
|
|
100
|
+
/** Style of the outer wrapper. */
|
|
69
101
|
containerStyle?: ViewStyle;
|
|
70
102
|
|
|
71
103
|
/** List component to use (FlashList, etc). @default FlatList */
|
|
72
104
|
ListComponent?: React.ComponentType<any>;
|
|
105
|
+
/** Extra props passed to `ListComponent`; they override the ones set here. */
|
|
73
106
|
listProps?: Record<string, unknown>;
|
|
74
107
|
}
|
|
75
108
|
|
|
76
109
|
const useStyles = createStyles((theme) => ({
|
|
77
110
|
container: { flex: 1 },
|
|
78
|
-
searchRow: { flexDirection: 'row', alignItems: 'center', marginBottom: theme.tokens.spacing.lg
|
|
111
|
+
searchRow: { flexDirection: 'row', alignItems: 'center', marginBottom: theme.tokens.spacing.lg, gap: theme.tokens.spacing.md },
|
|
79
112
|
searchInput: { flex: 1, marginBottom: 0 },
|
|
80
|
-
scanButton: { backgroundColor: theme.colors.primary, width:
|
|
113
|
+
scanButton: { backgroundColor: theme.colors.primary, width: SCAN_SIZE, height: SCAN_SIZE, borderRadius: theme.tokens.radius.lg, ...theme.tokens.shadow.sm },
|
|
81
114
|
listArea: { flex: 1 },
|
|
82
115
|
listContent: { paddingBottom: theme.tokens.spacing.xl },
|
|
83
116
|
loadingOverlay: {
|
|
@@ -99,14 +132,30 @@ const useStyles = createStyles((theme) => ({
|
|
|
99
132
|
gap: theme.tokens.spacing.md,
|
|
100
133
|
padding: theme.tokens.spacing.md,
|
|
101
134
|
},
|
|
102
|
-
skeletonText: { flex: 1
|
|
135
|
+
skeletonText: { flex: 1 },
|
|
136
|
+
empty: { marginTop: theme.tokens.spacing.xxxl },
|
|
103
137
|
}));
|
|
104
138
|
|
|
139
|
+
/** The scan button is square and as tall as the search Input beside it. */
|
|
140
|
+
const SCAN_SIZE = 50;
|
|
141
|
+
/** Same as Avatar's default size: the placeholder row matches a typical avatar row. */
|
|
142
|
+
const SKELETON_AVATAR = 44;
|
|
143
|
+
|
|
105
144
|
/**
|
|
106
145
|
* SearchList
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
146
|
+
* Search box + list + loading/pagination/empty states. The content of each
|
|
147
|
+
* item (renderItem) is up to whoever uses it, since it differs completely
|
|
148
|
+
* between use cases.
|
|
149
|
+
*
|
|
150
|
+
* Design:
|
|
151
|
+
* - Proximity + common region: the search row (box, scan, extra actions)
|
|
152
|
+
* sits right on top of the results it filters.
|
|
153
|
+
* - Doherty threshold: every wait has immediate feedback (placeholders, a
|
|
154
|
+
* spinner in the box while searching, a footer loader while paging), and
|
|
155
|
+
* "no results" only appears once it is known there is nothing.
|
|
156
|
+
* - Jakob's law: the clear (×) inside the box, pull-to-refresh, the keyboard
|
|
157
|
+
* dismissed when the list is dragged.
|
|
158
|
+
* - Fitts's law: the scan button is as tall as the box, a 50pt target.
|
|
110
159
|
*/
|
|
111
160
|
export function SearchList<T>({
|
|
112
161
|
header,
|
|
@@ -115,7 +164,7 @@ export function SearchList<T>({
|
|
|
115
164
|
searchPlaceholder,
|
|
116
165
|
searchIsLoading = false,
|
|
117
166
|
onScanPress,
|
|
118
|
-
scanAccessibilityLabel
|
|
167
|
+
scanAccessibilityLabel,
|
|
119
168
|
extraActions,
|
|
120
169
|
data,
|
|
121
170
|
keyExtractor,
|
|
@@ -154,8 +203,8 @@ export function SearchList<T>({
|
|
|
154
203
|
|
|
155
204
|
const handleEndReached = onLoadMore ?? onEndReached;
|
|
156
205
|
|
|
157
|
-
//
|
|
158
|
-
//
|
|
206
|
+
// The custom footer and the pagination loader coexist: previously the first
|
|
207
|
+
// covered the second and the list stopped signaling it was fetching more.
|
|
159
208
|
const renderFooter = useCallback(() => {
|
|
160
209
|
const custom =
|
|
161
210
|
typeof ListFooterComponent === 'function' ? (
|
|
@@ -181,12 +230,12 @@ export function SearchList<T>({
|
|
|
181
230
|
);
|
|
182
231
|
}, [ListFooterComponent, isLoadingMore, resolvedLoadingMoreText, noMoreText, hasMore, data.length, styles.footer, styles.footerText]);
|
|
183
232
|
|
|
184
|
-
//
|
|
185
|
-
//
|
|
233
|
+
// Empty only once it is known there is nothing: during the initial load
|
|
234
|
+
// placeholders are shown, not "no results".
|
|
186
235
|
const renderEmpty = useCallback(() => {
|
|
187
236
|
if (isLoading || isRefreshing) return null;
|
|
188
|
-
return <EmptyState title={emptyTitle} description={emptyDescription} iconName={emptyIconName} style={
|
|
189
|
-
}, [isLoading, isRefreshing, emptyTitle, emptyDescription, emptyIconName]);
|
|
237
|
+
return <EmptyState title={emptyTitle} description={emptyDescription} iconName={emptyIconName} style={styles.empty} />;
|
|
238
|
+
}, [isLoading, isRefreshing, emptyTitle, emptyDescription, emptyIconName, styles.empty]);
|
|
190
239
|
|
|
191
240
|
if (!isReady) {
|
|
192
241
|
return (
|
|
@@ -216,11 +265,11 @@ export function SearchList<T>({
|
|
|
216
265
|
/>
|
|
217
266
|
{onScanPress && (
|
|
218
267
|
<IconButton
|
|
219
|
-
iconName="
|
|
268
|
+
iconName="scan"
|
|
220
269
|
onPress={onScanPress}
|
|
221
270
|
color={colors.onPrimary}
|
|
222
271
|
style={styles.scanButton}
|
|
223
|
-
accessibilityLabel={scanAccessibilityLabel}
|
|
272
|
+
accessibilityLabel={scanAccessibilityLabel ?? labels.scan}
|
|
224
273
|
/>
|
|
225
274
|
)}
|
|
226
275
|
{extraActions}
|
|
@@ -228,22 +277,37 @@ export function SearchList<T>({
|
|
|
228
277
|
|
|
229
278
|
<View style={styles.listArea}>
|
|
230
279
|
{showOverlayLoading && (
|
|
231
|
-
<View
|
|
280
|
+
<View
|
|
281
|
+
style={styles.loadingOverlay}
|
|
282
|
+
accessible
|
|
283
|
+
accessibilityRole="progressbar"
|
|
284
|
+
accessibilityLabel={resolvedLoadingText}
|
|
285
|
+
accessibilityState={{ busy: true }}
|
|
286
|
+
>
|
|
232
287
|
<ActivityIndicator size="large" color={colors.primary} />
|
|
233
288
|
<Text style={styles.footerText}>{resolvedLoadingText}</Text>
|
|
234
289
|
</View>
|
|
235
290
|
)}
|
|
236
291
|
|
|
237
292
|
{isInitialLoading ? (
|
|
238
|
-
<View
|
|
293
|
+
<View
|
|
294
|
+
testID="list-skeleton"
|
|
295
|
+
style={[styles.listContent, contentContainerStyle]}
|
|
296
|
+
// The placeholders are hidden from screen readers: the group
|
|
297
|
+
// announces that the list is loading instead of silence.
|
|
298
|
+
accessible
|
|
299
|
+
accessibilityRole="progressbar"
|
|
300
|
+
accessibilityLabel={resolvedLoadingText}
|
|
301
|
+
accessibilityState={{ busy: true }}
|
|
302
|
+
>
|
|
239
303
|
{Array.from({ length: skeletonCount }, (_, i) => (
|
|
240
304
|
<React.Fragment key={i}>
|
|
241
305
|
{renderSkeletonItem?.() ?? (
|
|
242
306
|
<View style={styles.skeletonRow}>
|
|
243
|
-
<Skeleton variant="circle" width={
|
|
307
|
+
<Skeleton variant="circle" width={SKELETON_AVATAR} />
|
|
244
308
|
<View style={styles.skeletonText}>
|
|
245
|
-
<Skeleton width="60%"
|
|
246
|
-
<Skeleton width="35%"
|
|
309
|
+
<Skeleton variant="text" width="60%" />
|
|
310
|
+
<Skeleton variant="text" width="35%" />
|
|
247
311
|
</View>
|
|
248
312
|
</View>
|
|
249
313
|
)}
|
package/src/organisms/Select.tsx
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState, type ComponentRef } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
ActivityIndicator,
|
|
4
4
|
Keyboard,
|
|
5
|
-
Modal,
|
|
6
5
|
Platform,
|
|
7
6
|
Pressable,
|
|
8
7
|
ScrollView,
|
|
@@ -14,6 +13,7 @@ import {
|
|
|
14
13
|
type ViewStyle,
|
|
15
14
|
} from 'react-native';
|
|
16
15
|
import { createStyles } from '../theme/createStyles';
|
|
16
|
+
import { NativeOverlay } from '../theme/NativeOverlay';
|
|
17
17
|
import { useAllowFontScaling, useColors, useLabels } from '../theme/UIProvider';
|
|
18
18
|
import { Icon } from '../icons/Icon';
|
|
19
19
|
import type { IconName } from '../icons/types';
|
|
@@ -21,37 +21,53 @@ import type { IconName } from '../icons/types';
|
|
|
21
21
|
export interface SelectOption<T> {
|
|
22
22
|
label: string;
|
|
23
23
|
value: T;
|
|
24
|
-
/**
|
|
24
|
+
/** Glyph from the library's icon set, left of the label. */
|
|
25
25
|
iconName?: IconName;
|
|
26
|
-
/** Emoji
|
|
26
|
+
/** Emoji left of the label. Takes precedence over `iconName`. */
|
|
27
27
|
emoji?: string;
|
|
28
28
|
disabled?: boolean;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export interface SelectProps<T> {
|
|
32
|
+
/** Text above the field; also its accessible name. */
|
|
32
33
|
label?: string;
|
|
34
|
+
/** Shows an asterisk next to the label. @default false */
|
|
33
35
|
required?: boolean;
|
|
36
|
+
/** Options of the dropdown: label, value and, optionally, icon, emoji or disabled. */
|
|
34
37
|
options: SelectOption<T>[];
|
|
38
|
+
/** Value of the selected option (controlled). `undefined` shows the placeholder. */
|
|
35
39
|
selectedValue: T | undefined;
|
|
40
|
+
/** Called with the value of the chosen option; the dropdown then closes. */
|
|
36
41
|
onSelect: (value: T) => void;
|
|
42
|
+
/** Text while nothing is selected. @default labels.selectPlaceholder */
|
|
37
43
|
placeholder?: string;
|
|
44
|
+
/** Error shown under the field. */
|
|
38
45
|
error?: string;
|
|
46
|
+
/** Shows a spinner in place of the chevron and blocks opening. @default false */
|
|
39
47
|
isLoading?: boolean;
|
|
48
|
+
/** Dims the field and blocks opening. @default false */
|
|
40
49
|
disabled?: boolean;
|
|
41
50
|
/** Adds a search box inside the dropdown. @default false */
|
|
42
51
|
searchable?: boolean;
|
|
52
|
+
/** Placeholder of the search box (with `searchable`). @default labels.search */
|
|
43
53
|
searchPlaceholder?: string;
|
|
44
54
|
/** Maximum height of the dropdown, if the available space allows it. @default 280 */
|
|
45
55
|
maxHeight?: number;
|
|
56
|
+
/** Called with `true` when the dropdown opens and `false` when it closes. */
|
|
46
57
|
onOpenChange?: (open: boolean) => void;
|
|
58
|
+
/** Style of the outer wrapper (label, field and error). */
|
|
47
59
|
containerStyle?: StyleProp<ViewStyle>;
|
|
48
60
|
testID?: string;
|
|
49
61
|
}
|
|
50
62
|
|
|
51
|
-
/**
|
|
63
|
+
/** Room between the field and the dropdown. */
|
|
52
64
|
const GAP = 8;
|
|
53
|
-
/**
|
|
65
|
+
/** Below this the dropdown can't even show two rows. */
|
|
54
66
|
const MIN_HEIGHT = 120;
|
|
67
|
+
/** Same height as Input and DateField, so a form's fields line up. */
|
|
68
|
+
const FIELD_MIN_HEIGHT = 50;
|
|
69
|
+
const ICON_SIZE = 20;
|
|
70
|
+
const CHECK_SIZE = 18;
|
|
55
71
|
|
|
56
72
|
const useStyles = createStyles((theme) => ({
|
|
57
73
|
container: { gap: theme.tokens.spacing.sm },
|
|
@@ -65,10 +81,11 @@ const useStyles = createStyles((theme) => ({
|
|
|
65
81
|
borderColor: theme.colors.border,
|
|
66
82
|
borderRadius: theme.tokens.radius.lg,
|
|
67
83
|
backgroundColor: theme.colors.inputBackground,
|
|
68
|
-
minHeight:
|
|
84
|
+
minHeight: FIELD_MIN_HEIGHT,
|
|
69
85
|
paddingHorizontal: theme.tokens.spacing.lg,
|
|
70
86
|
},
|
|
71
87
|
fieldOpen: { borderColor: theme.colors.primary },
|
|
88
|
+
fieldError: { borderColor: theme.colors.error },
|
|
72
89
|
fieldDisabled: { opacity: 0.5 },
|
|
73
90
|
fieldValue: { flexDirection: 'row', alignItems: 'center', gap: theme.tokens.spacing.sm, flexShrink: 1 },
|
|
74
91
|
fieldText: { fontSize: theme.tokens.fontSize.lg, color: theme.colors.text, flexShrink: 1 },
|
|
@@ -79,7 +96,7 @@ const useStyles = createStyles((theme) => ({
|
|
|
79
96
|
borderWidth: 2,
|
|
80
97
|
borderColor: theme.colors.border,
|
|
81
98
|
borderRadius: theme.tokens.radius.lg,
|
|
82
|
-
backgroundColor: theme.colors.
|
|
99
|
+
backgroundColor: theme.colors.surfaceRaised,
|
|
83
100
|
overflow: 'hidden',
|
|
84
101
|
...theme.tokens.shadow.md,
|
|
85
102
|
},
|
|
@@ -96,7 +113,7 @@ const useStyles = createStyles((theme) => ({
|
|
|
96
113
|
alignItems: 'center',
|
|
97
114
|
justifyContent: 'space-between',
|
|
98
115
|
paddingHorizontal: theme.tokens.spacing.lg,
|
|
99
|
-
paddingVertical: theme.tokens.spacing.md +
|
|
116
|
+
paddingVertical: theme.tokens.spacing.md + theme.tokens.spacing.xxs,
|
|
100
117
|
borderBottomWidth: 1,
|
|
101
118
|
borderBottomColor: theme.colors.border,
|
|
102
119
|
},
|
|
@@ -104,7 +121,7 @@ const useStyles = createStyles((theme) => ({
|
|
|
104
121
|
optionRowSelected: { backgroundColor: theme.colors.primarySoft },
|
|
105
122
|
optionRowDisabled: { opacity: 0.4 },
|
|
106
123
|
optionLabel: { flexDirection: 'row', alignItems: 'center', gap: theme.tokens.spacing.sm, flexShrink: 1 },
|
|
107
|
-
optionText: { fontSize: theme.tokens.fontSize.lg
|
|
124
|
+
optionText: { fontSize: theme.tokens.fontSize.lg, color: theme.colors.text },
|
|
108
125
|
optionTextSelected: { fontWeight: theme.tokens.fontWeight.semibold, color: theme.colors.primaryText },
|
|
109
126
|
emoji: { fontSize: theme.tokens.fontSize.lg },
|
|
110
127
|
empty: {
|
|
@@ -125,10 +142,18 @@ interface Anchor {
|
|
|
125
142
|
|
|
126
143
|
/**
|
|
127
144
|
* Select
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* Modal
|
|
145
|
+
* Single-option dropdown. The list is measured against the window and drawn
|
|
146
|
+
* inside a Modal, not in the field's tree: that way no `overflow: hidden`
|
|
147
|
+
* clips it, it doesn't depend on zIndex (unreliable on Android) and the
|
|
148
|
+
* Modal's backdrop handles closing by tapping outside and with the back button.
|
|
149
|
+
*
|
|
150
|
+
* Design:
|
|
151
|
+
* - Proximity + continuity: the list opens right against the field, toward
|
|
152
|
+
* wherever there is more room, so the eye goes from field to options.
|
|
153
|
+
* - Von Restorff: the picked option alone has the accent fill and a check.
|
|
154
|
+
* - Jakob's law: same field as Input and DateField (height, border, label,
|
|
155
|
+
* red border with an error); tap outside or Android back closes it.
|
|
156
|
+
* - Hick's law: past a handful of options, `searchable` narrows the list.
|
|
132
157
|
*/
|
|
133
158
|
export function Select<T extends string | number>({
|
|
134
159
|
label,
|
|
@@ -153,12 +178,13 @@ export function Select<T extends string | number>({
|
|
|
153
178
|
const allowFontScaling = useAllowFontScaling();
|
|
154
179
|
const { height: windowHeight } = useWindowDimensions();
|
|
155
180
|
|
|
156
|
-
const fieldRef = useRef<View
|
|
181
|
+
const fieldRef = useRef<ComponentRef<typeof View>>(null);
|
|
157
182
|
const [open, setOpen] = useState(false);
|
|
158
183
|
const [anchor, setAnchor] = useState<Anchor | null>(null);
|
|
159
184
|
const [query, setQuery] = useState('');
|
|
160
185
|
const [keyboardHeight, setKeyboardHeight] = useState(0);
|
|
161
186
|
const isDisabled = disabled || isLoading;
|
|
187
|
+
const fieldName = label ?? placeholder ?? labels.selectPlaceholder;
|
|
162
188
|
const selectedOption = options.find((o) => o.value === selectedValue);
|
|
163
189
|
|
|
164
190
|
const close = useCallback(() => {
|
|
@@ -168,16 +194,16 @@ export function Select<T extends string | number>({
|
|
|
168
194
|
onOpenChange?.(false);
|
|
169
195
|
}, [onOpenChange]);
|
|
170
196
|
|
|
171
|
-
//
|
|
172
|
-
//
|
|
197
|
+
// Opening doesn't wait for the measurement: if `measureInWindow` doesn't
|
|
198
|
+
// respond, the list still appears (centered) instead of the field ceasing to work.
|
|
173
199
|
const openList = () => {
|
|
174
200
|
fieldRef.current?.measureInWindow((x, y, width, height) => setAnchor({ x, y, width, height }));
|
|
175
201
|
setOpen(true);
|
|
176
202
|
onOpenChange?.(true);
|
|
177
203
|
};
|
|
178
204
|
|
|
179
|
-
//
|
|
180
|
-
//
|
|
205
|
+
// The search box opens the keyboard over the dropdown: without subtracting
|
|
206
|
+
// it, the list is drawn where it can no longer be seen.
|
|
181
207
|
useEffect(() => {
|
|
182
208
|
if (!open) return;
|
|
183
209
|
setKeyboardHeight(Keyboard.metrics()?.height ?? 0);
|
|
@@ -196,8 +222,8 @@ export function Select<T extends string | number>({
|
|
|
196
222
|
return options.filter((option) => option.label.toLowerCase().includes(needle));
|
|
197
223
|
}, [options, query, searchable]);
|
|
198
224
|
|
|
199
|
-
//
|
|
200
|
-
//
|
|
225
|
+
// It opens toward wherever there is more room: against the bottom edge of
|
|
226
|
+
// the screen, upward is the only way for it to fit.
|
|
201
227
|
const placement = useMemo((): ViewStyle => {
|
|
202
228
|
if (!anchor) return { left: GAP * 2, right: GAP * 2, top: windowHeight * 0.25, maxHeight };
|
|
203
229
|
const usableHeight = windowHeight - keyboardHeight;
|
|
@@ -221,7 +247,7 @@ export function Select<T extends string | number>({
|
|
|
221
247
|
</Text>
|
|
222
248
|
);
|
|
223
249
|
}
|
|
224
|
-
return option.iconName ? <Icon name={option.iconName} size={
|
|
250
|
+
return option.iconName ? <Icon name={option.iconName} size={ICON_SIZE} color={color} /> : null;
|
|
225
251
|
};
|
|
226
252
|
|
|
227
253
|
return (
|
|
@@ -235,11 +261,12 @@ export function Select<T extends string | number>({
|
|
|
235
261
|
|
|
236
262
|
<Pressable
|
|
237
263
|
ref={fieldRef}
|
|
238
|
-
style={[styles.field, open && styles.fieldOpen, isDisabled && styles.fieldDisabled]}
|
|
264
|
+
style={[styles.field, !!error && styles.fieldError, open && styles.fieldOpen, isDisabled && styles.fieldDisabled]}
|
|
239
265
|
onPress={openList}
|
|
240
266
|
disabled={isDisabled}
|
|
241
267
|
accessibilityRole="combobox"
|
|
242
|
-
accessibilityLabel={
|
|
268
|
+
accessibilityLabel={required ? `${fieldName}, ${labels.required}` : fieldName}
|
|
269
|
+
accessibilityHint={error || undefined}
|
|
243
270
|
accessibilityValue={{ text: selectedOption?.label ?? '' }}
|
|
244
271
|
accessibilityState={{ expanded: open, disabled: isDisabled }}
|
|
245
272
|
>
|
|
@@ -256,15 +283,22 @@ export function Select<T extends string | number>({
|
|
|
256
283
|
{isLoading ? (
|
|
257
284
|
<ActivityIndicator size="small" color={colors.primary} />
|
|
258
285
|
) : (
|
|
259
|
-
<Icon name={open ? 'chevron-up' : 'chevron-down'} size={
|
|
286
|
+
<Icon name={open ? 'chevron-up' : 'chevron-down'} size={ICON_SIZE} color={colors.textLight} />
|
|
260
287
|
)}
|
|
261
288
|
</Pressable>
|
|
262
289
|
|
|
263
290
|
{open && (
|
|
264
|
-
<
|
|
291
|
+
<NativeOverlay transparent animationType="fade" statusBarTranslucent onRequestClose={close}>
|
|
265
292
|
<Pressable style={styles.backdrop} onPress={close} testID="select-backdrop" />
|
|
266
293
|
|
|
267
|
-
<View
|
|
294
|
+
<View
|
|
295
|
+
style={[styles.sheet, placement]}
|
|
296
|
+
accessibilityRole="radiogroup"
|
|
297
|
+
// On iOS it may be drawn inside another open overlay: the reader stays in the list.
|
|
298
|
+
accessibilityViewIsModal
|
|
299
|
+
onAccessibilityEscape={close}
|
|
300
|
+
testID="select-list"
|
|
301
|
+
>
|
|
268
302
|
{searchable && (
|
|
269
303
|
<TextInput
|
|
270
304
|
allowFontScaling={allowFontScaling}
|
|
@@ -309,7 +343,7 @@ export function Select<T extends string | number>({
|
|
|
309
343
|
{option.label}
|
|
310
344
|
</Text>
|
|
311
345
|
</View>
|
|
312
|
-
{isSelected && <Icon name="check" size={
|
|
346
|
+
{isSelected && <Icon name="check" size={CHECK_SIZE} color={colors.primary} />}
|
|
313
347
|
</Pressable>
|
|
314
348
|
);
|
|
315
349
|
})}
|
|
@@ -321,7 +355,7 @@ export function Select<T extends string | number>({
|
|
|
321
355
|
)}
|
|
322
356
|
</ScrollView>
|
|
323
357
|
</View>
|
|
324
|
-
</
|
|
358
|
+
</NativeOverlay>
|
|
325
359
|
)}
|
|
326
360
|
|
|
327
361
|
{!!error && (
|