orn-ui 0.1.1 → 0.2.1
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/README.md +20 -2
- package/lib/commonjs/atoms/PressableScale.js +9 -10
- package/lib/commonjs/atoms/PressableScale.js.map +1 -1
- package/lib/commonjs/atoms/Skeleton.js +96 -0
- package/lib/commonjs/atoms/Skeleton.js.map +1 -0
- package/lib/commonjs/atoms/Transition.js +171 -0
- package/lib/commonjs/atoms/Transition.js.map +1 -0
- package/lib/commonjs/atoms/index.js +20 -0
- package/lib/commonjs/atoms/index.js.map +1 -1
- package/lib/commonjs/molecules/OptionCard.js +55 -22
- package/lib/commonjs/molecules/OptionCard.js.map +1 -1
- package/lib/commonjs/molecules/{ThemeToggle.js → SegmentedControl.js} +36 -36
- package/lib/commonjs/molecules/SegmentedControl.js.map +1 -0
- package/lib/commonjs/molecules/Stepper.js +124 -47
- package/lib/commonjs/molecules/Stepper.js.map +1 -1
- package/lib/commonjs/molecules/Steps.js +36 -6
- package/lib/commonjs/molecules/Steps.js.map +1 -1
- package/lib/commonjs/molecules/index.js +9 -3
- package/lib/commonjs/molecules/index.js.map +1 -1
- package/lib/commonjs/organisms/AlertProvider.js +26 -0
- package/lib/commonjs/organisms/AlertProvider.js.map +1 -1
- package/lib/commonjs/organisms/BottomSheet.js +118 -39
- package/lib/commonjs/organisms/BottomSheet.js.map +1 -1
- package/lib/commonjs/organisms/DateField.js +46 -15
- package/lib/commonjs/organisms/DateField.js.map +1 -1
- package/lib/commonjs/organisms/DatePicker.js +129 -50
- package/lib/commonjs/organisms/DatePicker.js.map +1 -1
- package/lib/commonjs/organisms/List.js +66 -44
- package/lib/commonjs/organisms/List.js.map +1 -1
- package/lib/commonjs/organisms/Modal.js +42 -87
- package/lib/commonjs/organisms/Modal.js.map +1 -1
- package/lib/commonjs/organisms/NavigationBar.js +210 -0
- package/lib/commonjs/organisms/NavigationBar.js.map +1 -0
- package/lib/commonjs/organisms/Screen.js +9 -2
- package/lib/commonjs/organisms/Screen.js.map +1 -1
- package/lib/commonjs/organisms/SearchList.js +95 -27
- package/lib/commonjs/organisms/SearchList.js.map +1 -1
- package/lib/commonjs/organisms/Select.js +222 -50
- package/lib/commonjs/organisms/Select.js.map +1 -1
- package/lib/commonjs/organisms/ThemeToggle.js +52 -0
- package/lib/commonjs/organisms/ThemeToggle.js.map +1 -0
- package/lib/commonjs/organisms/Toast.js +8 -22
- package/lib/commonjs/organisms/Toast.js.map +1 -1
- package/lib/commonjs/organisms/ToastProvider.js +24 -0
- package/lib/commonjs/organisms/ToastProvider.js.map +1 -1
- package/lib/commonjs/organisms/Wizard.js +23 -2
- package/lib/commonjs/organisms/Wizard.js.map +1 -1
- package/lib/commonjs/organisms/imperativeBridge.js +28 -0
- package/lib/commonjs/organisms/imperativeBridge.js.map +1 -0
- package/lib/commonjs/organisms/index.js +50 -0
- package/lib/commonjs/organisms/index.js.map +1 -1
- package/lib/commonjs/theme/colors.js +207 -0
- package/lib/commonjs/theme/colors.js.map +1 -0
- package/lib/commonjs/theme/createTheme.js +25 -2
- package/lib/commonjs/theme/createTheme.js.map +1 -1
- package/lib/commonjs/theme/index.js +25 -0
- package/lib/commonjs/theme/index.js.map +1 -1
- package/lib/commonjs/theme/labels.js +5 -1
- package/lib/commonjs/theme/labels.js.map +1 -1
- package/lib/commonjs/theme/palettes.js +5 -2
- package/lib/commonjs/theme/palettes.js.map +1 -1
- package/lib/module/atoms/PressableScale.js +9 -10
- package/lib/module/atoms/PressableScale.js.map +1 -1
- package/lib/module/atoms/Skeleton.js +91 -0
- package/lib/module/atoms/Skeleton.js.map +1 -0
- package/lib/module/atoms/Transition.js +165 -0
- package/lib/module/atoms/Transition.js.map +1 -0
- package/lib/module/atoms/index.js +2 -0
- package/lib/module/atoms/index.js.map +1 -1
- package/lib/module/molecules/OptionCard.js +56 -23
- package/lib/module/molecules/OptionCard.js.map +1 -1
- package/lib/module/molecules/{ThemeToggle.js → SegmentedControl.js} +36 -36
- package/lib/module/molecules/SegmentedControl.js.map +1 -0
- package/lib/module/molecules/Stepper.js +125 -49
- package/lib/module/molecules/Stepper.js.map +1 -1
- package/lib/module/molecules/Steps.js +37 -7
- package/lib/module/molecules/Steps.js.map +1 -1
- package/lib/module/molecules/index.js +2 -2
- package/lib/module/molecules/index.js.map +1 -1
- package/lib/module/organisms/AlertProvider.js +24 -1
- package/lib/module/organisms/AlertProvider.js.map +1 -1
- package/lib/module/organisms/BottomSheet.js +120 -41
- package/lib/module/organisms/BottomSheet.js.map +1 -1
- package/lib/module/organisms/DateField.js +49 -18
- package/lib/module/organisms/DateField.js.map +1 -1
- package/lib/module/organisms/DatePicker.js +131 -52
- package/lib/module/organisms/DatePicker.js.map +1 -1
- package/lib/module/organisms/List.js +68 -46
- package/lib/module/organisms/List.js.map +1 -1
- package/lib/module/organisms/Modal.js +44 -89
- package/lib/module/organisms/Modal.js.map +1 -1
- package/lib/module/organisms/NavigationBar.js +205 -0
- package/lib/module/organisms/NavigationBar.js.map +1 -0
- package/lib/module/organisms/Screen.js +9 -2
- package/lib/module/organisms/Screen.js.map +1 -1
- package/lib/module/organisms/SearchList.js +96 -28
- package/lib/module/organisms/SearchList.js.map +1 -1
- package/lib/module/organisms/Select.js +224 -52
- package/lib/module/organisms/Select.js.map +1 -1
- package/lib/module/organisms/ThemeToggle.js +47 -0
- package/lib/module/organisms/ThemeToggle.js.map +1 -0
- package/lib/module/organisms/Toast.js +10 -24
- package/lib/module/organisms/Toast.js.map +1 -1
- package/lib/module/organisms/ToastProvider.js +21 -0
- package/lib/module/organisms/ToastProvider.js.map +1 -1
- package/lib/module/organisms/Wizard.js +24 -3
- package/lib/module/organisms/Wizard.js.map +1 -1
- package/lib/module/organisms/imperativeBridge.js +24 -0
- package/lib/module/organisms/imperativeBridge.js.map +1 -0
- package/lib/module/organisms/index.js +4 -2
- package/lib/module/organisms/index.js.map +1 -1
- package/lib/module/theme/colors.js +194 -0
- package/lib/module/theme/colors.js.map +1 -0
- package/lib/module/theme/createTheme.js +25 -2
- package/lib/module/theme/createTheme.js.map +1 -1
- package/lib/module/theme/index.js +1 -0
- package/lib/module/theme/index.js.map +1 -1
- package/lib/module/theme/labels.js +5 -1
- package/lib/module/theme/labels.js.map +1 -1
- package/lib/module/theme/palettes.js +5 -2
- package/lib/module/theme/palettes.js.map +1 -1
- package/lib/typescript/atoms/PressableScale.d.ts +3 -2
- package/lib/typescript/atoms/PressableScale.d.ts.map +1 -1
- package/lib/typescript/atoms/Skeleton.d.ts +29 -0
- package/lib/typescript/atoms/Skeleton.d.ts.map +1 -0
- package/lib/typescript/atoms/Transition.d.ts +51 -0
- package/lib/typescript/atoms/Transition.d.ts.map +1 -0
- package/lib/typescript/atoms/index.d.ts +4 -0
- package/lib/typescript/atoms/index.d.ts.map +1 -1
- package/lib/typescript/molecules/OptionCard.d.ts +11 -6
- package/lib/typescript/molecules/OptionCard.d.ts.map +1 -1
- package/lib/typescript/molecules/SegmentedControl.d.ts +24 -0
- package/lib/typescript/molecules/SegmentedControl.d.ts.map +1 -0
- package/lib/typescript/molecules/Stepper.d.ts +35 -3
- package/lib/typescript/molecules/Stepper.d.ts.map +1 -1
- package/lib/typescript/molecules/Steps.d.ts.map +1 -1
- package/lib/typescript/molecules/index.d.ts +4 -4
- package/lib/typescript/molecules/index.d.ts.map +1 -1
- package/lib/typescript/organisms/AlertProvider.d.ts +9 -0
- package/lib/typescript/organisms/AlertProvider.d.ts.map +1 -1
- package/lib/typescript/organisms/BottomSheet.d.ts +26 -4
- package/lib/typescript/organisms/BottomSheet.d.ts.map +1 -1
- package/lib/typescript/organisms/DateField.d.ts +23 -5
- package/lib/typescript/organisms/DateField.d.ts.map +1 -1
- package/lib/typescript/organisms/DatePicker.d.ts +5 -1
- package/lib/typescript/organisms/DatePicker.d.ts.map +1 -1
- package/lib/typescript/organisms/List.d.ts +9 -2
- package/lib/typescript/organisms/List.d.ts.map +1 -1
- package/lib/typescript/organisms/Modal.d.ts +3 -5
- package/lib/typescript/organisms/Modal.d.ts.map +1 -1
- package/lib/typescript/organisms/NavigationBar.d.ts +32 -0
- package/lib/typescript/organisms/NavigationBar.d.ts.map +1 -0
- package/lib/typescript/organisms/Screen.d.ts +2 -1
- package/lib/typescript/organisms/Screen.d.ts.map +1 -1
- package/lib/typescript/organisms/SearchList.d.ts +21 -5
- package/lib/typescript/organisms/SearchList.d.ts.map +1 -1
- package/lib/typescript/organisms/Select.d.ts +19 -4
- package/lib/typescript/organisms/Select.d.ts.map +1 -1
- package/lib/typescript/{molecules → organisms}/ThemeToggle.d.ts +4 -4
- package/lib/typescript/organisms/ThemeToggle.d.ts.map +1 -0
- package/lib/typescript/organisms/Toast.d.ts.map +1 -1
- package/lib/typescript/organisms/ToastProvider.d.ts +8 -0
- package/lib/typescript/organisms/ToastProvider.d.ts.map +1 -1
- package/lib/typescript/organisms/Wizard.d.ts +7 -1
- package/lib/typescript/organisms/Wizard.d.ts.map +1 -1
- package/lib/typescript/organisms/imperativeBridge.d.ts +12 -0
- package/lib/typescript/organisms/imperativeBridge.d.ts.map +1 -0
- package/lib/typescript/organisms/index.d.ts +7 -3
- package/lib/typescript/organisms/index.d.ts.map +1 -1
- package/lib/typescript/theme/colors.d.ts +44 -0
- package/lib/typescript/theme/colors.d.ts.map +1 -0
- package/lib/typescript/theme/createTheme.d.ts +3 -0
- package/lib/typescript/theme/createTheme.d.ts.map +1 -1
- package/lib/typescript/theme/index.d.ts +3 -1
- package/lib/typescript/theme/index.d.ts.map +1 -1
- package/lib/typescript/theme/labels.d.ts +6 -0
- package/lib/typescript/theme/labels.d.ts.map +1 -1
- package/lib/typescript/theme/palettes.d.ts.map +1 -1
- package/lib/typescript/theme/types.d.ts +9 -0
- package/lib/typescript/theme/types.d.ts.map +1 -1
- package/package.json +67 -6
- package/registry/alert-provider.json +9 -2
- package/registry/bottom-sheet.json +6 -2
- package/registry/core.json +9 -5
- package/registry/date-field.json +2 -1
- package/registry/date-picker.json +1 -1
- package/registry/index.json +99 -30
- package/registry/list.json +2 -1
- package/registry/manifest.json +44 -6
- package/registry/modal.json +3 -2
- package/registry/navigation-bar.json +18 -0
- package/registry/option-card.json +3 -2
- package/registry/pressable-scale.json +1 -1
- package/registry/screen.json +1 -1
- package/registry/search-list.json +2 -1
- package/registry/segmented-control.json +18 -0
- package/registry/select.json +1 -1
- package/registry/skeleton.json +18 -0
- package/registry/stepper.json +3 -2
- package/registry/steps.json +1 -1
- package/registry/theme-toggle.json +5 -4
- package/registry/toast-provider.json +11 -3
- package/registry/toast.json +3 -2
- package/registry/transition.json +19 -0
- package/registry/wizard.json +3 -2
- package/src/atoms/PressableScale.tsx +16 -6
- package/src/atoms/Skeleton.tsx +114 -0
- package/src/atoms/Transition.tsx +189 -0
- package/src/atoms/index.ts +4 -0
- package/src/molecules/OptionCard.tsx +62 -25
- package/src/molecules/SegmentedControl.tsx +84 -0
- package/src/molecules/Stepper.tsx +147 -32
- package/src/molecules/Steps.tsx +27 -8
- package/src/molecules/index.ts +4 -4
- package/src/organisms/AlertProvider.tsx +26 -1
- package/src/organisms/BottomSheet.tsx +214 -62
- package/src/organisms/DateField.tsx +79 -19
- package/src/organisms/DatePicker.tsx +198 -80
- package/src/organisms/List.tsx +73 -41
- package/src/organisms/Modal.tsx +49 -61
- package/src/organisms/NavigationBar.tsx +226 -0
- package/src/organisms/Screen.tsx +11 -3
- package/src/organisms/SearchList.tsx +131 -35
- package/src/organisms/Select.tsx +223 -45
- package/src/organisms/ThemeToggle.tsx +30 -0
- package/src/organisms/Toast.tsx +8 -21
- package/src/organisms/ToastProvider.tsx +24 -0
- package/src/organisms/Wizard.tsx +37 -3
- package/src/organisms/imperativeBridge.ts +28 -0
- package/src/organisms/index.ts +7 -3
- package/src/theme/colors.ts +175 -0
- package/src/theme/createTheme.ts +38 -4
- package/src/theme/index.ts +3 -1
- package/src/theme/labels.ts +13 -0
- package/src/theme/palettes.ts +5 -2
- package/src/theme/types.ts +10 -0
- package/lib/commonjs/molecules/ThemeToggle.js.map +0 -1
- package/lib/module/molecules/ThemeToggle.js.map +0 -1
- package/lib/typescript/molecules/ThemeToggle.d.ts.map +0 -1
- package/src/molecules/ThemeToggle.tsx +0 -67
package/src/organisms/Select.tsx
CHANGED
|
@@ -1,12 +1,31 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import {
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
ActivityIndicator,
|
|
4
|
+
Keyboard,
|
|
5
|
+
Modal,
|
|
6
|
+
Platform,
|
|
7
|
+
Pressable,
|
|
8
|
+
ScrollView,
|
|
9
|
+
Text,
|
|
10
|
+
TextInput,
|
|
11
|
+
View,
|
|
12
|
+
useWindowDimensions,
|
|
13
|
+
type StyleProp,
|
|
14
|
+
type ViewStyle,
|
|
15
|
+
} from 'react-native';
|
|
3
16
|
import { createStyles } from '../theme/createStyles';
|
|
4
17
|
import { useAllowFontScaling, useColors, useLabels } from '../theme/UIProvider';
|
|
5
18
|
import { Icon } from '../icons/Icon';
|
|
19
|
+
import type { IconName } from '../icons/types';
|
|
6
20
|
|
|
7
21
|
export interface SelectOption<T> {
|
|
8
22
|
label: string;
|
|
9
23
|
value: T;
|
|
24
|
+
/** Glifo del set de la librería, a la izquierda del label. */
|
|
25
|
+
iconName?: IconName;
|
|
26
|
+
/** Emoji a la izquierda del label. Gana sobre `iconName`. */
|
|
27
|
+
emoji?: string;
|
|
28
|
+
disabled?: boolean;
|
|
10
29
|
}
|
|
11
30
|
|
|
12
31
|
export interface SelectProps<T> {
|
|
@@ -18,12 +37,24 @@ export interface SelectProps<T> {
|
|
|
18
37
|
placeholder?: string;
|
|
19
38
|
error?: string;
|
|
20
39
|
isLoading?: boolean;
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
/** Agrega un buscador dentro del desplegable. @default false */
|
|
42
|
+
searchable?: boolean;
|
|
43
|
+
searchPlaceholder?: string;
|
|
44
|
+
/** Alto máximo del desplegable, si el espacio disponible lo permite. @default 280 */
|
|
45
|
+
maxHeight?: number;
|
|
46
|
+
onOpenChange?: (open: boolean) => void;
|
|
21
47
|
containerStyle?: StyleProp<ViewStyle>;
|
|
48
|
+
testID?: string;
|
|
22
49
|
}
|
|
23
50
|
|
|
51
|
+
/** Aire entre el campo y el desplegable. */
|
|
52
|
+
const GAP = 8;
|
|
53
|
+
/** Por debajo de esto el desplegable no muestra ni dos filas. */
|
|
54
|
+
const MIN_HEIGHT = 120;
|
|
55
|
+
|
|
24
56
|
const useStyles = createStyles((theme) => ({
|
|
25
57
|
container: { gap: theme.tokens.spacing.sm },
|
|
26
|
-
containerOpen: { zIndex: 20, elevation: 20 },
|
|
27
58
|
label: { fontWeight: theme.tokens.fontWeight.bold, fontSize: theme.tokens.fontSize.md, color: theme.colors.text },
|
|
28
59
|
required: { color: theme.colors.errorText },
|
|
29
60
|
field: {
|
|
@@ -37,23 +68,29 @@ const useStyles = createStyles((theme) => ({
|
|
|
37
68
|
minHeight: 50,
|
|
38
69
|
paddingHorizontal: theme.tokens.spacing.lg,
|
|
39
70
|
},
|
|
40
|
-
|
|
71
|
+
fieldOpen: { borderColor: theme.colors.primary },
|
|
72
|
+
fieldDisabled: { opacity: 0.5 },
|
|
73
|
+
fieldValue: { flexDirection: 'row', alignItems: 'center', gap: theme.tokens.spacing.sm, flexShrink: 1 },
|
|
74
|
+
fieldText: { fontSize: theme.tokens.fontSize.lg, color: theme.colors.text, flexShrink: 1 },
|
|
41
75
|
fieldPlaceholder: { color: theme.colors.textLight },
|
|
42
|
-
|
|
76
|
+
backdrop: { flex: 1 },
|
|
77
|
+
sheet: {
|
|
43
78
|
position: 'absolute',
|
|
44
|
-
top: '100%',
|
|
45
|
-
left: 0,
|
|
46
|
-
right: 0,
|
|
47
|
-
marginTop: theme.tokens.spacing.sm,
|
|
48
79
|
borderWidth: 2,
|
|
49
80
|
borderColor: theme.colors.border,
|
|
50
81
|
borderRadius: theme.tokens.radius.lg,
|
|
51
82
|
backgroundColor: theme.colors.surface,
|
|
52
83
|
overflow: 'hidden',
|
|
53
|
-
zIndex: 20,
|
|
54
|
-
elevation: 20,
|
|
55
84
|
...theme.tokens.shadow.md,
|
|
56
85
|
},
|
|
86
|
+
search: {
|
|
87
|
+
fontSize: theme.tokens.fontSize.md,
|
|
88
|
+
color: theme.colors.text,
|
|
89
|
+
paddingHorizontal: theme.tokens.spacing.lg,
|
|
90
|
+
paddingVertical: theme.tokens.spacing.md,
|
|
91
|
+
borderBottomWidth: 1,
|
|
92
|
+
borderBottomColor: theme.colors.border,
|
|
93
|
+
},
|
|
57
94
|
optionRow: {
|
|
58
95
|
flexDirection: 'row',
|
|
59
96
|
alignItems: 'center',
|
|
@@ -65,16 +102,33 @@ const useStyles = createStyles((theme) => ({
|
|
|
65
102
|
},
|
|
66
103
|
optionRowLast: { borderBottomWidth: 0 },
|
|
67
104
|
optionRowSelected: { backgroundColor: theme.colors.primarySoft },
|
|
105
|
+
optionRowDisabled: { opacity: 0.4 },
|
|
106
|
+
optionLabel: { flexDirection: 'row', alignItems: 'center', gap: theme.tokens.spacing.sm, flexShrink: 1 },
|
|
68
107
|
optionText: { fontSize: theme.tokens.fontSize.lg - 1, color: theme.colors.text },
|
|
69
108
|
optionTextSelected: { fontWeight: theme.tokens.fontWeight.semibold, color: theme.colors.primaryText },
|
|
109
|
+
emoji: { fontSize: theme.tokens.fontSize.lg },
|
|
110
|
+
empty: {
|
|
111
|
+
padding: theme.tokens.spacing.lg,
|
|
112
|
+
textAlign: 'center',
|
|
113
|
+
color: theme.colors.textLight,
|
|
114
|
+
fontSize: theme.tokens.fontSize.md,
|
|
115
|
+
},
|
|
70
116
|
errorText: { fontSize: theme.tokens.fontSize.sm, color: theme.colors.errorText },
|
|
71
117
|
}));
|
|
72
118
|
|
|
119
|
+
interface Anchor {
|
|
120
|
+
x: number;
|
|
121
|
+
y: number;
|
|
122
|
+
width: number;
|
|
123
|
+
height: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
73
126
|
/**
|
|
74
127
|
* Select
|
|
75
|
-
* Dropdown
|
|
76
|
-
*
|
|
77
|
-
*
|
|
128
|
+
* Dropdown de una sola opción. La lista se mide contra la ventana y se dibuja
|
|
129
|
+
* dentro de un Modal, no en el árbol del campo: así ningún `overflow: hidden`
|
|
130
|
+
* la recorta, no depende del zIndex (poco confiable en Android) y el fondo del
|
|
131
|
+
* Modal resuelve cerrar tocando afuera y con el botón atrás.
|
|
78
132
|
*/
|
|
79
133
|
export function Select<T extends string | number>({
|
|
80
134
|
label,
|
|
@@ -85,17 +139,93 @@ export function Select<T extends string | number>({
|
|
|
85
139
|
placeholder,
|
|
86
140
|
error,
|
|
87
141
|
isLoading = false,
|
|
142
|
+
disabled = false,
|
|
143
|
+
searchable = false,
|
|
144
|
+
searchPlaceholder,
|
|
145
|
+
maxHeight = 280,
|
|
146
|
+
onOpenChange,
|
|
88
147
|
containerStyle,
|
|
148
|
+
testID,
|
|
89
149
|
}: SelectProps<T>) {
|
|
90
150
|
const colors = useColors();
|
|
91
151
|
const styles = useStyles();
|
|
92
152
|
const labels = useLabels();
|
|
93
153
|
const allowFontScaling = useAllowFontScaling();
|
|
154
|
+
const { height: windowHeight } = useWindowDimensions();
|
|
155
|
+
|
|
156
|
+
const fieldRef = useRef<View>(null);
|
|
94
157
|
const [open, setOpen] = useState(false);
|
|
158
|
+
const [anchor, setAnchor] = useState<Anchor | null>(null);
|
|
159
|
+
const [query, setQuery] = useState('');
|
|
160
|
+
const [keyboardHeight, setKeyboardHeight] = useState(0);
|
|
161
|
+
const isDisabled = disabled || isLoading;
|
|
95
162
|
const selectedOption = options.find((o) => o.value === selectedValue);
|
|
96
163
|
|
|
164
|
+
const close = useCallback(() => {
|
|
165
|
+
setOpen(false);
|
|
166
|
+
setAnchor(null);
|
|
167
|
+
setQuery('');
|
|
168
|
+
onOpenChange?.(false);
|
|
169
|
+
}, [onOpenChange]);
|
|
170
|
+
|
|
171
|
+
// Abrir no espera a la medición: si `measureInWindow` no responde, la lista
|
|
172
|
+
// igual aparece (centrada) en vez de que el campo deje de funcionar.
|
|
173
|
+
const openList = () => {
|
|
174
|
+
fieldRef.current?.measureInWindow((x, y, width, height) => setAnchor({ x, y, width, height }));
|
|
175
|
+
setOpen(true);
|
|
176
|
+
onOpenChange?.(true);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// El buscador abre el teclado encima del desplegable: sin descontarlo, la
|
|
180
|
+
// lista se dibuja donde ya no se ve.
|
|
181
|
+
useEffect(() => {
|
|
182
|
+
if (!open) return;
|
|
183
|
+
setKeyboardHeight(Keyboard.metrics()?.height ?? 0);
|
|
184
|
+
const [show, hide] =
|
|
185
|
+
Platform.OS === 'ios' ? ['keyboardWillShow', 'keyboardWillHide'] : ['keyboardDidShow', 'keyboardDidHide'];
|
|
186
|
+
const subs = [
|
|
187
|
+
Keyboard.addListener(show as 'keyboardDidShow', (e) => setKeyboardHeight(e.endCoordinates.height)),
|
|
188
|
+
Keyboard.addListener(hide as 'keyboardDidHide', () => setKeyboardHeight(0)),
|
|
189
|
+
];
|
|
190
|
+
return () => subs.forEach((sub) => sub.remove());
|
|
191
|
+
}, [open]);
|
|
192
|
+
|
|
193
|
+
const visibleOptions = useMemo(() => {
|
|
194
|
+
if (!searchable || query.trim() === '') return options;
|
|
195
|
+
const needle = query.trim().toLowerCase();
|
|
196
|
+
return options.filter((option) => option.label.toLowerCase().includes(needle));
|
|
197
|
+
}, [options, query, searchable]);
|
|
198
|
+
|
|
199
|
+
// Se abre hacia donde haya más aire: contra el borde inferior de la pantalla,
|
|
200
|
+
// hacia arriba es la única forma de que entre.
|
|
201
|
+
const placement = useMemo((): ViewStyle => {
|
|
202
|
+
if (!anchor) return { left: GAP * 2, right: GAP * 2, top: windowHeight * 0.25, maxHeight };
|
|
203
|
+
const usableHeight = windowHeight - keyboardHeight;
|
|
204
|
+
const below = usableHeight - (anchor.y + anchor.height) - GAP * 2;
|
|
205
|
+
const above = anchor.y - GAP * 2;
|
|
206
|
+
const downwards = below >= Math.min(maxHeight, above);
|
|
207
|
+
return {
|
|
208
|
+
left: anchor.x,
|
|
209
|
+
width: anchor.width,
|
|
210
|
+
maxHeight: Math.max(Math.min(maxHeight, downwards ? below : above), MIN_HEIGHT),
|
|
211
|
+
...(downwards ? { top: anchor.y + anchor.height + GAP } : { bottom: windowHeight - anchor.y + GAP }),
|
|
212
|
+
};
|
|
213
|
+
}, [anchor, keyboardHeight, maxHeight, windowHeight]);
|
|
214
|
+
|
|
215
|
+
const renderLeading = (option: SelectOption<T> | undefined, color: string) => {
|
|
216
|
+
if (!option) return null;
|
|
217
|
+
if (option.emoji) {
|
|
218
|
+
return (
|
|
219
|
+
<Text allowFontScaling={allowFontScaling} style={styles.emoji}>
|
|
220
|
+
{option.emoji}
|
|
221
|
+
</Text>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
return option.iconName ? <Icon name={option.iconName} size={20} color={color} /> : null;
|
|
225
|
+
};
|
|
226
|
+
|
|
97
227
|
return (
|
|
98
|
-
<View style={[styles.container,
|
|
228
|
+
<View style={[styles.container, containerStyle]} testID={testID}>
|
|
99
229
|
{label && (
|
|
100
230
|
<Text allowFontScaling={allowFontScaling} style={styles.label}>
|
|
101
231
|
{label}
|
|
@@ -104,16 +234,25 @@ export function Select<T extends string | number>({
|
|
|
104
234
|
)}
|
|
105
235
|
|
|
106
236
|
<Pressable
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
237
|
+
ref={fieldRef}
|
|
238
|
+
style={[styles.field, open && styles.fieldOpen, isDisabled && styles.fieldDisabled]}
|
|
239
|
+
onPress={openList}
|
|
240
|
+
disabled={isDisabled}
|
|
241
|
+
accessibilityRole="combobox"
|
|
111
242
|
accessibilityLabel={label ?? placeholder ?? labels.selectPlaceholder}
|
|
112
|
-
|
|
243
|
+
accessibilityValue={{ text: selectedOption?.label ?? '' }}
|
|
244
|
+
accessibilityState={{ expanded: open, disabled: isDisabled }}
|
|
113
245
|
>
|
|
114
|
-
<
|
|
115
|
-
{selectedOption
|
|
116
|
-
|
|
246
|
+
<View style={styles.fieldValue}>
|
|
247
|
+
{renderLeading(selectedOption, colors.textLight)}
|
|
248
|
+
<Text
|
|
249
|
+
allowFontScaling={allowFontScaling}
|
|
250
|
+
numberOfLines={1}
|
|
251
|
+
style={[styles.fieldText, !selectedOption && styles.fieldPlaceholder]}
|
|
252
|
+
>
|
|
253
|
+
{selectedOption?.label || placeholder || labels.selectPlaceholder}
|
|
254
|
+
</Text>
|
|
255
|
+
</View>
|
|
117
256
|
{isLoading ? (
|
|
118
257
|
<ActivityIndicator size="small" color={colors.primary} />
|
|
119
258
|
) : (
|
|
@@ -121,29 +260,68 @@ export function Select<T extends string | number>({
|
|
|
121
260
|
)}
|
|
122
261
|
</Pressable>
|
|
123
262
|
|
|
124
|
-
{open &&
|
|
125
|
-
<
|
|
126
|
-
{
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
263
|
+
{open && (
|
|
264
|
+
<Modal visible transparent animationType="fade" statusBarTranslucent onRequestClose={close}>
|
|
265
|
+
<Pressable style={styles.backdrop} onPress={close} testID="select-backdrop" />
|
|
266
|
+
|
|
267
|
+
<View style={[styles.sheet, placement]} accessibilityRole="radiogroup" testID="select-list">
|
|
268
|
+
{searchable && (
|
|
269
|
+
<TextInput
|
|
270
|
+
allowFontScaling={allowFontScaling}
|
|
271
|
+
style={styles.search}
|
|
272
|
+
value={query}
|
|
273
|
+
onChangeText={setQuery}
|
|
274
|
+
placeholder={searchPlaceholder ?? labels.search}
|
|
275
|
+
placeholderTextColor={colors.textLight}
|
|
276
|
+
autoFocus
|
|
277
|
+
accessibilityLabel={searchPlaceholder ?? labels.search}
|
|
278
|
+
/>
|
|
279
|
+
)}
|
|
280
|
+
|
|
281
|
+
<ScrollView keyboardShouldPersistTaps="handled" showsVerticalScrollIndicator={false} bounces={false}>
|
|
282
|
+
{visibleOptions.map((option, index) => {
|
|
283
|
+
const isSelected = option.value === selectedValue;
|
|
284
|
+
return (
|
|
285
|
+
<Pressable
|
|
286
|
+
key={String(option.value)}
|
|
287
|
+
style={[
|
|
288
|
+
styles.optionRow,
|
|
289
|
+
index === visibleOptions.length - 1 && styles.optionRowLast,
|
|
290
|
+
isSelected && styles.optionRowSelected,
|
|
291
|
+
option.disabled && styles.optionRowDisabled,
|
|
292
|
+
]}
|
|
293
|
+
onPress={() => {
|
|
294
|
+
onSelect(option.value);
|
|
295
|
+
close();
|
|
296
|
+
}}
|
|
297
|
+
disabled={option.disabled}
|
|
298
|
+
accessibilityRole="radio"
|
|
299
|
+
accessibilityLabel={option.label}
|
|
300
|
+
accessibilityState={{ selected: isSelected, checked: isSelected, disabled: !!option.disabled }}
|
|
301
|
+
>
|
|
302
|
+
<View style={styles.optionLabel}>
|
|
303
|
+
{renderLeading(option, isSelected ? colors.primary : colors.textLight)}
|
|
304
|
+
<Text
|
|
305
|
+
allowFontScaling={allowFontScaling}
|
|
306
|
+
numberOfLines={1}
|
|
307
|
+
style={[styles.optionText, isSelected && styles.optionTextSelected]}
|
|
308
|
+
>
|
|
309
|
+
{option.label}
|
|
310
|
+
</Text>
|
|
311
|
+
</View>
|
|
312
|
+
{isSelected && <Icon name="check" size={18} color={colors.primary} />}
|
|
313
|
+
</Pressable>
|
|
314
|
+
);
|
|
315
|
+
})}
|
|
316
|
+
|
|
317
|
+
{visibleOptions.length === 0 && (
|
|
318
|
+
<Text allowFontScaling={allowFontScaling} style={styles.empty}>
|
|
319
|
+
{labels.noResultsTitle}
|
|
141
320
|
</Text>
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
</View>
|
|
321
|
+
)}
|
|
322
|
+
</ScrollView>
|
|
323
|
+
</View>
|
|
324
|
+
</Modal>
|
|
147
325
|
)}
|
|
148
326
|
|
|
149
327
|
{!!error && (
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React, { memo, useMemo } from 'react';
|
|
2
|
+
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
+
import { useThemeMode, type ThemeMode } from '../theme/UIProvider';
|
|
4
|
+
import { SegmentedControl } from '../molecules/SegmentedControl';
|
|
5
|
+
|
|
6
|
+
export interface ThemeToggleProps {
|
|
7
|
+
style?: StyleProp<ViewStyle>;
|
|
8
|
+
labels?: Partial<Record<ThemeMode, string>>;
|
|
9
|
+
testID?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const MODES: ThemeMode[] = ['system', 'light', 'dark'];
|
|
13
|
+
const DEFAULT_LABELS: Record<ThemeMode, string> = { system: 'Auto', light: 'Light', dark: 'Dark' };
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* ThemeToggle
|
|
17
|
+
* Conecta un SegmentedControl con UIProvider.setMode. Sin persistencia — el
|
|
18
|
+
* consumidor la resuelve vía onModeChange en <UIProvider>.
|
|
19
|
+
*/
|
|
20
|
+
export const ThemeToggle = memo(({ style, labels: labelOverrides, testID }: ThemeToggleProps) => {
|
|
21
|
+
const { mode, setMode } = useThemeMode();
|
|
22
|
+
|
|
23
|
+
const options = useMemo(() => {
|
|
24
|
+
const labels = { ...DEFAULT_LABELS, ...labelOverrides };
|
|
25
|
+
return MODES.map((value) => ({ value, label: labels[value] }));
|
|
26
|
+
}, [labelOverrides]);
|
|
27
|
+
|
|
28
|
+
return <SegmentedControl options={options} value={mode} onChange={setMode} style={style} testID={testID} />;
|
|
29
|
+
});
|
|
30
|
+
ThemeToggle.displayName = 'ThemeToggle';
|
package/src/organisms/Toast.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import React, { memo
|
|
2
|
-
import {
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import { Pressable, Text, View, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
3
|
import { createStyles } from '../theme/createStyles';
|
|
4
4
|
import { useAllowFontScaling, useColors } from '../theme/UIProvider';
|
|
5
|
+
import { Transition } from '../atoms/Transition';
|
|
5
6
|
import { Icon } from '../icons/Icon';
|
|
6
7
|
import type { IconName } from '../icons/types';
|
|
7
8
|
|
|
@@ -120,24 +121,10 @@ Toast.displayName = 'Toast';
|
|
|
120
121
|
|
|
121
122
|
/** Envuelve un Toast con su animación de entrada/salida. Uso interno del provider. */
|
|
122
123
|
export const AnimatedToast = memo(
|
|
123
|
-
({ children, onHeight }: { children: React.ReactNode; onHeight?: (h: number) => void }) =>
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}, [progress]);
|
|
129
|
-
|
|
130
|
-
return (
|
|
131
|
-
<Animated.View
|
|
132
|
-
onLayout={(e) => onHeight?.(e.nativeEvent.layout.height)}
|
|
133
|
-
style={{
|
|
134
|
-
opacity: progress,
|
|
135
|
-
transform: [{ translateY: progress.interpolate({ inputRange: [0, 1], outputRange: [-16, 0] }) }],
|
|
136
|
-
}}
|
|
137
|
-
>
|
|
138
|
-
{children}
|
|
139
|
-
</Animated.View>
|
|
140
|
-
);
|
|
141
|
-
}
|
|
124
|
+
({ children, onHeight }: { children: React.ReactNode; onHeight?: (h: number) => void }) => (
|
|
125
|
+
<Transition preset={['fade', 'slide-down']} distance={16} spring>
|
|
126
|
+
<View onLayout={(e) => onHeight?.(e.nativeEvent.layout.height)}>{children}</View>
|
|
127
|
+
</Transition>
|
|
128
|
+
)
|
|
142
129
|
);
|
|
143
130
|
AnimatedToast.displayName = 'AnimatedToast';
|
|
@@ -3,6 +3,7 @@ import { View } from 'react-native';
|
|
|
3
3
|
import { AnimatedToast, Toast, type ToastVariant } from './Toast';
|
|
4
4
|
import { useInsets } from '../theme/UIProvider';
|
|
5
5
|
import { createStyles } from '../theme/createStyles';
|
|
6
|
+
import { createImperativeBridge } from './imperativeBridge';
|
|
6
7
|
|
|
7
8
|
export interface ToastOptions {
|
|
8
9
|
title: string;
|
|
@@ -26,6 +27,24 @@ interface ToastContextValue {
|
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
const ToastContext = createContext<ToastContextValue | undefined>(undefined);
|
|
30
|
+
const bridge = createImperativeBridge<ToastContextValue>('ToastProvider');
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Muestra un toast desde fuera del árbol de React —un servicio, un
|
|
34
|
+
* interceptor— sin pasar por el hook. Devuelve el id, o '' si no hay
|
|
35
|
+
* <ToastProvider> montado.
|
|
36
|
+
*/
|
|
37
|
+
export function showToast(options: ToastOptions): string {
|
|
38
|
+
return bridge.get()?.show(options) ?? '';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function hideToast(id: string): void {
|
|
42
|
+
bridge.get()?.hide(id);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function hideAllToasts(): void {
|
|
46
|
+
bridge.get()?.hideAll();
|
|
47
|
+
}
|
|
29
48
|
|
|
30
49
|
const useStyles = createStyles((theme) => ({
|
|
31
50
|
host: { position: 'absolute', left: 0, right: 0, paddingHorizontal: theme.tokens.spacing.lg, gap: theme.tokens.spacing.sm },
|
|
@@ -122,6 +141,11 @@ export function ToastProvider({
|
|
|
122
141
|
|
|
123
142
|
const value = useMemo<ToastContextValue>(() => ({ show, hide, hideAll }), [show, hide, hideAll]);
|
|
124
143
|
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
bridge.set(value);
|
|
146
|
+
return () => bridge.set(null);
|
|
147
|
+
}, [value]);
|
|
148
|
+
|
|
125
149
|
const hostPosition =
|
|
126
150
|
position === 'top'
|
|
127
151
|
? { top: insets.top + 8 }
|
package/src/organisms/Wizard.tsx
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import React, { useCallback, useMemo, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { ScrollView, View, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
3
|
import { createStyles } from '../theme/createStyles';
|
|
4
4
|
import { Steps, type StepItem } from '../molecules/Steps';
|
|
5
5
|
import { Button } from '../atoms/Button';
|
|
6
|
+
import { Transition } from '../atoms/Transition';
|
|
6
7
|
|
|
7
8
|
export interface WizardStep extends StepItem {
|
|
8
9
|
content: React.ReactNode;
|
|
@@ -31,6 +32,12 @@ export interface WizardProps {
|
|
|
31
32
|
* @default true
|
|
32
33
|
*/
|
|
33
34
|
scrollableContent?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* El contenido del paso entra deslizándose en el sentido de la navegación.
|
|
37
|
+
* Sólo opacity y transform, así que la animación corre en el hilo nativo.
|
|
38
|
+
* @default true
|
|
39
|
+
*/
|
|
40
|
+
animated?: boolean;
|
|
34
41
|
style?: StyleProp<ViewStyle>;
|
|
35
42
|
}
|
|
36
43
|
|
|
@@ -61,6 +68,7 @@ export function Wizard({
|
|
|
61
68
|
nextLabel = 'Next',
|
|
62
69
|
finishLabel = 'Finish',
|
|
63
70
|
scrollableContent = true,
|
|
71
|
+
animated = true,
|
|
64
72
|
style,
|
|
65
73
|
}: WizardProps) {
|
|
66
74
|
const styles = useStyles();
|
|
@@ -82,6 +90,15 @@ export function Wizard({
|
|
|
82
90
|
[steps]
|
|
83
91
|
);
|
|
84
92
|
|
|
93
|
+
// El ref todavía guarda el paso anterior durante el render que trae el nuevo.
|
|
94
|
+
const previous = useRef(current);
|
|
95
|
+
const forward = current >= previous.current;
|
|
96
|
+
const entered = useRef(false);
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
previous.current = current;
|
|
99
|
+
entered.current = true;
|
|
100
|
+
}, [current]);
|
|
101
|
+
|
|
85
102
|
const activeStep = steps[current];
|
|
86
103
|
const isFirst = current === 0;
|
|
87
104
|
const isLast = current === steps.length - 1;
|
|
@@ -92,6 +109,22 @@ export function Wizard({
|
|
|
92
109
|
else goTo(current + 1);
|
|
93
110
|
};
|
|
94
111
|
|
|
112
|
+
// El paso saliente no se anima: mantener los dos montados a la vez duplicaría
|
|
113
|
+
// los formularios y su estado.
|
|
114
|
+
const renderContent = () =>
|
|
115
|
+
animated ? (
|
|
116
|
+
<Transition
|
|
117
|
+
key={current}
|
|
118
|
+
testID="wizard-step"
|
|
119
|
+
preset={['fade', forward ? 'slide-left' : 'slide-right']}
|
|
120
|
+
appear={entered.current}
|
|
121
|
+
>
|
|
122
|
+
{activeStep?.content}
|
|
123
|
+
</Transition>
|
|
124
|
+
) : (
|
|
125
|
+
activeStep?.content
|
|
126
|
+
);
|
|
127
|
+
|
|
95
128
|
return (
|
|
96
129
|
<View style={[styles.container, style]}>
|
|
97
130
|
<Steps
|
|
@@ -109,16 +142,17 @@ export function Wizard({
|
|
|
109
142
|
de los campos. */}
|
|
110
143
|
{scrollableContent ? (
|
|
111
144
|
<ScrollView
|
|
145
|
+
testID="wizard-content"
|
|
112
146
|
style={styles.content}
|
|
113
147
|
contentContainerStyle={styles.contentInner}
|
|
114
148
|
keyboardShouldPersistTaps="handled"
|
|
115
149
|
automaticallyAdjustKeyboardInsets
|
|
116
150
|
showsVerticalScrollIndicator={false}
|
|
117
151
|
>
|
|
118
|
-
{
|
|
152
|
+
{renderContent()}
|
|
119
153
|
</ScrollView>
|
|
120
154
|
) : (
|
|
121
|
-
<View style={styles.content}>{
|
|
155
|
+
<View style={styles.content}>{renderContent()}</View>
|
|
122
156
|
)}
|
|
123
157
|
|
|
124
158
|
<View style={styles.footer}>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Puente entre el proveedor montado y el código que no es un componente
|
|
3
|
+
* (servicios, capa de API, interceptores). El proveedor se registra al montar y
|
|
4
|
+
* se borra al desmontar, así que fuera del árbol nunca queda una referencia
|
|
5
|
+
* viva a un contexto muerto.
|
|
6
|
+
*/
|
|
7
|
+
export interface ImperativeBridge<T> {
|
|
8
|
+
set: (value: T | null) => void;
|
|
9
|
+
get: () => T | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function createImperativeBridge<T>(providerName: string): ImperativeBridge<T> {
|
|
13
|
+
let current: T | null = null;
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
set: (value) => {
|
|
17
|
+
current = value;
|
|
18
|
+
},
|
|
19
|
+
get: () => {
|
|
20
|
+
if (!current && __DEV__) {
|
|
21
|
+
console.warn(
|
|
22
|
+
`orn-ui: se llamó a la API imperativa sin un <${providerName}> montado. La llamada se ignoró.`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
return current;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
package/src/organisms/index.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export { Modal } from './Modal';
|
|
2
2
|
export type { ModalProps, ModalVariant } from './Modal';
|
|
3
3
|
export { BottomSheet } from './BottomSheet';
|
|
4
|
-
export type { BottomSheetProps } from './BottomSheet';
|
|
4
|
+
export type { BottomSheetProps, FooterPlacement } from './BottomSheet';
|
|
5
5
|
export { Select } from './Select';
|
|
6
6
|
export type { SelectProps, SelectOption } from './Select';
|
|
7
7
|
export { Alert } from './Alert';
|
|
8
8
|
export type { AlertProps, AlertButton, AlertType } from './Alert';
|
|
9
|
-
export { AlertProvider, useAlert } from './AlertProvider';
|
|
9
|
+
export { AlertProvider, useAlert, showAlert, showConfirm, hideAlert } from './AlertProvider';
|
|
10
10
|
export type { AlertOptions, ConfirmOptions } from './AlertProvider';
|
|
11
11
|
export { Screen } from './Screen';
|
|
12
12
|
export type { ScreenProps, ScreenEdge } from './Screen';
|
|
@@ -16,7 +16,7 @@ export { SearchList } from './SearchList';
|
|
|
16
16
|
export type { SearchListProps } from './SearchList';
|
|
17
17
|
export { Toast } from './Toast';
|
|
18
18
|
export type { ToastProps, ToastVariant } from './Toast';
|
|
19
|
-
export { ToastProvider, useToast } from './ToastProvider';
|
|
19
|
+
export { ToastProvider, useToast, showToast, hideToast, hideAllToasts } from './ToastProvider';
|
|
20
20
|
export type { ToastProviderProps, ToastOptions } from './ToastProvider';
|
|
21
21
|
export { DatePicker, buildMonthGrid, DEFAULT_MONTH_NAMES, DEFAULT_WEEKDAY_NAMES } from './DatePicker';
|
|
22
22
|
export type { DatePickerProps, DateRange } from './DatePicker';
|
|
@@ -24,3 +24,7 @@ export { DateField } from './DateField';
|
|
|
24
24
|
export type { DateFieldProps } from './DateField';
|
|
25
25
|
export { Wizard } from './Wizard';
|
|
26
26
|
export type { WizardProps, WizardStep } from './Wizard';
|
|
27
|
+
export { ThemeToggle } from './ThemeToggle';
|
|
28
|
+
export type { ThemeToggleProps } from './ThemeToggle';
|
|
29
|
+
export { NavigationBar } from './NavigationBar';
|
|
30
|
+
export type { NavigationBarProps, NavigationBarItem } from './NavigationBar';
|