@sincpro/mobile-ui 0.4.1 → 0.6.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 (82) hide show
  1. package/dist/Actions/Actions.Button.d.ts +151 -0
  2. package/dist/Actions/Actions.Button.js +217 -0
  3. package/dist/Actions/Actions.FAB.d.ts +10 -0
  4. package/dist/Actions/Actions.FAB.js +17 -0
  5. package/dist/Actions/Actions.FilterButton.d.ts +10 -0
  6. package/dist/Actions/Actions.FilterButton.js +28 -0
  7. package/dist/Actions/Actions.IconButton.d.ts +67 -0
  8. package/dist/Actions/Actions.IconButton.js +38 -0
  9. package/dist/Actions/index.d.ts +22 -0
  10. package/dist/Actions/index.js +17 -0
  11. package/dist/Dialog/BottomSheet/BottomSheet.Root.js +1 -0
  12. package/dist/Dialog/Dialog.Sheet.js +6 -4
  13. package/dist/Display/Display.CircleButton.d.ts +15 -0
  14. package/dist/Display/Display.CircleButton.js +39 -0
  15. package/dist/Display/Display.MenuButton.d.ts +51 -8
  16. package/dist/Display/Display.MenuButton.js +79 -13
  17. package/dist/Display/Display.MenuCard.d.ts +15 -0
  18. package/dist/Display/Display.MenuCard.js +24 -0
  19. package/dist/Display/Display.MenuGrid.d.ts +19 -0
  20. package/dist/Display/Display.MenuGrid.js +7 -0
  21. package/dist/Display/Display.SwipeableRow.js +2 -1
  22. package/dist/Display/Display.Tooltip.d.ts +1 -1
  23. package/dist/Display/Display.Tooltip.js +16 -5
  24. package/dist/Display/index.d.ts +9 -0
  25. package/dist/Display/index.js +6 -0
  26. package/dist/Feedback/Feedback.Empty.js +2 -2
  27. package/dist/Feedback/Feedback.Error.js +2 -2
  28. package/dist/Feedback/Feedback.Progress.js +2 -2
  29. package/dist/Feedback/Feedback.Skeleton.js +2 -2
  30. package/dist/Feedback/Feedback.Splash.js +1 -0
  31. package/dist/Form/Form.Button.d.ts +3 -151
  32. package/dist/Form/Form.Button.js +1 -283
  33. package/dist/Form/Form.FAB.d.ts +3 -10
  34. package/dist/Form/Form.FAB.js +1 -17
  35. package/dist/Form/Form.FilterButton.d.ts +3 -12
  36. package/dist/Form/Form.FilterButton.js +1 -34
  37. package/dist/Form/Form.IconButton.d.ts +3 -67
  38. package/dist/Form/Form.IconButton.js +1 -64
  39. package/dist/Navigation/Navigation.AppBar.d.ts +63 -4
  40. package/dist/Navigation/Navigation.AppBar.js +70 -18
  41. package/dist/Navigation/Navigation.BottomNav.js +2 -1
  42. package/dist/Navigation/Navigation.Header.js +3 -2
  43. package/dist/Navigation/Navigation.Menu.js +6 -3
  44. package/dist/Navigation/Navigation.MenuSection.d.ts +19 -0
  45. package/dist/Navigation/Navigation.MenuSection.js +25 -0
  46. package/dist/Navigation/Navigation.SegmentedControl.d.ts +15 -1
  47. package/dist/Navigation/Navigation.SegmentedControl.js +22 -4
  48. package/dist/Navigation/Navigation.TabIcon.js +1 -2
  49. package/dist/Navigation/Navigation.TabItem.js +2 -2
  50. package/dist/Navigation/Navigation.Tabs.d.ts +8 -3
  51. package/dist/Navigation/Navigation.Tabs.js +19 -6
  52. package/dist/Navigation/index.d.ts +8 -7
  53. package/dist/Navigation/index.js +2 -0
  54. package/dist/index.d.ts +2 -0
  55. package/dist/index.js +2 -0
  56. package/dist/layouts/BottomInset.d.ts +16 -0
  57. package/dist/layouts/BottomInset.js +17 -0
  58. package/dist/layouts/TabNavigatorLayout.js +2 -2
  59. package/dist/primitives/BaseButton.js +3 -3
  60. package/dist/theme/default.js +1 -1
  61. package/dist/views/FormViewV2.d.ts +99 -9
  62. package/dist/views/FormViewV2.js +79 -19
  63. package/dist/views/ListViewV2.d.ts +58 -5
  64. package/dist/views/ListViewV2.js +58 -11
  65. package/dist/widgets/CircleButton.d.ts +3 -19
  66. package/dist/widgets/CircleButton.js +1 -41
  67. package/dist/widgets/JSONViewer.js +7 -7
  68. package/dist/widgets/MenuCard.d.ts +3 -13
  69. package/dist/widgets/MenuCard.js +1 -8
  70. package/dist/widgets/MenuGrid.d.ts +5 -17
  71. package/dist/widgets/MenuGrid.js +1 -6
  72. package/dist/widgets/MenuSection.d.ts +3 -0
  73. package/dist/widgets/MenuSection.js +1 -0
  74. package/dist/widgets/PromoBanner.d.ts +38 -0
  75. package/dist/widgets/PromoBanner.js +61 -0
  76. package/dist/widgets/PromoModal.d.ts +41 -0
  77. package/dist/widgets/PromoModal.js +75 -0
  78. package/dist/widgets/QuickActions.d.ts +26 -0
  79. package/dist/widgets/QuickActions.js +41 -0
  80. package/dist/widgets/index.d.ts +11 -1
  81. package/dist/widgets/index.js +4 -0
  82. package/package.json +2 -1
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx } from "nativewind/jsx-runtime";
2
+ import Icon from "../Display/Display.Icon";
3
+ import { useTheme } from "../theme";
4
+ import { cn } from "../theme/tw";
5
+ import { View } from "react-native";
6
+ import { tv } from "tailwind-variants";
7
+ const circleButtonVariants = tv({
8
+ base: "rounded-full items-center justify-center shadow-sm",
9
+ variants: {
10
+ size: {
11
+ sm: "w-12 h-12",
12
+ md: "w-15 h-15",
13
+ lg: "w-20 h-20",
14
+ },
15
+ variant: {
16
+ primary: "bg-primary",
17
+ secondary: "bg-secondary",
18
+ accent: "bg-accent",
19
+ success: "bg-success",
20
+ danger: "bg-danger",
21
+ warning: "bg-warning",
22
+ },
23
+ },
24
+ defaultVariants: {
25
+ size: "md",
26
+ variant: "accent",
27
+ },
28
+ });
29
+ const SIZE_MAP = {
30
+ sm: { icon: 24 },
31
+ md: { icon: 30 },
32
+ lg: { icon: 40 },
33
+ };
34
+ export function CircleButton({ iconType = "ionicons", size = "md", color, variant = "accent", className, customIcon, }) {
35
+ const theme = useTheme();
36
+ const iconColor = color ?? theme.text.inverse;
37
+ return (_jsx(View, { className: cn(circleButtonVariants({ size, variant }), className), children: _jsx(Icon, { color: iconColor, customIcon: customIcon, size: SIZE_MAP[size].icon, type: iconType }) }));
38
+ }
39
+ export default CircleButton;
@@ -1,5 +1,13 @@
1
+ import { type SwipeRowAction } from "../Display/Display.SwipeableRow";
1
2
  import { type VariantProps } from "../theme/tw";
2
3
  import { ComponentType, ReactNode } from "react";
4
+ export type MenuButtonSwipeAction = SwipeRowAction;
5
+ /**
6
+ * Inline status/count badge on the right of the row.
7
+ * Presets: `"active"` (green pill), `"inactive"` (yellow pill), `"new"` (accent pill).
8
+ * Any string / number renders a danger count chip.
9
+ */
10
+ export type MenuButtonBadge = "active" | "inactive" | "new" | string | number;
3
11
  declare const menuButton: import("tailwind-variants").TVReturnType<{
4
12
  variant: {
5
13
  default: {
@@ -10,6 +18,10 @@ declare const menuButton: import("tailwind-variants").TVReturnType<{
10
18
  iconContainer: string;
11
19
  label: string;
12
20
  };
21
+ success: {
22
+ iconContainer: string;
23
+ label: string;
24
+ };
13
25
  };
14
26
  showDivider: {
15
27
  true: {
@@ -27,8 +39,6 @@ declare const menuButton: import("tailwind-variants").TVReturnType<{
27
39
  content: string;
28
40
  label: string;
29
41
  rightContainer: string;
30
- chevronContainer: string;
31
- chevron: string;
32
42
  }, undefined, {
33
43
  variant: {
34
44
  default: {
@@ -39,6 +49,10 @@ declare const menuButton: import("tailwind-variants").TVReturnType<{
39
49
  iconContainer: string;
40
50
  label: string;
41
51
  };
52
+ success: {
53
+ iconContainer: string;
54
+ label: string;
55
+ };
42
56
  };
43
57
  showDivider: {
44
58
  true: {
@@ -56,8 +70,6 @@ declare const menuButton: import("tailwind-variants").TVReturnType<{
56
70
  content: string;
57
71
  label: string;
58
72
  rightContainer: string;
59
- chevronContainer: string;
60
- chevron: string;
61
73
  }, import("tailwind-variants").TVReturnType<{
62
74
  variant: {
63
75
  default: {
@@ -68,6 +80,10 @@ declare const menuButton: import("tailwind-variants").TVReturnType<{
68
80
  iconContainer: string;
69
81
  label: string;
70
82
  };
83
+ success: {
84
+ iconContainer: string;
85
+ label: string;
86
+ };
71
87
  };
72
88
  showDivider: {
73
89
  true: {
@@ -85,10 +101,8 @@ declare const menuButton: import("tailwind-variants").TVReturnType<{
85
101
  content: string;
86
102
  label: string;
87
103
  rightContainer: string;
88
- chevronContainer: string;
89
- chevron: string;
90
104
  }, undefined, unknown, unknown, undefined>>;
91
- interface MenuButtonProps extends VariantProps<typeof menuButton> {
105
+ export interface MenuButtonProps extends VariantProps<typeof menuButton> {
92
106
  icon?: ComponentType<{
93
107
  size?: number;
94
108
  color?: string;
@@ -96,8 +110,37 @@ interface MenuButtonProps extends VariantProps<typeof menuButton> {
96
110
  label: string;
97
111
  description?: string;
98
112
  onPress?: () => void;
113
+ /** Arbitrary right-side slot — takes precedence over all built-in right elements. */
99
114
  rightComponent?: ReactNode;
100
115
  className?: string;
116
+ /**
117
+ * Inline toggle switch on the right.
118
+ * The row's `onPress` is still fired when the row body is tapped.
119
+ * The switch itself calls `onToggle` independently.
120
+ */
121
+ toggle?: {
122
+ value: boolean;
123
+ onToggle: (v: boolean) => void;
124
+ };
125
+ /**
126
+ * Current-value label shown on the right before the chevron.
127
+ * Great for settings rows: "Siempre", "Nunca", "24 hs".
128
+ */
129
+ value?: string;
130
+ /**
131
+ * Status / count badge on the right.
132
+ * Presets: `"active"` | `"inactive"` | `"new"`.
133
+ * Any string or number renders a plain danger count chip.
134
+ */
135
+ badge?: MenuButtonBadge;
136
+ /** Replace right content with a spinner while an async action is running. */
137
+ loading?: boolean;
138
+ /**
139
+ * Swipe-left (right-side) or swipe-right (left-side) reveal actions.
140
+ * Delegates to `Display.SwipeableRow` — same `SwipeRowAction` shape.
141
+ */
142
+ swipeActions?: MenuButtonSwipeAction[];
143
+ swipeLeftActions?: MenuButtonSwipeAction[];
101
144
  }
102
- declare function MenuButton({ icon: Icon, label, description, onPress, rightComponent, showDivider, variant, disabled, className, }: MenuButtonProps): import("react").JSX.Element;
145
+ declare function MenuButton({ icon: IconComp, label, description, onPress, rightComponent, showDivider, variant, disabled, className, toggle, value, badge, loading, swipeActions, swipeLeftActions, }: MenuButtonProps): import("react").JSX.Element;
103
146
  export default MenuButton;
@@ -1,17 +1,60 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
- import { theme } from "../theme";
2
+ import SwipeableRow from "../Display/Display.SwipeableRow";
3
+ import Spinner from "../Feedback/Feedback.Spinner";
4
+ import { useTheme } from "../theme";
3
5
  import { tv } from "../theme/tw";
4
6
  import { Typography } from "../Typography";
5
- import { TouchableOpacity, View } from "react-native";
7
+ import { Switch, TouchableOpacity, View } from "react-native";
8
+ function BadgeChip({ badge }) {
9
+ const theme = useTheme();
10
+ if (badge === "active") {
11
+ return (_jsxs(View, { style: {
12
+ flexDirection: "row",
13
+ alignItems: "center",
14
+ gap: 4,
15
+ backgroundColor: theme.successLight,
16
+ paddingHorizontal: 8,
17
+ paddingVertical: 3,
18
+ borderRadius: 999,
19
+ }, children: [_jsx(View, { style: { width: 6, height: 6, borderRadius: 999, backgroundColor: theme.success } }), _jsx(Typography.Text, { semibold: true, style: { color: theme.success, fontSize: 11 }, variant: "caption", children: "Activo" })] }));
20
+ }
21
+ if (badge === "inactive") {
22
+ return (_jsxs(View, { style: {
23
+ flexDirection: "row",
24
+ alignItems: "center",
25
+ gap: 4,
26
+ backgroundColor: theme.warningLight,
27
+ paddingHorizontal: 8,
28
+ paddingVertical: 3,
29
+ borderRadius: 999,
30
+ }, children: [_jsx(View, { style: { width: 6, height: 6, borderRadius: 999, backgroundColor: theme.warning } }), _jsx(Typography.Text, { semibold: true, style: { color: theme.warning, fontSize: 11 }, variant: "caption", children: "Inactivo" })] }));
31
+ }
32
+ if (badge === "new") {
33
+ return (_jsx(View, { style: {
34
+ backgroundColor: theme.accent,
35
+ paddingHorizontal: 8,
36
+ paddingVertical: 3,
37
+ borderRadius: 999,
38
+ }, children: _jsx(Typography.Text, { semibold: true, style: { color: theme.text.onAccent, fontSize: 11 }, variant: "caption", children: "Nuevo" }) }));
39
+ }
40
+ return (_jsx(View, { style: {
41
+ backgroundColor: theme.danger,
42
+ minWidth: 20,
43
+ height: 20,
44
+ borderRadius: 999,
45
+ alignItems: "center",
46
+ justifyContent: "center",
47
+ paddingHorizontal: 6,
48
+ }, children: _jsx(Typography.Text, { style: { color: "#FFFFFF", fontSize: 11, lineHeight: 14 }, variant: "caption", children: badge }) }));
49
+ }
50
+ // ─── Tailwind slots ───────────────────────────────────────────────────────────
6
51
  const menuButton = tv({
7
52
  slots: {
8
53
  container: "flex-row items-center py-3 px-4 bg-bg-card",
9
54
  iconContainer: "mr-3 w-8 h-8 rounded-lg items-center justify-center",
10
55
  content: "flex-1",
11
56
  label: "",
12
- rightContainer: "ml-2",
13
- chevronContainer: "ml-2 w-5 h-5 items-center justify-center",
14
- chevron: "text-xl",
57
+ rightContainer: "ml-2 flex-row items-center gap-2",
15
58
  },
16
59
  variants: {
17
60
  variant: {
@@ -23,16 +66,16 @@ const menuButton = tv({
23
66
  iconContainer: "bg-danger-light",
24
67
  label: "text-danger",
25
68
  },
69
+ success: {
70
+ iconContainer: "bg-success-light",
71
+ label: "text-success",
72
+ },
26
73
  },
27
74
  showDivider: {
28
- true: {
29
- container: "border-b border-border-default",
30
- },
75
+ true: { container: "border-b border-border-default" },
31
76
  },
32
77
  disabled: {
33
- true: {
34
- container: "opacity-40",
35
- },
78
+ true: { container: "opacity-40" },
36
79
  },
37
80
  },
38
81
  defaultVariants: {
@@ -41,10 +84,33 @@ const menuButton = tv({
41
84
  disabled: false,
42
85
  },
43
86
  });
44
- function MenuButton({ icon: Icon, label, description, onPress, rightComponent, showDivider = true, variant = "default", disabled = false, className, }) {
87
+ // ─── Component ───────────────────────────────────────────────────────────────
88
+ function MenuButton({ icon: IconComp, label, description, onPress, rightComponent, showDivider = true, variant = "default", disabled = false, className, toggle, value, badge, loading = false, swipeActions, swipeLeftActions, }) {
89
+ const theme = useTheme();
45
90
  const styles = menuButton({ variant, showDivider, disabled });
46
91
  const isDanger = variant === "danger";
47
92
  const isClickable = !!onPress && !disabled;
48
- return (_jsxs(TouchableOpacity, { activeOpacity: 0.5, className: styles.container({ className }), disabled: disabled || !onPress, onPress: onPress, children: [Icon && (_jsx(View, { className: styles.iconContainer(), children: _jsx(Icon, { color: isDanger ? theme.danger : theme.text.secondary, size: 20 }) })), _jsxs(View, { className: styles.content(), children: [_jsx(Typography.Text, { className: styles.label(), variant: "body", children: label }), description && (_jsx(Typography.Text, { className: "text-text-secondary", variant: "bodySmall", children: description }))] }), rightComponent && _jsx(View, { className: styles.rightContainer(), children: rightComponent }), !rightComponent && isClickable && (_jsx(View, { className: styles.chevronContainer(), children: _jsx(Typography.Text, { className: styles.chevron(), children: "\u203A" }) }))] }));
93
+ const iconColor = isDanger ? theme.danger : theme.text.secondary;
94
+ // Right-side priority: loading > explicit slot > toggle > badge/value/chevron
95
+ const rightSlot = (() => {
96
+ if (loading)
97
+ return _jsx(Spinner, { size: "small" });
98
+ if (rightComponent)
99
+ return rightComponent;
100
+ if (toggle) {
101
+ return (_jsx(Switch, { onValueChange: toggle.onToggle, thumbColor: theme.bg.card, trackColor: { false: theme.border.strong, true: theme.accent }, value: toggle.value }));
102
+ }
103
+ const hasBadge = badge != null;
104
+ const hasValue = !!value;
105
+ const hasChevron = isClickable && !hasBadge;
106
+ if (!hasBadge && !hasValue && !hasChevron)
107
+ return null;
108
+ return (_jsxs(View, { style: { flexDirection: "row", alignItems: "center", gap: 6 }, children: [hasBadge ? _jsx(BadgeChip, { badge: badge }) : null, hasValue ? (_jsx(Typography.Text, { style: { color: theme.text.secondary }, variant: "bodySmall", children: value })) : null, isClickable ? (_jsx(Typography.Text, { style: { color: theme.text.tertiary, fontSize: 20 }, children: "\u203A" })) : null] }));
109
+ })();
110
+ const row = (_jsxs(TouchableOpacity, { activeOpacity: 0.5, className: styles.container({ className }), disabled: disabled || (!onPress && !toggle), onPress: onPress, children: [IconComp ? (_jsx(View, { className: styles.iconContainer(), children: _jsx(IconComp, { color: iconColor, size: 20 }) })) : null, _jsxs(View, { className: styles.content(), children: [_jsx(Typography.Text, { className: styles.label(), variant: "body", children: label }), description ? (_jsx(Typography.Text, { className: "text-text-secondary", variant: "bodySmall", children: description })) : null] }), rightSlot ? _jsx(View, { className: styles.rightContainer(), children: rightSlot }) : null] }));
111
+ if (swipeActions?.length || swipeLeftActions?.length) {
112
+ return (_jsx(SwipeableRow, { leftActions: swipeLeftActions, rightActions: swipeActions, children: row }));
113
+ }
114
+ return row;
49
115
  }
50
116
  export default MenuButton;
@@ -0,0 +1,15 @@
1
+ import { IconType } from "../utils/icon";
2
+ import { ComponentType } from "react";
3
+ export interface MenuCardProps {
4
+ title: string;
5
+ iconType?: IconType;
6
+ onPress?: () => void;
7
+ customIcon?: ComponentType<{
8
+ size?: number;
9
+ color?: string;
10
+ }>;
11
+ /** `"md"` = 2-column grid (default). `"sm"` = 3-column grid, smaller icon. */
12
+ size?: "sm" | "md";
13
+ }
14
+ export declare function MenuCard({ title, iconType, onPress, customIcon, size, }: MenuCardProps): import("react").JSX.Element;
15
+ export default MenuCard;
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import CircleButton from "../Display/Display.CircleButton";
3
+ import { useTheme } from "../theme";
4
+ import { Typography } from "../Typography";
5
+ import { TouchableOpacity, View } from "react-native";
6
+ export function MenuCard({ title, iconType = "ionicons", onPress, customIcon, size = "md", }) {
7
+ const theme = useTheme();
8
+ const isSmall = size === "sm";
9
+ return (_jsx(TouchableOpacity, { activeOpacity: 0.7, onPress: onPress, style: {
10
+ backgroundColor: theme.bg.card,
11
+ borderRadius: 16,
12
+ padding: isSmall ? 12 : 16,
13
+ margin: isSmall ? 4 : 8,
14
+ width: isSmall ? "30%" : "45%",
15
+ borderWidth: 1,
16
+ borderColor: theme.border.default,
17
+ ...theme.shadow.sm,
18
+ }, children: _jsxs(View, { style: { alignItems: "center", justifyContent: "center" }, children: [_jsx(CircleButton, { customIcon: customIcon, iconType: iconType, size: isSmall ? "sm" : "md" }), _jsx(Typography.Text, { numberOfLines: 2, style: {
19
+ color: theme.text.primary,
20
+ marginTop: isSmall ? 8 : 12,
21
+ textAlign: "center",
22
+ }, variant: isSmall ? "caption" : "label", children: title })] }) }));
23
+ }
24
+ export default MenuCard;
@@ -0,0 +1,19 @@
1
+ import { IconType } from "../utils/icon";
2
+ import { ComponentType } from "react";
3
+ export interface MenuGridItem {
4
+ id: string;
5
+ title: string;
6
+ iconType?: IconType;
7
+ onPress?: () => void;
8
+ customIcon?: ComponentType<{
9
+ size?: number;
10
+ color?: string;
11
+ }>;
12
+ }
13
+ export interface MenuGridProps {
14
+ items: MenuGridItem[];
15
+ /** `"md"` = 2-column (default). `"sm"` = 3-column, smaller cards. */
16
+ size?: "sm" | "md";
17
+ }
18
+ export declare function MenuGrid({ items, size }: MenuGridProps): import("react").JSX.Element;
19
+ export default MenuGrid;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "nativewind/jsx-runtime";
2
+ import MenuCard from "../Display/Display.MenuCard";
3
+ import { View } from "react-native";
4
+ export function MenuGrid({ items, size = "md" }) {
5
+ return (_jsx(View, { className: "flex-row flex-wrap justify-start p-2 bg-bg-page", children: items.map((item) => (_jsx(MenuCard, { customIcon: item.customIcon, iconType: item.iconType, onPress: item.onPress, size: size, title: item.title }, item.id))) }));
6
+ }
7
+ export default MenuGrid;
@@ -1,11 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
2
  import Icon from "../Display/Display.Icon";
3
- import { theme } from "../theme";
3
+ import { useTheme } from "../theme";
4
4
  import { Typography } from "../Typography";
5
5
  import { useRef } from "react";
6
6
  import { TouchableOpacity, View } from "react-native";
7
7
  import { Swipeable } from "react-native-gesture-handler";
8
8
  function ActionButtons({ actions, onDone, }) {
9
+ const theme = useTheme();
9
10
  return (_jsx(View, { style: { flexDirection: "row" }, children: actions.map((action, index) => (_jsxs(TouchableOpacity, { accessibilityLabel: action.label ?? "Acción", accessibilityRole: "button", activeOpacity: 0.8, onPress: () => {
10
11
  onDone();
11
12
  action.onPress();
@@ -2,7 +2,7 @@ import { type ReactNode } from "react";
2
2
  export interface TooltipProps {
3
3
  content: string;
4
4
  children: ReactNode;
5
- placement?: "top" | "bottom";
5
+ placement?: "top" | "bottom" | "left" | "right";
6
6
  width?: number;
7
7
  testID?: string;
8
8
  }
@@ -1,18 +1,29 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
2
  import Pressable from "../primitives/Pressable";
3
- import { theme } from "../theme";
3
+ import { useTheme } from "../theme";
4
4
  import { Typography } from "../Typography";
5
5
  import { useState } from "react";
6
6
  import { View } from "react-native";
7
+ function resolvePosition(placement) {
8
+ switch (placement) {
9
+ case "bottom":
10
+ return { top: "100%", left: 0, marginVertical: 6 };
11
+ case "left":
12
+ return { right: "100%", top: 0, marginHorizontal: 6 };
13
+ case "right":
14
+ return { left: "100%", top: 0, marginHorizontal: 6 };
15
+ case "top":
16
+ default:
17
+ return { bottom: "100%", left: 0, marginVertical: 6 };
18
+ }
19
+ }
7
20
  function Tooltip({ content, children, placement = "top", width = 200, testID, }) {
21
+ const theme = useTheme();
8
22
  const [visible, setVisible] = useState(false);
9
- const isTop = placement === "top";
10
23
  return (_jsxs(View, { style: { position: "relative", alignSelf: "flex-start" }, testID: testID, children: [_jsx(Pressable, { accessibilityHint: content, accessibilityRole: "button", onPress: () => setVisible((v) => !v), children: children }), visible ? (_jsx(View, { style: {
11
24
  position: "absolute",
12
- [isTop ? "bottom" : "top"]: "100%",
13
- left: 0,
25
+ ...resolvePosition(placement),
14
26
  width,
15
- marginVertical: 6,
16
27
  paddingHorizontal: 10,
17
28
  paddingVertical: 8,
18
29
  borderRadius: 8,
@@ -2,6 +2,7 @@ import AvatarGroup from "../Display/Display.AvatarGroup";
2
2
  import Badge, { type BadgeProps, BadgeVariants } from "../Display/Display.Badge";
3
3
  import Carousel from "../Display/Display.Carousel";
4
4
  import Chip from "../Display/Display.Chip";
5
+ import { CircleButton } from "../Display/Display.CircleButton";
5
6
  import CopyableText from "../Display/Display.CopyableText";
6
7
  import CountRecords from "../Display/Display.CountRecords";
7
8
  import DataTable from "../Display/Display.DataTable";
@@ -15,6 +16,8 @@ import ListItem from "../Display/Display.ListItem";
15
16
  import MediaCard from "../Display/Display.MediaCard";
16
17
  import MediaEmbed from "../Display/Display.MediaEmbed";
17
18
  import MenuButton from "../Display/Display.MenuButton";
19
+ import { MenuCard } from "../Display/Display.MenuCard";
20
+ import { MenuGrid } from "../Display/Display.MenuGrid";
18
21
  import Monetary from "../Display/Display.Monetary";
19
22
  import Pattern from "../Display/Display.Pattern";
20
23
  import PriceTag from "../Display/Display.PriceTag";
@@ -84,9 +87,15 @@ export declare const Display: {
84
87
  Icon: import("react").FC<import("../Display/Display.Icon").IconProps>;
85
88
  CountRecords: typeof CountRecords;
86
89
  Logo: import("react").FC<import("../Display/Display.Logo").LogoProps>;
90
+ CircleButton: typeof CircleButton;
87
91
  MenuButton: typeof MenuButton;
92
+ MenuCard: typeof MenuCard;
93
+ MenuGrid: typeof MenuGrid;
88
94
  Monetary: typeof Monetary;
89
95
  Pattern: typeof Pattern;
90
96
  };
91
97
  export { type BadgeProps, BadgeVariants };
98
+ export type { CircleButtonProps } from "../Display/Display.CircleButton";
99
+ export type { MenuCardProps } from "../Display/Display.MenuCard";
100
+ export type { MenuGridItem } from "../Display/Display.MenuGrid";
92
101
  export default Display;
@@ -5,6 +5,7 @@ import Badge, { BadgeVariants, } from "../Display/Display.Badge";
5
5
  import Card from "../Display/Display.Card";
6
6
  import Carousel from "../Display/Display.Carousel";
7
7
  import Chip from "../Display/Display.Chip";
8
+ import { CircleButton } from "../Display/Display.CircleButton";
8
9
  import CopyableText from "../Display/Display.CopyableText";
9
10
  import CountRecords from "../Display/Display.CountRecords";
10
11
  import DataTable from "../Display/Display.DataTable";
@@ -20,6 +21,8 @@ import Logo from "../Display/Display.Logo";
20
21
  import MediaCard from "../Display/Display.MediaCard";
21
22
  import MediaEmbed from "../Display/Display.MediaEmbed";
22
23
  import MenuButton from "../Display/Display.MenuButton";
24
+ import { MenuCard } from "../Display/Display.MenuCard";
25
+ import { MenuGrid } from "../Display/Display.MenuGrid";
23
26
  import Monetary from "../Display/Display.Monetary";
24
27
  import Pattern from "../Display/Display.Pattern";
25
28
  import PriceTag from "../Display/Display.PriceTag";
@@ -75,7 +78,10 @@ export const Display = {
75
78
  Icon,
76
79
  CountRecords,
77
80
  Logo,
81
+ CircleButton,
78
82
  MenuButton,
83
+ MenuCard,
84
+ MenuGrid,
79
85
  Monetary,
80
86
  Pattern,
81
87
  };
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
- import { Display } from "../Display";
2
+ import Icon from "../Display/Display.Icon";
3
3
  import { cn } from "../theme/tw";
4
4
  import { Typography } from "../Typography";
5
5
  import { View } from "react-native";
6
6
  function Empty({ handleGoHome, className }) {
7
- return (_jsxs(View, { className: cn("flex-1 items-center justify-center px-4", className), children: [_jsx(Display.Icon, { name: "sad-cry", size: 80, type: "fontawesome5" }), _jsx(Typography.Text, { className: "pt-4 text-text-primary", semibold: true, variant: "h3", children: "\u00A1Oops!" }), _jsx(Typography.Text, { className: "pt-2 text-center text-base leading-5.5 text-text-secondary", variant: "h5", children: "No se encontraron registros disponibles." })] }));
7
+ return (_jsxs(View, { className: cn("flex-1 items-center justify-center px-4", className), children: [_jsx(Icon, { name: "sad-cry", size: 80, type: "fontawesome5" }), _jsx(Typography.Text, { className: "pt-4 text-text-primary", semibold: true, variant: "h3", children: "\u00A1Oops!" }), _jsx(Typography.Text, { className: "pt-2 text-center text-base leading-5.5 text-text-secondary", variant: "h5", children: "No se encontraron registros disponibles." })] }));
8
8
  }
9
9
  export default Empty;
@@ -1,10 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
- import { Display } from "../Display";
2
+ import Icon from "../Display/Display.Icon";
3
3
  import Button from "../Form/Form.Button";
4
4
  import { cn } from "../theme/tw";
5
5
  import { Typography } from "../Typography";
6
6
  import { View } from "react-native";
7
7
  function Error({ message, onRetry, onBack, onHome, className }) {
8
- return (_jsxs(View, { className: cn("flex-1 items-center justify-center px-4", className), children: [_jsx(Display.Icon, { color: "#ef4444", name: "exclamation-circle", size: 80, type: "fontawesome5" }), _jsx(Typography.Text, { className: "pt-4 text-text-primary", semibold: true, variant: "h3", children: "\u00A1Error!" }), _jsx(Typography.Text, { 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" }))] })] }));
8
+ return (_jsxs(View, { className: cn("flex-1 items-center justify-center px-4", className), children: [_jsx(Icon, { color: "#ef4444", name: "exclamation-circle", size: 80, type: "fontawesome5" }), _jsx(Typography.Text, { className: "pt-4 text-text-primary", semibold: true, variant: "h3", children: "\u00A1Error!" }), _jsx(Typography.Text, { 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" }))] })] }));
9
9
  }
10
10
  export default Error;
@@ -1,13 +1,13 @@
1
1
  import { jsx as _jsx } from "nativewind/jsx-runtime";
2
2
  import { cn } from "../theme/tw";
3
3
  import { motion } from "../tokens/motion";
4
- import { useEffect, useRef } from "react";
4
+ import { useEffect, useState } from "react";
5
5
  import { Animated, Easing, View } from "react-native";
6
6
  function clamp(n) {
7
7
  return Math.max(0, Math.min(1, n));
8
8
  }
9
9
  function Progress({ value = 0, indeterminate = false, height = 6, className, testID, }) {
10
- const translate = useRef(new Animated.Value(0)).current;
10
+ const [translate] = useState(() => new Animated.Value(0));
11
11
  useEffect(() => {
12
12
  if (!indeterminate)
13
13
  return;
@@ -1,10 +1,10 @@
1
1
  import { jsx as _jsx } from "nativewind/jsx-runtime";
2
2
  import { cn } from "../theme/tw";
3
3
  import { motion } from "../tokens/motion";
4
- import { useEffect, useRef } from "react";
4
+ import { useEffect, useState } from "react";
5
5
  import { Animated, Easing } from "react-native";
6
6
  function Skeleton({ width = "100%", height = 16, radius = 8, circle = false, className, style, }) {
7
- const opacity = useRef(new Animated.Value(0.5)).current;
7
+ const [opacity] = useState(() => new Animated.Value(0.5));
8
8
  useEffect(() => {
9
9
  const pulse = Animated.loop(Animated.sequence([
10
10
  Animated.timing(opacity, {
@@ -23,6 +23,7 @@ function Splash({ isReady, logo, minDuration = 800, message = "Inicializando..."
23
23
  clearTimeout(minDurationTimeoutRef.current);
24
24
  }
25
25
  if (!isReady) {
26
+ // eslint-disable-next-line react-hooks/set-state-in-effect
26
27
  setShowContent(false);
27
28
  return;
28
29
  }