@sincpro/mobile-ui 2.0.0 → 3.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 (204) hide show
  1. package/README.md +7 -2
  2. package/assets/brand-mesh.png +0 -0
  3. package/assets/fonts/FFL.txt +57 -0
  4. package/assets/fonts/Satoshi-Black.otf +0 -0
  5. package/assets/fonts/Satoshi-Bold.otf +0 -0
  6. package/assets/fonts/Satoshi-Medium.otf +0 -0
  7. package/assets/fonts/Satoshi-Regular.otf +0 -0
  8. package/dist/Actions/Actions.Button.d.ts +2 -1
  9. package/dist/Actions/Actions.Button.js +31 -37
  10. package/dist/Actions/Actions.Dock.d.ts +6 -6
  11. package/dist/Actions/Actions.Dock.js +14 -72
  12. package/dist/Actions/Actions.FAB.d.ts +2 -1
  13. package/dist/Actions/Actions.FilterButton.js +1 -1
  14. package/dist/Actions/Actions.IconButton.d.ts +3 -4
  15. package/dist/Actions/Actions.IconButton.js +2 -2
  16. package/dist/Dialog/Confirmation.context.d.ts +5 -14
  17. package/dist/Dialog/Confirmation.context.js +11 -51
  18. package/dist/Dialog/Dialog.ActionSheet.d.ts +8 -7
  19. package/dist/Dialog/Dialog.ActionSheet.js +45 -19
  20. package/dist/Dialog/Dialog.Confirmation.d.ts +14 -11
  21. package/dist/Dialog/Dialog.Confirmation.js +38 -14
  22. package/dist/Dialog/Dialog.EditValue.d.ts +21 -16
  23. package/dist/Dialog/Dialog.EditValue.js +73 -14
  24. package/dist/Dialog/Dialog.Sheet.d.ts +8 -24
  25. package/dist/Dialog/Dialog.Sheet.js +15 -60
  26. package/dist/Dialog/index.d.ts +14 -14
  27. package/dist/Dialog/index.js +5 -3
  28. package/dist/Dialog/requestConfirmation.d.ts +3 -0
  29. package/dist/Dialog/requestConfirmation.js +13 -0
  30. package/dist/Display/Display.Accordion.js +1 -1
  31. package/dist/Display/Display.Avatar.d.ts +6 -6
  32. package/dist/Display/Display.Avatar.js +2 -2
  33. package/dist/Display/Display.Badge.js +4 -4
  34. package/dist/Display/Display.Card.js +5 -2
  35. package/dist/Display/Display.Carousel.js +1 -1
  36. package/dist/Display/Display.CircleButton.d.ts +3 -8
  37. package/dist/Display/Display.CircleButton.js +2 -2
  38. package/dist/Display/Display.CopyableText.js +1 -5
  39. package/dist/Display/Display.Date.d.ts +3 -2
  40. package/dist/Display/Display.Date.js +3 -3
  41. package/dist/Display/Display.GradientSurface.d.ts +52 -9
  42. package/dist/Display/Display.GradientSurface.js +9 -14
  43. package/dist/Display/Display.Icon.d.ts +7 -11
  44. package/dist/Display/Display.Icon.js +15 -30
  45. package/dist/Display/Display.Image.d.ts +2 -1
  46. package/dist/Display/Display.Image.js +1 -1
  47. package/dist/Display/Display.KeyValue.js +1 -1
  48. package/dist/Display/Display.ListItem.js +1 -1
  49. package/dist/Display/Display.MenuButton.js +4 -4
  50. package/dist/Display/Display.MenuCard.d.ts +3 -8
  51. package/dist/Display/Display.MenuCard.js +2 -2
  52. package/dist/Display/Display.MenuGrid.d.ts +2 -7
  53. package/dist/Display/Display.MenuGrid.js +1 -1
  54. package/dist/Display/Display.Monetary.d.ts +2 -6
  55. package/dist/Display/Display.Monetary.js +2 -2
  56. package/dist/Display/Display.PriceTag.js +1 -1
  57. package/dist/Display/Display.Rating.js +1 -1
  58. package/dist/Display/Display.SearchHistory.d.ts +2 -1
  59. package/dist/Display/Display.SearchHistory.js +1 -1
  60. package/dist/Display/Display.StatCard.d.ts +2 -1
  61. package/dist/Display/Display.StatCard.js +1 -1
  62. package/dist/Display/Display.StoryViewer.js +10 -6
  63. package/dist/Display/Display.SwipeableRow.d.ts +2 -1
  64. package/dist/Display/Display.Timeline.js +1 -1
  65. package/dist/Display/icon-map.d.ts +344 -0
  66. package/dist/Display/icon-map.js +86 -0
  67. package/dist/Display/index.d.ts +2 -0
  68. package/dist/Display/index.js +1 -0
  69. package/dist/Feedback/Feedback.Banner.js +13 -12
  70. package/dist/Feedback/Feedback.Empty.js +1 -1
  71. package/dist/Feedback/Feedback.EmptyState.d.ts +2 -1
  72. package/dist/Feedback/Feedback.EmptyState.js +1 -1
  73. package/dist/Feedback/Feedback.Error.js +1 -1
  74. package/dist/Feedback/Feedback.PermissionCard.d.ts +2 -1
  75. package/dist/Feedback/Feedback.PermissionCard.js +2 -2
  76. package/dist/Feedback/Toast.context.js +3 -3
  77. package/dist/Form/Form.ControlledDropdown.d.ts +5 -11
  78. package/dist/Form/Form.ControlledDropdown.js +5 -2
  79. package/dist/Form/Form.DatePicker.d.ts +5 -9
  80. package/dist/Form/Form.DatePicker.js +116 -21
  81. package/dist/Form/Form.Dropdown.d.ts +11 -16
  82. package/dist/Form/Form.Dropdown.js +54 -90
  83. package/dist/Form/Form.FractionalQuantityInput.js +4 -11
  84. package/dist/Form/Form.Input.d.ts +3 -3
  85. package/dist/Form/Form.KeyboardForm.d.ts +2 -2
  86. package/dist/Form/Form.KeyboardForm.js +2 -2
  87. package/dist/Form/Form.KeyboardToolbar.d.ts +2 -1
  88. package/dist/Form/Form.KeyboardToolbar.js +3 -7
  89. package/dist/Form/Form.PasswordInput.js +1 -1
  90. package/dist/Form/Form.QuantitySelector.js +1 -1
  91. package/dist/Form/Form.Select.d.ts +12 -8
  92. package/dist/Form/Form.Select.js +64 -31
  93. package/dist/Form/Form.Slider.d.ts +4 -7
  94. package/dist/Form/Form.Slider.js +11 -10
  95. package/dist/Form/Form.Switch.js +2 -1
  96. package/dist/Form/index.d.ts +6 -14
  97. package/dist/Form/index.js +0 -11
  98. package/dist/Navigation/Navigation.AppBar.d.ts +2 -1
  99. package/dist/Navigation/Navigation.AppBar.js +2 -2
  100. package/dist/Navigation/Navigation.BottomNav.d.ts +4 -8
  101. package/dist/Navigation/Navigation.BottomNav.js +2 -2
  102. package/dist/Navigation/Navigation.CollapsingHeader.js +3 -2
  103. package/dist/Navigation/Navigation.Menu.d.ts +16 -11
  104. package/dist/Navigation/Navigation.Menu.js +97 -42
  105. package/dist/Navigation/Navigation.Search.js +2 -2
  106. package/dist/Navigation/Navigation.SearchBar.js +3 -4
  107. package/dist/Navigation/Navigation.SegmentedControl.d.ts +7 -25
  108. package/dist/Navigation/Navigation.SegmentedControl.js +18 -39
  109. package/dist/Navigation/Navigation.Steps.js +1 -1
  110. package/dist/Navigation/Navigation.Tabs.d.ts +2 -1
  111. package/dist/Navigation/index.d.ts +0 -6
  112. package/dist/Navigation/index.js +0 -8
  113. package/dist/Typography/Typography.SelectableText.js +3 -7
  114. package/dist/Typography/Typography.Text.d.ts +1 -6
  115. package/dist/Typography/Typography.Text.js +3 -6
  116. package/dist/index.d.ts +1 -4
  117. package/dist/index.js +1 -4
  118. package/dist/layouts/AuthContainer.d.ts +4 -2
  119. package/dist/layouts/AuthContainer.js +3 -4
  120. package/dist/layouts/Container.d.ts +0 -4
  121. package/dist/layouts/Container.js +3 -4
  122. package/dist/layouts/ScrollContainer.d.ts +7 -3
  123. package/dist/layouts/ScrollContainer.js +5 -4
  124. package/dist/primitives/BaseButton.d.ts +2 -1
  125. package/dist/theme/build.js +0 -4
  126. package/dist/theme/css-vars.js +0 -4
  127. package/dist/theme/dark.js +2 -6
  128. package/dist/theme/default.js +1 -5
  129. package/dist/theme/index.d.ts +1 -1
  130. package/dist/theme/index.js +1 -1
  131. package/dist/theme/types.d.ts +0 -11
  132. package/dist/theme/typography.d.ts +8 -7
  133. package/dist/theme/typography.js +25 -4
  134. package/dist/utils/timezone.d.ts +0 -5
  135. package/dist/views/AuthFormView.d.ts +3 -1
  136. package/dist/views/AuthFormView.js +2 -2
  137. package/dist/views/FormViewV2.d.ts +6 -13
  138. package/dist/views/FormViewV2.js +29 -25
  139. package/dist/views/ListViewV2.context.d.ts +5 -5
  140. package/dist/views/ListViewV2.d.ts +23 -18
  141. package/dist/views/ListViewV2.js +45 -48
  142. package/dist/views/types/IListView.d.ts +5 -4
  143. package/dist/views/types/IListView.js +18 -15
  144. package/dist/widgets/CollectionOptions.d.ts +2 -2
  145. package/dist/widgets/CollectionOptions.js +92 -65
  146. package/dist/widgets/FilterBar.js +1 -1
  147. package/dist/widgets/PromoModal.js +10 -4
  148. package/dist/widgets/QuickActions.d.ts +2 -7
  149. package/dist/widgets/QuickActions.js +1 -1
  150. package/dist/widgets/ReorderableList.js +3 -3
  151. package/dist/widgets/TimeZoneSelectorModal.d.ts +1 -6
  152. package/dist/widgets/TimeZoneSelectorModal.js +17 -12
  153. package/dist/widgets/index.d.ts +0 -11
  154. package/dist/widgets/index.js +0 -7
  155. package/package.json +36 -38
  156. package/tailwind.preset.js +0 -4
  157. package/dist/Dialog/BottomSheet/BottomSheet.Root.d.ts +0 -324
  158. package/dist/Dialog/BottomSheet/BottomSheet.Root.js +0 -373
  159. package/dist/Dialog/BottomSheet/index.d.ts +0 -1
  160. package/dist/Dialog/BottomSheet/index.js +0 -1
  161. package/dist/Dialog/Dialog.NativeSheet.d.ts +0 -10
  162. package/dist/Dialog/Dialog.NativeSheet.js +0 -21
  163. package/dist/Form/Form.Button.d.ts +0 -3
  164. package/dist/Form/Form.Button.js +0 -1
  165. package/dist/Form/Form.FAB.d.ts +0 -3
  166. package/dist/Form/Form.FAB.js +0 -1
  167. package/dist/Form/Form.FilterButton.d.ts +0 -3
  168. package/dist/Form/Form.FilterButton.js +0 -1
  169. package/dist/Form/Form.IconButton.d.ts +0 -3
  170. package/dist/Form/Form.IconButton.js +0 -1
  171. package/dist/Form/Form.SearchBar.d.ts +0 -13
  172. package/dist/Form/Form.SearchBar.js +0 -26
  173. package/dist/Navigation/Navigation.Header.d.ts +0 -16
  174. package/dist/Navigation/Navigation.Header.js +0 -17
  175. package/dist/Navigation/Navigation.HeaderToolbar.d.ts +0 -15
  176. package/dist/Navigation/Navigation.HeaderToolbar.js +0 -18
  177. package/dist/Navigation/Navigation.TabIcon.d.ts +0 -18
  178. package/dist/Navigation/Navigation.TabIcon.js +0 -12
  179. package/dist/Navigation/Navigation.TabItem.d.ts +0 -19
  180. package/dist/Navigation/Navigation.TabItem.js +0 -13
  181. package/dist/icons/OdooIcon.d.ts +0 -11
  182. package/dist/icons/OdooIcon.js +0 -11
  183. package/dist/icons/PrinterIcon.d.ts +0 -9
  184. package/dist/icons/PrinterIcon.js +0 -9
  185. package/dist/layouts/TabNavigatorLayout.d.ts +0 -26
  186. package/dist/layouts/TabNavigatorLayout.js +0 -60
  187. package/dist/utils/icon.d.ts +0 -1
  188. package/dist/utils/icon.js +0 -1
  189. package/dist/widgets/CircleButton.d.ts +0 -3
  190. package/dist/widgets/CircleButton.js +0 -1
  191. package/dist/widgets/GeoPermissionCard.d.ts +0 -13
  192. package/dist/widgets/GeoPermissionCard.js +0 -24
  193. package/dist/widgets/HomeHeader.d.ts +0 -15
  194. package/dist/widgets/HomeHeader.js +0 -13
  195. package/dist/widgets/MenuCard.d.ts +0 -3
  196. package/dist/widgets/MenuCard.js +0 -1
  197. package/dist/widgets/MenuGrid.d.ts +0 -5
  198. package/dist/widgets/MenuGrid.js +0 -1
  199. package/dist/widgets/MenuSection.d.ts +0 -3
  200. package/dist/widgets/MenuSection.js +0 -1
  201. package/dist/widgets/ScreenHeader.d.ts +0 -25
  202. package/dist/widgets/ScreenHeader.js +0 -32
  203. package/dist/widgets/TimeZoneSelector.d.ts +0 -12
  204. package/dist/widgets/TimeZoneSelector.js +0 -24
@@ -114,6 +114,6 @@ function Carousel({ data, renderItem, keyExtractor, itemWidth, gap = 12, showDot
114
114
  },
115
115
  dotStyle,
116
116
  index === current ? activeDotStyle : undefined,
117
- ] }, keys[index]))) })) : null, data.length > 1 ? (renderControls ? (renderControls(controls)) : (_jsxs(View, { className: controlsClassName ?? "flex-row items-center justify-center gap-3 mt-2", children: [_jsx(IconButton, { accessibilityLabel: previousAccessibilityLabel, disabled: !controls.canPrevious, icon: "chevron-left", onPress: controls.previous }), _jsx(Typography.Text, { ...paginationTextProps, accessibilityLiveRegion: "polite", children: formatPageLabel(current, data.length) }), _jsx(IconButton, { accessibilityLabel: nextAccessibilityLabel, disabled: !controls.canNext, icon: "chevron-right", onPress: controls.next })] }))) : null] }));
117
+ ] }, keys[index]))) })) : null, data.length > 1 ? (renderControls ? (renderControls(controls)) : (_jsxs(View, { className: controlsClassName ?? "flex-row items-center justify-center gap-3 mt-2", children: [_jsx(IconButton, { accessibilityLabel: previousAccessibilityLabel, disabled: !controls.canPrevious, icon: "back", onPress: controls.previous }), _jsx(Typography.Text, { ...paginationTextProps, accessibilityLiveRegion: "polite", children: formatPageLabel(current, data.length) }), _jsx(IconButton, { accessibilityLabel: nextAccessibilityLabel, disabled: !controls.canNext, icon: "next", onPress: controls.next })] }))) : null] }));
118
118
  }
119
119
  export default Carousel;
@@ -1,15 +1,10 @@
1
- import { IconType } from "../utils/icon.js";
2
- import { ComponentType } from "react";
1
+ import type { IconName } from "./index.js";
3
2
  export interface CircleButtonProps {
4
- iconType?: IconType;
5
3
  size?: "sm" | "md" | "lg";
6
4
  color?: string;
7
5
  variant?: "primary" | "secondary" | "accent" | "success" | "danger" | "warning";
8
6
  className?: string;
9
- customIcon?: ComponentType<{
10
- size?: number;
11
- color?: string;
12
- }>;
7
+ icon: IconName;
13
8
  }
14
- export declare function CircleButton({ iconType, size, color, variant, className, customIcon, }: CircleButtonProps): import("react").JSX.Element;
9
+ export declare function CircleButton({ size, color, variant, className, icon, }: CircleButtonProps): import("react").JSX.Element;
15
10
  export default CircleButton;
@@ -31,9 +31,9 @@ const SIZE_MAP = {
31
31
  md: { icon: 30 },
32
32
  lg: { icon: 40 },
33
33
  };
34
- export function CircleButton({ iconType = "ionicons", size = "md", color, variant = "accent", className, customIcon, }) {
34
+ export function CircleButton({ size = "md", color, variant = "accent", className, icon, }) {
35
35
  const theme = useTheme();
36
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 }) }));
37
+ return (_jsx(View, { className: cn(circleButtonVariants({ size, variant }), className), children: _jsx(Icon, { color: iconColor, name: icon, size: SIZE_MAP[size].icon }) }));
38
38
  }
39
39
  export default CircleButton;
@@ -73,10 +73,6 @@ function CopyableText({ value, getValue, label, className, accessibilityLabel, t
73
73
  justifyContent: selectable ? "center" : "flex-start",
74
74
  gap: 8,
75
75
  flexShrink: selectable ? 0 : 1,
76
- }, testID: testID, children: [!selectable ? (_jsx(Typography.Text, { className: className, style: { flexShrink: 1 }, variant: "body", children: displayText })) : null, _jsx(View, { accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", style: { width: 24, alignItems: "center" }, children: status === "copying" ? (_jsx(ActivityIndicator, { color: theme.text.accent, size: "small" })) : (_jsx(Icon, { color: isCopied ? theme.success : theme.text.accent, name: isCopied
77
- ? "checkmark"
78
- : status === "error"
79
- ? "alert-circle-outline"
80
- : "copy-outline", size: 20 })) })] })] }), status === "error" ? (_jsx(Typography.Text, { accessibilityLiveRegion: "polite", className: "text-danger", variant: "bodySmall", children: "No se pudo copiar. Toca para reintentar." })) : null] }));
76
+ }, testID: testID, children: [!selectable ? (_jsx(Typography.Text, { className: className, style: { flexShrink: 1 }, variant: "body", children: displayText })) : null, _jsx(View, { accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", style: { width: 24, alignItems: "center" }, children: status === "copying" ? (_jsx(ActivityIndicator, { color: theme.text.accent, size: "small" })) : (_jsx(Icon, { color: isCopied ? theme.success : theme.text.accent, name: isCopied ? "check" : status === "error" ? "error" : "copy", size: 20 })) })] })] }), status === "error" ? (_jsx(Typography.Text, { accessibilityLiveRegion: "polite", className: "text-danger", variant: "bodySmall", children: "No se pudo copiar. Toca para reintentar." })) : null] }));
81
77
  }
82
78
  export default CopyableText;
@@ -1,3 +1,4 @@
1
+ import type { IconName } from "./index.js";
1
2
  import { typographyVariants } from "../theme/typography.js";
2
3
  import { type StyleProp, type TextStyle } from "react-native";
3
4
  type DateFormat = "short" | "long" | "numeric";
@@ -22,8 +23,8 @@ interface DateFieldProps {
22
23
  timezone?: string;
23
24
  /** App-like layout. Default `inline` (no visual change vs. previous version). */
24
25
  variant?: DateVariant;
25
- /** Override the leading icon (ionicons). Defaults: calendar, or clock if `showTime`. */
26
- icon?: string;
26
+ /** Override the semantic icon. Defaults: calendar, or time if `showTime`. */
27
+ icon?: IconName;
27
28
  /** Show relative time ("hace 2 h") instead of the absolute date. */
28
29
  relative?: boolean;
29
30
  }
@@ -30,10 +30,10 @@ function DateField({ value, format = "short", prefix = "", textVariant = "body",
30
30
  : formatDateWithDefaultTimezone(value, { showTime, locale });
31
31
  const formatted = value && relative ? relativeTime(value) : absolute;
32
32
  const text = prefix ? `${prefix}${formatted}` : formatted;
33
- const iconName = icon ?? (showTime && !relative ? "time-outline" : "calendar-outline");
34
- const label = (_jsx(Typography.Text, { bold: bold, color: color, semibold: semibold && !bold, style: style, variant: textVariant, children: text }));
33
+ const iconName = icon ?? (showTime && !relative ? "time" : "calendar");
34
+ const label = (_jsx(Typography.Text, { bold: bold, semibold: semibold && !bold, style: [{ color }, style], variant: textVariant, children: text }));
35
35
  if (variant === "inline") {
36
- return (_jsx(Typography.Text, { bold: bold, className: className, color: color, semibold: semibold && !bold, style: style, variant: textVariant, children: text }));
36
+ return (_jsx(Typography.Text, { bold: bold, className: className, semibold: semibold && !bold, style: [{ color }, style], variant: textVariant, children: text }));
37
37
  }
38
38
  if (variant === "pill") {
39
39
  return (_jsxs(View, { className: cn("flex-row items-center self-start gap-1.5 rounded-full bg-bg-muted px-2.5 py-1", className), children: [_jsx(Icon, { color: color ?? theme.icon.secondary, name: iconName, size: 14 }), label] }));
@@ -64,15 +64,7 @@ declare const GRADIENT_PRESETS: {
64
64
  };
65
65
  };
66
66
  };
67
- /**
68
- * Aliases kept for backwards compatibility. These were the original names; the
69
- * canonical ones are above. Use the canonical names in new code.
70
- * @deprecated use `warm` instead of `gold-mesh`.
71
- */
72
- declare const DEPRECATED_PRESETS: {
73
- readonly "gold-mesh": "warm";
74
- };
75
- export type GradientPreset = keyof typeof GRADIENT_PRESETS | keyof typeof DEPRECATED_PRESETS | "brand";
67
+ export type GradientPreset = keyof typeof GRADIENT_PRESETS | "brand";
76
68
  declare const surface: import("tailwind-variants").TVReturnType<{
77
69
  radius: {
78
70
  none: "rounded-none";
@@ -125,6 +117,57 @@ export interface GradientSurfaceProps extends VariantProps<typeof surface> {
125
117
  style?: ViewStyle;
126
118
  testID?: string;
127
119
  }
120
+ export declare function resolveGradientPreset(preset?: GradientPreset): {
121
+ colors: readonly [string, string] | readonly ["#1A1714", "#C89B3C", "#B88A2E"] | readonly ["#1C3765", "#2A4A85", "#8FA9E8"];
122
+ start: {
123
+ readonly x: 0;
124
+ readonly y: 0;
125
+ };
126
+ end: {
127
+ readonly x: 1;
128
+ readonly y: 1;
129
+ };
130
+ } | {
131
+ colors: readonly [string, string] | readonly ["#1A1714", "#C89B3C", "#B88A2E"] | readonly ["#1C3765", "#2A4A85", "#8FA9E8"];
132
+ start: {
133
+ readonly x: 0;
134
+ readonly y: 0;
135
+ };
136
+ end: {
137
+ readonly x: 1;
138
+ readonly y: 1;
139
+ };
140
+ } | {
141
+ colors: readonly [string, string] | readonly ["#1A1714", "#C89B3C", "#B88A2E"] | readonly ["#1C3765", "#2A4A85", "#8FA9E8"];
142
+ start: {
143
+ readonly x: 0;
144
+ readonly y: 0;
145
+ };
146
+ end: {
147
+ readonly x: 1;
148
+ readonly y: 1;
149
+ };
150
+ } | {
151
+ colors: readonly [string, string] | readonly ["#1A1714", "#C89B3C", "#B88A2E"] | readonly ["#1C3765", "#2A4A85", "#8FA9E8"];
152
+ start: {
153
+ readonly x: 0;
154
+ readonly y: 0;
155
+ };
156
+ end: {
157
+ readonly x: 1;
158
+ readonly y: 1;
159
+ };
160
+ } | {
161
+ colors: readonly [string, string] | readonly ["#1A1714", "#C89B3C", "#B88A2E"] | readonly ["#1C3765", "#2A4A85", "#8FA9E8"];
162
+ start: {
163
+ readonly x: 0;
164
+ readonly y: 0;
165
+ };
166
+ end: {
167
+ readonly x: 1;
168
+ readonly y: 1;
169
+ };
170
+ };
128
171
  /**
129
172
  * Modern gradient surface. Wraps `expo-linear-gradient` with named presets +
130
173
  * tokenized radius/padding. iOS + Android (no native dep beyond the already-peer
@@ -35,14 +35,6 @@ const GRADIENT_PRESETS = {
35
35
  end: { x: 1, y: 1 },
36
36
  },
37
37
  };
38
- /**
39
- * Aliases kept for backwards compatibility. These were the original names; the
40
- * canonical ones are above. Use the canonical names in new code.
41
- * @deprecated use `warm` instead of `gold-mesh`.
42
- */
43
- const DEPRECATED_PRESETS = {
44
- "gold-mesh": "warm",
45
- };
46
38
  const surface = tv({
47
39
  variants: {
48
40
  radius: {
@@ -60,18 +52,21 @@ const surface = tv({
60
52
  },
61
53
  defaultVariants: { radius: "lg", padding: "none" },
62
54
  });
55
+ export function resolveGradientPreset(preset = "warm") {
56
+ const surface = preset === "brand" ? GRADIENT_PRESETS.warm : GRADIENT_PRESETS[preset];
57
+ return {
58
+ ...surface,
59
+ colors: preset === "brand" ? theme.gradient.primary : surface.colors,
60
+ };
61
+ }
63
62
  /**
64
63
  * Modern gradient surface. Wraps `expo-linear-gradient` with named presets +
65
64
  * tokenized radius/padding. iOS + Android (no native dep beyond the already-peer
66
65
  * `expo-linear-gradient`). Replaces the legacy `GradientContainer`.
67
66
  */
68
67
  function GradientSurface({ preset = "warm", colors, start, end, radius, padding, children, className, style, testID, }) {
69
- const resolvedPreset = (preset in DEPRECATED_PRESETS
70
- ? DEPRECATED_PRESETS[preset]
71
- : preset);
72
- const p = resolvedPreset === "brand" ? GRADIENT_PRESETS.warm : GRADIENT_PRESETS[resolvedPreset];
73
- const presetColors = resolvedPreset === "brand" ? theme.gradient.primary : p.colors;
74
- const resolvedColors = (colors ?? presetColors);
68
+ const p = resolveGradientPreset(preset);
69
+ const resolvedColors = colors ?? p.colors;
75
70
  return (_jsxs(View, { className: cn("overflow-hidden", surface({ radius })), style: style, testID: testID, children: [_jsx(LinearGradient, { colors: [...resolvedColors], end: end ?? p.end, start: start ?? p.start, style: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0 } }), _jsx(View, { className: cn(surface({ padding }), className), children: children })] }));
76
71
  }
77
72
  export default GradientSurface;
@@ -1,15 +1,11 @@
1
- import { IconType } from "../utils/icon.js";
2
- import { ComponentType, FC } from "react";
3
- export interface IconProps {
4
- name?: any;
1
+ import { type IconName } from "./icon-map.js";
2
+ import type { FC } from "react";
3
+ import { type ColorValue, type ViewProps } from "react-native";
4
+ export type IconProps = Omit<ViewProps, "children"> & {
5
+ name: IconName;
5
6
  size?: number;
6
- color?: string;
7
- type?: IconType;
8
- customIcon?: ComponentType<{
9
- size?: number;
10
- color?: string;
11
- }>;
7
+ color?: ColorValue;
12
8
  className?: string;
13
- }
9
+ };
14
10
  declare const Icon: FC<IconProps>;
15
11
  export default Icon;
@@ -1,36 +1,21 @@
1
1
  import { jsx as _jsx } from "nativewind/jsx-runtime";
2
- import { AntDesign, Feather, FontAwesome, FontAwesome5, Ionicons, MaterialCommunityIcons, MaterialIcons, SimpleLineIcons, } from "@expo/vector-icons";
2
+ import { ICON_MAP } from "./icon-map.js";
3
3
  import { useTheme } from "../theme/index.js";
4
+ import { cn } from "../theme/tw.js";
5
+ import { SymbolView } from "expo-symbols";
4
6
  import { View } from "react-native";
5
- const Icon = ({ name, size = 24, color, type = "ionicons", customIcon, className, }) => {
7
+ const Icon = ({ name, size = 24, color, className, style, accessible, accessibilityLabel, accessibilityRole, ...viewProps }) => {
6
8
  const theme = useTheme();
7
- const iconColor = color || theme.text.primary;
8
- const renderIcon = () => {
9
- if (type === "custom" && customIcon) {
10
- const CustomIconComponent = customIcon;
11
- return _jsx(CustomIconComponent, { color: iconColor, size: size });
12
- }
13
- switch (type) {
14
- case "ionicons":
15
- return _jsx(Ionicons, { color: iconColor, name: name, size: size });
16
- case "fontawesome":
17
- return _jsx(FontAwesome, { color: iconColor, name: name, size: size });
18
- case "material":
19
- return _jsx(MaterialIcons, { color: iconColor, name: name, size: size });
20
- case "fontawesome5":
21
- return _jsx(FontAwesome5, { color: iconColor, name: name, size: size });
22
- case "simplelineicons":
23
- return _jsx(SimpleLineIcons, { color: iconColor, name: name, size: size });
24
- case "antdesign":
25
- return _jsx(AntDesign, { color: iconColor, name: name, size: size });
26
- case "feather":
27
- return _jsx(Feather, { color: iconColor, name: name, size: size });
28
- case "MaterialCommunityIcons":
29
- return _jsx(MaterialCommunityIcons, { color: iconColor, name: name, size: size });
30
- default:
31
- return null;
32
- }
33
- };
34
- return (_jsx(View, { className: `justify-center items-center ${className || ""}`, children: renderIcon() }));
9
+ if (typeof name !== "string" || !Object.hasOwn(ICON_MAP, name)) {
10
+ throw new Error(`Unknown icon name: ${String(name)}`);
11
+ }
12
+ if (!Number.isFinite(size) || size <= 0) {
13
+ throw new RangeError(`Invalid icon size: ${String(size)}`);
14
+ }
15
+ if (["type", "customIcon", "fallback", "children"].some((key) => Object.hasOwn(viewProps, key))) {
16
+ throw new Error("Display.Icon accepts semantic names, not icon families or custom renderers");
17
+ }
18
+ const icon = ICON_MAP[name];
19
+ return (_jsx(View, { ...viewProps, accessibilityLabel: accessibilityLabel, accessibilityRole: accessibilityRole ?? (accessibilityLabel ? "image" : undefined), accessible: accessible ?? Boolean(accessibilityLabel), className: cn("justify-center items-center", className), style: style, children: _jsx(SymbolView, { accessibilityElementsHidden: true, accessible: false, importantForAccessibility: "no-hide-descendants", name: { ios: icon.sf, android: icon.md, web: icon.md }, size: size, tintColor: color ?? theme.text.primary }) }));
35
20
  };
36
21
  export default Icon;
@@ -1,3 +1,4 @@
1
+ import type { IconName } from "./index.js";
1
2
  import { type ExpoImageProps } from "./Display.ExpoImage.js";
2
3
  import { type ImageSource } from "../utils/image.js";
3
4
  import { type ReactNode } from "react";
@@ -8,7 +9,7 @@ export interface DisplayImageProps extends Omit<ExpoImageProps, "source" | "styl
8
9
  height?: number;
9
10
  aspectRatio?: number;
10
11
  radius?: number;
11
- fallbackIcon?: string;
12
+ fallbackIcon?: IconName;
12
13
  className?: string;
13
14
  style?: ExpoImageProps["style"];
14
15
  containerStyle?: StyleProp<ViewStyle>;
@@ -8,7 +8,7 @@ import { cn } from "../theme/tw.js";
8
8
  import { imageSourceKey } from "../utils/image.js";
9
9
  import { useLayoutEffect, useRef, useState } from "react";
10
10
  import { View } from "react-native";
11
- function ImageContent({ source, width = "100%", height, aspectRatio, radius = 12, contentFit = "cover", placeholder, fallbackIcon = "image-outline", className, style, containerStyle, renderError, testID, onError, onLoadStart, onLoadEnd, onLoad, accessibilityLabel, accessible, retry, ...imageProps }) {
11
+ function ImageContent({ source, width = "100%", height, aspectRatio, radius = 12, contentFit = "cover", placeholder, fallbackIcon = "image", className, style, containerStyle, renderError, testID, onError, onLoadStart, onLoadEnd, onLoad, accessibilityLabel, accessible, retry, ...imageProps }) {
12
12
  const theme = useTheme();
13
13
  const [loading, setLoading] = useState(true);
14
14
  const [failed, setFailed] = useState(false);
@@ -6,6 +6,6 @@ import CopyableText from "./Display.CopyableText";
6
6
  function KeyValue({ label, value, align = "row", className, testID, copyable = false, selectable = false, }) {
7
7
  const { width, fontScale } = useWindowDimensions();
8
8
  const isRow = align === "row" || (align === "auto" && width >= 360 && fontScale <= 1.3);
9
- return (_jsxs(View, { className: cn(isRow ? "flex-row items-center justify-between gap-3" : "gap-0.5", "py-2", className), testID: testID, children: [_jsx(Typography.Text, { className: "text-text-secondary", variant: "caption", children: label }), typeof value === "string" || typeof value === "number" ? (_jsx(View, { className: cn("flex-shrink min-w-0", isRow && "flex-1 items-end"), children: copyable ? (_jsx(CopyableText, { accessibilityLabel: `Copiar ${label}`, selectable: selectable, value: String(value) })) : selectable ? (_jsx(Typography.SelectableText, { className: cn("text-text-primary", isRow && "text-right"), semibold: isRow, style: { alignSelf: "stretch" }, value: String(value) })) : (_jsx(Typography.Text, { className: cn("text-text-primary", isRow && "text-right"), selectable: selectable, semibold: isRow, children: value })) })) : (value)] }));
9
+ return (_jsxs(View, { className: cn(isRow ? "flex-row items-center justify-between gap-3" : "gap-0.5", "py-2", className), testID: testID, children: [_jsx(Typography.Text, { className: "min-w-0 shrink text-text-secondary", variant: "labelSmall", children: label }), typeof value === "string" || typeof value === "number" ? (_jsx(View, { className: cn("flex-shrink min-w-0", isRow && "flex-1 items-end"), children: copyable ? (_jsx(CopyableText, { accessibilityLabel: `Copiar ${label}`, selectable: selectable, value: String(value) })) : selectable ? (_jsx(Typography.SelectableText, { className: cn("text-text-primary", isRow && "text-right"), semibold: isRow, style: { alignSelf: "stretch" }, value: String(value) })) : (_jsx(Typography.Text, { className: cn("text-text-primary", isRow && "text-right"), selectable: selectable, semibold: isRow, children: value })) })) : (value)] }));
10
10
  }
11
11
  export default KeyValue;
@@ -14,7 +14,7 @@ function ListItem({ title, subtitle, leading, trailing, chevron = false, onPress
14
14
  (value !== undefined || trailing != null) &&
15
15
  (layout === "stacked" || (layout === "auto" && (fontScale > 1.3 || width < 360)));
16
16
  const right = trailing ??
17
- (value || chevron ? (_jsxs(View, { className: "flex-row items-center gap-2", children: [value ? (_jsx(Typography.Text, { className: "text-text-secondary flex-shrink", variant: comfortable ? "bodyReadable" : "body", ...valueTextProps, children: value })) : null, chevron ? (_jsx(View, { accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", children: _jsx(Icon, { color: theme.icon.tertiary, name: "chevron-forward", size: 18 }) })) : null] })) : null);
17
+ (value || chevron ? (_jsxs(View, { className: "flex-row items-center gap-2", children: [value ? (_jsx(Typography.Text, { className: "text-text-secondary flex-shrink", variant: comfortable ? "bodyReadable" : "body", ...valueTextProps, children: value })) : null, chevron ? (_jsx(View, { accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", children: _jsx(Icon, { color: theme.icon.tertiary, name: "next", size: 18 }) })) : null] })) : null);
18
18
  const content = (_jsxs(_Fragment, { children: [leading ? _jsx(View, { className: "mr-3 justify-center", children: leading }) : null, _jsxs(View, { className: cn("flex-1", mobile ? "min-w-0 gap-1" : "gap-0.5"), children: [_jsx(Typography.Text, { className: mobile && disabled ? "text-text-disabled" : "text-text-primary", semibold: !mobile || comfortable, variant: comfortable ? "bodyReadable" : "body", ...titleTextProps, children: title }), subtitle ? (_jsx(Typography.Text, { className: "text-text-secondary", variant: comfortable ? "bodyLarge" : "bodySmall", ...subtitleTextProps, children: subtitle })) : null, stacked && right ? _jsx(View, { className: "self-start mt-1", children: right }) : null] }), !stacked && right ? (_jsx(View, { className: cn("ml-3 justify-center", mobile && "flex-shrink"), style: mobile ? { maxWidth: "45%" } : undefined, children: right })) : null] }));
19
19
  const base = cn("flex-row items-center px-4", mobile && !comfortable ? "py-2" : "py-3", !mobile && "bg-bg-card", (showDivider ?? !mobile) && "border-b border-border-default", disabled && !mobile && "opacity-50", className);
20
20
  if (onPress) {
@@ -12,7 +12,7 @@ function BadgeChip({ badge }) {
12
12
  flexDirection: "row",
13
13
  alignItems: "center",
14
14
  gap: 4,
15
- backgroundColor: theme.successLight,
15
+ backgroundColor: theme.container.success,
16
16
  paddingHorizontal: 8,
17
17
  paddingVertical: 3,
18
18
  borderRadius: 999,
@@ -23,7 +23,7 @@ function BadgeChip({ badge }) {
23
23
  flexDirection: "row",
24
24
  alignItems: "center",
25
25
  gap: 4,
26
- backgroundColor: theme.warningLight,
26
+ backgroundColor: theme.container.warning,
27
27
  paddingHorizontal: 8,
28
28
  paddingVertical: 3,
29
29
  borderRadius: 999,
@@ -63,11 +63,11 @@ const menuButton = tv({
63
63
  label: "text-text-primary",
64
64
  },
65
65
  danger: {
66
- iconContainer: "bg-danger-light",
66
+ iconContainer: "bg-container-danger",
67
67
  label: "text-danger",
68
68
  },
69
69
  success: {
70
- iconContainer: "bg-success-light",
70
+ iconContainer: "bg-container-success",
71
71
  label: "text-success",
72
72
  },
73
73
  },
@@ -1,15 +1,10 @@
1
- import { IconType } from "../utils/icon.js";
2
- import { ComponentType } from "react";
1
+ import type { IconName } from "./index.js";
3
2
  export interface MenuCardProps {
4
3
  title: string;
5
- iconType?: IconType;
6
4
  onPress?: () => void;
7
- customIcon?: ComponentType<{
8
- size?: number;
9
- color?: string;
10
- }>;
5
+ icon: IconName;
11
6
  /** `"md"` = 2-column grid (default). `"sm"` = 3-column grid, smaller icon. */
12
7
  size?: "sm" | "md";
13
8
  }
14
- export declare function MenuCard({ title, iconType, onPress, customIcon, size, }: MenuCardProps): import("react").JSX.Element;
9
+ export declare function MenuCard({ title, onPress, icon, size }: MenuCardProps): import("react").JSX.Element;
15
10
  export default MenuCard;
@@ -3,7 +3,7 @@ import CircleButton from "./Display.CircleButton.js";
3
3
  import { useTheme } from "../theme/index.js";
4
4
  import { Typography } from "../Typography/index.js";
5
5
  import { TouchableOpacity, View } from "react-native";
6
- export function MenuCard({ title, iconType = "ionicons", onPress, customIcon, size = "md", }) {
6
+ export function MenuCard({ title, onPress, icon, size = "md" }) {
7
7
  const theme = useTheme();
8
8
  const isSmall = size === "sm";
9
9
  return (_jsx(TouchableOpacity, { activeOpacity: 0.7, onPress: onPress, style: {
@@ -15,7 +15,7 @@ export function MenuCard({ title, iconType = "ionicons", onPress, customIcon, si
15
15
  borderWidth: 1,
16
16
  borderColor: theme.border.default,
17
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: {
18
+ }, children: _jsxs(View, { style: { alignItems: "center", justifyContent: "center" }, children: [_jsx(CircleButton, { icon: icon, size: isSmall ? "sm" : "md" }), _jsx(Typography.Text, { numberOfLines: 2, style: {
19
19
  color: theme.text.primary,
20
20
  marginTop: isSmall ? 8 : 12,
21
21
  textAlign: "center",
@@ -1,14 +1,9 @@
1
- import { IconType } from "../utils/icon.js";
2
- import { ComponentType } from "react";
1
+ import type { IconName } from "./index.js";
3
2
  export interface MenuGridItem {
4
3
  id: string;
5
4
  title: string;
6
- iconType?: IconType;
7
5
  onPress?: () => void;
8
- customIcon?: ComponentType<{
9
- size?: number;
10
- color?: string;
11
- }>;
6
+ icon: IconName;
12
7
  }
13
8
  export interface MenuGridProps {
14
9
  items: MenuGridItem[];
@@ -2,6 +2,6 @@ import { jsx as _jsx } from "nativewind/jsx-runtime";
2
2
  import MenuCard from "./Display.MenuCard.js";
3
3
  import { View } from "react-native";
4
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))) }));
5
+ return (_jsx(View, { className: "flex-row flex-wrap justify-start p-2 bg-bg-page", children: items.map((item) => (_jsx(MenuCard, { icon: item.icon, onPress: item.onPress, size: size, title: item.title }, item.id))) }));
6
6
  }
7
7
  export default MenuGrid;
@@ -4,16 +4,12 @@ interface MonetaryFieldProps {
4
4
  value: number | string | null | undefined;
5
5
  currencySymbol?: string;
6
6
  prefix?: string;
7
- /** @deprecated Use className="text-{color}" instead */
8
- color?: string;
9
7
  textVariant?: keyof typeof typographyVariants;
10
- /** @deprecated Use className for styling */
11
8
  style?: StyleProp<TextStyle>;
12
- /** @deprecated Use className="text-{color}" instead */
13
- symbolColor?: string;
9
+ symbolStyle?: StyleProp<TextStyle>;
14
10
  semibold?: boolean;
15
11
  bold?: boolean;
16
12
  className?: string;
17
13
  }
18
- declare function MonetaryField({ value, currencySymbol, prefix, color, textVariant, style, symbolColor, semibold, bold, className, }: MonetaryFieldProps): import("react").JSX.Element;
14
+ declare function MonetaryField({ value, currencySymbol, prefix, textVariant, style, symbolStyle, semibold, bold, className, }: MonetaryFieldProps): import("react").JSX.Element;
19
15
  export default MonetaryField;
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
2
  import { Typography } from "../Typography/index.js";
3
3
  import { formatTwoDecimals } from "../utils/monetary.js";
4
- function MonetaryField({ value, currencySymbol = "₡", prefix = "", color, textVariant = "data", style = {}, symbolColor, semibold = true, bold = false, className, }) {
4
+ function MonetaryField({ value, currencySymbol = "₡", prefix = "", textVariant = "data", style = {}, symbolStyle, semibold = true, bold = false, className, }) {
5
5
  const isEmptyValue = value === null || value === undefined || value === "";
6
6
  const formatted = isEmptyValue ? "0.00" : formatTwoDecimals(value);
7
- return (_jsxs(Typography.Text, { bold: bold, className: className, color: color, semibold: semibold && !bold, style: style, variant: textVariant, children: [_jsx(Typography.Text, { className: className, color: symbolColor || color, variant: textVariant, children: currencySymbol }), " ", prefix && `${prefix} `, formatted] }));
7
+ return (_jsxs(Typography.Text, { bold: bold, className: className, semibold: semibold && !bold, style: style, variant: textVariant, children: [_jsx(Typography.Text, { className: className, style: [style, symbolStyle], variant: textVariant, children: currencySymbol }), " ", prefix && `${prefix} `, formatted] }));
8
8
  }
9
9
  export default MonetaryField;
@@ -18,6 +18,6 @@ function PriceTag({ amount, originalAmount, currency = "Bs", size = "md", classN
18
18
  const variants = sizeMap[size];
19
19
  const hasDiscount = typeof originalAmount === "number" && originalAmount > amount;
20
20
  const off = hasDiscount ? Math.round((1 - amount / originalAmount) * 100) : 0;
21
- return (_jsxs(View, { className: cn("flex-row items-baseline gap-2", className), testID: testID, children: [_jsx(Typography.Text, { className: "text-text-primary", semibold: true, variant: variants.price, children: format(amount, currency) }), hasDiscount ? (_jsxs(_Fragment, { children: [_jsx(Typography.Text, { className: "text-text-tertiary", style: { textDecorationLine: "line-through" }, variant: variants.original, children: format(originalAmount, currency) }), _jsx(View, { className: "bg-danger-light rounded px-1.5 py-0.5", children: _jsxs(Typography.Text, { className: "text-danger font-semibold", variant: "caption", children: ["-", off, "%"] }) })] })) : null] }));
21
+ return (_jsxs(View, { className: cn("flex-row items-baseline gap-2", className), testID: testID, children: [_jsx(Typography.Text, { className: "text-text-primary", semibold: true, variant: variants.price, children: format(amount, currency) }), hasDiscount ? (_jsxs(_Fragment, { children: [_jsx(Typography.Text, { className: "text-text-tertiary", style: { textDecorationLine: "line-through" }, variant: variants.original, children: format(originalAmount, currency) }), _jsx(View, { className: "bg-container-danger rounded px-1.5 py-0.5", children: _jsxs(Typography.Text, { className: "text-danger font-semibold", variant: "caption", children: ["-", off, "%"] }) })] })) : null] }));
22
22
  }
23
23
  export default PriceTag;
@@ -7,7 +7,7 @@ function Rating({ value, max = 5, size = 24, onChange, readOnly = false, classNa
7
7
  const interactive = !readOnly && !!onChange;
8
8
  return (_jsx(View, { accessibilityLabel: `Calificación ${value} de ${max}`, accessibilityRole: interactive ? "adjustable" : "image", className: className, style: { flexDirection: "row", gap: 4 }, testID: testID, children: Array.from({ length: max }).map((_, index) => {
9
9
  const filled = index < Math.round(value);
10
- const star = (_jsx(Icon, { color: filled ? theme.warning : theme.icon.disabled, name: filled ? "star" : "star-outline", size: size }));
10
+ const star = (_jsx(Icon, { color: filled ? theme.warning : theme.icon.disabled, name: filled ? "rating" : "rating-empty", size: size }));
11
11
  if (!interactive)
12
12
  return _jsx(View, { children: star }, index);
13
13
  return (_jsx(Pressable, { accessibilityLabel: `${index + 1} estrellas`, hitSlop: 6, onPress: () => onChange?.(index + 1), children: star }, index));
@@ -1,10 +1,11 @@
1
+ import type { IconName } from "./index.js";
1
2
  export interface SearchHistoryProps {
2
3
  items: string[];
3
4
  onSelect: (value: string) => void;
4
5
  onRemove: (value: string) => void;
5
6
  onClearAll?: () => void;
6
7
  title?: string;
7
- icon?: string;
8
+ icon?: IconName;
8
9
  className?: string;
9
10
  testID?: string;
10
11
  }
@@ -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 SearchHistory({ items, onSelect, onRemove, onClearAll, title = "Búsquedas recientes", icon = "time-outline", className, testID, }) {
8
+ function SearchHistory({ items, onSelect, onRemove, onClearAll, title = "Búsquedas recientes", icon = "time", className, testID, }) {
9
9
  if (items.length === 0)
10
10
  return null;
11
11
  return (_jsxs(View, { className: cn("gap-1", className), testID: testID, children: [_jsxs(View, { className: "flex-row items-center justify-between px-4 py-1", children: [_jsx(Typography.Text, { className: "text-text-secondary", semibold: true, variant: "bodySmall", children: title }), onClearAll ? (_jsx(Pressable, { accessibilityLabel: "Borrar todo", hitSlop: 8, onPress: onClearAll, children: _jsx(Typography.Text, { className: "text-text-accent", variant: "bodySmall", children: "Borrar todo" }) })) : null] }), items.map((value) => (_jsxs(Pressable, { accessibilityLabel: value, className: "flex-row items-center gap-3 px-4 py-2.5", onPress: () => onSelect(value), children: [_jsx(Icon, { color: theme.icon.tertiary, name: icon, size: 18 }), _jsx(Typography.Text, { className: "flex-1 text-text-primary", numberOfLines: 1, children: value }), _jsx(Pressable, { accessibilityLabel: `Quitar ${value}`, hitSlop: 10, onPress: () => onRemove(value), children: _jsx(Icon, { color: theme.icon.tertiary, name: "close", size: 16 }) })] }, value)))] }));
@@ -1,8 +1,9 @@
1
+ import type { IconName } from "./index.js";
1
2
  import type { ReactNode } from "react";
2
3
  export interface StatCardProps {
3
4
  label: string;
4
5
  value: string | number;
5
- icon?: string;
6
+ icon?: IconName;
6
7
  delta?: number;
7
8
  deltaSuffix?: string;
8
9
  hint?: string;
@@ -8,6 +8,6 @@ function StatCard({ label, value, icon, delta, deltaSuffix = "%", hint, trailing
8
8
  const hasDelta = typeof delta === "number";
9
9
  const up = hasDelta && delta >= 0;
10
10
  const deltaColor = up ? theme.success : theme.danger;
11
- return (_jsxs(View, { className: cn("bg-bg-card rounded-lg border border-border-light p-4 gap-1", className), style: theme.shadow.sm, testID: testID, children: [_jsxs(View, { className: "flex-row items-center justify-between", children: [_jsx(Typography.Text, { className: "text-text-secondary", variant: "caption", children: label }), icon ? _jsx(Icon, { color: theme.icon.secondary, name: icon, size: 18 }) : null, trailing] }), _jsx(Typography.Text, { className: "text-text-primary", semibold: true, variant: "h3", children: value }), hasDelta || hint ? (_jsxs(View, { className: "flex-row items-center gap-1", children: [hasDelta ? (_jsxs(_Fragment, { children: [_jsx(Icon, { color: deltaColor, name: up ? "arrow-up" : "arrow-down", size: 14 }), _jsxs(Typography.Text, { style: { color: deltaColor }, variant: "caption", children: [Math.abs(delta), deltaSuffix] })] })) : null, hint ? (_jsx(Typography.Text, { className: "text-text-tertiary", variant: "caption", children: hint })) : null] })) : null] }));
11
+ return (_jsxs(View, { className: cn("bg-bg-card rounded-lg border border-border-light p-4 gap-1", className), style: theme.shadow.sm, testID: testID, children: [_jsxs(View, { className: "flex-row items-center justify-between", children: [_jsx(Typography.Text, { className: "text-text-secondary", variant: "caption", children: label }), icon ? _jsx(Icon, { color: theme.icon.secondary, name: icon, size: 18 }) : null, trailing] }), _jsx(Typography.Text, { className: "text-text-primary", semibold: true, variant: "h3", children: value }), hasDelta || hint ? (_jsxs(View, { className: "flex-row items-center gap-1", children: [hasDelta ? (_jsxs(_Fragment, { children: [_jsx(Icon, { color: deltaColor, name: up ? "up" : "down", size: 14 }), _jsxs(Typography.Text, { style: { color: deltaColor }, variant: "caption", children: [Math.abs(delta), deltaSuffix] })] })) : null, hint ? (_jsx(Typography.Text, { className: "text-text-tertiary", variant: "caption", children: hint })) : null] })) : null] }));
12
12
  }
13
13
  export default StatCard;
@@ -2,14 +2,16 @@ import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
2
  import ExpoImage from "./Display.ExpoImage.js";
3
3
  import Icon from "./Display.Icon.js";
4
4
  import Pressable from "../primitives/Pressable.js";
5
- import { theme } from "../theme/index.js";
5
+ import { useTheme } from "../theme/index.js";
6
6
  import { Typography } from "../Typography/index.js";
7
7
  import { useReducedMotion } from "../utils/useReducedMotion.js";
8
8
  import { LinearGradient } from "expo-linear-gradient";
9
9
  import { Modal, Pressable as RNPressable, View } from "react-native";
10
+ import { KeyboardStickyView } from "react-native-keyboard-controller";
10
11
  import { useSafeAreaInsets } from "react-native-safe-area-context";
11
12
  function StoryViewer({ visible, slides, index, onIndexChange, onClose, title, subtitle, time, avatar, progress = 1, muted, paused, onToggleMute, onTogglePause, onMore, footer, testID, }) {
12
13
  const insets = useSafeAreaInsets();
14
+ const theme = useTheme();
13
15
  const reducedMotion = useReducedMotion();
14
16
  const current = slides[index];
15
17
  const goPrev = () => {
@@ -45,7 +47,8 @@ function StoryViewer({ visible, slides, index, onIndexChange, onClose, title, su
45
47
  left: 0,
46
48
  right: 0,
47
49
  paddingTop: insets.top + 8,
48
- paddingHorizontal: 10,
50
+ paddingLeft: insets.left + 10,
51
+ paddingRight: insets.right + 10,
49
52
  }, children: [_jsx(View, { style: { flexDirection: "row", gap: 4 }, children: slides.map((_, i) => (_jsx(View, { style: {
50
53
  flex: 1,
51
54
  height: 2.5,
@@ -60,9 +63,9 @@ function StoryViewer({ visible, slides, index, onIndexChange, onClose, title, su
60
63
  ? `${Math.round(progress * 100)}%`
61
64
  : "0%",
62
65
  backgroundColor: "#fff",
63
- } }) }, i))) }), _jsxs(View, { style: { flexDirection: "row", alignItems: "center", marginTop: 12 }, children: [avatar ? (_jsx(ExpoImage, { source: avatar, style: { width: 36, height: 36, borderRadius: 18, marginRight: 8 } })) : null, _jsxs(View, { style: { flex: 1 }, children: [_jsxs(View, { style: { flexDirection: "row", alignItems: "center", gap: 6 }, children: [_jsx(Typography.Text, { className: "text-white", semibold: true, variant: "body", children: title }), time ? (_jsx(Typography.Text, { style: { color: "rgba(255,255,255,0.7)" }, variant: "caption", children: time })) : null] }), subtitle ? (_jsx(Typography.Text, { numberOfLines: 1, style: { color: "rgba(255,255,255,0.8)" }, variant: "caption", children: subtitle })) : null] }), onToggleMute ? (_jsx(Pressable, { accessibilityLabel: muted ? "Activar sonido" : "Silenciar", hitSlop: 8, onPress: onToggleMute, children: _jsx(Icon, { color: "#fff", name: muted ? "volume-mute" : "volume-high", size: 22 }) })) : null, onTogglePause ? (_jsx(Pressable, { accessibilityLabel: paused ? "Reanudar" : "Pausar", hitSlop: 8, onPress: onTogglePause, style: { marginLeft: 14 }, children: _jsx(Icon, { color: "#fff", name: paused ? "play" : "pause", size: 22 }) })) : null, onMore ? (_jsx(Pressable, { accessibilityLabel: "M\u00E1s opciones", hitSlop: 8, onPress: onMore, style: { marginLeft: 14 }, children: _jsx(Icon, { color: "#fff", name: "ellipsis-horizontal", size: 22 }) })) : null, _jsx(Pressable, { accessibilityLabel: "Cerrar", hitSlop: 8, onPress: onClose, style: { marginLeft: 14 }, children: _jsx(Icon, { color: "#fff", name: "close", size: 24 }) })] })] }), index < slides.length - 1 ? (_jsx(Pressable, { accessibilityLabel: "Siguiente", onPress: goNext, style: {
66
+ } }) }, i))) }), _jsxs(View, { style: { flexDirection: "row", alignItems: "center", marginTop: 12 }, children: [avatar ? (_jsx(ExpoImage, { source: avatar, style: { width: 36, height: 36, borderRadius: 18, marginRight: 8 } })) : null, _jsxs(View, { style: { flex: 1 }, children: [_jsxs(View, { style: { flexDirection: "row", alignItems: "center", gap: 6 }, children: [_jsx(Typography.Text, { className: "text-white", semibold: true, variant: "body", children: title }), time ? (_jsx(Typography.Text, { style: { color: "rgba(255,255,255,0.7)" }, variant: "caption", children: time })) : null] }), subtitle ? (_jsx(Typography.Text, { numberOfLines: 1, style: { color: "rgba(255,255,255,0.8)" }, variant: "caption", children: subtitle })) : null] }), onToggleMute ? (_jsx(Pressable, { accessibilityLabel: muted ? "Activar sonido" : "Silenciar", hitSlop: 8, onPress: onToggleMute, children: _jsx(Icon, { color: "#fff", name: muted ? "mute" : "volume", size: 22 }) })) : null, onTogglePause ? (_jsx(Pressable, { accessibilityLabel: paused ? "Reanudar" : "Pausar", hitSlop: 8, onPress: onTogglePause, style: { marginLeft: 14 }, children: _jsx(Icon, { color: "#fff", name: paused ? "play" : "pause", size: 22 }) })) : null, onMore ? (_jsx(Pressable, { accessibilityLabel: "M\u00E1s opciones", hitSlop: 8, onPress: onMore, style: { marginLeft: 14 }, children: _jsx(Icon, { color: "#fff", name: "more", size: 22 }) })) : null, _jsx(Pressable, { accessibilityLabel: "Cerrar", hitSlop: 8, onPress: onClose, style: { marginLeft: 14 }, children: _jsx(Icon, { color: "#fff", name: "close", size: 24 }) })] })] }), index < slides.length - 1 ? (_jsx(Pressable, { accessibilityLabel: "Siguiente", onPress: goNext, style: {
64
67
  position: "absolute",
65
- right: 8,
68
+ right: insets.right + 8,
66
69
  top: "50%",
67
70
  width: 36,
68
71
  height: 36,
@@ -70,13 +73,14 @@ function StoryViewer({ visible, slides, index, onIndexChange, onClose, title, su
70
73
  backgroundColor: "rgba(255,255,255,0.9)",
71
74
  alignItems: "center",
72
75
  justifyContent: "center",
73
- }, children: _jsx(Icon, { color: theme.text.primary, name: "chevron-forward", size: 22 }) })) : null, footer ? (_jsx(View, { style: {
76
+ }, children: _jsx(Icon, { color: theme.text.primary, name: "next", size: 22 }) })) : null, footer ? (_jsx(KeyboardStickyView, { style: {
74
77
  position: "absolute",
75
78
  left: 0,
76
79
  right: 0,
77
80
  bottom: 0,
78
81
  paddingBottom: insets.bottom + 8,
79
- paddingHorizontal: 16,
82
+ paddingLeft: insets.left + 16,
83
+ paddingRight: insets.right + 16,
80
84
  }, children: footer })) : null] }) }));
81
85
  }
82
86
  export default StoryViewer;
@@ -1,7 +1,8 @@
1
+ import type { IconName } from "./index.js";
1
2
  import { type ReactNode } from "react";
2
3
  export interface SwipeRowAction {
3
4
  label?: string;
4
- icon?: string;
5
+ icon?: IconName;
5
6
  color?: string;
6
7
  onPress: () => void;
7
8
  }