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
|
@@ -1,15 +1,27 @@
|
|
|
1
|
-
import React, { memo, useState } from 'react';
|
|
2
|
-
import {
|
|
1
|
+
import React, { memo, useRef, useState, type ComponentRef } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
TextInput,
|
|
4
|
+
TouchableOpacity,
|
|
5
|
+
View,
|
|
6
|
+
type AccessibilityActionEvent,
|
|
7
|
+
type StyleProp,
|
|
8
|
+
type ViewStyle,
|
|
9
|
+
} from 'react-native';
|
|
3
10
|
import { createStyles } from '../theme/createStyles';
|
|
4
|
-
import { useAllowFontScaling, useColors, useTheme } from '../theme/UIProvider';
|
|
11
|
+
import { useAllowFontScaling, useColors, useLabels, useTheme } from '../theme/UIProvider';
|
|
12
|
+
import { useKeyboardField } from '../theme/useKeyboardField';
|
|
5
13
|
import { Icon } from '../icons/Icon';
|
|
6
14
|
|
|
7
15
|
export type StepperSize = 'sm' | 'md' | 'lg';
|
|
8
16
|
|
|
9
17
|
export interface StepperProps {
|
|
18
|
+
/** Current value, as text (it may be mid-typing, e.g. "" or "1."). */
|
|
10
19
|
value: string;
|
|
20
|
+
/** Receives the cleaned-up text while typing, and the clamped value on blur. */
|
|
11
21
|
onChangeText: (text: string) => void;
|
|
22
|
+
/** "+" pressed (or swipe up with a screen reader). Do the arithmetic here. */
|
|
12
23
|
onIncrement: () => void;
|
|
24
|
+
/** "-" pressed (or swipe down with a screen reader). */
|
|
13
25
|
onDecrement: () => void;
|
|
14
26
|
/** Scale of the control: height, buttons, icon and typography. @default 'md' */
|
|
15
27
|
size?: StepperSize;
|
|
@@ -30,7 +42,11 @@ export interface StepperProps {
|
|
|
30
42
|
/** Accepts a decimal separator, normalizing comma to dot. @default false */
|
|
31
43
|
allowDecimals?: boolean;
|
|
32
44
|
style?: StyleProp<ViewStyle>;
|
|
45
|
+
/** Accessible name of the numeric field. @default labels.quantity */
|
|
46
|
+
accessibilityLabel?: string;
|
|
47
|
+
/** Accessible name of the "+" button. @default labels.increment */
|
|
33
48
|
incrementAccessibilityLabel?: string;
|
|
49
|
+
/** Accessible name of the "-" button. @default labels.decrement */
|
|
34
50
|
decrementAccessibilityLabel?: string;
|
|
35
51
|
testID?: string;
|
|
36
52
|
}
|
|
@@ -41,6 +57,14 @@ const SIZES: Record<StepperSize, { height: number; icon: number; inputWidth: num
|
|
|
41
57
|
lg: { height: 52, icon: 22, inputWidth: 64 },
|
|
42
58
|
};
|
|
43
59
|
|
|
60
|
+
/** Minimum touch target (Fitts): smaller buttons reach it with hitSlop. */
|
|
61
|
+
const MIN_TOUCH = 44;
|
|
62
|
+
|
|
63
|
+
const ADJUST_ACTIONS = [
|
|
64
|
+
{ name: 'increment' as const },
|
|
65
|
+
{ name: 'decrement' as const },
|
|
66
|
+
];
|
|
67
|
+
|
|
44
68
|
const useStyles = createStyles((theme) => ({
|
|
45
69
|
control: {
|
|
46
70
|
flexDirection: 'row',
|
|
@@ -60,12 +84,12 @@ const useStyles = createStyles((theme) => ({
|
|
|
60
84
|
borderRightWidth: 1,
|
|
61
85
|
borderColor: theme.colors.border,
|
|
62
86
|
},
|
|
63
|
-
//
|
|
64
|
-
//
|
|
87
|
+
// The background tells the field apart from a read-only value: it is the
|
|
88
|
+
// only hint that you can type there before touching it.
|
|
65
89
|
editable: { backgroundColor: theme.colors.inputBackground },
|
|
66
90
|
}));
|
|
67
91
|
|
|
68
|
-
/**
|
|
92
|
+
/** Keeps only what can be part of a number allowed by the range. */
|
|
69
93
|
export function sanitizeNumeric(text: string, { allowDecimals = false, allowNegative = false } = {}): string {
|
|
70
94
|
const normalized = allowDecimals ? text.replace(',', '.') : text;
|
|
71
95
|
const negative = allowNegative && normalized.trimStart().startsWith('-');
|
|
@@ -77,12 +101,20 @@ export function sanitizeNumeric(text: string, { allowDecimals = false, allowNega
|
|
|
77
101
|
|
|
78
102
|
/**
|
|
79
103
|
* Stepper
|
|
80
|
-
*
|
|
81
|
-
*
|
|
104
|
+
* "-" button + editable numeric input + "+" button. `size` controls only the
|
|
105
|
+
* scale and `block` only the width, so both axes combine freely.
|
|
106
|
+
*
|
|
107
|
+
* The component doesn't do the buttons' arithmetic —that lives in
|
|
108
|
+
* `onIncrement`/`onDecrement`—, but it does clean up what is typed and signals
|
|
109
|
+
* when the value hit an end of the range by disabling the button that would pass it.
|
|
82
110
|
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
111
|
+
* Gestalt, common region: the three parts share one border, so they read as a
|
|
112
|
+
* single control; the field is set apart by its fill (it can be typed into).
|
|
113
|
+
* Fitts: every button reaches 44pt, `sm` through hitSlop.
|
|
114
|
+
*
|
|
115
|
+
* Accessibility: the field is an adjustable element — VoiceOver/TalkBack users
|
|
116
|
+
* swipe up/down to step the value — and every name comes from `labels`. Like
|
|
117
|
+
* Input, it stays visible above the keyboard while typing.
|
|
86
118
|
*/
|
|
87
119
|
export const Stepper = memo(
|
|
88
120
|
({
|
|
@@ -97,19 +129,25 @@ export const Stepper = memo(
|
|
|
97
129
|
max,
|
|
98
130
|
allowDecimals = false,
|
|
99
131
|
style,
|
|
100
|
-
|
|
101
|
-
|
|
132
|
+
accessibilityLabel,
|
|
133
|
+
incrementAccessibilityLabel,
|
|
134
|
+
decrementAccessibilityLabel,
|
|
102
135
|
testID,
|
|
103
136
|
}: StepperProps) => {
|
|
104
137
|
const theme = useTheme();
|
|
105
138
|
const colors = useColors();
|
|
106
139
|
const styles = useStyles();
|
|
140
|
+
const labels = useLabels();
|
|
107
141
|
const allowFontScaling = useAllowFontScaling();
|
|
108
142
|
const [focused, setFocused] = useState(false);
|
|
143
|
+
const controlRef = useRef<ComponentRef<typeof View>>(null);
|
|
144
|
+
const keyboard = useKeyboardField(controlRef);
|
|
109
145
|
|
|
110
146
|
const { height, icon, inputWidth } = SIZES[size];
|
|
111
147
|
const fontSize =
|
|
112
148
|
size === 'sm' ? theme.tokens.fontSize.md : size === 'md' ? theme.tokens.fontSize.lg : theme.tokens.fontSize.xl;
|
|
149
|
+
const slopV = Math.max(0, (MIN_TOUCH - height) / 2);
|
|
150
|
+
const hitSlop = slopV > 0 ? { top: slopV, bottom: slopV, left: slopV, right: slopV } : undefined;
|
|
113
151
|
|
|
114
152
|
const allowNegative = min !== undefined && min < 0;
|
|
115
153
|
const numeric = Number(value);
|
|
@@ -121,15 +159,22 @@ export const Stepper = memo(
|
|
|
121
159
|
onChangeText(sanitizeNumeric(text, { allowDecimals, allowNegative }));
|
|
122
160
|
};
|
|
123
161
|
|
|
124
|
-
//
|
|
125
|
-
//
|
|
162
|
+
// Clamping to the range waits for blur: doing it while typing makes it
|
|
163
|
+
// impossible to reach "12" when the minimum is 5, because the "1" would
|
|
164
|
+
// already be corrected.
|
|
126
165
|
const handleBlur = () => {
|
|
127
166
|
setFocused(false);
|
|
167
|
+
keyboard.blur();
|
|
128
168
|
if (!isNumber) return;
|
|
129
169
|
const clamped = Math.min(max ?? numeric, Math.max(min ?? numeric, numeric));
|
|
130
170
|
if (clamped !== numeric) onChangeText(String(clamped));
|
|
131
171
|
};
|
|
132
172
|
|
|
173
|
+
const handleAccessibilityAction = (e: AccessibilityActionEvent) => {
|
|
174
|
+
if (e.nativeEvent.actionName === 'increment' && !atMax) onIncrement();
|
|
175
|
+
if (e.nativeEvent.actionName === 'decrement' && !atMin) onDecrement();
|
|
176
|
+
};
|
|
177
|
+
|
|
133
178
|
const stepButton = (
|
|
134
179
|
direction: 'decrement' | 'increment',
|
|
135
180
|
onPress: () => void,
|
|
@@ -139,6 +184,7 @@ export const Stepper = memo(
|
|
|
139
184
|
<TouchableOpacity
|
|
140
185
|
onPress={onPress}
|
|
141
186
|
disabled={disabled}
|
|
187
|
+
hitSlop={hitSlop}
|
|
142
188
|
style={[styles.btn, { width: height }]}
|
|
143
189
|
accessibilityRole="button"
|
|
144
190
|
accessibilityLabel={label}
|
|
@@ -155,6 +201,7 @@ export const Stepper = memo(
|
|
|
155
201
|
|
|
156
202
|
return (
|
|
157
203
|
<View
|
|
204
|
+
ref={controlRef}
|
|
158
205
|
testID={testID}
|
|
159
206
|
style={[
|
|
160
207
|
styles.control,
|
|
@@ -164,7 +211,7 @@ export const Stepper = memo(
|
|
|
164
211
|
style,
|
|
165
212
|
]}
|
|
166
213
|
>
|
|
167
|
-
{stepButton('decrement', onDecrement, atMin, decrementAccessibilityLabel)}
|
|
214
|
+
{stepButton('decrement', onDecrement, atMin, decrementAccessibilityLabel ?? labels.decrement)}
|
|
168
215
|
<TextInput
|
|
169
216
|
allowFontScaling={allowFontScaling}
|
|
170
217
|
style={[
|
|
@@ -176,14 +223,21 @@ export const Stepper = memo(
|
|
|
176
223
|
]}
|
|
177
224
|
value={value}
|
|
178
225
|
onChangeText={handleChangeText}
|
|
179
|
-
onFocus={() =>
|
|
226
|
+
onFocus={() => {
|
|
227
|
+
setFocused(true);
|
|
228
|
+
keyboard.focus();
|
|
229
|
+
}}
|
|
180
230
|
onBlur={handleBlur}
|
|
181
231
|
editable={editable}
|
|
182
232
|
keyboardType={allowDecimals ? 'decimal-pad' : 'number-pad'}
|
|
183
233
|
selectTextOnFocus
|
|
184
|
-
accessibilityLabel=
|
|
234
|
+
accessibilityLabel={accessibilityLabel ?? labels.quantity}
|
|
235
|
+
accessibilityRole="adjustable"
|
|
236
|
+
accessibilityValue={{ text: value }}
|
|
237
|
+
accessibilityActions={ADJUST_ACTIONS}
|
|
238
|
+
onAccessibilityAction={handleAccessibilityAction}
|
|
185
239
|
/>
|
|
186
|
-
{stepButton('increment', onIncrement, atMax, incrementAccessibilityLabel)}
|
|
240
|
+
{stepButton('increment', onIncrement, atMax, incrementAccessibilityLabel ?? labels.increment)}
|
|
187
241
|
</View>
|
|
188
242
|
);
|
|
189
243
|
}
|
package/src/molecules/Steps.tsx
CHANGED
|
@@ -1,21 +1,33 @@
|
|
|
1
|
-
import React, { memo } from 'react';
|
|
2
|
-
import {
|
|
1
|
+
import React, { memo, useEffect, useRef, useState, type ComponentRef } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
ScrollView,
|
|
4
|
+
Text,
|
|
5
|
+
TouchableOpacity,
|
|
6
|
+
View,
|
|
7
|
+
type LayoutChangeEvent,
|
|
8
|
+
type StyleProp,
|
|
9
|
+
type ViewStyle,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
import { useReduceMotion } from '../theme/useReduceMotion';
|
|
3
12
|
import { createStyles } from '../theme/createStyles';
|
|
4
|
-
import { useAllowFontScaling, useColors } from '../theme/UIProvider';
|
|
13
|
+
import { useAllowFontScaling, useColors, useLabels } from '../theme/UIProvider';
|
|
5
14
|
import { Icon } from '../icons/Icon';
|
|
6
15
|
|
|
7
16
|
export interface StepItem {
|
|
17
|
+
/** Short name of the step ("Payment"). */
|
|
8
18
|
label: string;
|
|
19
|
+
/** Optional second line with a detail ("Card or cash"). */
|
|
9
20
|
description?: string;
|
|
10
21
|
}
|
|
11
22
|
|
|
12
23
|
export type StepStatus = 'completed' | 'current' | 'pending';
|
|
13
24
|
|
|
14
25
|
export interface StepsProps {
|
|
26
|
+
/** The sequence, in order. */
|
|
15
27
|
steps: StepItem[];
|
|
16
28
|
/** Index of the current step (0-based). Earlier steps become 'completed'. */
|
|
17
29
|
current: number;
|
|
18
|
-
/** @default 'horizontal' */
|
|
30
|
+
/** Lays the steps out in a row or in a column. @default 'horizontal' */
|
|
19
31
|
orientation?: 'horizontal' | 'vertical';
|
|
20
32
|
/**
|
|
21
33
|
* What the circle of an already-completed step shows: a check, or its number.
|
|
@@ -25,51 +37,73 @@ export interface StepsProps {
|
|
|
25
37
|
/** If provided, each step is tappable and reports its index. */
|
|
26
38
|
onStepPress?: (index: number) => void;
|
|
27
39
|
style?: StyleProp<ViewStyle>;
|
|
40
|
+
/** Root testID; each step gets `<testID>-<index>`. */
|
|
41
|
+
testID?: string;
|
|
28
42
|
}
|
|
29
43
|
|
|
30
44
|
const CIRCLE = 32;
|
|
31
|
-
|
|
45
|
+
const CIRCLE_BORDER = 2;
|
|
46
|
+
const CHECK_SIZE = 18;
|
|
47
|
+
const CONNECTOR = 2;
|
|
48
|
+
/** Minimum width of a horizontal column: below it, labels break mid-word. */
|
|
32
49
|
const STEP_MIN_WIDTH = 76;
|
|
50
|
+
/** Share of the row a horizontal connector takes next to a step (flex: 1). */
|
|
51
|
+
const CONNECTOR_FLEX = 0.3;
|
|
52
|
+
const PRESSED_OPACITY = 0.7;
|
|
33
53
|
|
|
34
54
|
const useStyles = createStyles((theme) => ({
|
|
35
55
|
rowContainer: { flexDirection: 'row', alignItems: 'flex-start' },
|
|
36
56
|
columnContainer: { flexDirection: 'column' },
|
|
37
57
|
|
|
38
58
|
// horizontal
|
|
39
|
-
// ScrollView
|
|
40
|
-
//
|
|
59
|
+
// ScrollView ships with flexGrow: 1 and was eating all the available height
|
|
60
|
+
// of the container, leaving the steps stuck to the top of a huge box.
|
|
41
61
|
hScrollView: { flexGrow: 0, flexShrink: 0 },
|
|
42
62
|
hScroll: { flexGrow: 1, flexDirection: 'row', alignItems: 'flex-start' },
|
|
43
|
-
// `minWidth`
|
|
44
|
-
// ("Almacé / n"):
|
|
45
|
-
//
|
|
63
|
+
// `minWidth` is what keeps RN from breaking words in half
|
|
64
|
+
// ("Almacé / n"): rather than squeezing the column below a regular
|
|
65
|
+
// word, the row becomes scrollable.
|
|
46
66
|
hStep: { flex: 1, minWidth: STEP_MIN_WIDTH, alignItems: 'center', gap: theme.tokens.spacing.sm },
|
|
47
|
-
hLabels: { alignItems: 'center', gap:
|
|
48
|
-
//
|
|
49
|
-
//
|
|
67
|
+
hLabels: { alignItems: 'center', gap: theme.tokens.spacing.xxs, paddingHorizontal: theme.tokens.spacing.xs },
|
|
68
|
+
// The connector takes a small fraction of the width: competing on equal
|
|
69
|
+
// terms with the steps (flex: 1), each label ended up with 1/(2n-1) of the
|
|
50
70
|
// total.
|
|
51
|
-
hConnector: {
|
|
52
|
-
|
|
53
|
-
|
|
71
|
+
hConnector: {
|
|
72
|
+
height: CONNECTOR,
|
|
73
|
+
flex: CONNECTOR_FLEX,
|
|
74
|
+
minWidth: theme.tokens.spacing.md,
|
|
75
|
+
marginTop: CIRCLE / 2 - CONNECTOR / 2,
|
|
76
|
+
},
|
|
77
|
+
hLabel: { fontSize: theme.tokens.fontSize.sm, lineHeight: theme.tokens.lineHeight.sm },
|
|
78
|
+
hDescription: { fontSize: theme.tokens.fontSize.xs, lineHeight: theme.tokens.lineHeight.sm },
|
|
54
79
|
|
|
55
80
|
// vertical
|
|
56
81
|
vStep: { flexDirection: 'row', gap: theme.tokens.spacing.md },
|
|
57
82
|
vIndicatorColumn: { alignItems: 'center' },
|
|
58
|
-
vConnector: { width:
|
|
59
|
-
vLabels: { flex: 1, gap:
|
|
83
|
+
vConnector: { width: CONNECTOR, flex: 1, minHeight: theme.tokens.spacing.xl, marginVertical: theme.tokens.spacing.xs },
|
|
84
|
+
vLabels: { flex: 1, gap: theme.tokens.spacing.xxs, paddingBottom: theme.tokens.spacing.lg },
|
|
85
|
+
// The last step has no connector below: no trailing space either.
|
|
86
|
+
vLabelsLast: { paddingBottom: 0 },
|
|
60
87
|
|
|
61
88
|
circle: {
|
|
62
89
|
width: CIRCLE,
|
|
63
90
|
height: CIRCLE,
|
|
64
91
|
borderRadius: CIRCLE / 2,
|
|
65
|
-
borderWidth:
|
|
92
|
+
borderWidth: CIRCLE_BORDER,
|
|
66
93
|
alignItems: 'center',
|
|
67
94
|
justifyContent: 'center',
|
|
68
95
|
},
|
|
69
96
|
circleNumber: { fontSize: theme.tokens.fontSize.md, fontWeight: theme.tokens.fontWeight.semibold },
|
|
70
|
-
label: {
|
|
97
|
+
label: {
|
|
98
|
+
fontSize: theme.tokens.fontSize.md,
|
|
99
|
+
lineHeight: theme.tokens.lineHeight.md,
|
|
100
|
+
fontWeight: theme.tokens.fontWeight.semibold,
|
|
101
|
+
color: theme.colors.text,
|
|
102
|
+
},
|
|
103
|
+
labelCurrent: { color: theme.colors.primaryText },
|
|
104
|
+
labelPending: { color: theme.colors.textLight, fontWeight: theme.tokens.fontWeight.medium },
|
|
71
105
|
labelCentered: { textAlign: 'center' },
|
|
72
|
-
description: { fontSize: theme.tokens.fontSize.sm, color: theme.colors.textLight },
|
|
106
|
+
description: { fontSize: theme.tokens.fontSize.sm, lineHeight: theme.tokens.lineHeight.sm, color: theme.colors.textLight },
|
|
73
107
|
descriptionCentered: { textAlign: 'center' },
|
|
74
108
|
}));
|
|
75
109
|
|
|
@@ -81,15 +115,51 @@ function statusOf(index: number, current: number): StepStatus {
|
|
|
81
115
|
|
|
82
116
|
/**
|
|
83
117
|
* Steps
|
|
84
|
-
*
|
|
85
|
-
*
|
|
118
|
+
* Multi-step progress indicator. The visual base of `Wizard`, but usable on
|
|
119
|
+
* its own to show progress through a flow that is navigated some other way.
|
|
120
|
+
*
|
|
121
|
+
* Gestalt, continuity: a connector line runs through the circles, so the steps
|
|
122
|
+
* read as one path; the stretch already walked is filled with the accent.
|
|
123
|
+
* Similarity: every step is the same circle + label, and the state only
|
|
124
|
+
* changes fill and color (done = filled, current = outlined accent, pending =
|
|
125
|
+
* grey). Von Restorff: the current step is the only accent-colored label.
|
|
126
|
+
*
|
|
127
|
+
* Accessibility: each step announces "Step 2 of 3: Payment, current", with
|
|
128
|
+
* every word from `labels`.
|
|
86
129
|
*/
|
|
87
130
|
export const Steps = memo(
|
|
88
|
-
({
|
|
131
|
+
({
|
|
132
|
+
steps,
|
|
133
|
+
current,
|
|
134
|
+
orientation = 'horizontal',
|
|
135
|
+
completedIndicator = 'check',
|
|
136
|
+
onStepPress,
|
|
137
|
+
style,
|
|
138
|
+
testID,
|
|
139
|
+
}: StepsProps) => {
|
|
89
140
|
const colors = useColors();
|
|
141
|
+
const labels = useLabels();
|
|
90
142
|
const styles = useStyles();
|
|
91
143
|
const allowFontScaling = useAllowFontScaling();
|
|
92
144
|
const isVertical = orientation === 'vertical';
|
|
145
|
+
const reduceMotion = useReduceMotion();
|
|
146
|
+
|
|
147
|
+
// Horizontal overflow: when the steps don't fit, the row scrolls. A hidden
|
|
148
|
+
// step gave no hint of existing, so the current step is brought into view
|
|
149
|
+
// and the scroll bar flashes once (visibility of system status).
|
|
150
|
+
const scrollRef = useRef<ComponentRef<typeof ScrollView>>(null);
|
|
151
|
+
const [viewportWidth, setViewportWidth] = useState(0);
|
|
152
|
+
const [contentWidth, setContentWidth] = useState(0);
|
|
153
|
+
const stepX = useRef<number[]>([]);
|
|
154
|
+
const overflows = !isVertical && viewportWidth > 0 && contentWidth > viewportWidth + 1;
|
|
155
|
+
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
if (!overflows) return;
|
|
158
|
+
const x = stepX.current[current] ?? 0;
|
|
159
|
+
// A quarter of the row of lead-in: the previous step stays partly visible.
|
|
160
|
+
scrollRef.current?.scrollTo({ x: Math.max(0, x - viewportWidth / 4), animated: !reduceMotion });
|
|
161
|
+
scrollRef.current?.flashScrollIndicators();
|
|
162
|
+
}, [overflows, current, viewportWidth, reduceMotion]);
|
|
93
163
|
|
|
94
164
|
const colorsFor = (status: StepStatus) => {
|
|
95
165
|
if (status === 'completed') return { bg: colors.primary, border: colors.primary, fg: colors.onPrimary };
|
|
@@ -103,7 +173,7 @@ export const Steps = memo(
|
|
|
103
173
|
return (
|
|
104
174
|
<View style={[styles.circle, { backgroundColor: c.bg, borderColor: c.border }]}>
|
|
105
175
|
{showCheck ? (
|
|
106
|
-
<Icon name="check" size={
|
|
176
|
+
<Icon name="check" size={CHECK_SIZE} color={c.fg} />
|
|
107
177
|
) : (
|
|
108
178
|
<Text allowFontScaling={allowFontScaling} style={[styles.circleNumber, { color: c.fg }]}>
|
|
109
179
|
{index + 1}
|
|
@@ -113,15 +183,16 @@ export const Steps = memo(
|
|
|
113
183
|
);
|
|
114
184
|
};
|
|
115
185
|
|
|
116
|
-
const renderLabels = (step: StepItem, status: StepStatus, centered: boolean) => (
|
|
117
|
-
<View style={centered ? styles.hLabels : styles.vLabels}>
|
|
186
|
+
const renderLabels = (step: StepItem, status: StepStatus, centered: boolean, isLast = false) => (
|
|
187
|
+
<View style={centered ? styles.hLabels : [styles.vLabels, isLast && styles.vLabelsLast]}>
|
|
118
188
|
<Text
|
|
119
189
|
allowFontScaling={allowFontScaling}
|
|
120
190
|
style={[
|
|
121
191
|
styles.label,
|
|
122
192
|
centered && styles.labelCentered,
|
|
123
193
|
centered && styles.hLabel,
|
|
124
|
-
status === '
|
|
194
|
+
status === 'current' && styles.labelCurrent,
|
|
195
|
+
status === 'pending' && styles.labelPending,
|
|
125
196
|
]}
|
|
126
197
|
>
|
|
127
198
|
{step.label}
|
|
@@ -137,23 +208,28 @@ export const Steps = memo(
|
|
|
137
208
|
</View>
|
|
138
209
|
);
|
|
139
210
|
|
|
211
|
+
const statusLabel = { completed: labels.stepCompleted, current: labels.stepCurrent, pending: labels.stepPending };
|
|
140
212
|
const stepAccessibility = (index: number, step: StepItem, status: StepStatus) => ({
|
|
141
213
|
accessibilityRole: (onStepPress ? 'button' : 'text') as 'button' | 'text',
|
|
142
|
-
accessibilityLabel:
|
|
214
|
+
accessibilityLabel: `${labels.stepOf
|
|
215
|
+
.replace('{n}', String(index + 1))
|
|
216
|
+
.replace('{total}', String(steps.length))
|
|
217
|
+
.replace('{label}', step.label)}, ${statusLabel[status]}`,
|
|
143
218
|
accessibilityState: { selected: status === 'current', disabled: false },
|
|
219
|
+
testID: testID && `${testID}-${index}`,
|
|
144
220
|
});
|
|
145
221
|
|
|
146
222
|
if (isVertical) {
|
|
147
223
|
return (
|
|
148
|
-
<View style={[styles.columnContainer, style]}>
|
|
224
|
+
<View style={[styles.columnContainer, style]} testID={testID}>
|
|
149
225
|
{steps.map((step, index) => {
|
|
150
226
|
const status = statusOf(index, current);
|
|
151
227
|
const isLast = index === steps.length - 1;
|
|
152
228
|
const Wrapper = onStepPress ? TouchableOpacity : View;
|
|
153
|
-
//
|
|
154
|
-
// a
|
|
229
|
+
// Only touchable when there is onStepPress: passing onPress/activeOpacity
|
|
230
|
+
// to a plain View doesn't crash, but leaves them as silent noise.
|
|
155
231
|
const interactiveProps = onStepPress
|
|
156
|
-
? { onPress: () => onStepPress(index), activeOpacity:
|
|
232
|
+
? { onPress: () => onStepPress(index), activeOpacity: PRESSED_OPACITY }
|
|
157
233
|
: undefined;
|
|
158
234
|
return (
|
|
159
235
|
<Wrapper
|
|
@@ -173,7 +249,7 @@ export const Steps = memo(
|
|
|
173
249
|
/>
|
|
174
250
|
)}
|
|
175
251
|
</View>
|
|
176
|
-
{renderLabels(step, status, false)}
|
|
252
|
+
{renderLabels(step, status, false, isLast)}
|
|
177
253
|
</Wrapper>
|
|
178
254
|
);
|
|
179
255
|
})}
|
|
@@ -183,11 +259,14 @@ export const Steps = memo(
|
|
|
183
259
|
|
|
184
260
|
return (
|
|
185
261
|
<ScrollView
|
|
186
|
-
|
|
262
|
+
ref={scrollRef}
|
|
263
|
+
testID={testID}
|
|
187
264
|
horizontal
|
|
188
|
-
showsHorizontalScrollIndicator={
|
|
189
|
-
|
|
190
|
-
|
|
265
|
+
showsHorizontalScrollIndicator={overflows}
|
|
266
|
+
onLayout={(e: LayoutChangeEvent) => setViewportWidth(e.nativeEvent.layout.width)}
|
|
267
|
+
onContentSizeChange={(width: number) => setContentWidth(width)}
|
|
268
|
+
// With room to spare the steps share the row just like before
|
|
269
|
+
// (flexGrow); when they don't fit, it scrolls instead of squeezing the text.
|
|
191
270
|
style={[styles.hScrollView, style]}
|
|
192
271
|
contentContainerStyle={styles.hScroll}
|
|
193
272
|
>
|
|
@@ -196,12 +275,15 @@ export const Steps = memo(
|
|
|
196
275
|
const isLast = index === steps.length - 1;
|
|
197
276
|
const Wrapper = onStepPress ? TouchableOpacity : View;
|
|
198
277
|
const interactiveProps = onStepPress
|
|
199
|
-
? { onPress: () => onStepPress(index), activeOpacity:
|
|
278
|
+
? { onPress: () => onStepPress(index), activeOpacity: PRESSED_OPACITY }
|
|
200
279
|
: undefined;
|
|
201
280
|
return (
|
|
202
281
|
<React.Fragment key={`${step.label}-${index}`}>
|
|
203
282
|
<Wrapper
|
|
204
283
|
style={styles.hStep}
|
|
284
|
+
onLayout={(e: LayoutChangeEvent) => {
|
|
285
|
+
stepX.current[index] = e.nativeEvent.layout.x;
|
|
286
|
+
}}
|
|
205
287
|
{...interactiveProps}
|
|
206
288
|
{...stepAccessibility(index, step, status)}
|
|
207
289
|
>
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import { View, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
3
|
import { createStyles } from '../theme/createStyles';
|
|
4
|
+
import { useTheme } from '../theme/UIProvider';
|
|
4
5
|
|
|
5
6
|
export interface SymmetricGridProps<T> {
|
|
7
|
+
/** Items to lay out, in reading order. */
|
|
6
8
|
data: T[];
|
|
9
|
+
/** Renders one cell; it gets the full cell width. */
|
|
7
10
|
renderItem: (item: T, index: number) => React.ReactElement;
|
|
11
|
+
/** Stable key per item. */
|
|
8
12
|
keyExtractor: (item: T, index: number) => string;
|
|
9
13
|
/** Columns per row. @default 2 */
|
|
10
14
|
columns?: number;
|
|
@@ -16,11 +20,11 @@ export interface SymmetricGridProps<T> {
|
|
|
16
20
|
*/
|
|
17
21
|
balanceLastRow?: boolean;
|
|
18
22
|
style?: StyleProp<ViewStyle>;
|
|
23
|
+
testID?: string;
|
|
19
24
|
}
|
|
20
25
|
|
|
21
|
-
const useStyles = createStyles((
|
|
22
|
-
|
|
23
|
-
row: { flexDirection: 'row', gap: theme.tokens.spacing.md },
|
|
26
|
+
const useStyles = createStyles(() => ({
|
|
27
|
+
row: { flexDirection: 'row' },
|
|
24
28
|
rowBalanced: { justifyContent: 'center' },
|
|
25
29
|
rowFull: { justifyContent: 'flex-start' },
|
|
26
30
|
cell: { flexGrow: 0, flexShrink: 0 },
|
|
@@ -34,10 +38,16 @@ function chunk<T>(data: T[], size: number): T[][] {
|
|
|
34
38
|
|
|
35
39
|
/**
|
|
36
40
|
* SymmetricGrid
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
+
* Law of Prägnanz (good form): groups items into even rows of N columns.
|
|
42
|
+
* The last row, when incomplete, is centered by default instead of sticking
|
|
43
|
+
* to one edge — that is what makes it read as a "short row" and not as a
|
|
44
|
+
* broken layout with a gap on one side.
|
|
45
|
+
*
|
|
46
|
+
* Gestalt, similarity: every cell has exactly the same width, also in the
|
|
47
|
+
* centered short row, so items read as peers. The horizontal gap is padding
|
|
48
|
+
* inside each cell (and a negative margin on the row), not flex `gap`: a
|
|
49
|
+
* `100 / columns %` width plus a gap made every row wider than the grid and
|
|
50
|
+
* pushed the right column out of the container.
|
|
41
51
|
*/
|
|
42
52
|
export function SymmetricGrid<T>({
|
|
43
53
|
data,
|
|
@@ -47,13 +57,16 @@ export function SymmetricGrid<T>({
|
|
|
47
57
|
gap,
|
|
48
58
|
balanceLastRow = true,
|
|
49
59
|
style,
|
|
60
|
+
testID,
|
|
50
61
|
}: SymmetricGridProps<T>) {
|
|
62
|
+
const theme = useTheme();
|
|
51
63
|
const styles = useStyles();
|
|
52
64
|
const rows = useMemo(() => chunk(data, columns), [data, columns]);
|
|
65
|
+
const space = gap ?? theme.tokens.spacing.md;
|
|
53
66
|
const cellWidth = `${100 / columns}%` as const;
|
|
54
67
|
|
|
55
68
|
return (
|
|
56
|
-
<View style={[
|
|
69
|
+
<View style={[{ gap: space }, style]} testID={testID}>
|
|
57
70
|
{rows.map((row, rowIndex) => {
|
|
58
71
|
const isLastRow = rowIndex === rows.length - 1;
|
|
59
72
|
const isIncomplete = row.length < columns;
|
|
@@ -61,15 +74,18 @@ export function SymmetricGrid<T>({
|
|
|
61
74
|
|
|
62
75
|
return (
|
|
63
76
|
<View
|
|
64
|
-
// `columns`
|
|
65
|
-
//
|
|
77
|
+
// `columns` in the key: if it changes, rows don't reuse DOM nodes
|
|
78
|
+
// from a row built with a different number of columns.
|
|
66
79
|
key={`row-${columns}-${rowIndex}`}
|
|
67
|
-
style={[styles.row, centered ? styles.rowBalanced : styles.rowFull,
|
|
80
|
+
style={[styles.row, centered ? styles.rowBalanced : styles.rowFull, { marginHorizontal: -space / 2 }]}
|
|
68
81
|
>
|
|
69
82
|
{row.map((item, colIndex) => {
|
|
70
83
|
const index = rowIndex * columns + colIndex;
|
|
71
84
|
return (
|
|
72
|
-
<View
|
|
85
|
+
<View
|
|
86
|
+
key={keyExtractor(item, index)}
|
|
87
|
+
style={[styles.cell, { width: cellWidth, paddingHorizontal: space / 2 }]}
|
|
88
|
+
>
|
|
73
89
|
{renderItem(item, index)}
|
|
74
90
|
</View>
|
|
75
91
|
);
|