@sincpro/mobile-ui 1.1.0 → 2.0.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/README.md +156 -29
- package/dist/Actions/Actions.Button.d.ts +10 -12
- package/dist/Actions/Actions.Button.js +9 -11
- package/dist/Actions/Actions.Dock.d.ts +20 -0
- package/dist/Actions/Actions.Dock.js +89 -0
- package/dist/Actions/Actions.FAB.d.ts +1 -2
- package/dist/Actions/Actions.FAB.js +5 -7
- package/dist/Actions/Actions.IconButton.d.ts +1 -2
- package/dist/Actions/Actions.IconButton.js +2 -2
- package/dist/Actions/index.d.ts +4 -0
- package/dist/Actions/index.js +3 -0
- package/dist/Dialog/BottomSheet/BottomSheet.Root.js +22 -34
- package/dist/Dialog/Dialog.ActionSheet.js +2 -16
- package/dist/Dialog/Dialog.Confirmation.js +7 -5
- package/dist/Dialog/Dialog.EditValue.js +3 -1
- package/dist/Dialog/Dialog.Sheet.d.ts +3 -3
- package/dist/Dialog/Dialog.Sheet.js +18 -25
- package/dist/Display/Display.Accordion.js +7 -3
- package/dist/Display/Display.AmountSummary.d.ts +28 -0
- package/dist/Display/Display.AmountSummary.js +25 -0
- package/dist/Display/Display.Carousel.d.ts +27 -4
- package/dist/Display/Display.Carousel.js +112 -12
- package/dist/Display/Display.ExpoImage.d.ts +8 -0
- package/dist/Display/Display.ExpoImage.js +15 -0
- package/dist/Display/Display.Image.d.ts +10 -6
- package/dist/Display/Display.Image.js +39 -6
- package/dist/Display/Display.ListItem.d.ts +1 -2
- package/dist/Display/Display.ListItem.js +3 -3
- package/dist/Display/Display.Logo.d.ts +2 -2
- package/dist/Display/Display.Logo.js +3 -2
- package/dist/Display/Display.MediaCard.d.ts +2 -2
- package/dist/Display/Display.MediaEmbed.d.ts +2 -2
- package/dist/Display/Display.ProductCard.d.ts +2 -2
- package/dist/Display/Display.Story.d.ts +3 -3
- package/dist/Display/Display.Story.js +5 -4
- package/dist/Display/Display.StoryTray.d.ts +4 -4
- package/dist/Display/Display.StoryTray.js +3 -2
- package/dist/Display/Display.StoryViewer.d.ts +3 -3
- package/dist/Display/Display.StoryViewer.js +6 -3
- package/dist/Display/index.d.ts +9 -0
- package/dist/Display/index.js +4 -0
- package/dist/Feedback/Feedback.DomainSplashScreen.d.ts +13 -4
- package/dist/Feedback/Feedback.DomainSplashScreen.js +68 -53
- package/dist/Feedback/Feedback.EmptyState.d.ts +3 -4
- package/dist/Feedback/Feedback.EmptyState.js +4 -3
- package/dist/Feedback/Feedback.Error.d.ts +1 -2
- package/dist/Feedback/Feedback.Error.js +2 -2
- package/dist/Feedback/Feedback.Loading.d.ts +6 -2
- package/dist/Feedback/Feedback.Loading.js +2 -2
- package/dist/Feedback/Feedback.Progress.d.ts +6 -3
- package/dist/Feedback/Feedback.Progress.js +29 -19
- package/dist/Feedback/Feedback.ProgressCircle.d.ts +3 -2
- package/dist/Feedback/Feedback.ProgressCircle.js +10 -7
- package/dist/Feedback/Feedback.Skeleton.d.ts +4 -4
- package/dist/Feedback/Feedback.Skeleton.js +15 -3
- package/dist/Feedback/Feedback.Spinner.d.ts +3 -3
- package/dist/Feedback/Feedback.Spinner.js +40 -9
- package/dist/Feedback/Feedback.Splash.d.ts +2 -2
- package/dist/Feedback/Feedback.Splash.js +5 -3
- package/dist/Feedback/Toast.context.d.ts +64 -3
- package/dist/Feedback/Toast.context.js +112 -31
- package/dist/Feedback/index.d.ts +15 -1
- package/dist/Feedback/index.js +4 -1
- package/dist/Form/Form.Checkbox.d.ts +1 -2
- package/dist/Form/Form.Checkbox.js +2 -2
- package/dist/Form/Form.DatePicker.d.ts +1 -2
- package/dist/Form/Form.DatePicker.js +2 -2
- package/dist/Form/Form.Dropdown.d.ts +15 -3
- package/dist/Form/Form.Dropdown.js +21 -6
- package/dist/Form/Form.ErrorSummary.d.ts +23 -0
- package/dist/Form/Form.ErrorSummary.js +53 -0
- package/dist/Form/Form.Input.d.ts +1 -2
- package/dist/Form/Form.Input.js +2 -2
- package/dist/Form/Form.Select.d.ts +1 -2
- package/dist/Form/Form.Select.js +6 -19
- package/dist/Form/index.d.ts +3 -0
- package/dist/Form/index.js +2 -0
- package/dist/Navigation/Navigation.AppBar.d.ts +2 -6
- package/dist/Navigation/Navigation.AppBar.js +9 -7
- package/dist/Navigation/Navigation.BottomNav.d.ts +1 -3
- package/dist/Navigation/Navigation.BottomNav.js +7 -6
- package/dist/Navigation/Navigation.Header.d.ts +2 -2
- package/dist/Navigation/Navigation.Menu.js +10 -3
- package/dist/Navigation/Navigation.Search.d.ts +41 -0
- package/dist/Navigation/Navigation.Search.js +47 -0
- package/dist/Navigation/Navigation.SearchBar.d.ts +1 -2
- package/dist/Navigation/Navigation.SearchBar.js +16 -19
- package/dist/Navigation/Navigation.SegmentedControl.d.ts +11 -3
- package/dist/Navigation/Navigation.SegmentedControl.js +29 -16
- package/dist/Navigation/index.d.ts +11 -2
- package/dist/Navigation/index.js +2 -0
- package/dist/branding.d.ts +3 -2
- package/dist/primitives/BaseButton.d.ts +10 -12
- package/dist/primitives/BaseButton.js +9 -11
- package/dist/theme/typography.d.ts +1 -1
- package/dist/utils/image.d.ts +3 -0
- package/dist/utils/image.js +13 -0
- package/dist/utils/keys.d.ts +1 -0
- package/dist/utils/keys.js +7 -0
- package/dist/utils/progress.d.ts +1 -0
- package/dist/utils/progress.js +5 -0
- package/dist/utils/reorder.js +2 -5
- package/dist/utils/useReducedMotion.d.ts +1 -0
- package/dist/utils/useReducedMotion.js +43 -0
- package/dist/utils/useSheetTranslation.d.ts +5 -0
- package/dist/utils/useSheetTranslation.js +23 -0
- package/dist/views/AppSplashView.d.ts +3 -2
- package/dist/views/AppSplashView.js +3 -2
- package/dist/views/AuthFormView.d.ts +2 -2
- package/dist/views/FormViewV2.d.ts +2 -2
- package/dist/views/ListViewV2.context.d.ts +3 -1
- package/dist/views/ListViewV2.context.js +41 -21
- package/dist/views/ListViewV2.d.ts +15 -7
- package/dist/views/ListViewV2.js +44 -23
- package/dist/views/types/IListView.js +6 -5
- package/dist/widgets/CollectionOptions.js +9 -7
- package/dist/widgets/FilterBar.d.ts +1 -1
- package/dist/widgets/FilterBar.js +1 -1
- package/dist/widgets/FilterBuilder.js +7 -7
- package/dist/widgets/HomeHeader.d.ts +2 -2
- package/dist/widgets/PromoModal.js +3 -1
- package/dist/widgets/ScreenHeader.d.ts +2 -2
- package/package.json +4 -1
|
@@ -1,13 +1,63 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
2
|
import Icon from "../Display/Display.Icon.js";
|
|
3
|
+
import Spinner from "./Feedback.Spinner.js";
|
|
3
4
|
import Pressable from "../primitives/Pressable.js";
|
|
4
|
-
import {
|
|
5
|
-
import { tv } from "../theme/tw.js";
|
|
5
|
+
import { useTheme } from "../theme/index.js";
|
|
6
|
+
import { cn, tv } from "../theme/tw.js";
|
|
6
7
|
import { Typography } from "../Typography/index.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
8
|
+
import { useReducedMotion } from "../utils/useReducedMotion.js";
|
|
9
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
10
|
+
import { View } from "react-native";
|
|
11
|
+
import { GestureDetector, usePanGesture } from "react-native-gesture-handler";
|
|
9
12
|
import Animated, { FadeInDown, FadeInUp, FadeOutDown, FadeOutUp, } from "react-native-reanimated";
|
|
10
13
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
14
|
+
function ToastRoot({ children, accessibilityHint = "Usa Cerrar o desliza hacia arriba o abajo para cerrar", className, position = "top", style, testID, ...viewProps }) {
|
|
15
|
+
const reducedMotion = useReducedMotion();
|
|
16
|
+
const styles = toast();
|
|
17
|
+
const entering = position === "top" ? FadeInDown : FadeInUp;
|
|
18
|
+
const exiting = position === "top" ? FadeOutUp : FadeOutDown;
|
|
19
|
+
return (_jsx(Animated.View, { ...viewProps, accessibilityHint: accessibilityHint, accessibilityLiveRegion: "polite", accessibilityRole: "alert", className: styles.root({ className }), entering: reducedMotion ? undefined : entering.duration(220), exiting: reducedMotion ? undefined : exiting.duration(180), style: style, testID: testID, children: children }));
|
|
20
|
+
}
|
|
21
|
+
function ToastIcon({ variant = "info", icon, color, size = 22 }) {
|
|
22
|
+
const theme = useTheme();
|
|
23
|
+
if (icon)
|
|
24
|
+
return _jsx(_Fragment, { children: icon });
|
|
25
|
+
if (variant === "loading") {
|
|
26
|
+
return (_jsx(Spinner, { accessibilityLabel: "Cargando", color: color ?? theme.text.accent, size: "small" }));
|
|
27
|
+
}
|
|
28
|
+
const colors = {
|
|
29
|
+
info: theme.info,
|
|
30
|
+
success: theme.success,
|
|
31
|
+
warning: theme.warning,
|
|
32
|
+
danger: theme.danger,
|
|
33
|
+
};
|
|
34
|
+
return _jsx(Icon, { color: color ?? colors[variant], name: iconByVariant[variant], size: size });
|
|
35
|
+
}
|
|
36
|
+
function ToastContent({ children, className, style }) {
|
|
37
|
+
return (_jsx(View, { className: cn("flex-1", className), style: style, children: children }));
|
|
38
|
+
}
|
|
39
|
+
function ToastTitle({ children, className }) {
|
|
40
|
+
return (_jsx(Typography.Text, { className: cn("text-sm font-semibold text-text-primary", className), children: children }));
|
|
41
|
+
}
|
|
42
|
+
function ToastDescription({ children, className }) {
|
|
43
|
+
return (_jsx(Typography.Text, { className: cn("text-sm text-text-secondary", className), children: children }));
|
|
44
|
+
}
|
|
45
|
+
function ToastActionButton({ label, onPress, className, style, ...props }) {
|
|
46
|
+
return (_jsx(Pressable, { ...props, accessibilityLabel: label, accessibilityRole: "button", className: cn("min-h-12 justify-center px-2", className), onPress: onPress, style: [{ minHeight: 48, minWidth: 48 }, style], children: _jsx(Typography.Text, { className: "text-sm font-semibold text-text-accent", children: label }) }));
|
|
47
|
+
}
|
|
48
|
+
function ToastClose({ onPress, accessibilityLabel = "Cerrar", className, style, ...props }) {
|
|
49
|
+
const theme = useTheme();
|
|
50
|
+
return (_jsx(Pressable, { ...props, accessibilityLabel: accessibilityLabel, accessibilityRole: "button", className: cn("min-h-12 min-w-12 items-center justify-center", className), hitSlop: 8, onPress: onPress, style: [{ minHeight: 48, minWidth: 48 }, style], children: _jsx(Icon, { color: theme.text.tertiary, name: "close", size: 18 }) }));
|
|
51
|
+
}
|
|
52
|
+
export const Toast = {
|
|
53
|
+
Root: ToastRoot,
|
|
54
|
+
Icon: ToastIcon,
|
|
55
|
+
Content: ToastContent,
|
|
56
|
+
Title: ToastTitle,
|
|
57
|
+
Description: ToastDescription,
|
|
58
|
+
Action: ToastActionButton,
|
|
59
|
+
Close: ToastClose,
|
|
60
|
+
};
|
|
11
61
|
const ToastContext = createContext(null);
|
|
12
62
|
const DEFAULT_DURATION = 3500;
|
|
13
63
|
// iOS-style card: rounded, hairline border + soft shadow, neutral surface. The variant is
|
|
@@ -20,39 +70,54 @@ const toast = tv({
|
|
|
20
70
|
},
|
|
21
71
|
});
|
|
22
72
|
const iconByVariant = {
|
|
23
|
-
info:
|
|
24
|
-
success:
|
|
25
|
-
warning:
|
|
26
|
-
danger:
|
|
73
|
+
info: "information-circle",
|
|
74
|
+
success: "checkmark-circle",
|
|
75
|
+
warning: "warning",
|
|
76
|
+
danger: "close-circle",
|
|
27
77
|
};
|
|
28
|
-
function ToastItem({ entry, onHide, position, }) {
|
|
78
|
+
function ToastItem({ entry, onHide, onUpdate, position, }) {
|
|
29
79
|
const variant = entry.variant ?? "info";
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
80
|
+
const showClose = entry.dismissible ?? true;
|
|
81
|
+
const dismiss = () => onHide(entry.id);
|
|
82
|
+
const pan = usePanGesture({
|
|
83
|
+
activeOffsetY: [-12, 12],
|
|
84
|
+
failOffsetX: [-24, 24],
|
|
85
|
+
onDeactivate: ({ canceled, translationY, velocityY }) => {
|
|
86
|
+
if (!canceled && (Math.abs(translationY) >= 48 || Math.abs(velocityY) >= 800)) {
|
|
87
|
+
dismiss();
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
return (_jsx(GestureDetector, { gesture: pan, children: _jsx(Toast.Root, { className: entry.className, onAccessibilityEscape: dismiss, position: position, children: entry.render ? (entry.render({
|
|
92
|
+
dismiss,
|
|
93
|
+
id: entry.id,
|
|
94
|
+
update: (options) => onUpdate(entry.id, options),
|
|
95
|
+
})) : (_jsxs(_Fragment, { children: [_jsx(Toast.Icon, { variant: variant }), _jsxs(Toast.Content, { children: [entry.title ? _jsx(Toast.Title, { children: entry.title }) : null, entry.message ? _jsx(Toast.Description, { children: entry.message }) : null] }), entry.action ? (_jsx(Toast.Action, { label: entry.action.label, onPress: () => {
|
|
96
|
+
entry.action?.onPress();
|
|
97
|
+
dismiss();
|
|
98
|
+
} })) : null, showClose ? _jsx(Toast.Close, { onPress: dismiss }) : null] })) }) }));
|
|
39
99
|
}
|
|
40
100
|
export function ToastProvider({ children, position = "bottom", }) {
|
|
41
101
|
const insets = useSafeAreaInsets();
|
|
42
102
|
const [toasts, setToasts] = useState([]);
|
|
103
|
+
const toastsRef = useRef([]);
|
|
43
104
|
const counter = useRef(0);
|
|
44
105
|
const timers = useRef({});
|
|
106
|
+
const commitToasts = useCallback((next) => {
|
|
107
|
+
toastsRef.current = next;
|
|
108
|
+
setToasts(next);
|
|
109
|
+
}, []);
|
|
45
110
|
const clearTimer = useCallback((id) => {
|
|
46
111
|
const timer = timers.current[id];
|
|
47
|
-
if (timer) {
|
|
112
|
+
if (timer !== undefined) {
|
|
48
113
|
clearTimeout(timer);
|
|
49
114
|
delete timers.current[id];
|
|
50
115
|
}
|
|
51
116
|
}, []);
|
|
52
117
|
const hide = useCallback((id) => {
|
|
53
|
-
|
|
118
|
+
commitToasts(toastsRef.current.filter((t) => t.id !== id));
|
|
54
119
|
clearTimer(id);
|
|
55
|
-
}, [clearTimer]);
|
|
120
|
+
}, [clearTimer, commitToasts]);
|
|
56
121
|
const scheduleDismiss = useCallback((id, variant, duration) => {
|
|
57
122
|
clearTimer(id);
|
|
58
123
|
const resolved = duration ?? (variant === "loading" ? 0 : DEFAULT_DURATION);
|
|
@@ -67,16 +132,32 @@ export function ToastProvider({ children, position = "bottom", }) {
|
|
|
67
132
|
id = `toast-${counter.current}`;
|
|
68
133
|
}
|
|
69
134
|
const toastId = id;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
135
|
+
const current = toastsRef.current;
|
|
136
|
+
const currentEntry = current.find((entry) => entry.id === toastId);
|
|
137
|
+
const nextEntry = {
|
|
138
|
+
...currentEntry,
|
|
139
|
+
...options,
|
|
140
|
+
id: toastId,
|
|
141
|
+
message: options.message ?? currentEntry?.message ?? "",
|
|
142
|
+
};
|
|
143
|
+
commitToasts(currentEntry
|
|
144
|
+
? current.map((entry) => (entry.id === toastId ? nextEntry : entry))
|
|
145
|
+
: [...current, nextEntry]);
|
|
146
|
+
scheduleDismiss(toastId, nextEntry.variant, nextEntry.duration);
|
|
74
147
|
return toastId;
|
|
75
|
-
}, [scheduleDismiss]);
|
|
148
|
+
}, [commitToasts, scheduleDismiss]);
|
|
76
149
|
const update = useCallback((id, options) => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
150
|
+
const current = toastsRef.current.find((entry) => entry.id === id);
|
|
151
|
+
if (!current)
|
|
152
|
+
return;
|
|
153
|
+
const updated = { ...current, ...options, id };
|
|
154
|
+
commitToasts(toastsRef.current.map((entry) => (entry.id === id ? updated : entry)));
|
|
155
|
+
scheduleDismiss(id, updated.variant, updated.duration);
|
|
156
|
+
}, [commitToasts, scheduleDismiss]);
|
|
157
|
+
useEffect(() => () => {
|
|
158
|
+
Object.values(timers.current).forEach(clearTimeout);
|
|
159
|
+
timers.current = {};
|
|
160
|
+
}, []);
|
|
80
161
|
const value = useMemo(() => {
|
|
81
162
|
const promise = (p, messages) => {
|
|
82
163
|
const id = show({ message: messages.loading, variant: "loading", duration: 0 });
|
|
@@ -110,7 +191,7 @@ export function ToastProvider({ children, position = "bottom", }) {
|
|
|
110
191
|
promise,
|
|
111
192
|
};
|
|
112
193
|
}, [show, update, hide]);
|
|
113
|
-
return (_jsxs(ToastContext.Provider, { value: value, children: [children, _jsx(View, { className: "absolute left-0 right-0 px-4 gap-2 z-toast", pointerEvents: "box-none", style: position === "top" ? { top: insets.top + 12 } : { bottom: insets.bottom + 12 }, children: toasts.map((entry) => (_jsx(ToastItem, { entry: entry, onHide: hide, position: position }, entry.id))) })] }));
|
|
194
|
+
return (_jsxs(ToastContext.Provider, { value: value, children: [children, _jsx(View, { className: "absolute left-0 right-0 px-4 gap-2 z-toast", pointerEvents: "box-none", style: position === "top" ? { top: insets.top + 12 } : { bottom: insets.bottom + 12 }, children: toasts.map((entry) => (_jsx(ToastItem, { entry: entry, onHide: hide, onUpdate: update, position: position }, entry.id))) })] }));
|
|
114
195
|
}
|
|
115
196
|
export function useToast() {
|
|
116
197
|
const ctx = useContext(ToastContext);
|
package/dist/Feedback/index.d.ts
CHANGED
|
@@ -9,7 +9,12 @@ import ProgressCircle from "./Feedback.ProgressCircle.js";
|
|
|
9
9
|
import Skeleton from "./Feedback.Skeleton.js";
|
|
10
10
|
import SplashScreen from "./Feedback.Splash.js";
|
|
11
11
|
export { ErrorBoundary } from "./ErrorBoundary.js";
|
|
12
|
-
export
|
|
12
|
+
export type { ProgressProps } from "./Feedback.Progress.js";
|
|
13
|
+
export type { ProgressCircleProps } from "./Feedback.ProgressCircle.js";
|
|
14
|
+
export type { SkeletonProps } from "./Feedback.Skeleton.js";
|
|
15
|
+
export type { SpinnerProps, SpinnerVariant, } from "./Feedback.Spinner.js";
|
|
16
|
+
export { Toast, type ToastAction, type ToastActionProps, type ToastCloseProps, type ToastContentProps, type ToastContextValue, type ToastCustomOptions, type ToastIconProps, type ToastOptions, type ToastPromiseMessages, ToastProvider, type ToastRenderProps, type ToastRootProps, type ToastTextProps, type ToastVariant, useToast, } from "./Toast.context.js";
|
|
17
|
+
export { useReducedMotion } from "../utils/useReducedMotion.js";
|
|
13
18
|
export declare const Feedback: {
|
|
14
19
|
Loading: typeof Loading;
|
|
15
20
|
Spinner: import("react").FC<import("./Feedback.Spinner.js").SpinnerProps>;
|
|
@@ -22,5 +27,14 @@ export declare const Feedback: {
|
|
|
22
27
|
EmptyState: typeof EmptyState;
|
|
23
28
|
Error: typeof Error;
|
|
24
29
|
SplashScreen: typeof SplashScreen;
|
|
30
|
+
Toast: {
|
|
31
|
+
Root: ({ children, accessibilityHint, className, position, style, testID, ...viewProps }: import("./Toast.context.js").ToastRootProps) => import("react").JSX.Element;
|
|
32
|
+
Icon: ({ variant, icon, color, size }: import("./Toast.context.js").ToastIconProps) => import("react").JSX.Element;
|
|
33
|
+
Content: ({ children, className, style }: import("./Toast.context.js").ToastContentProps) => import("react").JSX.Element;
|
|
34
|
+
Title: ({ children, className }: import("./Toast.context.js").ToastTextProps) => import("react").JSX.Element;
|
|
35
|
+
Description: ({ children, className }: import("./Toast.context.js").ToastTextProps) => import("react").JSX.Element;
|
|
36
|
+
Action: ({ label, onPress, className, style, ...props }: import("./Toast.context.js").ToastActionProps) => import("react").JSX.Element;
|
|
37
|
+
Close: ({ onPress, accessibilityLabel, className, style, ...props }: import("./Toast.context.js").ToastCloseProps) => import("react").JSX.Element;
|
|
38
|
+
};
|
|
25
39
|
};
|
|
26
40
|
export default Feedback;
|
package/dist/Feedback/index.js
CHANGED
|
@@ -9,8 +9,10 @@ import ProgressCircle from "./Feedback.ProgressCircle.js";
|
|
|
9
9
|
import Skeleton from "./Feedback.Skeleton.js";
|
|
10
10
|
import Spinner from "./Feedback.Spinner.js";
|
|
11
11
|
import SplashScreen from "./Feedback.Splash.js";
|
|
12
|
+
import { Toast } from "./Toast.context.js";
|
|
12
13
|
export { ErrorBoundary } from "./ErrorBoundary.js";
|
|
13
|
-
export { ToastProvider, useToast, } from "./Toast.context.js";
|
|
14
|
+
export { Toast, ToastProvider, useToast, } from "./Toast.context.js";
|
|
15
|
+
export { useReducedMotion } from "../utils/useReducedMotion.js";
|
|
14
16
|
export const Feedback = {
|
|
15
17
|
Loading,
|
|
16
18
|
Spinner,
|
|
@@ -23,5 +25,6 @@ export const Feedback = {
|
|
|
23
25
|
EmptyState,
|
|
24
26
|
Error,
|
|
25
27
|
SplashScreen,
|
|
28
|
+
Toast,
|
|
26
29
|
};
|
|
27
30
|
export default Feedback;
|
|
@@ -8,9 +8,8 @@ interface RowCheckBoxProps {
|
|
|
8
8
|
onCheck?: (isChecked: boolean) => void;
|
|
9
9
|
disabled?: boolean;
|
|
10
10
|
accessibilityLabel?: string;
|
|
11
|
-
accessibleSizing?: boolean;
|
|
12
11
|
className?: string;
|
|
13
12
|
testID?: string;
|
|
14
13
|
}
|
|
15
|
-
declare function RowCheckBox({ mainText, subText, rightText, rightComponent, leftComponent, isChecked, onCheck, disabled, accessibilityLabel,
|
|
14
|
+
declare function RowCheckBox({ mainText, subText, rightText, rightComponent, leftComponent, isChecked, onCheck, disabled, accessibilityLabel, className, testID, }: RowCheckBoxProps): import("react").JSX.Element;
|
|
16
15
|
export default RowCheckBox;
|
|
@@ -4,7 +4,7 @@ import { cn } from "../theme/tw.js";
|
|
|
4
4
|
import { Typography } from "../Typography/index.js";
|
|
5
5
|
import { Checkbox } from "expo-checkbox";
|
|
6
6
|
import { Pressable, View } from "react-native";
|
|
7
|
-
function RowCheckBox({ mainText, subText, rightText, rightComponent, leftComponent, isChecked = false, onCheck = () => { }, disabled = false, accessibilityLabel,
|
|
7
|
+
function RowCheckBox({ mainText, subText, rightText, rightComponent, leftComponent, isChecked = false, onCheck = () => { }, disabled = false, accessibilityLabel, className, testID, }) {
|
|
8
8
|
const handlePress = () => {
|
|
9
9
|
if (!disabled) {
|
|
10
10
|
onCheck(!isChecked);
|
|
@@ -25,6 +25,6 @@ function RowCheckBox({ mainText, subText, rightText, rightComponent, leftCompone
|
|
|
25
25
|
}
|
|
26
26
|
return null;
|
|
27
27
|
};
|
|
28
|
-
return (_jsxs(Pressable, { accessibilityLabel: accessibilityLabel ?? [mainText, subText, rightText].filter(Boolean).join(", "), accessibilityRole: "checkbox", accessibilityState: { checked: isChecked, disabled }, className: cn("flex-row justify-between items-center", disabled && "opacity-50", className), disabled: disabled, onPress: handlePress, style:
|
|
28
|
+
return (_jsxs(Pressable, { accessibilityLabel: accessibilityLabel ?? [mainText, subText, rightText].filter(Boolean).join(", "), accessibilityRole: "checkbox", accessibilityState: { checked: isChecked, disabled }, className: cn("flex-row justify-between items-center", disabled && "opacity-50", className), disabled: disabled, onPress: handlePress, style: { minHeight: 48, minWidth: 48, paddingVertical: 8 }, testID: testID, children: [_jsx(View, { className: "flex-1 min-w-0", children: renderLeftComponent() }), _jsxs(View, { className: "flex-row items-center flex-shrink", children: [renderRightComponent(), _jsx(Checkbox, { accessibilityElementsHidden: true, accessible: false, className: "ml-2", color: theme.primary, disabled: disabled, importantForAccessibility: "no-hide-descendants", pointerEvents: "none", value: isChecked })] })] }));
|
|
29
29
|
}
|
|
30
30
|
export default RowCheckBox;
|
|
@@ -8,7 +8,6 @@ export interface DatePickerProps {
|
|
|
8
8
|
minimumDate?: Date;
|
|
9
9
|
maximumDate?: Date;
|
|
10
10
|
disabled?: boolean;
|
|
11
|
-
accessibleSizing?: boolean;
|
|
12
11
|
className?: string;
|
|
13
12
|
testID?: string;
|
|
14
13
|
}
|
|
@@ -17,5 +16,5 @@ export interface DatePickerProps {
|
|
|
17
16
|
* (`@react-native-community/datetimepicker`) — a case where native wins (expected UX).
|
|
18
17
|
* Subpath `Form/DatePicker`; **optional** peer dep.
|
|
19
18
|
*/
|
|
20
|
-
declare function DatePicker({ value, onChange, mode, label, placeholder, format, minimumDate, maximumDate, disabled,
|
|
19
|
+
declare function DatePicker({ value, onChange, mode, label, placeholder, format, minimumDate, maximumDate, disabled, className, testID, }: DatePickerProps): import("react").JSX.Element;
|
|
21
20
|
export default DatePicker;
|
|
@@ -24,7 +24,7 @@ const iconByMode = {
|
|
|
24
24
|
* (`@react-native-community/datetimepicker`) — a case where native wins (expected UX).
|
|
25
25
|
* Subpath `Form/DatePicker`; **optional** peer dep.
|
|
26
26
|
*/
|
|
27
|
-
function DatePicker({ value, onChange, mode = "date", label, placeholder = "Seleccionar…", format, minimumDate, maximumDate, disabled = false,
|
|
27
|
+
function DatePicker({ value, onChange, mode = "date", label, placeholder = "Seleccionar…", format, minimumDate, maximumDate, disabled = false, className, testID, }) {
|
|
28
28
|
const [open, setOpen] = useState(false);
|
|
29
29
|
const theme = useTheme();
|
|
30
30
|
const fmt = format ?? ((d) => defaultFormat(d, mode));
|
|
@@ -34,6 +34,6 @@ function DatePicker({ value, onChange, mode = "date", label, placeholder = "Sele
|
|
|
34
34
|
if (date)
|
|
35
35
|
onChange(date);
|
|
36
36
|
};
|
|
37
|
-
return (_jsxs(View, { className: cn("gap-1", className), testID: testID, children: [label ? (_jsx(Typography.Text, { className: "font-medium text-text-primary", variant: "bodySmall", children: label })) : null, _jsxs(Pressable, { accessibilityHint: "Abre el selector de fecha", accessibilityLabel: label ?? placeholder, accessibilityRole: "button", accessibilityState: { disabled }, accessibilityValue: { text: value ? fmt(value) : placeholder }, className: "flex-row items-center justify-between rounded-lg border border-input bg-bg-card px-3 min-h-[
|
|
37
|
+
return (_jsxs(View, { className: cn("gap-1", className), testID: testID, children: [label ? (_jsx(Typography.Text, { className: "font-medium text-text-primary", variant: "bodySmall", children: label })) : null, _jsxs(Pressable, { accessibilityHint: "Abre el selector de fecha", accessibilityLabel: label ?? placeholder, accessibilityRole: "button", accessibilityState: { disabled }, accessibilityValue: { text: value ? fmt(value) : placeholder }, className: "flex-row items-center justify-between rounded-lg border border-input bg-bg-card px-3 min-h-[48px]", disabled: disabled, onPress: () => setOpen(true), style: { minHeight: 48, minWidth: 48 }, children: [_jsx(Typography.Text, { className: cn("flex-1 text-sm", value ? "text-text-primary" : "text-text-tertiary"), style: { paddingVertical: 12 }, children: value ? fmt(value) : placeholder }), _jsx(Icon, { color: theme.icon.secondary, name: iconByMode[mode], size: 18 })] }), open ? (_jsx(DateTimePicker, { maximumDate: maximumDate, minimumDate: minimumDate, mode: mode === "datetime" ? "date" : mode, onDismiss: () => setOpen(false), onValueChange: handleValueChange, value: value ?? new Date() })) : null] }));
|
|
38
38
|
}
|
|
39
39
|
export default DatePicker;
|
|
@@ -1,14 +1,26 @@
|
|
|
1
|
+
import { Typography } from "../Typography/index.js";
|
|
2
|
+
import { type ComponentProps, type ReactNode } from "react";
|
|
3
|
+
import { type TouchableOpacityProps, type ViewProps } from "react-native";
|
|
1
4
|
export interface DropdownOption {
|
|
2
5
|
id: string;
|
|
3
6
|
title: string;
|
|
4
7
|
onPress: () => void;
|
|
5
8
|
disabled?: boolean;
|
|
6
9
|
}
|
|
7
|
-
interface DropdownButtonProps {
|
|
10
|
+
export interface DropdownButtonProps {
|
|
8
11
|
title: string;
|
|
9
|
-
options: DropdownOption[];
|
|
12
|
+
options: readonly DropdownOption[];
|
|
10
13
|
variant?: "primary" | "secondary" | "outline";
|
|
11
14
|
size?: "xs" | "small" | "medium" | "large";
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
closeAccessibilityLabel?: string;
|
|
17
|
+
buttonProps?: Omit<TouchableOpacityProps, "children" | "onPress" | "disabled">;
|
|
18
|
+
contentProps?: Omit<ViewProps, "children">;
|
|
19
|
+
optionProps?: Omit<TouchableOpacityProps, "children" | "onPress" | "disabled">;
|
|
20
|
+
titleTextProps?: ComponentProps<typeof Typography.Text>;
|
|
21
|
+
optionTextProps?: ComponentProps<typeof Typography.Text>;
|
|
22
|
+
renderOption?: (option: DropdownOption) => ReactNode;
|
|
23
|
+
testID?: string;
|
|
12
24
|
}
|
|
13
|
-
declare function DropdownButton({ title, options, variant, size, }: DropdownButtonProps): import("react").JSX.Element;
|
|
25
|
+
declare function DropdownButton({ title, options, variant, size, disabled, closeAccessibilityLabel, buttonProps, contentProps, optionProps, titleTextProps, optionTextProps, renderOption, testID, }: DropdownButtonProps): import("react").JSX.Element;
|
|
14
26
|
export default DropdownButton;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "nativewind/jsx-runtime";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import IconButton from "../Actions/Actions.IconButton.js";
|
|
3
|
+
import Icon from "../Display/Display.Icon.js";
|
|
4
|
+
import { useTheme } from "../theme/index.js";
|
|
5
|
+
import { cn } from "../theme/tw.js";
|
|
4
6
|
import { Typography } from "../Typography/index.js";
|
|
7
|
+
import { useReducedMotion } from "../utils/useReducedMotion.js";
|
|
5
8
|
import { useState } from "react";
|
|
6
|
-
import { Modal, TouchableOpacity, TouchableWithoutFeedback, View } from "react-native";
|
|
9
|
+
import { Modal, ScrollView, TouchableOpacity, TouchableWithoutFeedback, View, } from "react-native";
|
|
7
10
|
import { tv } from "tailwind-variants";
|
|
8
11
|
const dropdown = tv({
|
|
9
12
|
slots: {
|
|
@@ -52,7 +55,9 @@ const dropdown = tv({
|
|
|
52
55
|
size: "small",
|
|
53
56
|
},
|
|
54
57
|
});
|
|
55
|
-
function DropdownButton({ title, options, variant = "secondary", size = "small", }) {
|
|
58
|
+
function DropdownButton({ title, options, variant = "secondary", size = "small", disabled = false, closeAccessibilityLabel = "Cerrar opciones", buttonProps, contentProps, optionProps, titleTextProps, optionTextProps, renderOption, testID, }) {
|
|
59
|
+
const theme = useTheme();
|
|
60
|
+
const reducedMotion = useReducedMotion();
|
|
56
61
|
const [isOpen, setIsOpen] = useState(false);
|
|
57
62
|
const handleOptionPress = (option) => {
|
|
58
63
|
if (!option.disabled) {
|
|
@@ -77,9 +82,19 @@ function DropdownButton({ title, options, variant = "secondary", size = "small",
|
|
|
77
82
|
xs: "bodySmall",
|
|
78
83
|
small: "bodySmall",
|
|
79
84
|
}[size];
|
|
80
|
-
return (_jsxs(_Fragment, { children: [_jsxs(TouchableOpacity, {
|
|
85
|
+
return (_jsxs(_Fragment, { children: [_jsxs(TouchableOpacity, { ...buttonProps, accessibilityLabel: buttonProps?.accessibilityLabel ?? title, accessibilityRole: "button", accessibilityState: {
|
|
86
|
+
...buttonProps?.accessibilityState,
|
|
87
|
+
disabled,
|
|
88
|
+
expanded: isOpen,
|
|
89
|
+
}, activeOpacity: 0.7, className: cn(styles.button(), buttonProps?.className), disabled: disabled, onPress: () => setIsOpen(true), style: [{ minHeight: 48, minWidth: 48 }, buttonProps?.style], testID: testID, children: [_jsx(Typography.Text, { className: textColorClass, variant: textVariant, children: title }), _jsx(Icon, { color: iconColor, name: "chevron-down", size: 16, type: "feather" })] }), _jsx(Modal, { animationType: reducedMotion ? "none" : "fade", onRequestClose: () => setIsOpen(false), presentationStyle: "overFullScreen", transparent: true, visible: isOpen, children: _jsxs(View, { className: styles.overlay(), children: [_jsx(TouchableWithoutFeedback, { accessible: false, onPress: () => setIsOpen(false), children: _jsx(View, { className: "absolute inset-0" }) }), _jsxs(View, { ...contentProps, accessibilityViewIsModal: true, className: cn(styles.dropdownContainer(), contentProps?.className), onAccessibilityEscape: () => setIsOpen(false), style: [
|
|
90
|
+
{
|
|
81
91
|
backgroundColor: theme.bg.popover,
|
|
82
92
|
...theme.shadow.lg,
|
|
83
|
-
},
|
|
93
|
+
},
|
|
94
|
+
contentProps?.style,
|
|
95
|
+
], children: [_jsxs(View, { className: styles.header(), children: [_jsx(Typography.Text, { ...titleTextProps, accessibilityRole: "header", className: cn("text-text-primary", titleTextProps?.className), semibold: titleTextProps?.semibold ?? true, variant: titleTextProps?.variant ?? "bodyLarge", children: title }), _jsx(IconButton, { accessibilityLabel: closeAccessibilityLabel, icon: "close", onPress: () => setIsOpen(false) })] }), _jsx(ScrollView, { contentContainerClassName: styles.optionsList(), keyboardShouldPersistTaps: "handled", style: { flexShrink: 1 }, children: options.map((option, index) => (_jsxs(View, { children: [_jsx(TouchableOpacity, { ...optionProps, accessibilityLabel: option.title, accessibilityRole: "button", accessibilityState: {
|
|
96
|
+
...optionProps?.accessibilityState,
|
|
97
|
+
disabled: option.disabled,
|
|
98
|
+
}, activeOpacity: 0.7, className: cn(dropdown({ disabled: option.disabled }).option(), optionProps?.className), disabled: option.disabled, onPress: () => handleOptionPress(option), style: [{ minHeight: 48, minWidth: 48 }, optionProps?.style], children: renderOption ? (renderOption(option)) : (_jsx(Typography.Text, { ...optionTextProps, className: cn(option.disabled ? "text-text-disabled" : "text-text-primary", optionTextProps?.className), variant: optionTextProps?.variant ?? "body", children: option.title })) }), index < options.length - 1 && _jsx(View, { className: styles.separator() })] }, option.id))) })] })] }) })] }));
|
|
84
99
|
}
|
|
85
100
|
export default DropdownButton;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Typography } from "../Typography/index.js";
|
|
2
|
+
import { type ComponentProps, type ReactNode } from "react";
|
|
3
|
+
import { type StyleProp, type ViewProps, type ViewStyle } from "react-native";
|
|
4
|
+
export interface ErrorSummaryItem {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly label: string;
|
|
7
|
+
readonly message: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ErrorSummaryProps extends Omit<ViewProps, "children"> {
|
|
10
|
+
errors: readonly ErrorSummaryItem[];
|
|
11
|
+
onSelectError: (id: string) => void;
|
|
12
|
+
title?: string;
|
|
13
|
+
announce?: boolean;
|
|
14
|
+
className?: string;
|
|
15
|
+
itemStyle?: StyleProp<ViewStyle>;
|
|
16
|
+
/** Custom content must preserve the message and remain scalable, multiline and non-interactive. */
|
|
17
|
+
renderErrorContent?: (error: ErrorSummaryItem, index: number) => ReactNode;
|
|
18
|
+
/** Custom content must remain scalable and multiline; the wrapper owns header semantics. */
|
|
19
|
+
renderHeader?: (title: string) => ReactNode;
|
|
20
|
+
itemTextProps?: Omit<ComponentProps<typeof Typography.Text>, "children" | "accessibilityRole" | "allowFontScaling" | "numberOfLines">;
|
|
21
|
+
titleTextProps?: Omit<ComponentProps<typeof Typography.Text>, "children" | "accessibilityRole" | "allowFontScaling" | "numberOfLines">;
|
|
22
|
+
}
|
|
23
|
+
export default function ErrorSummary({ errors, onSelectError, title, announce, className, style, itemStyle, renderErrorContent, renderHeader, itemTextProps, titleTextProps, ...props }: ErrorSummaryProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
|
+
import Pressable from "../primitives/Pressable.js";
|
|
3
|
+
import { useTheme } from "../theme/index.js";
|
|
4
|
+
import { Typography } from "../Typography/index.js";
|
|
5
|
+
import { uniqueKeys } from "../utils/keys.js";
|
|
6
|
+
import { useEffect, useRef } from "react";
|
|
7
|
+
import { AccessibilityInfo, StyleSheet, View, } from "react-native";
|
|
8
|
+
export default function ErrorSummary({ errors, onSelectError, title = "Revisa los siguientes errores", announce = true, className, style, itemStyle, renderErrorContent, renderHeader, itemTextProps, titleTextProps, ...props }) {
|
|
9
|
+
const theme = useTheme();
|
|
10
|
+
const nonemptyEpisode = useRef(false);
|
|
11
|
+
uniqueKeys(errors, (error) => error.id, "ErrorSummary");
|
|
12
|
+
for (const { label, message } of errors) {
|
|
13
|
+
if (typeof label !== "string" || !label.trim())
|
|
14
|
+
throw new Error("Form.ErrorSummary requires non-empty error labels.");
|
|
15
|
+
if (typeof message !== "string" || !message.trim())
|
|
16
|
+
throw new Error("Form.ErrorSummary requires non-empty error messages.");
|
|
17
|
+
}
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (errors.length === 0) {
|
|
20
|
+
nonemptyEpisode.current = false;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (nonemptyEpisode.current)
|
|
24
|
+
return;
|
|
25
|
+
nonemptyEpisode.current = true;
|
|
26
|
+
if (announce)
|
|
27
|
+
AccessibilityInfo.announceForAccessibility([title, ...errors.map(({ label, message }) => `${label}: ${message}`)].join("\n"));
|
|
28
|
+
}, [announce, errors, title]);
|
|
29
|
+
if (errors.length === 0)
|
|
30
|
+
return null;
|
|
31
|
+
return (_jsxs(View, { className: className, style: [
|
|
32
|
+
{
|
|
33
|
+
backgroundColor: theme.bg.card,
|
|
34
|
+
borderColor: theme.danger,
|
|
35
|
+
borderWidth: 2,
|
|
36
|
+
borderRadius: 8,
|
|
37
|
+
padding: 16,
|
|
38
|
+
gap: 8,
|
|
39
|
+
},
|
|
40
|
+
style,
|
|
41
|
+
], ...props, children: [renderHeader ? (_jsx(View, { accessibilityLabel: title, accessibilityRole: "header", accessible: true, children: renderHeader(title) })) : (_jsx(Typography.Text, { semibold: true, variant: "body", ...titleTextProps, accessibilityRole: "header", allowFontScaling: true, numberOfLines: 0, children: title })), errors.map((error, index) => (_jsx(Pressable, { accessibilityLabel: `${error.label}: ${error.message}`, accessibilityRole: "button", accessible: true, hitSlop: 0, onPress: () => onSelectError(error.id), style: [styles.item, itemStyle, styles.target], children: renderErrorContent ? (renderErrorContent(error, index)) : (_jsxs(_Fragment, { children: [_jsx(Typography.Text, { semibold: true, variant: "bodySmall", ...itemTextProps, allowFontScaling: true, numberOfLines: 0, children: error.label }), _jsx(Typography.Text, { className: "text-text-accent", underline: true, ...itemTextProps, allowFontScaling: true, numberOfLines: 0, children: error.message })] })) }, error.id)))] }));
|
|
42
|
+
}
|
|
43
|
+
const styles = StyleSheet.create({
|
|
44
|
+
item: {
|
|
45
|
+
justifyContent: "center",
|
|
46
|
+
paddingVertical: 8,
|
|
47
|
+
gap: 4,
|
|
48
|
+
},
|
|
49
|
+
target: {
|
|
50
|
+
minHeight: 48,
|
|
51
|
+
minWidth: 48,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
@@ -6,7 +6,6 @@ interface FormInputProps extends TextInputProps {
|
|
|
6
6
|
error?: string;
|
|
7
7
|
tooltip?: string;
|
|
8
8
|
className?: string;
|
|
9
|
-
accessibleSizing?: boolean;
|
|
10
9
|
}
|
|
11
|
-
declare function FormInput({ label, error, tooltip, style, multiline, numberOfLines, onFocus, onBlur, className,
|
|
10
|
+
declare function FormInput({ label, error, tooltip, style, multiline, numberOfLines, onFocus, onBlur, className, ref, ...props }: FormInputProps): import("react").JSX.Element;
|
|
12
11
|
export default FormInput;
|
package/dist/Form/Form.Input.js
CHANGED
|
@@ -15,7 +15,7 @@ const inputContainer = tv({
|
|
|
15
15
|
},
|
|
16
16
|
defaultVariants: { state: "default" },
|
|
17
17
|
});
|
|
18
|
-
function FormInput({ label, error, tooltip, style, multiline = false, numberOfLines = 1, onFocus, onBlur, className,
|
|
18
|
+
function FormInput({ label, error, tooltip, style, multiline = false, numberOfLines = 1, onFocus, onBlur, className, ref, ...props }) {
|
|
19
19
|
const [isFocused, setIsFocused] = useState(false);
|
|
20
20
|
const hasError = Boolean(error);
|
|
21
21
|
const handleFocus = (e) => {
|
|
@@ -33,6 +33,6 @@ function FormInput({ label, error, tooltip, style, multiline = false, numberOfLi
|
|
|
33
33
|
return "focused";
|
|
34
34
|
return "default";
|
|
35
35
|
};
|
|
36
|
-
return (_jsxs(View, { className: cn("mb-2", className), children: [_jsx(Typography.Text, { className: "mb-1 font-medium text-text-primary", variant: "bodySmall", children: label }), _jsx(View, { className: inputContainer({ state: getState() }), children: _jsx(TextInput, { accessibilityHint: error || tooltip, accessibilityLabel: label, className: cn("px-3 py-2.5 text-sm min-h-[
|
|
36
|
+
return (_jsxs(View, { className: cn("mb-2", className), children: [_jsx(Typography.Text, { className: "mb-1 font-medium text-text-primary", variant: "bodySmall", children: label }), _jsx(View, { className: inputContainer({ state: getState() }), children: _jsx(TextInput, { accessibilityHint: error || tooltip, accessibilityLabel: label, className: cn("px-3 py-2.5 text-sm min-h-[48px] text-text-primary", multiline && "min-h-[64px] pt-2.5"), multiline: multiline, numberOfLines: numberOfLines, onBlur: handleBlur, onFocus: handleFocus, placeholderTextColor: theme.text.tertiary, ref: ref, selectionColor: theme.primary, style: [{ minHeight: multiline ? 64 : 48 }, style], textAlignVertical: multiline ? "top" : "center", underlineColorAndroid: "transparent", ...props }) }), hasError && (_jsx(Typography.Text, { accessibilityLiveRegion: "polite", className: "mt-0.5 text-danger", variant: "bodySmall", children: error }))] }));
|
|
37
37
|
}
|
|
38
38
|
export default FormInput;
|
|
@@ -10,10 +10,9 @@ export interface SelectProps<T extends string | number> {
|
|
|
10
10
|
placeholder?: string;
|
|
11
11
|
title?: string;
|
|
12
12
|
disabled?: boolean;
|
|
13
|
-
accessibleSizing?: boolean;
|
|
14
13
|
className?: string;
|
|
15
14
|
testID?: string;
|
|
16
15
|
showCloseButton?: boolean;
|
|
17
16
|
}
|
|
18
|
-
declare function Select<T extends string | number>({ options, value, onChange, label, placeholder, title, disabled,
|
|
17
|
+
declare function Select<T extends string | number>({ options, value, onChange, label, placeholder, title, disabled, className, testID, showCloseButton, }: SelectProps<T>): import("react").JSX.Element;
|
|
19
18
|
export default Select;
|
package/dist/Form/Form.Select.js
CHANGED
|
@@ -4,33 +4,20 @@ import Pressable from "../primitives/Pressable.js";
|
|
|
4
4
|
import { themeToVars, useTheme } from "../theme/index.js";
|
|
5
5
|
import { cn } from "../theme/tw.js";
|
|
6
6
|
import { Typography } from "../Typography/index.js";
|
|
7
|
+
import { useSheetTranslation } from "../utils/useSheetTranslation.js";
|
|
7
8
|
import { vars } from "nativewind";
|
|
8
|
-
import {
|
|
9
|
+
import { useState } from "react";
|
|
9
10
|
import { Animated, Modal, ScrollView, View } from "react-native";
|
|
10
11
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
11
|
-
function Select({ options, value, onChange, label, placeholder = "Seleccionar…", title, disabled = false,
|
|
12
|
+
function Select({ options, value, onChange, label, placeholder = "Seleccionar…", title, disabled = false, className, testID, showCloseButton = true, }) {
|
|
12
13
|
const theme = useTheme();
|
|
13
14
|
const insets = useSafeAreaInsets();
|
|
14
15
|
const [open, setOpen] = useState(false);
|
|
15
16
|
const selected = options.find((o) => o.value === value) ?? null;
|
|
16
|
-
|
|
17
|
-
const translateY = useRef(new Animated.Value(800)).current;
|
|
18
|
-
useEffect(() => {
|
|
19
|
-
if (open) {
|
|
20
|
-
Animated.spring(translateY, {
|
|
21
|
-
toValue: 0,
|
|
22
|
-
useNativeDriver: true,
|
|
23
|
-
damping: 20,
|
|
24
|
-
stiffness: 300,
|
|
25
|
-
}).start();
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
translateY.setValue(800);
|
|
29
|
-
}
|
|
30
|
-
}, [open, translateY]);
|
|
17
|
+
const { translateY } = useSheetTranslation(open);
|
|
31
18
|
return (_jsxs(View, { className: cn("gap-1", className), testID: testID, children: [label ? (_jsx(Typography.Text, { className: "font-medium text-text-primary", variant: "bodySmall", children: label })) : null, _jsxs(Pressable, { accessibilityHint: "Abre la lista de opciones", accessibilityLabel: label
|
|
32
19
|
? `${label}: ${selected?.label ?? placeholder}`
|
|
33
|
-
: (selected?.label ?? placeholder), accessibilityRole: "button", accessibilityState: { disabled, expanded: open }, className:
|
|
20
|
+
: (selected?.label ?? placeholder), accessibilityRole: "button", accessibilityState: { disabled, expanded: open }, className: "flex-row items-center justify-between rounded-lg border border-input bg-bg-card px-3 min-h-[48px]", disabled: disabled, onPress: () => setOpen(true), style: { minHeight: 48, minWidth: 48 }, children: [_jsx(Typography.Text, { className: cn("flex-1 text-sm", selected ? "text-text-primary" : "text-text-tertiary"), style: { paddingVertical: 12 }, children: selected ? selected.label : placeholder }), _jsx(Icon, { color: theme.icon.secondary, name: "chevron-down", size: 18 })] }), _jsx(Modal, { animationType: "none", onRequestClose: () => setOpen(false), presentationStyle: "overFullScreen", transparent: true, visible: open, children: _jsxs(View, { className: "flex-1 justify-end", children: [_jsx(View, { accessibilityElementsHidden: true, className: "absolute inset-0 bg-black/50", importantForAccessibility: "no-hide-descendants", onResponderRelease: () => setOpen(false), onStartShouldSetResponder: () => true }), _jsxs(Animated.View, { accessibilityViewIsModal: true, className: "rounded-t-3xl", importantForAccessibility: "yes", onAccessibilityEscape: () => setOpen(false), style: [
|
|
34
21
|
vars(themeToVars(theme)),
|
|
35
22
|
{
|
|
36
23
|
transform: [{ translateY }],
|
|
@@ -48,7 +35,7 @@ function Select({ options, value, onChange, label, placeholder = "Seleccionar…
|
|
|
48
35
|
return (_jsxs(Pressable, { accessibilityRole: "menuitem", accessibilityState: { selected: isSelected }, className: "flex-row items-center justify-between px-4 py-3 border-b border-border-light", onPress: () => {
|
|
49
36
|
onChange(option.value);
|
|
50
37
|
setOpen(false);
|
|
51
|
-
}, style:
|
|
38
|
+
}, style: { minHeight: 48, minWidth: 48 }, children: [_jsx(Typography.Text, { className: cn("flex-1 text-sm", isSelected ? "text-text-accent font-semibold" : "text-text-primary"), children: option.label }), isSelected ? (_jsx(Icon, { color: theme.text.accent, name: "checkmark", size: 18 })) : null] }, String(option.value)));
|
|
52
39
|
}) })] })] }) })] }));
|
|
53
40
|
}
|
|
54
41
|
export default Select;
|
package/dist/Form/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import ControlledMonetaryInput from "./Form.ControlledMonetaryInput.js";
|
|
|
6
6
|
import ControlledRadioButton from "./Form.ControlledRadioButton.js";
|
|
7
7
|
import DatePicker from "./Form.DatePicker.js";
|
|
8
8
|
import Dropdown from "./Form.Dropdown.js";
|
|
9
|
+
import ErrorSummary from "./Form.ErrorSummary.js";
|
|
9
10
|
import FAB from "./Form.FAB.js";
|
|
10
11
|
import { FilterButtonGroup } from "./Form.FilterButton.js";
|
|
11
12
|
import FractionalQuantityInput from "./Form.FractionalQuantityInput.js";
|
|
@@ -27,6 +28,7 @@ export declare const Form: {
|
|
|
27
28
|
InputField: import("react").FC<import("./Form.InputField.js").InputFieldProps>;
|
|
28
29
|
KeyboardForm: typeof KeyboardForm;
|
|
29
30
|
KeyboardToolbar: typeof KeyboardToolbar;
|
|
31
|
+
ErrorSummary: typeof ErrorSummary;
|
|
30
32
|
Button: import("react").FC<import("./Form.Button.js").ButtonProps> & {
|
|
31
33
|
Icon: import("react").FC<import("./Form.Button.js").ButtonIconProps>;
|
|
32
34
|
};
|
|
@@ -54,6 +56,7 @@ export declare const Form: {
|
|
|
54
56
|
FilterButtonGroup: typeof FilterButtonGroup;
|
|
55
57
|
};
|
|
56
58
|
export { type DropdownOption };
|
|
59
|
+
export type { ErrorSummaryItem, ErrorSummaryProps, } from "./Form.ErrorSummary.js";
|
|
57
60
|
export type { KeyboardFormProps } from "./Form.KeyboardForm.js";
|
|
58
61
|
export type { KeyboardToolbarAction, KeyboardToolbarProps, } from "./Form.KeyboardToolbar.js";
|
|
59
62
|
export default Form;
|
package/dist/Form/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import ControlledMonetaryInput from "./Form.ControlledMonetaryInput.js";
|
|
|
7
7
|
import ControlledRadioButton from "./Form.ControlledRadioButton.js";
|
|
8
8
|
import DatePicker from "./Form.DatePicker.js";
|
|
9
9
|
import Dropdown from "./Form.Dropdown.js";
|
|
10
|
+
import ErrorSummary from "./Form.ErrorSummary.js";
|
|
10
11
|
import FAB from "./Form.FAB.js";
|
|
11
12
|
import { FilterButton, FilterButtonGroup } from "./Form.FilterButton.js";
|
|
12
13
|
import FractionalQuantityInput from "./Form.FractionalQuantityInput.js";
|
|
@@ -31,6 +32,7 @@ export const Form = {
|
|
|
31
32
|
InputField,
|
|
32
33
|
KeyboardForm,
|
|
33
34
|
KeyboardToolbar,
|
|
35
|
+
ErrorSummary,
|
|
34
36
|
Button: ButtonWithIcon,
|
|
35
37
|
IconButton,
|
|
36
38
|
PasswordInput,
|