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
|
@@ -1,29 +1,89 @@
|
|
|
1
|
-
import React, { memo } from 'react';
|
|
1
|
+
import React, { memo, useState } from 'react';
|
|
2
2
|
import { TextInput, TouchableOpacity, View, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
3
|
import { createStyles } from '../theme/createStyles';
|
|
4
|
-
import { useAllowFontScaling, useColors } from '../theme/UIProvider';
|
|
4
|
+
import { useAllowFontScaling, useColors, useTheme } from '../theme/UIProvider';
|
|
5
5
|
import { Icon } from '../icons/Icon';
|
|
6
6
|
|
|
7
|
+
export type StepperSize = 'sm' | 'md' | 'lg';
|
|
8
|
+
|
|
7
9
|
export interface StepperProps {
|
|
8
10
|
value: string;
|
|
9
11
|
onChangeText: (text: string) => void;
|
|
10
12
|
onIncrement: () => void;
|
|
11
13
|
onDecrement: () => void;
|
|
12
|
-
|
|
14
|
+
/** Escala del control: alto, botones, ícono y tipografía. @default 'md' */
|
|
15
|
+
size?: StepperSize;
|
|
16
|
+
/** Ocupa todo el ancho disponible y el input se lleva el sobrante. @default false */
|
|
17
|
+
block?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* En false el valor sólo se cambia con los botones: el teclado no se abre y
|
|
20
|
+
* el campo pierde el fondo que lo anuncia como editable. @default true
|
|
21
|
+
*/
|
|
22
|
+
editable?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Mínimo permitido. Deshabilita "-" al alcanzarlo y acota lo tipeado al
|
|
25
|
+
* salir del campo. Los negativos se aceptan sólo si `min` es negativo.
|
|
26
|
+
*/
|
|
27
|
+
min?: number;
|
|
28
|
+
/** Máximo permitido. Deshabilita "+" al alcanzarlo y acota lo tipeado. */
|
|
29
|
+
max?: number;
|
|
30
|
+
/** Acepta un separador decimal, normalizando la coma a punto. @default false */
|
|
31
|
+
allowDecimals?: boolean;
|
|
13
32
|
style?: StyleProp<ViewStyle>;
|
|
14
33
|
incrementAccessibilityLabel?: string;
|
|
15
34
|
decrementAccessibilityLabel?: string;
|
|
35
|
+
testID?: string;
|
|
16
36
|
}
|
|
17
37
|
|
|
38
|
+
const SIZES: Record<StepperSize, { height: number; icon: number; inputWidth: number }> = {
|
|
39
|
+
sm: { height: 36, icon: 18, inputWidth: 48 },
|
|
40
|
+
md: { height: 44, icon: 20, inputWidth: 56 },
|
|
41
|
+
lg: { height: 52, icon: 22, inputWidth: 64 },
|
|
42
|
+
};
|
|
43
|
+
|
|
18
44
|
const useStyles = createStyles((theme) => ({
|
|
19
|
-
control: {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
45
|
+
control: {
|
|
46
|
+
flexDirection: 'row',
|
|
47
|
+
alignItems: 'center',
|
|
48
|
+
alignSelf: 'flex-start',
|
|
49
|
+
borderWidth: 1,
|
|
50
|
+
borderColor: theme.colors.border,
|
|
51
|
+
borderRadius: theme.tokens.radius.md,
|
|
52
|
+
},
|
|
53
|
+
controlBlock: { alignSelf: 'stretch' },
|
|
54
|
+
btn: { height: '100%', justifyContent: 'center', alignItems: 'center' },
|
|
55
|
+
input: {
|
|
56
|
+
textAlign: 'center',
|
|
57
|
+
height: '100%',
|
|
58
|
+
color: theme.colors.text,
|
|
59
|
+
borderLeftWidth: 1,
|
|
60
|
+
borderRightWidth: 1,
|
|
61
|
+
borderColor: theme.colors.border,
|
|
62
|
+
},
|
|
63
|
+
// El fondo distingue el campo de un valor de sólo lectura: es la única
|
|
64
|
+
// señal de que ahí se puede tipear antes de tocarlo.
|
|
65
|
+
editable: { backgroundColor: theme.colors.inputBackground },
|
|
24
66
|
}));
|
|
25
67
|
|
|
26
|
-
/**
|
|
68
|
+
/** Deja sólo lo que puede formar parte del número permitido por el rango. */
|
|
69
|
+
export function sanitizeNumeric(text: string, { allowDecimals = false, allowNegative = false } = {}): string {
|
|
70
|
+
const normalized = allowDecimals ? text.replace(',', '.') : text;
|
|
71
|
+
const negative = allowNegative && normalized.trimStart().startsWith('-');
|
|
72
|
+
const digits = normalized.replace(allowDecimals ? /[^0-9.]/g : /[^0-9]/g, '');
|
|
73
|
+
const [whole = '', ...decimals] = digits.split('.');
|
|
74
|
+
const body = decimals.length > 0 ? `${whole}.${decimals.join('')}` : whole;
|
|
75
|
+
return negative ? `-${body}` : body;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Stepper
|
|
80
|
+
* Botón "-" + input numérico editable + botón "+". `size` controla sólo la
|
|
81
|
+
* escala y `block` sólo el ancho, así que los dos ejes se combinan libremente.
|
|
82
|
+
*
|
|
83
|
+
* El componente no hace la aritmética de los botones —eso vive en
|
|
84
|
+
* `onIncrement`/`onDecrement`—, pero sí limpia lo que se tipea y avisa cuándo
|
|
85
|
+
* el valor llegó a un extremo del rango deshabilitando el botón que lo pasaría.
|
|
86
|
+
*/
|
|
27
87
|
export const Stepper = memo(
|
|
28
88
|
({
|
|
29
89
|
value,
|
|
@@ -31,44 +91,99 @@ export const Stepper = memo(
|
|
|
31
91
|
onIncrement,
|
|
32
92
|
onDecrement,
|
|
33
93
|
size = 'md',
|
|
94
|
+
block = false,
|
|
95
|
+
editable = true,
|
|
96
|
+
min,
|
|
97
|
+
max,
|
|
98
|
+
allowDecimals = false,
|
|
34
99
|
style,
|
|
35
100
|
incrementAccessibilityLabel = 'Increment',
|
|
36
101
|
decrementAccessibilityLabel = 'Decrement',
|
|
102
|
+
testID,
|
|
37
103
|
}: StepperProps) => {
|
|
104
|
+
const theme = useTheme();
|
|
38
105
|
const colors = useColors();
|
|
39
106
|
const styles = useStyles();
|
|
40
107
|
const allowFontScaling = useAllowFontScaling();
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
const
|
|
108
|
+
const [focused, setFocused] = useState(false);
|
|
109
|
+
|
|
110
|
+
const { height, icon, inputWidth } = SIZES[size];
|
|
111
|
+
const fontSize =
|
|
112
|
+
size === 'sm' ? theme.tokens.fontSize.md : size === 'md' ? theme.tokens.fontSize.lg : theme.tokens.fontSize.xl;
|
|
113
|
+
|
|
114
|
+
const allowNegative = min !== undefined && min < 0;
|
|
115
|
+
const numeric = Number(value);
|
|
116
|
+
const isNumber = value.trim() !== '' && Number.isFinite(numeric);
|
|
117
|
+
const atMin = isNumber && min !== undefined && numeric <= min;
|
|
118
|
+
const atMax = isNumber && max !== undefined && numeric >= max;
|
|
119
|
+
|
|
120
|
+
const handleChangeText = (text: string) => {
|
|
121
|
+
onChangeText(sanitizeNumeric(text, { allowDecimals, allowNegative }));
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// El recorte al rango espera al blur: hacerlo mientras se tipea impide
|
|
125
|
+
// llegar a "12" cuando el mínimo es 5, porque el "1" ya se corregiría.
|
|
126
|
+
const handleBlur = () => {
|
|
127
|
+
setFocused(false);
|
|
128
|
+
if (!isNumber) return;
|
|
129
|
+
const clamped = Math.min(max ?? numeric, Math.max(min ?? numeric, numeric));
|
|
130
|
+
if (clamped !== numeric) onChangeText(String(clamped));
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const stepButton = (
|
|
134
|
+
direction: 'decrement' | 'increment',
|
|
135
|
+
onPress: () => void,
|
|
136
|
+
disabled: boolean,
|
|
137
|
+
label: string
|
|
138
|
+
) => (
|
|
139
|
+
<TouchableOpacity
|
|
140
|
+
onPress={onPress}
|
|
141
|
+
disabled={disabled}
|
|
142
|
+
style={[styles.btn, { width: height }]}
|
|
143
|
+
accessibilityRole="button"
|
|
144
|
+
accessibilityLabel={label}
|
|
145
|
+
accessibilityState={{ disabled }}
|
|
146
|
+
testID={testID && `${testID}-${direction}`}
|
|
147
|
+
>
|
|
148
|
+
<Icon
|
|
149
|
+
name={direction === 'increment' ? 'plus' : 'minus'}
|
|
150
|
+
size={icon}
|
|
151
|
+
color={disabled ? colors.textLight : colors.primary}
|
|
152
|
+
/>
|
|
153
|
+
</TouchableOpacity>
|
|
154
|
+
);
|
|
44
155
|
|
|
45
156
|
return (
|
|
46
|
-
<View
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
157
|
+
<View
|
|
158
|
+
testID={testID}
|
|
159
|
+
style={[
|
|
160
|
+
styles.control,
|
|
161
|
+
{ height },
|
|
162
|
+
block && styles.controlBlock,
|
|
163
|
+
focused && { borderColor: colors.primary },
|
|
164
|
+
style,
|
|
165
|
+
]}
|
|
166
|
+
>
|
|
167
|
+
{stepButton('decrement', onDecrement, atMin, decrementAccessibilityLabel)}
|
|
55
168
|
<TextInput
|
|
56
169
|
allowFontScaling={allowFontScaling}
|
|
57
|
-
style={[
|
|
170
|
+
style={[
|
|
171
|
+
styles.input,
|
|
172
|
+
{ fontSize },
|
|
173
|
+
editable && styles.editable,
|
|
174
|
+
focused && { borderColor: colors.primary },
|
|
175
|
+
block ? { flex: 1 } : { width: inputWidth },
|
|
176
|
+
]}
|
|
58
177
|
value={value}
|
|
59
|
-
onChangeText={
|
|
60
|
-
|
|
178
|
+
onChangeText={handleChangeText}
|
|
179
|
+
onFocus={() => setFocused(true)}
|
|
180
|
+
onBlur={handleBlur}
|
|
181
|
+
editable={editable}
|
|
182
|
+
keyboardType={allowDecimals ? 'decimal-pad' : 'number-pad'}
|
|
61
183
|
selectTextOnFocus
|
|
62
184
|
accessibilityLabel="Quantity"
|
|
63
185
|
/>
|
|
64
|
-
|
|
65
|
-
onPress={onIncrement}
|
|
66
|
-
style={[styles.btn, { width: btnSize }, isSmall && styles.btnSm]}
|
|
67
|
-
accessibilityRole="button"
|
|
68
|
-
accessibilityLabel={incrementAccessibilityLabel}
|
|
69
|
-
>
|
|
70
|
-
<Icon name="plus" size={iconSize} color={colors.primary} />
|
|
71
|
-
</TouchableOpacity>
|
|
186
|
+
{stepButton('increment', onIncrement, atMax, incrementAccessibilityLabel)}
|
|
72
187
|
</View>
|
|
73
188
|
);
|
|
74
189
|
}
|
package/src/molecules/Steps.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { memo } from 'react';
|
|
2
|
-
import { Text, TouchableOpacity, View, type StyleProp, type ViewStyle } from 'react-native';
|
|
2
|
+
import { ScrollView, Text, TouchableOpacity, View, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
3
|
import { createStyles } from '../theme/createStyles';
|
|
4
4
|
import { useAllowFontScaling, useColors } from '../theme/UIProvider';
|
|
5
5
|
import { Icon } from '../icons/Icon';
|
|
@@ -28,19 +28,29 @@ export interface StepsProps {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const CIRCLE = 32;
|
|
31
|
+
/** Ancho mínimo de una columna horizontal: por debajo, las etiquetas se parten. */
|
|
32
|
+
const STEP_MIN_WIDTH = 76;
|
|
31
33
|
|
|
32
34
|
const useStyles = createStyles((theme) => ({
|
|
33
35
|
rowContainer: { flexDirection: 'row', alignItems: 'flex-start' },
|
|
34
36
|
columnContainer: { flexDirection: 'column' },
|
|
35
37
|
|
|
36
38
|
// horizontal
|
|
37
|
-
|
|
39
|
+
// ScrollView trae flexGrow: 1 de fábrica y se comía todo el alto disponible
|
|
40
|
+
// del contenedor, dejando los pasos pegados arriba de una caja enorme.
|
|
41
|
+
hScrollView: { flexGrow: 0, flexShrink: 0 },
|
|
42
|
+
hScroll: { flexGrow: 1, flexDirection: 'row', alignItems: 'flex-start' },
|
|
43
|
+
// `minWidth` es lo que evita que RN parta palabras a la mitad
|
|
44
|
+
// ("Almacé / n"): antes que apretar la columna por debajo de una palabra
|
|
45
|
+
// corriente, la fila se vuelve scrollable.
|
|
46
|
+
hStep: { flex: 1, minWidth: STEP_MIN_WIDTH, alignItems: 'center', gap: theme.tokens.spacing.sm },
|
|
38
47
|
hLabels: { alignItems: 'center', gap: 2, paddingHorizontal: 4 },
|
|
39
48
|
// El conector toma una fracción chica del ancho: compitiendo de igual a
|
|
40
49
|
// igual con los pasos (flex: 1) cada etiqueta se quedaba con 1/(2n-1) del
|
|
41
|
-
// total
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
// total.
|
|
51
|
+
hConnector: { height: 2, flex: 0.3, minWidth: 12, marginTop: CIRCLE / 2 - 1 },
|
|
52
|
+
hLabel: { fontSize: theme.tokens.fontSize.sm },
|
|
53
|
+
hDescription: { fontSize: theme.tokens.fontSize.xs },
|
|
44
54
|
|
|
45
55
|
// vertical
|
|
46
56
|
vStep: { flexDirection: 'row', gap: theme.tokens.spacing.md },
|
|
@@ -110,6 +120,7 @@ export const Steps = memo(
|
|
|
110
120
|
style={[
|
|
111
121
|
styles.label,
|
|
112
122
|
centered && styles.labelCentered,
|
|
123
|
+
centered && styles.hLabel,
|
|
113
124
|
status === 'pending' && { color: colors.textLight },
|
|
114
125
|
]}
|
|
115
126
|
>
|
|
@@ -118,7 +129,7 @@ export const Steps = memo(
|
|
|
118
129
|
{!!step.description && (
|
|
119
130
|
<Text
|
|
120
131
|
allowFontScaling={allowFontScaling}
|
|
121
|
-
style={[styles.description, centered && styles.descriptionCentered]}
|
|
132
|
+
style={[styles.description, centered && styles.descriptionCentered, centered && styles.hDescription]}
|
|
122
133
|
>
|
|
123
134
|
{step.description}
|
|
124
135
|
</Text>
|
|
@@ -167,7 +178,15 @@ export const Steps = memo(
|
|
|
167
178
|
}
|
|
168
179
|
|
|
169
180
|
return (
|
|
170
|
-
<
|
|
181
|
+
<ScrollView
|
|
182
|
+
testID="steps-row"
|
|
183
|
+
horizontal
|
|
184
|
+
showsHorizontalScrollIndicator={false}
|
|
185
|
+
// Con espacio de sobra los pasos se reparten la fila igual que antes
|
|
186
|
+
// (flexGrow); cuando no entran, se desplaza en vez de apretar el texto.
|
|
187
|
+
style={[styles.hScrollView, style]}
|
|
188
|
+
contentContainerStyle={styles.hScroll}
|
|
189
|
+
>
|
|
171
190
|
{steps.map((step, index) => {
|
|
172
191
|
const status = statusOf(index, current);
|
|
173
192
|
const isLast = index === steps.length - 1;
|
|
@@ -191,7 +210,7 @@ export const Steps = memo(
|
|
|
191
210
|
</React.Fragment>
|
|
192
211
|
);
|
|
193
212
|
})}
|
|
194
|
-
</
|
|
213
|
+
</ScrollView>
|
|
195
214
|
);
|
|
196
215
|
}
|
|
197
216
|
);
|
package/src/molecules/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { Stepper } from './Stepper';
|
|
2
|
-
export type { StepperProps } from './Stepper';
|
|
1
|
+
export { Stepper, sanitizeNumeric } from './Stepper';
|
|
2
|
+
export type { StepperProps, StepperSize } from './Stepper';
|
|
3
3
|
export { OptionCard } from './OptionCard';
|
|
4
4
|
export type { OptionCardProps } from './OptionCard';
|
|
5
5
|
export { InfoRow } from './InfoRow';
|
|
@@ -8,7 +8,7 @@ export { FormActions } from './FormActions';
|
|
|
8
8
|
export type { FormActionsProps } from './FormActions';
|
|
9
9
|
export { AvatarHeader } from './AvatarHeader';
|
|
10
10
|
export type { AvatarHeaderProps } from './AvatarHeader';
|
|
11
|
-
export {
|
|
12
|
-
export type {
|
|
11
|
+
export { SegmentedControl } from './SegmentedControl';
|
|
12
|
+
export type { SegmentedControlProps, SegmentedControlOption } from './SegmentedControl';
|
|
13
13
|
export { Steps } from './Steps';
|
|
14
14
|
export type { StepsProps, StepItem, StepStatus } from './Steps';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React, { createContext, useCallback, useContext, useMemo, useRef, useState } from 'react';
|
|
1
|
+
import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { Alert, type AlertButton, type AlertType } from './Alert';
|
|
3
|
+
import { createImperativeBridge } from './imperativeBridge';
|
|
3
4
|
|
|
4
5
|
export interface AlertOptions {
|
|
5
6
|
title: string;
|
|
@@ -23,6 +24,25 @@ interface AlertContextValue {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
const AlertContext = createContext<AlertContextValue | undefined>(undefined);
|
|
27
|
+
const bridge = createImperativeBridge<AlertContextValue>('AlertProvider');
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Abre un alert desde fuera del árbol de React —un servicio, un interceptor—
|
|
31
|
+
* sin pasar por el hook. Sin <AlertProvider> montado resuelve de inmediato, así
|
|
32
|
+
* que un `await` nunca queda colgado.
|
|
33
|
+
*/
|
|
34
|
+
export function showAlert(options: AlertOptions): Promise<void> {
|
|
35
|
+
return bridge.get()?.alert(options) ?? Promise.resolve();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Igual que showAlert, pero resuelve true/false. Sin provider, false. */
|
|
39
|
+
export function showConfirm(options: ConfirmOptions): Promise<boolean> {
|
|
40
|
+
return bridge.get()?.confirm(options) ?? Promise.resolve(false);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function hideAlert(): void {
|
|
44
|
+
bridge.get()?.hide();
|
|
45
|
+
}
|
|
26
46
|
|
|
27
47
|
interface InternalState {
|
|
28
48
|
visible: boolean;
|
|
@@ -108,6 +128,11 @@ export function AlertProvider({ children }: { children: React.ReactNode }) {
|
|
|
108
128
|
|
|
109
129
|
const value = useMemo<AlertContextValue>(() => ({ alert: alertFn, confirm: confirmFn, hide }), [alertFn, confirmFn, hide]);
|
|
110
130
|
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
bridge.set(value);
|
|
133
|
+
return () => bridge.set(null);
|
|
134
|
+
}, [value]);
|
|
135
|
+
|
|
111
136
|
return (
|
|
112
137
|
<AlertContext.Provider value={value}>
|
|
113
138
|
{children}
|