@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.
Files changed (123) hide show
  1. package/README.md +156 -29
  2. package/dist/Actions/Actions.Button.d.ts +10 -12
  3. package/dist/Actions/Actions.Button.js +9 -11
  4. package/dist/Actions/Actions.Dock.d.ts +20 -0
  5. package/dist/Actions/Actions.Dock.js +89 -0
  6. package/dist/Actions/Actions.FAB.d.ts +1 -2
  7. package/dist/Actions/Actions.FAB.js +5 -7
  8. package/dist/Actions/Actions.IconButton.d.ts +1 -2
  9. package/dist/Actions/Actions.IconButton.js +2 -2
  10. package/dist/Actions/index.d.ts +4 -0
  11. package/dist/Actions/index.js +3 -0
  12. package/dist/Dialog/BottomSheet/BottomSheet.Root.js +22 -34
  13. package/dist/Dialog/Dialog.ActionSheet.js +2 -16
  14. package/dist/Dialog/Dialog.Confirmation.js +7 -5
  15. package/dist/Dialog/Dialog.EditValue.js +3 -1
  16. package/dist/Dialog/Dialog.Sheet.d.ts +3 -3
  17. package/dist/Dialog/Dialog.Sheet.js +18 -25
  18. package/dist/Display/Display.Accordion.js +7 -3
  19. package/dist/Display/Display.AmountSummary.d.ts +28 -0
  20. package/dist/Display/Display.AmountSummary.js +25 -0
  21. package/dist/Display/Display.Carousel.d.ts +27 -4
  22. package/dist/Display/Display.Carousel.js +112 -12
  23. package/dist/Display/Display.ExpoImage.d.ts +8 -0
  24. package/dist/Display/Display.ExpoImage.js +15 -0
  25. package/dist/Display/Display.Image.d.ts +10 -6
  26. package/dist/Display/Display.Image.js +39 -6
  27. package/dist/Display/Display.ListItem.d.ts +1 -2
  28. package/dist/Display/Display.ListItem.js +3 -3
  29. package/dist/Display/Display.Logo.d.ts +2 -2
  30. package/dist/Display/Display.Logo.js +3 -2
  31. package/dist/Display/Display.MediaCard.d.ts +2 -2
  32. package/dist/Display/Display.MediaEmbed.d.ts +2 -2
  33. package/dist/Display/Display.ProductCard.d.ts +2 -2
  34. package/dist/Display/Display.Story.d.ts +3 -3
  35. package/dist/Display/Display.Story.js +5 -4
  36. package/dist/Display/Display.StoryTray.d.ts +4 -4
  37. package/dist/Display/Display.StoryTray.js +3 -2
  38. package/dist/Display/Display.StoryViewer.d.ts +3 -3
  39. package/dist/Display/Display.StoryViewer.js +6 -3
  40. package/dist/Display/index.d.ts +9 -0
  41. package/dist/Display/index.js +4 -0
  42. package/dist/Feedback/Feedback.DomainSplashScreen.d.ts +13 -4
  43. package/dist/Feedback/Feedback.DomainSplashScreen.js +68 -53
  44. package/dist/Feedback/Feedback.EmptyState.d.ts +3 -4
  45. package/dist/Feedback/Feedback.EmptyState.js +4 -3
  46. package/dist/Feedback/Feedback.Error.d.ts +1 -2
  47. package/dist/Feedback/Feedback.Error.js +2 -2
  48. package/dist/Feedback/Feedback.Loading.d.ts +6 -2
  49. package/dist/Feedback/Feedback.Loading.js +2 -2
  50. package/dist/Feedback/Feedback.Progress.d.ts +6 -3
  51. package/dist/Feedback/Feedback.Progress.js +29 -19
  52. package/dist/Feedback/Feedback.ProgressCircle.d.ts +3 -2
  53. package/dist/Feedback/Feedback.ProgressCircle.js +10 -7
  54. package/dist/Feedback/Feedback.Skeleton.d.ts +4 -4
  55. package/dist/Feedback/Feedback.Skeleton.js +15 -3
  56. package/dist/Feedback/Feedback.Spinner.d.ts +3 -3
  57. package/dist/Feedback/Feedback.Spinner.js +40 -9
  58. package/dist/Feedback/Feedback.Splash.d.ts +2 -2
  59. package/dist/Feedback/Feedback.Splash.js +5 -3
  60. package/dist/Feedback/Toast.context.d.ts +64 -3
  61. package/dist/Feedback/Toast.context.js +112 -31
  62. package/dist/Feedback/index.d.ts +15 -1
  63. package/dist/Feedback/index.js +4 -1
  64. package/dist/Form/Form.Checkbox.d.ts +1 -2
  65. package/dist/Form/Form.Checkbox.js +2 -2
  66. package/dist/Form/Form.DatePicker.d.ts +1 -2
  67. package/dist/Form/Form.DatePicker.js +2 -2
  68. package/dist/Form/Form.Dropdown.d.ts +15 -3
  69. package/dist/Form/Form.Dropdown.js +21 -6
  70. package/dist/Form/Form.ErrorSummary.d.ts +23 -0
  71. package/dist/Form/Form.ErrorSummary.js +53 -0
  72. package/dist/Form/Form.Input.d.ts +1 -2
  73. package/dist/Form/Form.Input.js +2 -2
  74. package/dist/Form/Form.Select.d.ts +1 -2
  75. package/dist/Form/Form.Select.js +6 -19
  76. package/dist/Form/index.d.ts +3 -0
  77. package/dist/Form/index.js +2 -0
  78. package/dist/Navigation/Navigation.AppBar.d.ts +2 -6
  79. package/dist/Navigation/Navigation.AppBar.js +9 -7
  80. package/dist/Navigation/Navigation.BottomNav.d.ts +1 -3
  81. package/dist/Navigation/Navigation.BottomNav.js +7 -6
  82. package/dist/Navigation/Navigation.Header.d.ts +2 -2
  83. package/dist/Navigation/Navigation.Menu.js +10 -3
  84. package/dist/Navigation/Navigation.Search.d.ts +41 -0
  85. package/dist/Navigation/Navigation.Search.js +47 -0
  86. package/dist/Navigation/Navigation.SearchBar.d.ts +1 -2
  87. package/dist/Navigation/Navigation.SearchBar.js +16 -19
  88. package/dist/Navigation/Navigation.SegmentedControl.d.ts +11 -3
  89. package/dist/Navigation/Navigation.SegmentedControl.js +29 -16
  90. package/dist/Navigation/index.d.ts +11 -2
  91. package/dist/Navigation/index.js +2 -0
  92. package/dist/branding.d.ts +3 -2
  93. package/dist/primitives/BaseButton.d.ts +10 -12
  94. package/dist/primitives/BaseButton.js +9 -11
  95. package/dist/theme/typography.d.ts +1 -1
  96. package/dist/utils/image.d.ts +3 -0
  97. package/dist/utils/image.js +13 -0
  98. package/dist/utils/keys.d.ts +1 -0
  99. package/dist/utils/keys.js +7 -0
  100. package/dist/utils/progress.d.ts +1 -0
  101. package/dist/utils/progress.js +5 -0
  102. package/dist/utils/reorder.js +2 -5
  103. package/dist/utils/useReducedMotion.d.ts +1 -0
  104. package/dist/utils/useReducedMotion.js +43 -0
  105. package/dist/utils/useSheetTranslation.d.ts +5 -0
  106. package/dist/utils/useSheetTranslation.js +23 -0
  107. package/dist/views/AppSplashView.d.ts +3 -2
  108. package/dist/views/AppSplashView.js +3 -2
  109. package/dist/views/AuthFormView.d.ts +2 -2
  110. package/dist/views/FormViewV2.d.ts +2 -2
  111. package/dist/views/ListViewV2.context.d.ts +3 -1
  112. package/dist/views/ListViewV2.context.js +41 -21
  113. package/dist/views/ListViewV2.d.ts +15 -7
  114. package/dist/views/ListViewV2.js +44 -23
  115. package/dist/views/types/IListView.js +6 -5
  116. package/dist/widgets/CollectionOptions.js +9 -7
  117. package/dist/widgets/FilterBar.d.ts +1 -1
  118. package/dist/widgets/FilterBar.js +1 -1
  119. package/dist/widgets/FilterBuilder.js +7 -7
  120. package/dist/widgets/HomeHeader.d.ts +2 -2
  121. package/dist/widgets/PromoModal.js +3 -1
  122. package/dist/widgets/ScreenHeader.d.ts +2 -2
  123. package/package.json +4 -1
@@ -1,11 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import ExpoImage from "../Display/Display.ExpoImage.js";
2
3
  import Icon from "../Display/Display.Icon.js";
3
4
  import { BaseButton } from "../primitives/BaseButton.js";
4
5
  import { theme } from "../theme/index.js";
5
6
  import { cn } from "../theme/tw.js";
6
7
  import { Typography } from "../Typography/index.js";
7
- import { Image, View } from "react-native";
8
- function EmptyState({ title, description, icon = "file-tray-outline", image, actionLabel, onAction, children, className, testID, accessibleSizing = false, }) {
9
- return (_jsxs(View, { className: cn("flex-1 items-center justify-center px-8 gap-3", className), testID: testID, children: [image ? (_jsx(Image, { resizeMode: "contain", source: image, style: { width: 140, height: 140 } })) : (_jsx(View, { className: "w-20 h-20 rounded-full bg-bg-muted items-center justify-center", children: _jsx(Icon, { color: theme.icon.tertiary, name: icon, size: 40 }) })), _jsx(Typography.Text, { accessibilityRole: "header", align: "center", className: "text-text-primary", semibold: true, variant: "subtitle", children: title }), description ? (_jsx(Typography.Text, { align: "center", className: "text-text-secondary", variant: "body", children: description })) : null, actionLabel && onAction ? (_jsx(View, { className: "mt-2", children: _jsx(BaseButton, { accessibleSizing: accessibleSizing, onPress: onAction, title: actionLabel, variant: "primary" }) })) : null, children] }));
8
+ import { View } from "react-native";
9
+ function EmptyState({ title, description, icon = "file-tray-outline", image, actionLabel, onAction, children, className, testID, }) {
10
+ return (_jsxs(View, { className: cn("flex-1 items-center justify-center px-8 gap-3", className), testID: testID, children: [image ? (_jsx(ExpoImage, { contentFit: "contain", source: image, style: { width: 140, height: 140 } })) : (_jsx(View, { className: "w-20 h-20 rounded-full bg-bg-muted items-center justify-center", children: _jsx(Icon, { color: theme.icon.tertiary, name: icon, size: 40 }) })), _jsx(Typography.Text, { accessibilityRole: "header", align: "center", className: "text-text-primary", semibold: true, variant: "subtitle", children: title }), description ? (_jsx(Typography.Text, { align: "center", className: "text-text-secondary", variant: "body", children: description })) : null, actionLabel && onAction ? (_jsx(View, { className: "mt-2", children: _jsx(BaseButton, { onPress: onAction, title: actionLabel, variant: "primary" }) })) : null, children] }));
10
11
  }
11
12
  export default EmptyState;
@@ -4,8 +4,7 @@ interface ErrorProps {
4
4
  onBack?: () => void;
5
5
  onHome?: () => void;
6
6
  className?: string;
7
- accessibleSizing?: boolean;
8
7
  testID?: string;
9
8
  }
10
- declare function Error({ message, onRetry, onBack, onHome, className, accessibleSizing, testID, }: ErrorProps): import("react").JSX.Element;
9
+ declare function Error({ message, onRetry, onBack, onHome, className, testID }: ErrorProps): import("react").JSX.Element;
11
10
  export default Error;
@@ -5,7 +5,7 @@ import { theme } from "../theme/index.js";
5
5
  import { cn } from "../theme/tw.js";
6
6
  import { Typography } from "../Typography/index.js";
7
7
  import { View } from "react-native";
8
- function Error({ message, onRetry, onBack, onHome, className, accessibleSizing = false, testID, }) {
9
- return (_jsxs(View, { className: cn("flex-1 items-center justify-center px-4", className), testID: testID, children: [_jsx(Icon, { color: theme.danger, name: "exclamation-circle", size: 80, type: "fontawesome5" }), _jsx(Typography.Text, { accessibilityRole: "header", className: "pt-4 text-text-primary", semibold: true, variant: "h3", children: "\u00A1Error!" }), _jsx(Typography.Text, { accessibilityLiveRegion: "polite", className: "pt-2 text-center text-base leading-5.5 text-text-secondary", variant: "h5", children: message || "Ocurrió un error al cargar la información" }), _jsxs(View, { className: "mt-8 w-full gap-3", children: [onRetry && (_jsx(Button, { accessibleSizing: accessibleSizing, className: "w-full", onPress: onRetry, title: "Reintentar" })), onBack && (_jsx(Button, { accessibleSizing: accessibleSizing, className: "w-full", onPress: onBack, title: "Volver", variant: "secondary" })), onHome && (_jsx(Button, { accessibleSizing: accessibleSizing, className: "w-full", onPress: onHome, title: "Ir al inicio", variant: "outline" }))] })] }));
8
+ function Error({ message, onRetry, onBack, onHome, className, testID }) {
9
+ return (_jsxs(View, { className: cn("flex-1 items-center justify-center px-4", className), testID: testID, children: [_jsx(Icon, { color: theme.danger, name: "exclamation-circle", size: 80, type: "fontawesome5" }), _jsx(Typography.Text, { accessibilityRole: "header", className: "pt-4 text-text-primary", semibold: true, variant: "h3", children: "\u00A1Error!" }), _jsx(Typography.Text, { accessibilityLiveRegion: "polite", className: "pt-2 text-center text-base leading-5.5 text-text-secondary", variant: "h5", children: message || "Ocurrió un error al cargar la información" }), _jsxs(View, { className: "mt-8 w-full gap-3", children: [onRetry && _jsx(Button, { className: "w-full", onPress: onRetry, title: "Reintentar" }), onBack && (_jsx(Button, { className: "w-full", onPress: onBack, title: "Volver", variant: "secondary" })), onHome && (_jsx(Button, { className: "w-full", onPress: onHome, title: "Ir al inicio", variant: "outline" }))] })] }));
10
10
  }
11
11
  export default Error;
@@ -1,11 +1,15 @@
1
1
  import { type SpinnerVariant } from "./Feedback.Spinner.js";
2
2
  import type React from "react";
3
+ import { type StyleProp, type ViewStyle } from "react-native";
3
4
  interface LoadingStateProps {
4
5
  message?: string;
5
6
  size?: "small" | "large";
6
7
  variant?: SpinnerVariant;
7
- style?: object;
8
+ style?: StyleProp<ViewStyle>;
8
9
  className?: string;
10
+ animated?: boolean;
11
+ accessibilityLabel?: string;
12
+ testID?: string;
9
13
  }
10
- declare function LoadingState({ message, size, variant, style, className, }: LoadingStateProps): React.JSX.Element;
14
+ declare function LoadingState({ message, size, variant, style, className, animated, accessibilityLabel, testID, }: LoadingStateProps): React.JSX.Element;
11
15
  export default LoadingState;
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "nativewind/jsx-runtime";
2
2
  import Spinner from "./Feedback.Spinner.js";
3
3
  import { cn } from "../theme/tw.js";
4
4
  import { View } from "react-native";
5
- function LoadingState({ message = "Cargando…", size = "small", variant = "spinner", style = {}, className, }) {
6
- return (_jsx(View, { className: cn("flex-1 justify-center items-center", className), style: style, children: _jsx(Spinner, { className: "mb-5", size: size, text: message, variant: variant }) }));
5
+ function LoadingState({ message = "Cargando…", size = "small", variant = "spinner", style = {}, className, animated, accessibilityLabel, testID, }) {
6
+ return (_jsx(View, { className: cn("flex-1 justify-center items-center", className), style: style, children: _jsx(Spinner, { accessibilityLabel: accessibilityLabel, animated: animated, className: "mb-5", size: size, testID: testID, text: message, variant: variant }) }));
7
7
  }
8
8
  export default LoadingState;
@@ -1,9 +1,12 @@
1
- export interface ProgressProps {
1
+ import { type StyleProp, type ViewProps, type ViewStyle } from "react-native";
2
+ export interface ProgressProps extends Omit<ViewProps, "children" | "accessibilityValue" | "accessibilityRole" | "accessibilityState"> {
2
3
  value?: number;
3
4
  indeterminate?: boolean;
4
5
  height?: number;
5
6
  className?: string;
6
- testID?: string;
7
+ animated?: boolean;
8
+ indicatorClassName?: string;
9
+ indicatorStyle?: StyleProp<ViewStyle>;
7
10
  }
8
- declare function Progress({ value, indeterminate, height, className, testID, }: ProgressProps): import("react").JSX.Element;
11
+ declare function Progress({ value, indeterminate, height, className, animated, indicatorClassName, indicatorStyle, style, ...props }: ProgressProps): import("react").JSX.Element;
9
12
  export default Progress;
@@ -1,37 +1,47 @@
1
1
  import { jsx as _jsx } from "nativewind/jsx-runtime";
2
2
  import { cn } from "../theme/tw.js";
3
3
  import { motion } from "../tokens/motion.js";
4
+ import { normalizeProgress } from "../utils/progress.js";
5
+ import { useReducedMotion } from "../utils/useReducedMotion.js";
4
6
  import { useEffect, useRef } from "react";
5
- import { Animated, Easing, View } from "react-native";
6
- function clamp(n) {
7
- return Math.max(0, Math.min(1, n));
8
- }
9
- function Progress({ value = 0, indeterminate = false, height = 6, className, testID, }) {
7
+ import { Animated, Easing, View, } from "react-native";
8
+ function Progress({ value = 0, indeterminate = false, height = 6, className, animated = true, indicatorClassName, indicatorStyle, style, ...props }) {
9
+ const reducedMotion = useReducedMotion();
10
+ const shouldAnimate = indeterminate && animated && !reducedMotion;
11
+ const progress = indeterminate ? 0 : normalizeProgress(value);
10
12
  // eslint-disable-next-line react-hooks/refs
11
13
  const translate = useRef(new Animated.Value(0)).current;
12
14
  useEffect(() => {
13
- if (!indeterminate)
15
+ translate.setValue(0);
16
+ if (!shouldAnimate)
14
17
  return;
15
18
  const loop = Animated.loop(Animated.timing(translate, {
16
19
  toValue: 1,
17
20
  duration: motion.duration.slower,
18
21
  easing: Easing.inOut(Easing.ease),
19
22
  useNativeDriver: true,
23
+ isInteraction: false,
20
24
  }));
21
25
  loop.start();
22
26
  return () => loop.stop();
23
- }, [indeterminate, translate]);
24
- return (_jsx(View, { accessibilityRole: "progressbar", accessibilityValue: indeterminate ? undefined : { now: Math.round(clamp(value) * 100), min: 0, max: 100 }, className: cn("w-full overflow-hidden rounded-full bg-bg-muted", className), style: { height }, testID: testID, children: indeterminate ? (_jsx(Animated.View, { className: "h-full rounded-full bg-primary", style: {
25
- width: "40%",
26
- transform: [
27
- {
28
- // eslint-disable-next-line react-hooks/refs
29
- translateX: translate.interpolate({
30
- inputRange: [0, 1],
31
- outputRange: ["-40%", "250%"],
32
- }),
33
- },
34
- ],
35
- } })) : (_jsx(View, { className: "h-full rounded-full bg-primary", style: { width: `${clamp(value) * 100}%` } })) }));
27
+ }, [shouldAnimate, translate]);
28
+ return (_jsx(View, { ...props, accessibilityRole: "progressbar", accessibilityState: { busy: indeterminate }, accessibilityValue: indeterminate ? undefined : { now: Math.round(progress * 100), min: 0, max: 100 }, accessible: true, className: cn("w-full overflow-hidden rounded-full bg-bg-muted", className), style: [{ height }, style], children: indeterminate ? (_jsx(Animated.View, { className: cn("h-full rounded-full bg-primary", indicatorClassName), style: [
29
+ {
30
+ width: "40%",
31
+ marginLeft: shouldAnimate ? 0 : "30%",
32
+ transform: shouldAnimate
33
+ ? [
34
+ {
35
+ // eslint-disable-next-line react-hooks/refs
36
+ translateX: translate.interpolate({
37
+ inputRange: [0, 1],
38
+ outputRange: ["-40%", "250%"],
39
+ }),
40
+ },
41
+ ]
42
+ : [],
43
+ },
44
+ indicatorStyle,
45
+ ] })) : (_jsx(View, { className: cn("h-full rounded-full bg-primary", indicatorClassName), style: [{ width: `${progress * 100}%` }, indicatorStyle] })) }));
36
46
  }
37
47
  export default Progress;
@@ -1,5 +1,6 @@
1
1
  import type { ReactNode } from "react";
2
- export interface ProgressCircleProps {
2
+ import { type ViewProps } from "react-native";
3
+ export interface ProgressCircleProps extends Omit<ViewProps, "accessibilityValue" | "accessibilityRole"> {
3
4
  value?: number;
4
5
  size?: number;
5
6
  strokeWidth?: number;
@@ -8,5 +9,5 @@ export interface ProgressCircleProps {
8
9
  children?: ReactNode;
9
10
  testID?: string;
10
11
  }
11
- declare function ProgressCircle({ value, size, strokeWidth, color, trackColor, children, testID, }: ProgressCircleProps): import("react").JSX.Element;
12
+ declare function ProgressCircle({ value, size, strokeWidth, color, trackColor, children, style, ...props }: ProgressCircleProps): import("react").JSX.Element;
12
13
  export default ProgressCircle;
@@ -1,15 +1,18 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
- import { theme } from "../theme/index.js";
2
+ import { useTheme } from "../theme/index.js";
3
+ import { normalizeProgress } from "../utils/progress.js";
3
4
  import { View } from "react-native";
4
5
  import Svg, { Circle } from "react-native-svg";
5
- function clamp(n) {
6
- return Math.max(0, Math.min(1, n));
7
- }
8
- function ProgressCircle({ value = 0, size = 64, strokeWidth = 6, color, trackColor, children, testID, }) {
6
+ function ProgressCircle({ value = 0, size = 64, strokeWidth = 6, color, trackColor, children, style, ...props }) {
7
+ const theme = useTheme();
8
+ const progress = normalizeProgress(value);
9
9
  const radius = (size - strokeWidth) / 2;
10
10
  const circumference = 2 * Math.PI * radius;
11
- const offset = circumference * (1 - clamp(value));
11
+ const offset = circumference * (1 - progress);
12
12
  const center = size / 2;
13
- return (_jsxs(View, { accessibilityRole: "progressbar", accessibilityValue: { now: Math.round(clamp(value) * 100), min: 0, max: 100 }, style: { width: size, height: size, alignItems: "center", justifyContent: "center" }, testID: testID, children: [_jsxs(Svg, { height: size, style: { position: "absolute" }, width: size, children: [_jsx(Circle, { cx: center, cy: center, fill: "none", r: radius, stroke: trackColor ?? theme.bg.muted, strokeWidth: strokeWidth }), _jsx(Circle, { cx: center, cy: center, fill: "none", r: radius, stroke: color ?? theme.primary, strokeDasharray: `${circumference} ${circumference}`, strokeDashoffset: offset, strokeLinecap: "round", strokeWidth: strokeWidth, transform: `rotate(-90 ${center} ${center})` })] }), children] }));
13
+ return (_jsxs(View, { ...props, accessibilityRole: "progressbar", accessibilityValue: { now: Math.round(progress * 100), min: 0, max: 100 }, accessible: true, style: [
14
+ { width: size, height: size, alignItems: "center", justifyContent: "center" },
15
+ style,
16
+ ], children: [_jsxs(Svg, { height: size, style: { position: "absolute" }, width: size, children: [_jsx(Circle, { cx: center, cy: center, fill: "none", r: radius, stroke: trackColor ?? theme.bg.muted, strokeWidth: strokeWidth }), _jsx(Circle, { cx: center, cy: center, fill: "none", r: radius, stroke: color ?? theme.primary, strokeDasharray: `${circumference} ${circumference}`, strokeDashoffset: offset, strokeLinecap: "round", strokeWidth: strokeWidth, transform: `rotate(-90 ${center} ${center})` })] }), children] }));
14
17
  }
15
18
  export default ProgressCircle;
@@ -1,11 +1,11 @@
1
- import { type DimensionValue, type ViewStyle } from "react-native";
2
- export interface SkeletonProps {
1
+ import { type DimensionValue, type ViewProps } from "react-native";
2
+ export interface SkeletonProps extends Omit<ViewProps, "children"> {
3
3
  width?: DimensionValue;
4
4
  height?: DimensionValue;
5
5
  radius?: number;
6
6
  circle?: boolean;
7
7
  className?: string;
8
- style?: ViewStyle;
8
+ animated?: boolean;
9
9
  }
10
- declare function Skeleton({ width, height, radius, circle, className, style, }: SkeletonProps): import("react").JSX.Element;
10
+ declare function Skeleton({ width, height, radius, circle, className, style, animated, ...props }: SkeletonProps): import("react").JSX.Element;
11
11
  export default Skeleton;
@@ -1,30 +1,42 @@
1
1
  import { jsx as _jsx } from "nativewind/jsx-runtime";
2
2
  import { cn } from "../theme/tw.js";
3
3
  import { motion } from "../tokens/motion.js";
4
+ import { useReducedMotion } from "../utils/useReducedMotion.js";
4
5
  import { useEffect, useRef } from "react";
5
6
  import { Animated, Easing } from "react-native";
6
- function Skeleton({ width = "100%", height = 16, radius = 8, circle = false, className, style, }) {
7
+ function Skeleton({ width = "100%", height = 16, radius = 8, circle = false, className, style, animated = true, ...props }) {
8
+ const reducedMotion = useReducedMotion();
9
+ const shouldAnimate = animated && !reducedMotion;
7
10
  // eslint-disable-next-line react-hooks/refs
8
11
  const opacity = useRef(new Animated.Value(0.5)).current;
9
12
  useEffect(() => {
13
+ opacity.setValue(0.5);
14
+ if (!shouldAnimate)
15
+ return;
10
16
  const pulse = Animated.loop(Animated.sequence([
11
17
  Animated.timing(opacity, {
12
18
  toValue: 1,
13
19
  duration: motion.duration.slow,
14
20
  easing: Easing.inOut(Easing.ease),
15
21
  useNativeDriver: true,
22
+ isInteraction: false,
16
23
  }),
17
24
  Animated.timing(opacity, {
18
25
  toValue: 0.5,
19
26
  duration: motion.duration.slow,
20
27
  easing: Easing.inOut(Easing.ease),
21
28
  useNativeDriver: true,
29
+ isInteraction: false,
22
30
  }),
23
31
  ]));
24
32
  pulse.start();
25
33
  return () => pulse.stop();
26
- }, [opacity]);
34
+ }, [opacity, shouldAnimate]);
27
35
  const resolvedRadius = circle && typeof height === "number" ? height / 2 : radius;
28
- return (_jsx(Animated.View, { accessibilityElementsHidden: true, className: cn("bg-bg-muted", className), importantForAccessibility: "no-hide-descendants", style: [{ width, height, borderRadius: resolvedRadius }, style, { opacity }] }));
36
+ return (_jsx(Animated.View, { ...props, accessibilityElementsHidden: true, className: cn("bg-bg-muted", className), importantForAccessibility: "no-hide-descendants", style: [
37
+ { width, height, borderRadius: resolvedRadius },
38
+ style,
39
+ { opacity: shouldAnimate ? opacity : 0.5 },
40
+ ] }));
29
41
  }
30
42
  export default Skeleton;
@@ -1,14 +1,14 @@
1
1
  import type React from "react";
2
- import { type ViewStyle } from "react-native";
2
+ import { type ViewProps } from "react-native";
3
3
  export type SpinnerVariant = "spinner" | "dots" | "ripple";
4
- export interface SpinnerProps {
4
+ export interface SpinnerProps extends Omit<ViewProps, "children" | "accessibilityRole" | "accessibilityState"> {
5
5
  /** Visual style of the loading indicator. Defaults to `"spinner"`. */
6
6
  variant?: SpinnerVariant;
7
7
  size?: "small" | "large";
8
8
  color?: string;
9
9
  text?: string;
10
- style?: ViewStyle;
11
10
  className?: string;
11
+ animated?: boolean;
12
12
  }
13
13
  declare const Spinner: React.FC<SpinnerProps>;
14
14
  export default Spinner;
@@ -1,11 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
- import { theme } from "../theme/index.js";
2
+ import { useTheme } from "../theme/index.js";
3
3
  import { cn } from "../theme/tw.js";
4
4
  import { Typography } from "../Typography/index.js";
5
+ import { useReducedMotion } from "../utils/useReducedMotion.js";
5
6
  import { useEffect } from "react";
6
7
  import { ActivityIndicator, View } from "react-native";
7
- import Animated, { Easing, useAnimatedStyle, useSharedValue, withDelay, withRepeat, withSequence, withTiming, } from "react-native-reanimated";
8
- function DotsLoader({ color, size }) {
8
+ import Animated, { cancelAnimation, Easing, useAnimatedStyle, useSharedValue, withDelay, withRepeat, withSequence, withTiming, } from "react-native-reanimated";
9
+ function DotsLoader({ color, size, animated }) {
9
10
  const dotSize = size === "large" ? 11 : 8;
10
11
  const bounce = size === "large" ? 13 : 9;
11
12
  const dot = {
@@ -18,24 +19,42 @@ function DotsLoader({ color, size }) {
18
19
  const y2 = useSharedValue(0);
19
20
  const y3 = useSharedValue(0);
20
21
  useEffect(() => {
22
+ y1.value = 0;
23
+ y2.value = 0;
24
+ y3.value = 0;
25
+ if (!animated)
26
+ return;
21
27
  const seq = withRepeat(withSequence(withTiming(-bounce, { duration: 280, easing: Easing.out(Easing.quad) }), withTiming(0, { duration: 280, easing: Easing.in(Easing.quad) }), withTiming(0, { duration: 200 })), -1, false);
22
28
  y1.value = withDelay(0, seq);
23
29
  y2.value = withDelay(160, seq);
24
30
  y3.value = withDelay(320, seq);
25
- }, []);
31
+ return () => {
32
+ cancelAnimation(y1);
33
+ cancelAnimation(y2);
34
+ cancelAnimation(y3);
35
+ };
36
+ }, [animated, bounce, y1, y2, y3]);
26
37
  const s1 = useAnimatedStyle(() => ({ transform: [{ translateY: y1.value }] }));
27
38
  const s2 = useAnimatedStyle(() => ({ transform: [{ translateY: y2.value }] }));
28
39
  const s3 = useAnimatedStyle(() => ({ transform: [{ translateY: y3.value }] }));
29
40
  return (_jsxs(View, { style: { flexDirection: "row", gap: 10, alignItems: "center" }, children: [_jsx(Animated.View, { style: [dot, s1] }), _jsx(Animated.View, { style: [dot, s2] }), _jsx(Animated.View, { style: [dot, s3] })] }));
30
41
  }
31
- function RippleLoader({ color, size }) {
42
+ function RippleLoader({ color, size, animated }) {
32
43
  const base = size === "large" ? 48 : 32;
33
44
  const scale = useSharedValue(0.2);
34
45
  const opacity = useSharedValue(0.85);
35
46
  useEffect(() => {
47
+ scale.value = animated ? 0.2 : 1;
48
+ opacity.value = 0.85;
49
+ if (!animated)
50
+ return;
36
51
  scale.value = withRepeat(withTiming(2.6, { duration: 1400, easing: Easing.out(Easing.quad) }), -1, false);
37
52
  opacity.value = withRepeat(withTiming(0, { duration: 1400, easing: Easing.in(Easing.quad) }), -1, false);
38
- }, []);
53
+ return () => {
54
+ cancelAnimation(scale);
55
+ cancelAnimation(opacity);
56
+ };
57
+ }, [animated, scale, opacity]);
39
58
  const animStyle = useAnimatedStyle(() => ({
40
59
  transform: [{ scale: scale.value }],
41
60
  opacity: opacity.value,
@@ -52,8 +71,20 @@ function RippleLoader({ color, size }) {
52
71
  animStyle,
53
72
  ] }) }));
54
73
  }
55
- const Spinner = ({ variant = "spinner", size = "large", color = theme.primary, text, style, className, }) => {
56
- const indicator = variant === "dots" ? (_jsx(DotsLoader, { color: color, size: size })) : variant === "ripple" ? (_jsx(RippleLoader, { color: color, size: size })) : (_jsx(ActivityIndicator, { color: color, size: size }));
57
- return (_jsxs(View, { className: cn("items-center justify-center", className), style: style, children: [indicator, text && (_jsx(Typography.Text, { className: "mt-2.5 text-base", variant: "body", children: text }))] }));
74
+ const Spinner = ({ variant = "spinner", size = "large", color, text, style, className, animated = true, accessibilityLabel = text ?? "Cargando", ...props }) => {
75
+ const theme = useTheme();
76
+ const reducedMotion = useReducedMotion();
77
+ const shouldAnimate = animated && !reducedMotion;
78
+ const resolvedColor = color ?? theme.primary;
79
+ const staticSize = size === "large" ? 36 : 20;
80
+ const indicator = variant === "dots" ? (_jsx(DotsLoader, { animated: shouldAnimate, color: resolvedColor, size: size })) : variant === "ripple" ? (_jsx(RippleLoader, { animated: shouldAnimate, color: resolvedColor, size: size })) : shouldAnimate ? (_jsx(ActivityIndicator, { color: resolvedColor, size: size })) : (_jsx(View, { style: {
81
+ width: staticSize,
82
+ height: staticSize,
83
+ borderRadius: staticSize / 2,
84
+ borderWidth: 2,
85
+ borderColor: resolvedColor,
86
+ borderTopColor: "transparent",
87
+ } }));
88
+ return (_jsxs(View, { ...props, accessibilityLabel: accessibilityLabel, accessibilityRole: "progressbar", accessibilityState: { busy: true }, accessible: true, className: cn("items-center justify-center", className), style: style, children: [_jsx(View, { accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", children: indicator }), text && (_jsx(Typography.Text, { className: "mt-2.5 text-base", variant: "body", children: text }))] }));
58
89
  };
59
90
  export default Spinner;
@@ -1,8 +1,8 @@
1
+ import type { ImageSource } from "../utils/image.js";
1
2
  import type { ReactNode } from "react";
2
- import { type ImageSourcePropType } from "react-native";
3
3
  interface SplashScreenProps {
4
4
  isReady: boolean;
5
- logo?: ImageSourcePropType;
5
+ logo?: ImageSource;
6
6
  minDuration?: number;
7
7
  message?: string;
8
8
  /** `default` keeps the legacy look; `hero` is the modern dark gradient + dotted texture. */
@@ -1,20 +1,22 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import ExpoImage from "../Display/Display.ExpoImage.js";
2
3
  import GradientSurface from "../Display/Display.GradientSurface.js";
3
4
  import Pattern from "../Display/Display.Pattern.js";
4
5
  import Loading from "./Feedback.Loading.js";
6
+ import Spinner from "./Feedback.Spinner.js";
5
7
  import GradientContainer from "../layouts/GradientContainer.js";
6
8
  import { theme } from "../theme/index.js";
7
9
  import { Typography } from "../Typography/index.js";
8
10
  import { useEffect, useRef, useState } from "react";
9
- import { ActivityIndicator, Image, View } from "react-native";
11
+ import { View } from "react-native";
10
12
  function renderLogo(logo) {
11
13
  if (!logo) {
12
14
  return null;
13
15
  }
14
- return _jsx(Image, { className: "w-[250px] h-[200px] mb-12", resizeMode: "contain", source: logo });
16
+ return (_jsx(ExpoImage, { className: "w-[250px] h-[200px] mb-12", contentFit: "contain", source: logo }));
15
17
  }
16
18
  function HeroSplash({ logo, message, title, subtitle, }) {
17
- return (_jsxs(GradientSurface, { className: "flex-1 items-center justify-center", preset: "warm", radius: "none", children: [_jsx(Pattern, { color: theme.primary, kind: "dots", opacity: 0.16, size: 26 }), _jsxs(View, { className: "items-center", children: [logo ? (_jsx(Image, { className: "w-28 h-28 mb-7", resizeMode: "contain", source: logo, style: { shadowColor: theme.primary, shadowOpacity: 0.7, shadowRadius: 28 } })) : null, title ? (_jsx(Typography.Text, { bold: true, style: { color: "#FFFFFF", fontSize: 28, letterSpacing: 1 }, children: title })) : null, subtitle ? (_jsx(Typography.Text, { style: { color: "rgba(255,255,255,0.6)", fontSize: 13, marginTop: 6 }, children: subtitle })) : null, _jsx(View, { className: "mt-10", children: _jsx(ActivityIndicator, { accessibilityLabel: message, color: theme.primary, size: "small" }) })] })] }));
19
+ return (_jsxs(GradientSurface, { className: "flex-1 items-center justify-center", preset: "warm", radius: "none", children: [_jsx(Pattern, { color: theme.primary, kind: "dots", opacity: 0.16, size: 26 }), _jsxs(View, { className: "items-center", children: [logo ? (_jsx(ExpoImage, { className: "w-28 h-28 mb-7", contentFit: "contain", source: logo, style: { shadowColor: theme.primary, shadowOpacity: 0.7, shadowRadius: 28 } })) : null, title ? (_jsx(Typography.Text, { bold: true, style: { color: "#FFFFFF", fontSize: 28, letterSpacing: 1 }, children: title })) : null, subtitle ? (_jsx(Typography.Text, { style: { color: "rgba(255,255,255,0.6)", fontSize: 13, marginTop: 6 }, children: subtitle })) : null, _jsx(View, { className: "mt-10", children: _jsx(Spinner, { accessibilityLabel: message, color: theme.primary, size: "small" }) })] })] }));
18
20
  }
19
21
  function Splash({ isReady, logo, minDuration = 800, message = "Inicializando...", variant = "default", title, subtitle, children, }) {
20
22
  const [showContent, setShowContent] = useState(false);
@@ -1,22 +1,83 @@
1
+ import { type PressableProps } from "../primitives/Pressable.js";
1
2
  import { type ReactNode } from "react";
3
+ import { type StyleProp, type ViewProps, type ViewStyle } from "react-native";
2
4
  export type ToastVariant = "info" | "success" | "warning" | "danger" | "loading";
3
5
  export interface ToastAction {
4
6
  label: string;
5
7
  onPress: () => void;
6
8
  }
9
+ export interface ToastRenderProps {
10
+ id: string;
11
+ dismiss: () => void;
12
+ update: (options: Partial<ToastOptions>) => void;
13
+ }
7
14
  export interface ToastOptions {
8
15
  message: string;
9
16
  title?: string;
10
17
  variant?: ToastVariant;
11
18
  duration?: number;
12
19
  action?: ToastAction;
20
+ className?: string;
21
+ render?: (controls: ToastRenderProps) => ReactNode;
13
22
  /** Stable id: calling show()/info()/… again with the same id updates the existing toast
14
23
  * (and resets its timer) instead of stacking a duplicate. */
15
24
  id?: string;
16
- /** Render a close (✕) button. Defaults to true for persistent toasts (duration 0 / loading),
17
- * false for auto-dismissing ones. */
25
+ /** Render a close (✕) button. Defaults to true, including auto-dismissing toasts. */
18
26
  dismissible?: boolean;
19
27
  }
28
+ export type ToastCustomOptions = Omit<ToastOptions, "message" | "render"> & {
29
+ message?: string;
30
+ render: (controls: ToastRenderProps) => ReactNode;
31
+ };
32
+ export interface ToastRootProps extends Omit<ViewProps, "children"> {
33
+ children: ReactNode;
34
+ accessibilityHint?: string;
35
+ className?: string;
36
+ position?: "top" | "bottom";
37
+ style?: StyleProp<ViewStyle>;
38
+ testID?: string;
39
+ }
40
+ declare function ToastRoot({ children, accessibilityHint, className, position, style, testID, ...viewProps }: ToastRootProps): import("react").JSX.Element;
41
+ export interface ToastIconProps {
42
+ variant?: ToastVariant;
43
+ icon?: ReactNode;
44
+ color?: string;
45
+ size?: number;
46
+ }
47
+ declare function ToastIcon({ variant, icon, color, size }: ToastIconProps): import("react").JSX.Element;
48
+ export interface ToastContentProps {
49
+ children: ReactNode;
50
+ className?: string;
51
+ style?: StyleProp<ViewStyle>;
52
+ }
53
+ declare function ToastContent({ children, className, style }: ToastContentProps): import("react").JSX.Element;
54
+ export interface ToastTextProps {
55
+ children: ReactNode;
56
+ className?: string;
57
+ }
58
+ declare function ToastTitle({ children, className }: ToastTextProps): import("react").JSX.Element;
59
+ declare function ToastDescription({ children, className }: ToastTextProps): import("react").JSX.Element;
60
+ export interface ToastActionProps extends Omit<PressableProps, "children" | "onPress"> {
61
+ label: string;
62
+ onPress: () => void;
63
+ className?: string;
64
+ }
65
+ declare function ToastActionButton({ label, onPress, className, style, ...props }: ToastActionProps): import("react").JSX.Element;
66
+ export interface ToastCloseProps extends Omit<PressableProps, "children" | "onPress"> {
67
+ onPress: () => void;
68
+ accessibilityLabel?: string;
69
+ className?: string;
70
+ }
71
+ declare function ToastClose({ onPress, accessibilityLabel, className, style, ...props }: ToastCloseProps): import("react").JSX.Element;
72
+ export declare const Toast: {
73
+ Root: typeof ToastRoot;
74
+ Icon: typeof ToastIcon;
75
+ Content: typeof ToastContent;
76
+ Title: typeof ToastTitle;
77
+ Description: typeof ToastDescription;
78
+ Action: typeof ToastActionButton;
79
+ Close: typeof ToastClose;
80
+ };
20
81
  type MessageOrFn<T> = string | ((value: T) => string);
21
82
  export interface ToastPromiseMessages<T> {
22
83
  loading: string;
@@ -24,7 +85,7 @@ export interface ToastPromiseMessages<T> {
24
85
  error: MessageOrFn<unknown>;
25
86
  }
26
87
  export interface ToastContextValue {
27
- show: (options: ToastOptions) => string;
88
+ show: (options: ToastOptions | ToastCustomOptions) => string;
28
89
  update: (id: string, options: Partial<ToastOptions>) => void;
29
90
  hide: (id: string) => void;
30
91
  info: (message: string, options?: Omit<ToastOptions, "message" | "variant">) => string;