orn-ui 0.2.9 → 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 +67 -52
- package/README.md +5 -9
- 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 +197 -156
- 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 +199 -158
- 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 +223 -165
- 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
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import React, { createContext, useContext, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
2
|
+
import { Modal, Platform, View, type ModalProps, type ViewStyle } from 'react-native';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* NativeOverlay
|
|
6
|
+
* The native `Modal` every orn-ui overlay (Alert, BottomSheet, Modal, Select)
|
|
7
|
+
* goes through.
|
|
8
|
+
*
|
|
9
|
+
* Why: iOS presents a native modal from the root view controller and only one
|
|
10
|
+
* at a time. A second `<Modal>` that is not rendered inside the first one is
|
|
11
|
+
* silently ignored while the first is presented, and also while it is still
|
|
12
|
+
* being dismissed. In practice an Alert from `showAlert()` over an open
|
|
13
|
+
* BottomSheet never showed, and neither did one opened right after closing a
|
|
14
|
+
* sheet. Android opens a window per modal and never had the problem: there
|
|
15
|
+
* this is a plain `Modal`.
|
|
16
|
+
*
|
|
17
|
+
* iOS, when an overlay becomes visible:
|
|
18
|
+
* - Rendered inside another overlay (nested): plain native modal; iOS presents
|
|
19
|
+
* it from that one, which works.
|
|
20
|
+
* - Another overlay is presented and this one is not inside it: it is drawn
|
|
21
|
+
* as a layer inside the topmost presented overlay instead of presenting a
|
|
22
|
+
* second native modal. Its React context then comes from that overlay's
|
|
23
|
+
* position: a provider that wraps only this overlay doesn't reach it.
|
|
24
|
+
* - Another overlay is closing (exit animation or native dismissal): it waits
|
|
25
|
+
* until that one is gone, then presents.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
export interface NativeOverlayProps
|
|
29
|
+
extends Pick<
|
|
30
|
+
ModalProps,
|
|
31
|
+
'transparent' | 'animationType' | 'presentationStyle' | 'statusBarTranslucent' | 'onRequestClose' | 'hardwareAccelerated'
|
|
32
|
+
> {
|
|
33
|
+
/**
|
|
34
|
+
* The overlay is logically hidden but still mounted to play its exit
|
|
35
|
+
* animation: it stays on screen, but no other overlay may layer into it.
|
|
36
|
+
*/
|
|
37
|
+
closing?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* A dialog (Alert) always ends up on top: whatever gets layered into it is
|
|
40
|
+
* drawn beneath its own content, and as a layer it is drawn after any
|
|
41
|
+
* non-dialog layer of the same host.
|
|
42
|
+
*/
|
|
43
|
+
dialog?: boolean;
|
|
44
|
+
children: React.ReactNode;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type Mode = 'pending' | 'native' | 'layer' | 'wait';
|
|
48
|
+
|
|
49
|
+
interface Host {
|
|
50
|
+
id: number;
|
|
51
|
+
closing: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const fill: ViewStyle = { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 };
|
|
55
|
+
/** After unmount, how long iOS may still be dismissing the native modal. */
|
|
56
|
+
const DISMISS_SETTLE_MS = 450;
|
|
57
|
+
|
|
58
|
+
// Module-level registry: overlays live in different React subtrees, and the
|
|
59
|
+
// constraint they share (one native presentation at a time) is global too.
|
|
60
|
+
const hosts: Host[] = [];
|
|
61
|
+
const layers = new Map<number, Map<number, { node: React.ReactNode; dialog: boolean }>>();
|
|
62
|
+
const ownerOf = new Map<number, number>();
|
|
63
|
+
let nextId = 1;
|
|
64
|
+
|
|
65
|
+
// Two channels, so publishing a layer (every render of a layered overlay)
|
|
66
|
+
// only re-renders that host's outlet, never the overlays themselves — which
|
|
67
|
+
// would publish again and loop.
|
|
68
|
+
const hostListeners = new Set<() => void>();
|
|
69
|
+
const layerListeners = new Map<number, Set<() => void>>();
|
|
70
|
+
|
|
71
|
+
/** The host stack changed: waiting/layered overlays re-evaluate. */
|
|
72
|
+
function notify() {
|
|
73
|
+
hostListeners.forEach((listener) => listener());
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** A host's layers changed: only its outlet re-renders. */
|
|
77
|
+
function notifyLayers(hostId: number) {
|
|
78
|
+
layerListeners.get(hostId)?.forEach((listener) => listener());
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function useChannel(subscribe: (listener: () => void) => () => void) {
|
|
82
|
+
const [, setVersion] = useState(0);
|
|
83
|
+
useEffect(() => subscribe(() => setVersion((v) => v + 1)), [subscribe]);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const subscribeHosts = (listener: () => void) => {
|
|
87
|
+
hostListeners.add(listener);
|
|
88
|
+
return () => {
|
|
89
|
+
hostListeners.delete(listener);
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
function liveTop(): Host | undefined {
|
|
94
|
+
const top = hosts[hosts.length - 1];
|
|
95
|
+
return top && !top.closing ? top : undefined;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function decide(parentHost: number | null): Mode {
|
|
99
|
+
if (hosts.length === 0) return 'native';
|
|
100
|
+
const top = liveTop();
|
|
101
|
+
if (!top) return 'wait';
|
|
102
|
+
// Nested inside the topmost overlay: iOS can present it from there.
|
|
103
|
+
if (top.id === parentHost) return 'native';
|
|
104
|
+
return 'layer';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function unpublish(id: number) {
|
|
108
|
+
const owner = ownerOf.get(id);
|
|
109
|
+
if (owner === undefined) return;
|
|
110
|
+
layers.get(owner)?.delete(id);
|
|
111
|
+
ownerOf.delete(id);
|
|
112
|
+
notifyLayers(owner);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Takes the native slot right away, before any later overlay decides. */
|
|
116
|
+
function claim(id: number, closing: boolean) {
|
|
117
|
+
if (hosts.some((h) => h.id === id)) return;
|
|
118
|
+
hosts.push({ id, closing });
|
|
119
|
+
notify();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function removeHost(id: number) {
|
|
123
|
+
const index = hosts.findIndex((h) => h.id === id);
|
|
124
|
+
if (index < 0) return;
|
|
125
|
+
hosts.splice(index, 1);
|
|
126
|
+
notify();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Id of the native overlay this subtree is rendered in, if any. */
|
|
130
|
+
const HostContext = createContext<number | null>(null);
|
|
131
|
+
|
|
132
|
+
/** Draws, inside a native host, the layers other overlays put into it. */
|
|
133
|
+
function LayerOutlet({ hostId }: { hostId: number }) {
|
|
134
|
+
const [, setVersion] = useState(0);
|
|
135
|
+
// Layout effect + an immediate re-read: a layer may have been published in
|
|
136
|
+
// the same commit, before this outlet subscribed.
|
|
137
|
+
useLayoutEffect(() => {
|
|
138
|
+
const listener = () => setVersion((v) => v + 1);
|
|
139
|
+
if (!layerListeners.has(hostId)) layerListeners.set(hostId, new Set());
|
|
140
|
+
layerListeners.get(hostId)!.add(listener);
|
|
141
|
+
if (layers.get(hostId)?.size) listener();
|
|
142
|
+
return () => {
|
|
143
|
+
layerListeners.get(hostId)?.delete(listener);
|
|
144
|
+
};
|
|
145
|
+
}, [hostId]);
|
|
146
|
+
const own = layers.get(hostId);
|
|
147
|
+
if (!own || own.size === 0) return null;
|
|
148
|
+
// Dialogs last, so they are drawn on top of sheets and modals.
|
|
149
|
+
const ordered = [...own.entries()].sort(([, a], [, b]) => Number(a.dialog) - Number(b.dialog));
|
|
150
|
+
return (
|
|
151
|
+
<>
|
|
152
|
+
{ordered.map(([key, layer]) => (
|
|
153
|
+
<View key={key} style={fill} pointerEvents="box-none">
|
|
154
|
+
{layer.node}
|
|
155
|
+
</View>
|
|
156
|
+
))}
|
|
157
|
+
</>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function NativeOverlay({ closing = false, dialog = false, children, ...modalProps }: NativeOverlayProps) {
|
|
162
|
+
const ios = Platform.OS === 'ios';
|
|
163
|
+
const parentHost = useContext(HostContext);
|
|
164
|
+
const idRef = useRef(0);
|
|
165
|
+
if (idRef.current === 0) idRef.current = nextId++;
|
|
166
|
+
const id = idRef.current;
|
|
167
|
+
useChannel(subscribeHosts);
|
|
168
|
+
|
|
169
|
+
// Decided in a layout effect, not during render: layout effects run in tree
|
|
170
|
+
// order before paint, so when two overlays mount in the same commit the
|
|
171
|
+
// first one claims the native slot before the second one decides.
|
|
172
|
+
const [mode, setMode] = useState<Mode>(ios ? 'pending' : 'native');
|
|
173
|
+
|
|
174
|
+
useLayoutEffect(() => {
|
|
175
|
+
if (!ios) return;
|
|
176
|
+
if (mode === 'pending') {
|
|
177
|
+
const next = decide(parentHost);
|
|
178
|
+
if (next === 'native') claim(id, closing);
|
|
179
|
+
setMode(next);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (mode === 'wait' && !closing) {
|
|
183
|
+
const next = decide(parentHost);
|
|
184
|
+
if (next === 'native') claim(id, closing);
|
|
185
|
+
if (next !== 'wait') setMode(next);
|
|
186
|
+
}
|
|
187
|
+
// The host it layered into started closing or went away: move out.
|
|
188
|
+
if (mode === 'layer' && !closing) {
|
|
189
|
+
const owner = ownerOf.get(id);
|
|
190
|
+
const host = owner === undefined ? undefined : hosts.find((h) => h.id === owner);
|
|
191
|
+
if (owner !== undefined && (!host || host.closing)) {
|
|
192
|
+
unpublish(id);
|
|
193
|
+
const next = decide(parentHost);
|
|
194
|
+
if (next === 'native') claim(id, closing);
|
|
195
|
+
setMode(next);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Native host bookkeeping.
|
|
201
|
+
useLayoutEffect(() => {
|
|
202
|
+
if (!ios || mode !== 'native') return;
|
|
203
|
+
const existing = hosts.find((h) => h.id === id);
|
|
204
|
+
if (!existing) hosts.push({ id, closing });
|
|
205
|
+
else if (existing.closing !== closing) existing.closing = closing;
|
|
206
|
+
else return;
|
|
207
|
+
notify();
|
|
208
|
+
}, [ios, mode, closing, id]);
|
|
209
|
+
|
|
210
|
+
// Layer publishing: every render, so prop changes reach the host.
|
|
211
|
+
useLayoutEffect(() => {
|
|
212
|
+
if (!ios || mode !== 'layer') return;
|
|
213
|
+
let owner = ownerOf.get(id);
|
|
214
|
+
if (owner === undefined) {
|
|
215
|
+
owner = liveTop()?.id;
|
|
216
|
+
// The host it chose started closing in the same commit (a sheet closed
|
|
217
|
+
// and an alert opened in one tick): decide again, usually to wait.
|
|
218
|
+
if (owner === undefined) {
|
|
219
|
+
const next = decide(parentHost);
|
|
220
|
+
if (next === 'native') claim(id, closing);
|
|
221
|
+
setMode(next);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
ownerOf.set(id, owner);
|
|
225
|
+
}
|
|
226
|
+
if (!layers.has(owner)) layers.set(owner, new Map());
|
|
227
|
+
layers.get(owner)!.set(id, { node: <HostContext.Provider value={owner}>{children}</HostContext.Provider>, dialog });
|
|
228
|
+
notifyLayers(owner);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Unmount: a layer just leaves; a native host stays "closing" while iOS
|
|
232
|
+
// finishes dismissing it, so the next overlay doesn't present too early.
|
|
233
|
+
useEffect(
|
|
234
|
+
() => () => {
|
|
235
|
+
if (!ios) return;
|
|
236
|
+
unpublish(id);
|
|
237
|
+
const host = hosts.find((h) => h.id === id);
|
|
238
|
+
if (host) {
|
|
239
|
+
host.closing = true;
|
|
240
|
+
notify();
|
|
241
|
+
setTimeout(() => removeHost(id), DISMISS_SETTLE_MS);
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
[ios, id]
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
if (!ios || mode === 'native') {
|
|
248
|
+
return (
|
|
249
|
+
<Modal visible {...modalProps}>
|
|
250
|
+
<HostContext.Provider value={id}>
|
|
251
|
+
{/* A dialog host draws what was layered into it underneath itself. */}
|
|
252
|
+
{ios && dialog && <LayerOutlet hostId={id} />}
|
|
253
|
+
{children}
|
|
254
|
+
{ios && !dialog && <LayerOutlet hostId={id} />}
|
|
255
|
+
</HostContext.Provider>
|
|
256
|
+
</Modal>
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// 'layer' renders inside the host; 'wait' and 'pending' render nothing yet.
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Test-only: clears the registry between tests. */
|
|
265
|
+
export function __resetNativeOverlays() {
|
|
266
|
+
hosts.splice(0);
|
|
267
|
+
layers.clear();
|
|
268
|
+
ownerOf.clear();
|
|
269
|
+
}
|
package/src/theme/UIProvider.tsx
CHANGED
|
@@ -1,30 +1,58 @@
|
|
|
1
|
-
import React, { createContext, useContext, useMemo, useState, useCallback,
|
|
2
|
-
import { Appearance, useColorScheme } from 'react-native';
|
|
1
|
+
import React, { createContext, useContext, useMemo, useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import { Appearance, useColorScheme, type ViewStyle } from 'react-native';
|
|
3
|
+
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
3
4
|
import { defaultTheme } from './createTheme';
|
|
4
5
|
import { defaultLabels, type Labels } from './labels';
|
|
5
|
-
import {
|
|
6
|
+
import type { EdgeInsets } from './insets';
|
|
6
7
|
import type { Theme, ThemePair } from './types';
|
|
7
8
|
import { renderDefaultIcon } from '../icons/defaultIcons';
|
|
8
9
|
import type { IconRenderer } from '../icons/types';
|
|
10
|
+
import { KeyboardHandled, KeyboardPanArea } from './KeyboardAvoidance';
|
|
9
11
|
|
|
10
12
|
export type ThemeMode = 'system' | 'light' | 'dark';
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* monta un `<SafeAreaProvider>` nuevo y remide dentro de esa ventana.
|
|
15
|
+
* A native `<Modal>`/`<BottomSheet>` opens its own window (always on Android;
|
|
16
|
+
* on iOS depending on `presentationStyle`), so the insets measured at the app
|
|
17
|
+
* root may not hold in there. The boundary mounts a new `<SafeAreaProvider>`
|
|
18
|
+
* inside that window and re-measures; until the first measurement arrives it
|
|
19
|
+
* repeats the ambient insets.
|
|
19
20
|
*/
|
|
20
21
|
export interface ModalSafeAreaBoundaryProps {
|
|
21
22
|
children: (insets: EdgeInsets) => React.ReactNode;
|
|
22
23
|
}
|
|
23
24
|
export type ModalSafeAreaBoundaryComponent = React.ComponentType<ModalSafeAreaBoundaryProps>;
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
const probeStyle: ViewStyle = { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 };
|
|
27
|
+
|
|
28
|
+
function sameInsets(a: EdgeInsets, b: EdgeInsets) {
|
|
29
|
+
return a.top === b.top && a.right === b.right && a.bottom === b.bottom && a.left === b.left;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function InsetsProbe({ onMeasure }: { onMeasure: (insets: EdgeInsets) => void }) {
|
|
33
|
+
const insets = useSafeAreaInsets();
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
onMeasure(insets);
|
|
36
|
+
}, [insets, onMeasure]);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function RemeasuringModalSafeAreaBoundary({ children }: ModalSafeAreaBoundaryProps) {
|
|
41
|
+
const ambient = useInsets();
|
|
42
|
+
const [measured, setMeasured] = useState<EdgeInsets | null>(null);
|
|
43
|
+
const onMeasure = useCallback(
|
|
44
|
+
(next: EdgeInsets) => setMeasured((prev) => (prev && sameInsets(prev, next) ? prev : next)),
|
|
45
|
+
[]
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
<SafeAreaProvider style={probeStyle} pointerEvents="none">
|
|
51
|
+
<InsetsProbe onMeasure={onMeasure} />
|
|
52
|
+
</SafeAreaProvider>
|
|
53
|
+
{children(measured ?? ambient)}
|
|
54
|
+
</>
|
|
55
|
+
);
|
|
28
56
|
}
|
|
29
57
|
|
|
30
58
|
interface UIContextValue {
|
|
@@ -51,38 +79,79 @@ export interface UIProviderProps {
|
|
|
51
79
|
onModeChange?: (mode: ThemeMode) => void;
|
|
52
80
|
/** Icon renderer. Defaults to the included zero-dep glyphs. */
|
|
53
81
|
icons?: IconRenderer;
|
|
54
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* Overrides the measured safe area insets. Leave it out: the provider
|
|
84
|
+
* measures them itself with react-native-safe-area-context.
|
|
85
|
+
*/
|
|
55
86
|
insets?: EdgeInsets;
|
|
56
87
|
labels?: Partial<Labels>;
|
|
57
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* Lets every orn-ui text follow the system font size (Dynamic Type / Android
|
|
90
|
+
* font scale). Opt-in: layouts are tuned for the default size, so turn it on
|
|
91
|
+
* only after checking your screens at large sizes.
|
|
92
|
+
* @default false
|
|
93
|
+
*/
|
|
58
94
|
allowFontScaling?: boolean;
|
|
59
95
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
96
|
+
* Mounts a `<SafeAreaProvider>` around the app. Pass `false` only if the app
|
|
97
|
+
* already mounts one above (e.g. expo-router does): insets are then read
|
|
98
|
+
* from that one.
|
|
99
|
+
* @default true
|
|
100
|
+
*/
|
|
101
|
+
mountSafeAreaProvider?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Keeps the focused Input visible above the keyboard by moving the whole app
|
|
104
|
+
* up just enough (like Android's adjustPan, on both platforms), with no
|
|
105
|
+
* Screen or ScrollView needed. Only the root provider does it. Turn it off if
|
|
106
|
+
* your app already handles the keyboard its own way.
|
|
107
|
+
* @default true
|
|
63
108
|
*/
|
|
64
|
-
|
|
109
|
+
keyboardAvoidance?: boolean;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface UIProviderBaseProps extends Omit<UIProviderProps, 'insets' | 'mountSafeAreaProvider'> {
|
|
113
|
+
insets: EdgeInsets;
|
|
65
114
|
}
|
|
66
115
|
|
|
67
116
|
/**
|
|
68
117
|
* UIProvider
|
|
69
|
-
*
|
|
70
|
-
* override
|
|
71
|
-
*
|
|
72
|
-
*
|
|
118
|
+
* The library's single entry point: resolves the active theme (system or
|
|
119
|
+
* manual override), measures the safe area insets (react-native-safe-area-context,
|
|
120
|
+
* a required peer) and injects icons, insets and default texts into the whole
|
|
121
|
+
* tree. It doesn't persist the mode — if the consumer wants to remember the
|
|
122
|
+
* preference across sessions, it controls `mode`/`onModeChange` and persists
|
|
123
|
+
* wherever it prefers.
|
|
73
124
|
*/
|
|
74
|
-
export function UIProvider({
|
|
125
|
+
export function UIProvider({ mountSafeAreaProvider = true, ...props }: UIProviderProps) {
|
|
126
|
+
if (!mountSafeAreaProvider) return <MeasuredUIProvider {...props} />;
|
|
127
|
+
return (
|
|
128
|
+
<SafeAreaProvider>
|
|
129
|
+
<MeasuredUIProvider {...props} />
|
|
130
|
+
</SafeAreaProvider>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function MeasuredUIProvider({ insets: override, ...rest }: Omit<UIProviderProps, 'mountSafeAreaProvider'>) {
|
|
135
|
+
const measured = useSafeAreaInsets();
|
|
136
|
+
return <UIProviderBase insets={override ?? measured} {...rest} />;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function UIProviderBase({
|
|
75
140
|
children,
|
|
76
141
|
theme = defaultTheme,
|
|
77
142
|
defaultMode = 'system',
|
|
78
143
|
mode: controlledMode,
|
|
79
144
|
onModeChange,
|
|
80
145
|
icons = renderDefaultIcon,
|
|
81
|
-
insets
|
|
146
|
+
insets,
|
|
82
147
|
labels: labelOverrides,
|
|
83
148
|
allowFontScaling = false,
|
|
84
|
-
|
|
85
|
-
}:
|
|
149
|
+
keyboardAvoidance = true,
|
|
150
|
+
}: UIProviderBaseProps) {
|
|
151
|
+
// Only the root provider shifts the app: a nested one (a modal with its own
|
|
152
|
+
// provider, a test) wraps nothing and doesn't change the layout.
|
|
153
|
+
const isRoot = useContext(UIContext) === undefined;
|
|
154
|
+
const ModalSafeAreaBoundary = RemeasuringModalSafeAreaBoundary;
|
|
86
155
|
const systemScheme = useColorScheme();
|
|
87
156
|
const [uncontrolledMode, setUncontrolledMode] = useState<ThemeMode>(defaultMode);
|
|
88
157
|
const isControlled = controlledMode !== undefined;
|
|
@@ -96,23 +165,6 @@ export function UIProvider({
|
|
|
96
165
|
[isControlled, onModeChange]
|
|
97
166
|
);
|
|
98
167
|
|
|
99
|
-
// Por instancia, no por módulo: dos `<UIProvider>` en el mismo árbol (tests,
|
|
100
|
-
// un modal con su propio provider) avisan cada uno si le faltan insets, en
|
|
101
|
-
// vez de que el segundo se calle porque el primero ya avisó una vez.
|
|
102
|
-
const warnedAboutInsets = useRef(false);
|
|
103
|
-
|
|
104
|
-
// Olvidar `insets` no rompe nada visible al montar: el default de ceros
|
|
105
|
-
// recién se nota cuando abrís un Modal `full` (se mete abajo del notch) o un
|
|
106
|
-
// BottomSheet (queda pegado a la barra de gestos). El aviso llega antes.
|
|
107
|
-
if (__DEV__ && insets === zeroInsets && !warnedAboutInsets.current) {
|
|
108
|
-
warnedAboutInsets.current = true;
|
|
109
|
-
console.warn(
|
|
110
|
-
'orn-ui: <UIProvider> sin `insets`, se usan {top:0,bottom:0,left:0,right:0}. ' +
|
|
111
|
-
'Los modales y el Screen quedan sin safe area. Pasá `insets={useSafeAreaInsets()}` ' +
|
|
112
|
-
"o usá <SafeAreaUIProvider> de 'orn-ui/safe-area', que lo cablea solo."
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
168
|
const scheme: 'light' | 'dark' =
|
|
117
169
|
mode === 'system' ? (systemScheme === 'dark' ? 'dark' : 'light') : mode;
|
|
118
170
|
|
|
@@ -137,7 +189,18 @@ export function UIProvider({
|
|
|
137
189
|
[resolvedTheme, mode, setMode, icons, insets, labels, allowFontScaling, ModalSafeAreaBoundary]
|
|
138
190
|
);
|
|
139
191
|
|
|
140
|
-
|
|
192
|
+
let content = children;
|
|
193
|
+
if (isRoot) {
|
|
194
|
+
content = keyboardAvoidance ? (
|
|
195
|
+
<KeyboardPanArea margin={resolvedTheme.tokens.spacing.md} topLimit={insets.top}>
|
|
196
|
+
{children}
|
|
197
|
+
</KeyboardPanArea>
|
|
198
|
+
) : (
|
|
199
|
+
<KeyboardHandled>{children}</KeyboardHandled>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return <UIContext.Provider value={value}>{content}</UIContext.Provider>;
|
|
141
204
|
}
|
|
142
205
|
|
|
143
206
|
function useUIContext(): UIContextValue {
|
|
@@ -177,10 +240,10 @@ export function useAllowFontScaling(): boolean {
|
|
|
177
240
|
return useUIContext().allowFontScaling;
|
|
178
241
|
}
|
|
179
242
|
|
|
180
|
-
/**
|
|
243
|
+
/** Internal: consumed by `Modal`/`BottomSheet` to re-measure insets inside their own native window. */
|
|
181
244
|
export function useModalSafeAreaBoundary(): ModalSafeAreaBoundaryComponent {
|
|
182
245
|
return useUIContext().ModalSafeAreaBoundary;
|
|
183
246
|
}
|
|
184
247
|
|
|
185
|
-
/** Re-
|
|
248
|
+
/** Re-exported so tests/consumers can simulate system changes without real RN. */
|
|
186
249
|
export const __testUtils = { Appearance };
|
package/src/theme/colors.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Dependency-free color utilities, used to derive a family of roles
|
|
3
|
+
* (fill / on-top / soft / readable) from a single brand hex.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
/**
|
|
6
|
+
/** Minimum AA contrast for normal text. */
|
|
7
7
|
export const AA_CONTRAST = 4.5;
|
|
8
8
|
|
|
9
9
|
export interface Rgb {
|
|
@@ -18,15 +18,15 @@ export interface Hsl {
|
|
|
18
18
|
l: number;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/** Roles
|
|
21
|
+
/** Roles a theme accent needs, in a given scheme. */
|
|
22
22
|
export interface AccentRoles {
|
|
23
|
-
/**
|
|
23
|
+
/** Solid fill: button background, current step's circle. */
|
|
24
24
|
base: string;
|
|
25
|
-
/**
|
|
25
|
+
/** Text/icon placed on top of the fill. */
|
|
26
26
|
on: string;
|
|
27
|
-
/**
|
|
27
|
+
/** Subtle background: badges, active indicator pill. */
|
|
28
28
|
soft: string;
|
|
29
|
-
/**
|
|
29
|
+
/** The accent legible on `surface`: labels, links. */
|
|
30
30
|
text: string;
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -35,7 +35,7 @@ const HEX = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i;
|
|
|
35
35
|
export function hexToRgb(hex: string): Rgb {
|
|
36
36
|
const match = HEX.exec(hex.trim());
|
|
37
37
|
if (!match) {
|
|
38
|
-
throw new Error(`orn-ui:
|
|
38
|
+
throw new Error(`orn-ui: expected a hex color like "#7c3aed", got "${hex}".`);
|
|
39
39
|
}
|
|
40
40
|
const digits = match[1]!;
|
|
41
41
|
const full = digits.length === 3 ? digits.replace(/./g, (c) => c + c) : digits;
|
|
@@ -100,13 +100,13 @@ function channelLuminance(value: number) {
|
|
|
100
100
|
return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
/**
|
|
103
|
+
/** WCAG relative luminance of a hex. */
|
|
104
104
|
export function luminance(hex: string): number {
|
|
105
105
|
const { r, g, b } = hexToRgb(hex);
|
|
106
106
|
return 0.2126 * channelLuminance(r) + 0.7152 * channelLuminance(g) + 0.0722 * channelLuminance(b);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
/**
|
|
109
|
+
/** WCAG contrast ratio between two hexes: 1 (equal) to 21 (white on black). */
|
|
110
110
|
export function contrast(a: string, b: string): number {
|
|
111
111
|
const la = luminance(a);
|
|
112
112
|
const lb = luminance(b);
|
|
@@ -114,8 +114,8 @@ export function contrast(a: string, b: string): number {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
117
|
+
* White if it reaches AA contrast —which is what you expect on top of a color
|
|
118
|
+
* fill— and otherwise whichever extreme gives the most contrast.
|
|
119
119
|
*/
|
|
120
120
|
function bestOnColor(background: string, light: string, dark: string): string {
|
|
121
121
|
if (contrast(background, light) >= AA_CONTRAST) return light;
|
|
@@ -123,9 +123,9 @@ function bestOnColor(background: string, light: string, dark: string): string {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
/**
|
|
126
|
-
*
|
|
127
|
-
* `against`.
|
|
128
|
-
*
|
|
126
|
+
* Shifts the hue's lightness until it reaches the requested contrast against
|
|
127
|
+
* `against`. If not even the extreme gets there —pure yellow on white, for
|
|
128
|
+
* example— it returns the most contrasting one found along the way.
|
|
129
129
|
*/
|
|
130
130
|
function reachContrast(hsl: Hsl, against: string, target: number, direction: 'darker' | 'lighter'): string {
|
|
131
131
|
const step = direction === 'darker' ? -0.02 : 0.02;
|
|
@@ -146,9 +146,9 @@ function reachContrast(hsl: Hsl, against: string, target: number, direction: 'da
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
/**
|
|
149
|
-
*
|
|
150
|
-
* `surface`
|
|
151
|
-
*
|
|
149
|
+
* Derives an accent's four roles from a hex, for one scheme.
|
|
150
|
+
* `surface` is the background the `text` role has to be read on, so each
|
|
151
|
+
* scheme passes its own.
|
|
152
152
|
*/
|
|
153
153
|
export function deriveAccent(hex: string, scheme: 'light' | 'dark', surface: string): AccentRoles {
|
|
154
154
|
const hsl = toHsl(hex);
|
|
@@ -163,8 +163,8 @@ export function deriveAccent(hex: string, scheme: 'light' | 'dark', surface: str
|
|
|
163
163
|
};
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
//
|
|
167
|
-
// #004cef
|
|
166
|
+
// In dark the accent goes up in lightness and what sits on top goes down: a
|
|
167
|
+
// #004cef over #1e1e1e can't be told apart from the background.
|
|
168
168
|
const base = toHex({ ...hsl, l: 0.72 });
|
|
169
169
|
return {
|
|
170
170
|
base,
|
|
@@ -1,26 +1,30 @@
|
|
|
1
|
-
import { StyleSheet } from 'react-native';
|
|
1
|
+
import { StyleSheet, type ImageStyle, type TextStyle, type ViewStyle } from 'react-native';
|
|
2
2
|
import { useTheme } from './UIProvider';
|
|
3
3
|
import type { Theme } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* createStyles(factory)
|
|
7
|
-
*
|
|
8
|
-
* theme (WeakMap<Theme, Styles>),
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* Returns a useStyles() hook that memoizes the resulting StyleSheet per
|
|
8
|
+
* theme (WeakMap<Theme, Styles>), not per component instance. All rows of a
|
|
9
|
+
* list share the same styles object: without this, N rows ⇒ N
|
|
10
|
+
* StyleSheet.create() per render, which is what every component of the
|
|
11
|
+
* original app did with `useMemo(() => createStyles(colors), [colors])`.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* The cache lives in the factory's closure (one WeakMap per createStyles
|
|
14
|
+
* call, i.e. one per component), so themes from different <UIProvider>s in
|
|
15
|
+
* the same tree don't collide with each other.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* The `factory` type follows RN's pattern for typed StyleSheets
|
|
18
|
+
* (`T extends NamedStyles<T> | NamedStyles<any>`): it is what gives TS the
|
|
19
|
+
* context to infer literals ('row', 'center', ...) instead of widening them
|
|
20
|
+
* to `string` inside the returned object. `NamedStyles` is declared here:
|
|
21
|
+
* react-native 0.88's generated types (Expo SDK 58) no longer export
|
|
22
|
+
* `StyleSheet.NamedStyles`.
|
|
21
23
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
type NamedStyles<T> = { [P in keyof T]: ViewStyle | TextStyle | ImageStyle };
|
|
25
|
+
|
|
26
|
+
export function createStyles<T extends NamedStyles<T> | NamedStyles<any>>(
|
|
27
|
+
factory: (theme: Theme) => T | NamedStyles<T>
|
|
24
28
|
): () => T {
|
|
25
29
|
const cache = new WeakMap<Theme, T>();
|
|
26
30
|
|
|
@@ -28,10 +32,10 @@ export function createStyles<T extends StyleSheet.NamedStyles<T> | StyleSheet.Na
|
|
|
28
32
|
const theme = useTheme();
|
|
29
33
|
let styles = cache.get(theme);
|
|
30
34
|
if (!styles) {
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
+
// The cast is safe: StyleSheet.create<T>(...) with the factory's same T
|
|
36
|
+
// always returns T; TS can't prove it on its own inside a generic body
|
|
37
|
+
// (T could be "instantiated" with an unrelated subtype from its point
|
|
38
|
+
// of view, even though at runtime it is the same).
|
|
35
39
|
styles = StyleSheet.create(factory(theme)) as T;
|
|
36
40
|
cache.set(theme, styles);
|
|
37
41
|
}
|