@sincpro/mobile-ui 2.0.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/README.md +7 -2
  2. package/dist/Actions/Actions.Button.d.ts +2 -1
  3. package/dist/Actions/Actions.Button.js +31 -37
  4. package/dist/Actions/Actions.Dock.d.ts +6 -6
  5. package/dist/Actions/Actions.Dock.js +14 -72
  6. package/dist/Actions/Actions.FAB.d.ts +2 -1
  7. package/dist/Actions/Actions.FilterButton.js +1 -1
  8. package/dist/Actions/Actions.IconButton.d.ts +3 -4
  9. package/dist/Actions/Actions.IconButton.js +2 -2
  10. package/dist/Dialog/Confirmation.context.d.ts +5 -14
  11. package/dist/Dialog/Confirmation.context.js +11 -51
  12. package/dist/Dialog/Dialog.ActionSheet.d.ts +8 -7
  13. package/dist/Dialog/Dialog.ActionSheet.js +45 -19
  14. package/dist/Dialog/Dialog.Confirmation.d.ts +14 -11
  15. package/dist/Dialog/Dialog.Confirmation.js +38 -14
  16. package/dist/Dialog/Dialog.EditValue.d.ts +21 -16
  17. package/dist/Dialog/Dialog.EditValue.js +73 -14
  18. package/dist/Dialog/Dialog.Sheet.d.ts +8 -24
  19. package/dist/Dialog/Dialog.Sheet.js +15 -60
  20. package/dist/Dialog/index.d.ts +14 -14
  21. package/dist/Dialog/index.js +5 -3
  22. package/dist/Dialog/requestConfirmation.d.ts +3 -0
  23. package/dist/Dialog/requestConfirmation.js +13 -0
  24. package/dist/Display/Display.Accordion.js +1 -1
  25. package/dist/Display/Display.Avatar.d.ts +6 -6
  26. package/dist/Display/Display.Avatar.js +2 -2
  27. package/dist/Display/Display.Badge.js +4 -4
  28. package/dist/Display/Display.Card.js +5 -2
  29. package/dist/Display/Display.Carousel.js +1 -1
  30. package/dist/Display/Display.CircleButton.d.ts +3 -8
  31. package/dist/Display/Display.CircleButton.js +2 -2
  32. package/dist/Display/Display.CopyableText.js +1 -5
  33. package/dist/Display/Display.Date.d.ts +3 -2
  34. package/dist/Display/Display.Date.js +3 -3
  35. package/dist/Display/Display.GradientSurface.d.ts +52 -9
  36. package/dist/Display/Display.GradientSurface.js +9 -14
  37. package/dist/Display/Display.Icon.d.ts +7 -11
  38. package/dist/Display/Display.Icon.js +15 -30
  39. package/dist/Display/Display.Image.d.ts +2 -1
  40. package/dist/Display/Display.Image.js +1 -1
  41. package/dist/Display/Display.KeyValue.js +1 -1
  42. package/dist/Display/Display.ListItem.js +1 -1
  43. package/dist/Display/Display.MenuButton.js +4 -4
  44. package/dist/Display/Display.MenuCard.d.ts +3 -8
  45. package/dist/Display/Display.MenuCard.js +2 -2
  46. package/dist/Display/Display.MenuGrid.d.ts +2 -7
  47. package/dist/Display/Display.MenuGrid.js +1 -1
  48. package/dist/Display/Display.Monetary.d.ts +2 -6
  49. package/dist/Display/Display.Monetary.js +2 -2
  50. package/dist/Display/Display.PriceTag.js +1 -1
  51. package/dist/Display/Display.Rating.js +1 -1
  52. package/dist/Display/Display.SearchHistory.d.ts +2 -1
  53. package/dist/Display/Display.SearchHistory.js +1 -1
  54. package/dist/Display/Display.StatCard.d.ts +2 -1
  55. package/dist/Display/Display.StatCard.js +1 -1
  56. package/dist/Display/Display.StoryViewer.js +10 -6
  57. package/dist/Display/Display.SwipeableRow.d.ts +2 -1
  58. package/dist/Display/Display.Timeline.js +1 -1
  59. package/dist/Display/icon-map.d.ts +344 -0
  60. package/dist/Display/icon-map.js +86 -0
  61. package/dist/Display/index.d.ts +2 -0
  62. package/dist/Display/index.js +1 -0
  63. package/dist/Feedback/Feedback.Banner.js +13 -12
  64. package/dist/Feedback/Feedback.Empty.js +1 -1
  65. package/dist/Feedback/Feedback.EmptyState.d.ts +2 -1
  66. package/dist/Feedback/Feedback.EmptyState.js +1 -1
  67. package/dist/Feedback/Feedback.Error.js +1 -1
  68. package/dist/Feedback/Feedback.PermissionCard.d.ts +2 -1
  69. package/dist/Feedback/Feedback.PermissionCard.js +2 -2
  70. package/dist/Feedback/Toast.context.js +3 -3
  71. package/dist/Form/Form.ControlledDropdown.d.ts +5 -11
  72. package/dist/Form/Form.ControlledDropdown.js +5 -2
  73. package/dist/Form/Form.DatePicker.d.ts +5 -9
  74. package/dist/Form/Form.DatePicker.js +116 -21
  75. package/dist/Form/Form.Dropdown.d.ts +11 -16
  76. package/dist/Form/Form.Dropdown.js +54 -90
  77. package/dist/Form/Form.FractionalQuantityInput.js +4 -11
  78. package/dist/Form/Form.Input.d.ts +3 -3
  79. package/dist/Form/Form.KeyboardForm.d.ts +2 -2
  80. package/dist/Form/Form.KeyboardForm.js +2 -2
  81. package/dist/Form/Form.KeyboardToolbar.d.ts +2 -1
  82. package/dist/Form/Form.KeyboardToolbar.js +3 -7
  83. package/dist/Form/Form.PasswordInput.js +1 -1
  84. package/dist/Form/Form.QuantitySelector.js +1 -1
  85. package/dist/Form/Form.Select.d.ts +12 -8
  86. package/dist/Form/Form.Select.js +64 -31
  87. package/dist/Form/Form.Slider.d.ts +4 -7
  88. package/dist/Form/Form.Slider.js +11 -10
  89. package/dist/Form/Form.Switch.js +2 -1
  90. package/dist/Form/index.d.ts +6 -14
  91. package/dist/Form/index.js +0 -11
  92. package/dist/Navigation/Navigation.AppBar.d.ts +2 -1
  93. package/dist/Navigation/Navigation.AppBar.js +2 -2
  94. package/dist/Navigation/Navigation.BottomNav.d.ts +4 -8
  95. package/dist/Navigation/Navigation.BottomNav.js +2 -2
  96. package/dist/Navigation/Navigation.CollapsingHeader.js +3 -2
  97. package/dist/Navigation/Navigation.Menu.d.ts +16 -11
  98. package/dist/Navigation/Navigation.Menu.js +97 -42
  99. package/dist/Navigation/Navigation.Search.js +2 -2
  100. package/dist/Navigation/Navigation.SearchBar.js +3 -4
  101. package/dist/Navigation/Navigation.SegmentedControl.d.ts +7 -25
  102. package/dist/Navigation/Navigation.SegmentedControl.js +18 -39
  103. package/dist/Navigation/Navigation.Steps.js +1 -1
  104. package/dist/Navigation/Navigation.Tabs.d.ts +2 -1
  105. package/dist/Navigation/index.d.ts +0 -6
  106. package/dist/Navigation/index.js +0 -8
  107. package/dist/Typography/Typography.SelectableText.js +3 -7
  108. package/dist/Typography/Typography.Text.d.ts +1 -6
  109. package/dist/Typography/Typography.Text.js +3 -6
  110. package/dist/index.d.ts +1 -4
  111. package/dist/index.js +1 -4
  112. package/dist/layouts/AuthContainer.d.ts +4 -2
  113. package/dist/layouts/AuthContainer.js +3 -4
  114. package/dist/layouts/Container.d.ts +0 -4
  115. package/dist/layouts/Container.js +3 -4
  116. package/dist/layouts/ScrollContainer.d.ts +7 -3
  117. package/dist/layouts/ScrollContainer.js +5 -4
  118. package/dist/primitives/BaseButton.d.ts +2 -1
  119. package/dist/theme/build.js +0 -4
  120. package/dist/theme/css-vars.js +0 -4
  121. package/dist/theme/dark.js +2 -6
  122. package/dist/theme/default.js +1 -5
  123. package/dist/theme/index.d.ts +1 -1
  124. package/dist/theme/index.js +1 -1
  125. package/dist/theme/types.d.ts +0 -11
  126. package/dist/theme/typography.d.ts +8 -7
  127. package/dist/theme/typography.js +25 -4
  128. package/dist/utils/timezone.d.ts +0 -5
  129. package/dist/views/AuthFormView.d.ts +3 -1
  130. package/dist/views/AuthFormView.js +2 -2
  131. package/dist/views/FormViewV2.d.ts +6 -13
  132. package/dist/views/FormViewV2.js +29 -25
  133. package/dist/views/ListViewV2.context.d.ts +5 -5
  134. package/dist/views/ListViewV2.d.ts +23 -18
  135. package/dist/views/ListViewV2.js +45 -48
  136. package/dist/views/types/IListView.d.ts +5 -4
  137. package/dist/views/types/IListView.js +18 -15
  138. package/dist/widgets/CollectionOptions.d.ts +2 -2
  139. package/dist/widgets/CollectionOptions.js +92 -65
  140. package/dist/widgets/FilterBar.js +1 -1
  141. package/dist/widgets/PromoModal.js +10 -4
  142. package/dist/widgets/QuickActions.d.ts +2 -7
  143. package/dist/widgets/QuickActions.js +1 -1
  144. package/dist/widgets/ReorderableList.js +3 -3
  145. package/dist/widgets/TimeZoneSelectorModal.d.ts +1 -6
  146. package/dist/widgets/TimeZoneSelectorModal.js +17 -12
  147. package/dist/widgets/index.d.ts +0 -11
  148. package/dist/widgets/index.js +0 -7
  149. package/package.json +35 -38
  150. package/tailwind.preset.js +0 -4
  151. package/dist/Dialog/BottomSheet/BottomSheet.Root.d.ts +0 -324
  152. package/dist/Dialog/BottomSheet/BottomSheet.Root.js +0 -373
  153. package/dist/Dialog/BottomSheet/index.d.ts +0 -1
  154. package/dist/Dialog/BottomSheet/index.js +0 -1
  155. package/dist/Dialog/Dialog.NativeSheet.d.ts +0 -10
  156. package/dist/Dialog/Dialog.NativeSheet.js +0 -21
  157. package/dist/Form/Form.Button.d.ts +0 -3
  158. package/dist/Form/Form.Button.js +0 -1
  159. package/dist/Form/Form.FAB.d.ts +0 -3
  160. package/dist/Form/Form.FAB.js +0 -1
  161. package/dist/Form/Form.FilterButton.d.ts +0 -3
  162. package/dist/Form/Form.FilterButton.js +0 -1
  163. package/dist/Form/Form.IconButton.d.ts +0 -3
  164. package/dist/Form/Form.IconButton.js +0 -1
  165. package/dist/Form/Form.SearchBar.d.ts +0 -13
  166. package/dist/Form/Form.SearchBar.js +0 -26
  167. package/dist/Navigation/Navigation.Header.d.ts +0 -16
  168. package/dist/Navigation/Navigation.Header.js +0 -17
  169. package/dist/Navigation/Navigation.HeaderToolbar.d.ts +0 -15
  170. package/dist/Navigation/Navigation.HeaderToolbar.js +0 -18
  171. package/dist/Navigation/Navigation.TabIcon.d.ts +0 -18
  172. package/dist/Navigation/Navigation.TabIcon.js +0 -12
  173. package/dist/Navigation/Navigation.TabItem.d.ts +0 -19
  174. package/dist/Navigation/Navigation.TabItem.js +0 -13
  175. package/dist/icons/OdooIcon.d.ts +0 -11
  176. package/dist/icons/OdooIcon.js +0 -11
  177. package/dist/icons/PrinterIcon.d.ts +0 -9
  178. package/dist/icons/PrinterIcon.js +0 -9
  179. package/dist/layouts/TabNavigatorLayout.d.ts +0 -26
  180. package/dist/layouts/TabNavigatorLayout.js +0 -60
  181. package/dist/utils/icon.d.ts +0 -1
  182. package/dist/utils/icon.js +0 -1
  183. package/dist/widgets/CircleButton.d.ts +0 -3
  184. package/dist/widgets/CircleButton.js +0 -1
  185. package/dist/widgets/GeoPermissionCard.d.ts +0 -13
  186. package/dist/widgets/GeoPermissionCard.js +0 -24
  187. package/dist/widgets/HomeHeader.d.ts +0 -15
  188. package/dist/widgets/HomeHeader.js +0 -13
  189. package/dist/widgets/MenuCard.d.ts +0 -3
  190. package/dist/widgets/MenuCard.js +0 -1
  191. package/dist/widgets/MenuGrid.d.ts +0 -5
  192. package/dist/widgets/MenuGrid.js +0 -1
  193. package/dist/widgets/MenuSection.d.ts +0 -3
  194. package/dist/widgets/MenuSection.js +0 -1
  195. package/dist/widgets/ScreenHeader.d.ts +0 -25
  196. package/dist/widgets/ScreenHeader.js +0 -32
  197. package/dist/widgets/TimeZoneSelector.d.ts +0 -12
  198. package/dist/widgets/TimeZoneSelector.js +0 -24
package/README.md CHANGED
@@ -11,11 +11,16 @@ This package is maintained by Sincpro as part of the mobile framework ecosystem.
11
11
  ## Quick Start
12
12
 
13
13
  ```bash
14
- npx expo install @sincpro/mobile-ui expo-image
14
+ npx expo install @sincpro/mobile-ui @expo/ui expo-symbols @shopify/flash-list expo-image react-native-keyboard-controller
15
15
  ```
16
16
 
17
17
  Wire the Tailwind preset, then import by category:
18
18
 
19
+ The SDK 58 major uses one native sheet engine, semantic SF/Material icons and FlashList
20
+ v2. Native dependencies are required peers, not implied by installing Expo.
21
+ See [migration contracts](docs/MIGRATIONS.md) before upgrading. NativeWind remains on
22
+ v4 until a v5 release explicitly supports SDK 58.
23
+
19
24
  ```js
20
25
  // tailwind.config.js
21
26
  module.exports = {
@@ -97,7 +102,7 @@ recoverable error. It uses fictional data and local state, not persistence or a
97
102
  the Expo renderer, disk caching, automatic source recycling and Reduce Motion policy.
98
103
  `Display.ExpoImage` exposes the direct renderer; `Display.Image` adds error/retry composition.
99
104
  See the [breaking migration guide](docs/MIGRATION-V2.md).
100
- - `ListViewV2.Content` exposes `listProps` for virtualization, `getItemLayout`, viewability
105
+ - `ListViewV2.Content` exposes FlashList v2 `listProps` for recycling and viewability
101
106
  and `extraData`, plus `listRef` and content styling. `Root hasMore={false}` stops
102
107
  pagination; `loadingMore` and refresh pause it. See the
103
108
  [mobile performance and usability plan](docs/MOBILE-QUALITY.md).
@@ -1,3 +1,4 @@
1
+ import type { IconName } from "../Display/index.js";
1
2
  import { type VariantProps } from "../theme/tw.js";
2
3
  import type React from "react";
3
4
  import type { AccessibilityProps } from "react-native";
@@ -140,7 +141,7 @@ export interface ButtonProps extends VariantProps<typeof button>, AccessibilityP
140
141
  testID?: string;
141
142
  }
142
143
  export interface ButtonIconProps extends VariantProps<typeof buttonIcon>, AccessibilityProps {
143
- icon: string;
144
+ icon: IconName;
144
145
  onPress?: () => void;
145
146
  confirm?: string;
146
147
  loading?: boolean;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
- import { useConfirmationContext } from "../Dialog/Confirmation.context.js";
2
+ import { requestConfirmation } from "../Dialog/requestConfirmation.js";
3
3
  import Icon from "../Display/Display.Icon.js";
4
- import { theme } from "../theme/index.js";
4
+ import { useTheme } from "../theme/index.js";
5
5
  import { tv } from "../theme/tw.js";
6
6
  import { Typography } from "../Typography/index.js";
7
7
  import { useCallback } from "react";
@@ -100,7 +100,7 @@ const textColor = tv({
100
100
  defaultVariants: { variant: "primary", disabled: false },
101
101
  });
102
102
  // ─── Color / size helpers ─────────────────────────────────────────────────────
103
- function getLoadingColor(variant) {
103
+ function getLoadingColor(variant, theme) {
104
104
  const map = {
105
105
  primary: theme.text.onAccent,
106
106
  accent: theme.text.accent,
@@ -113,9 +113,9 @@ function getLoadingColor(variant) {
113
113
  link: theme.text.accent,
114
114
  transparent: theme.text.inverse,
115
115
  };
116
- return map[variant] ?? theme.text.inverse;
116
+ return map[variant];
117
117
  }
118
- function getIconColor(variant, disabled) {
118
+ function getIconColor(variant, disabled, theme) {
119
119
  if (disabled) {
120
120
  const map = {
121
121
  primary: theme.text.onAccent + "70",
@@ -129,7 +129,7 @@ function getIconColor(variant, disabled) {
129
129
  link: theme.icon.disabled,
130
130
  transparent: theme.icon.inverse + "70",
131
131
  };
132
- return map[variant] ?? theme.icon.disabled;
132
+ return map[variant];
133
133
  }
134
134
  const map = {
135
135
  primary: theme.text.onAccent,
@@ -143,7 +143,7 @@ function getIconColor(variant, disabled) {
143
143
  link: theme.text.accent,
144
144
  transparent: theme.icon.inverse,
145
145
  };
146
- return map[variant] ?? theme.icon.inverse;
146
+ return map[variant];
147
147
  }
148
148
  const iconSizeMap = { small: 16, medium: 20, large: 28 };
149
149
  const textVariantMap = {
@@ -152,28 +152,25 @@ const textVariantMap = {
152
152
  large: "buttonLarge",
153
153
  };
154
154
  const Button = ({ title, onPress, confirm, variant = "primary", size = "medium", loading = false, disabled = false, className, icon, testID, accessibilityLabel, accessibilityState, ...accessibilityProps }) => {
155
- const { show, hide } = useConfirmationContext();
156
- const textColorClass = textColor({ variant: variant, disabled });
157
- const loadingColorValue = getLoadingColor(variant);
158
- const textVariantValue = textVariantMap[size] ?? "button";
159
- const handlePress = useCallback(() => {
155
+ const theme = useTheme();
156
+ const resolvedVariant = variant ?? "primary";
157
+ const resolvedSize = size ?? "medium";
158
+ const textColorClass = textColor({ variant: resolvedVariant, disabled });
159
+ const loadingColorValue = getLoadingColor(resolvedVariant, theme);
160
+ const textVariantValue = textVariantMap[resolvedSize];
161
+ const handlePress = useCallback(async () => {
160
162
  if (confirm && onPress) {
161
- show({
163
+ const confirmed = await requestConfirmation({
162
164
  title: "Confirmar acción",
163
165
  message: confirm,
164
166
  confirmText: "Confirmar",
165
167
  cancelText: "Cancelar",
166
- onConfirm: () => {
167
- hide();
168
- onPress();
169
- },
170
- onCancel: () => hide(),
171
168
  });
169
+ if (!confirmed)
170
+ return;
172
171
  }
173
- else if (onPress) {
174
- onPress();
175
- }
176
- }, [onPress, confirm, show, hide]);
172
+ onPress?.();
173
+ }, [onPress, confirm]);
177
174
  if (variant === "link") {
178
175
  return (_jsx(TouchableOpacity, { activeOpacity: 0.7, className: className, disabled: disabled || loading, onPress: handlePress, ...accessibilityProps, accessibilityLabel: accessibilityLabel ?? title, accessibilityRole: "button", accessibilityState: {
179
176
  ...accessibilityState,
@@ -189,28 +186,25 @@ const Button = ({ title, onPress, confirm, variant = "primary", size = "medium",
189
186
  }, style: { minHeight: 48, minWidth: 48 }, testID: testID, children: loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsxs(_Fragment, { children: [icon ? _jsx(View, { className: "mr-2", children: icon }) : null, _jsx(Typography.Text, { allowFontScaling: true, className: textColorClass, semibold: emphasize, style: { flexShrink: 1 }, variant: textVariantValue, children: title })] })) }));
190
187
  };
191
188
  const ButtonIcon = ({ icon, onPress, confirm, variant = "primary", size = "medium", loading = false, disabled = false, className, testID, accessibilityState, ...accessibilityProps }) => {
192
- const { show, hide } = useConfirmationContext();
193
- const iconColorValue = getIconColor(variant, disabled);
194
- const iconSizeValue = iconSizeMap[size] ?? 20;
195
- const loadingColorValue = getLoadingColor(variant);
196
- const handlePress = useCallback(() => {
189
+ const theme = useTheme();
190
+ const resolvedVariant = variant ?? "primary";
191
+ const resolvedSize = size ?? "medium";
192
+ const iconColorValue = getIconColor(resolvedVariant, disabled, theme);
193
+ const iconSizeValue = iconSizeMap[resolvedSize];
194
+ const loadingColorValue = getLoadingColor(resolvedVariant, theme);
195
+ const handlePress = useCallback(async () => {
197
196
  if (confirm && onPress) {
198
- show({
197
+ const confirmed = await requestConfirmation({
199
198
  title: "Confirmar acción",
200
199
  message: confirm,
201
200
  confirmText: "Confirmar",
202
201
  cancelText: "Cancelar",
203
- onConfirm: () => {
204
- hide();
205
- onPress();
206
- },
207
- onCancel: () => hide(),
208
202
  });
203
+ if (!confirmed)
204
+ return;
209
205
  }
210
- else if (onPress) {
211
- onPress();
212
- }
213
- }, [onPress, confirm, show, hide]);
206
+ onPress?.();
207
+ }, [onPress, confirm]);
214
208
  return (_jsx(TouchableOpacity, { activeOpacity: 0.8, className: buttonIcon({ variant, size, disabled, className }), disabled: disabled || loading, onPress: handlePress, ...accessibilityProps, accessibilityRole: "button", accessibilityState: {
215
209
  ...accessibilityState,
216
210
  disabled: disabled || loading,
@@ -1,20 +1,20 @@
1
1
  import { type ReactNode } from "react";
2
- export interface DockProps {
2
+ export type DockProps = {
3
3
  primaryAction: ReactNode;
4
4
  children: ReactNode;
5
5
  summary?: ReactNode;
6
6
  title?: string;
7
7
  moreLabel?: string;
8
8
  collapseLabel?: string;
9
- backdropLabel?: string;
10
9
  expanded?: boolean;
11
10
  defaultExpanded?: boolean;
12
11
  onExpandedChange?: (expanded: boolean) => void;
13
- dismissOnBackdrop?: boolean;
14
- maxExpandedHeightRatio?: number;
12
+ dismissible?: boolean;
13
+ snapPoints?: (string | number)[];
15
14
  placement?: "fixed" | "footer";
15
+ safeAreaBottom?: boolean;
16
16
  className?: string;
17
17
  testID?: string;
18
- }
19
- declare function Dock({ primaryAction, children, summary, title, moreLabel, collapseLabel, backdropLabel, expanded: controlledExpanded, defaultExpanded, onExpandedChange, dismissOnBackdrop, maxExpandedHeightRatio, placement, className, testID, }: DockProps): import("react").JSX.Element;
18
+ };
19
+ declare function Dock({ primaryAction, children, summary, title, moreLabel, collapseLabel, expanded: controlledExpanded, defaultExpanded, onExpandedChange, dismissible, snapPoints, placement, safeAreaBottom, className, testID, }: DockProps): import("react").JSX.Element;
20
20
  export default Dock;
@@ -1,89 +1,31 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "nativewind/jsx-runtime";
2
- /* eslint-disable react-hooks/refs */
2
+ import Sheet from "../Dialog/Dialog.Sheet.js";
3
3
  import Icon from "../Display/Display.Icon.js";
4
4
  import Pressable from "../primitives/Pressable.js";
5
- import { themeToVars, useTheme } from "../theme/index.js";
5
+ import { useTheme } from "../theme/index.js";
6
6
  import { cn } from "../theme/tw.js";
7
7
  import { Typography } from "../Typography/index.js";
8
- import { useReducedMotion } from "../utils/useReducedMotion.js";
9
- import { vars } from "nativewind";
10
- import { useEffect, useRef, useState } from "react";
11
- import { Animated, KeyboardAvoidingView, Modal, PanResponder, Platform, ScrollView, useWindowDimensions, View, } from "react-native";
8
+ import { useState } from "react";
9
+ import { View } from "react-native";
10
+ import { KeyboardStickyView } from "react-native-keyboard-controller";
12
11
  import { useSafeAreaInsets } from "react-native-safe-area-context";
13
- function Dock({ primaryAction, children, summary, title = "Más opciones", moreLabel = "Más opciones", collapseLabel = "Ocultar opciones", backdropLabel = "Cerrar panel", expanded: controlledExpanded, defaultExpanded = false, onExpandedChange, dismissOnBackdrop = true, maxExpandedHeightRatio = 0.6, placement = "fixed", className, testID, }) {
14
- const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);
15
- const expanded = controlledExpanded ?? internalExpanded;
12
+ function Dock({ primaryAction, children, summary, title = "Más opciones", moreLabel = "Más opciones", collapseLabel = "Ocultar opciones", expanded: controlledExpanded, defaultExpanded = false, onExpandedChange, dismissible = true, snapPoints, placement = "fixed", safeAreaBottom = placement === "fixed", className, testID, }) {
16
13
  const theme = useTheme();
17
- const reducedMotion = useReducedMotion();
18
14
  const insets = useSafeAreaInsets();
19
- const { height } = useWindowDimensions();
20
- const [barHeight, setBarHeight] = useState(0);
21
- const translateY = useRef(new Animated.Value(0)).current;
15
+ const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);
16
+ const expanded = controlledExpanded ?? internalExpanded;
22
17
  const requestExpanded = (next) => {
23
18
  if (next === expanded)
24
19
  return;
20
+ if (!next && !dismissible)
21
+ return;
25
22
  if (controlledExpanded === undefined)
26
23
  setInternalExpanded(next);
27
24
  onExpandedChange?.(next);
28
25
  };
29
- const gestureRef = useRef({ expanded, requestExpanded, reducedMotion });
30
- gestureRef.current = { expanded, requestExpanded, reducedMotion };
31
- const resolvedRatio = Number.isFinite(maxExpandedHeightRatio)
32
- ? Math.min(0.9, Math.max(0.1, maxExpandedHeightRatio))
33
- : 0.6;
34
- const maxPanelHeight = Math.max(0, Math.min(height * resolvedRatio, height - insets.top - insets.bottom - barHeight));
35
- const resetDrag = () => {
36
- translateY.stopAnimation();
37
- if (gestureRef.current.reducedMotion) {
38
- translateY.setValue(0);
39
- return;
40
- }
41
- Animated.spring(translateY, {
42
- toValue: 0,
43
- useNativeDriver: true,
44
- damping: 24,
45
- stiffness: 260,
46
- }).start();
47
- };
48
- useEffect(() => {
49
- translateY.stopAnimation();
50
- translateY.setValue(0);
51
- return () => translateY.stopAnimation();
52
- }, [expanded, reducedMotion, translateY]);
53
- const [pan] = useState(() => PanResponder.create({
54
- onStartShouldSetPanResponder: () => true,
55
- onPanResponderGrant: () => translateY.stopAnimation(),
56
- onPanResponderTerminationRequest: () => false,
57
- onPanResponderMove: (_, gesture) => {
58
- if (gestureRef.current.expanded)
59
- translateY.setValue(Math.max(0, gesture.dy));
60
- },
61
- onPanResponderRelease: (_, gesture) => {
62
- const isVertical = Math.abs(gesture.dy) > Math.abs(gesture.dx);
63
- const shouldChange = gestureRef.current.expanded
64
- ? gesture.dy > 40 || gesture.vy > 0.5
65
- : gesture.dy < -40 || gesture.vy < -0.5;
66
- if (isVertical && shouldChange) {
67
- gestureRef.current.requestExpanded(!gestureRef.current.expanded);
68
- }
69
- resetDrag();
70
- },
71
- onPanResponderTerminate: resetDrag,
72
- }));
73
- const handle = (_jsx(View, { className: "min-h-12 items-center justify-center", ...pan.panHandlers, testID: testID ? `${testID}-handle` : undefined, children: _jsx(View, { className: "h-1 w-10 rounded-full bg-border-strong" }) }));
74
- const bar = (_jsxs(View, { className: cn("gap-3 border-t border-border-light bg-bg-card px-4 pb-2", className), onLayout: (event) => setBarHeight(event.nativeEvent.layout.height), testID: testID ? `${testID}-bar` : undefined, children: [!expanded ? handle : null, summary, _jsxs(View, { className: "flex-row items-center gap-3", children: [_jsxs(Pressable, { accessibilityLabel: expanded ? collapseLabel : moreLabel, accessibilityRole: "button", accessibilityState: { expanded }, className: "min-h-12 flex-1 flex-row items-center justify-center gap-1 rounded-lg bg-bg-muted px-3", onPress: () => requestExpanded(!expanded), testID: testID ? `${testID}-more` : undefined, children: [_jsx(Typography.Text, { className: "text-text-primary", variant: "buttonSmall", children: expanded ? collapseLabel : moreLabel }), _jsx(Icon, { color: theme.icon.secondary, name: expanded ? "chevron-down" : "chevron-up", size: 16 })] }), _jsx(View, { className: "flex-1", children: primaryAction })] })] }));
75
- return (_jsxs(_Fragment, { children: [_jsx(View, { className: cn(placement === "fixed" && "absolute bottom-0 left-0 right-0 z-20"), style: expanded ? { height: barHeight } : undefined, testID: testID, children: !expanded ? bar : null }), _jsx(Modal, { animationType: "none", onRequestClose: () => requestExpanded(false), presentationStyle: "overFullScreen", testID: testID ? `${testID}-modal` : undefined, transparent: true, visible: expanded, children: _jsx(KeyboardAvoidingView, { behavior: Platform.OS === "ios" ? "padding" : "height", style: { flex: 1 }, testID: testID ? `${testID}-keyboard` : undefined, children: _jsxs(View, { accessibilityViewIsModal: true, style: { flex: 1, justifyContent: "flex-end" }, children: [_jsx(Pressable, { accessibilityLabel: backdropLabel, accessibilityRole: "button", className: "absolute inset-0 bg-black/50", disabled: !dismissOnBackdrop, onPress: dismissOnBackdrop ? () => requestExpanded(false) : undefined, testID: testID ? `${testID}-backdrop` : undefined }), _jsxs(View, { onAccessibilityEscape: () => requestExpanded(false), style: [
76
- vars(themeToVars(theme)),
77
- {
78
- paddingBottom: insets.bottom,
79
- backgroundColor: theme.bg.card,
80
- maxHeight: height - insets.top,
81
- flexShrink: 1,
82
- },
83
- ], children: [_jsx(View, { style: { overflow: "hidden", flexShrink: 1 }, children: _jsxs(Animated.View, { style: {
84
- maxHeight: maxPanelHeight,
85
- flexShrink: 1,
86
- transform: [{ translateY }],
87
- }, testID: testID ? `${testID}-expanded-content` : undefined, children: [handle, _jsxs(ScrollView, { contentContainerStyle: { paddingHorizontal: 16, paddingBottom: 16 }, keyboardShouldPersistTaps: "handled", nestedScrollEnabled: true, style: { flexShrink: 1 }, testID: testID ? `${testID}-scroll` : undefined, children: [title ? (_jsx(Typography.Text, { accessibilityRole: "header", className: "pb-2 text-text-primary", semibold: true, variant: "subtitle", children: title })) : null, children] })] }) }), bar] })] }) }) })] }));
26
+ const bar = (_jsxs(View, { className: cn("gap-3 bg-bg-card py-2", !expanded && "border-t border-border-light px-4", className), style: { paddingBottom: !expanded && safeAreaBottom ? insets.bottom + 8 : 8 }, testID: testID ? `${testID}-bar` : undefined, children: [summary, _jsxs(View, { className: "flex-row items-center gap-3", children: [_jsxs(Pressable, { accessibilityLabel: expanded ? collapseLabel : moreLabel, accessibilityRole: "button", accessibilityState: { expanded, disabled: expanded && !dismissible }, className: "flex-1 flex-row items-center justify-center gap-2 rounded-lg bg-bg-muted px-3", disabled: expanded && !dismissible, onPress: () => requestExpanded(!expanded), style: { minWidth: 48, minHeight: 48 }, testID: testID ? `${testID}-more` : undefined, children: [_jsx(Typography.Text, { className: expanded && !dismissible ? "text-text-disabled" : "text-text-primary", variant: "buttonSmall", children: expanded ? collapseLabel : moreLabel }), _jsx(Icon, { color: expanded && !dismissible ? theme.icon.disabled : theme.icon.secondary, name: expanded ? "expand" : "collapse", size: 16 })] }), _jsx(View, { className: "flex-1", children: primaryAction })] })] }));
27
+ return (_jsxs(_Fragment, { children: [_jsx(KeyboardStickyView, { enabled: placement === "fixed" && !expanded, offset: { opened: safeAreaBottom ? insets.bottom : 0 }, style: placement === "fixed"
28
+ ? { position: "absolute", bottom: 0, left: 0, right: 0, zIndex: 20 }
29
+ : undefined, testID: testID, children: !expanded ? bar : null }), _jsx(Sheet, { dismissible: dismissible, footer: expanded ? bar : undefined, onClose: () => requestExpanded(false), snapPoints: snapPoints, testID: testID ? `${testID}-sheet` : undefined, title: title, visible: expanded, children: children })] }));
88
30
  }
89
31
  export default Dock;
@@ -1,5 +1,6 @@
1
+ import type { IconName } from "../Display/index.js";
1
2
  export interface FABProps {
2
- icon: string;
3
+ icon: IconName;
3
4
  onPress: () => void;
4
5
  label?: string;
5
6
  position?: "bottom-right" | "bottom-left";
@@ -31,7 +31,7 @@ export const FilterButton = ({ title, active, onPress, variant = "default", disa
31
31
  return (_jsxs(Pressable, { accessibilityLabel: title, accessibilityRole: "button", accessibilityState: { selected: active, disabled }, activeOpacity: 1 - theme.state.press, className: cn("flex-row items-center self-start rounded-full px-3 py-2 gap-2 max-w-full", active ? "bg-container-primary" : "bg-bg-muted", className), disabled: disabled, onPress: onPress, style: {
32
32
  minHeight: 48,
33
33
  minWidth: 48,
34
- }, testID: testID, children: [active ? (_jsx(View, { accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", children: _jsx(Icon, { color: theme.container.onPrimary, name: "checkmark", size: 18 }) })) : null, _jsx(Typography.Text, { className: cn("flex-shrink", active ? "text-container-on-primary" : "text-text-primary"), variant: "bodyLarge", children: title })] }));
34
+ }, testID: testID, children: [active ? (_jsx(View, { accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", children: _jsx(Icon, { color: theme.container.onPrimary, name: "check", size: 18 }) })) : null, _jsx(Typography.Text, { className: cn("flex-shrink", active ? "text-container-on-primary" : "text-text-primary"), variant: "bodyLarge", children: title })] }));
35
35
  }
36
36
  return (_jsx(TouchableOpacity, { accessibilityLabel: title, accessibilityRole: "button", accessibilityState: { selected: active, disabled }, activeOpacity: 0.8, className: filterButton({ active, className }), disabled: disabled, onPress: onPress, testID: testID, children: _jsx(Typography.Text, { className: filterText({ active }), variant: "buttonSmall", children: title }) }));
37
37
  };
@@ -1,5 +1,5 @@
1
+ import type { IconName } from "../Display/index.js";
1
2
  import { type VariantProps } from "../theme/tw.js";
2
- import { IconType } from "../utils/icon.js";
3
3
  import type { AccessibilityProps } from "react-native";
4
4
  declare const iconButton: import("tailwind-variants").TVReturnType<{
5
5
  variant: {
@@ -57,13 +57,12 @@ declare const iconButton: import("tailwind-variants").TVReturnType<{
57
57
  };
58
58
  }, undefined>>;
59
59
  export interface IconButtonProps extends VariantProps<typeof iconButton>, AccessibilityProps {
60
- icon?: string;
61
- type?: IconType;
60
+ icon: IconName;
62
61
  onPress: () => void;
63
62
  loading?: boolean;
64
63
  iconColor?: string;
65
64
  className?: string;
66
65
  testID?: string;
67
66
  }
68
- declare function IconButton({ icon, type, onPress, variant, size, loading, disabled, rounded, iconColor, className, testID, accessibilityState, ...accessibilityProps }: IconButtonProps): import("react").JSX.Element;
67
+ declare function IconButton({ icon, onPress, variant, size, loading, disabled, rounded, iconColor, className, testID, accessibilityState, ...accessibilityProps }: IconButtonProps): import("react").JSX.Element;
69
68
  export default IconButton;
@@ -31,12 +31,12 @@ const iconButton = tv({
31
31
  disabled: false,
32
32
  },
33
33
  });
34
- function IconButton({ icon, type = "feather", onPress, variant = "ghost", size = "medium", loading = false, disabled = false, rounded = false, iconColor, className, testID, accessibilityState, ...accessibilityProps }) {
34
+ function IconButton({ icon, onPress, variant = "ghost", size = "medium", loading = false, disabled = false, rounded = false, iconColor, className, testID, accessibilityState, ...accessibilityProps }) {
35
35
  const resolvedColor = iconColor ?? theme.text.accent;
36
36
  return (_jsx(TouchableOpacity, { activeOpacity: 0.7, className: iconButton({ variant, size, rounded, disabled, className }), disabled: disabled || loading, onPress: onPress, ...accessibilityProps, accessibilityRole: "button", accessibilityState: {
37
37
  ...accessibilityState,
38
38
  disabled: disabled || loading,
39
39
  busy: loading,
40
- }, style: { minHeight: 48, minWidth: 48 }, testID: testID, children: loading ? (_jsx(ActivityIndicator, { color: resolvedColor, size: "small" })) : (_jsx(Icon, { color: resolvedColor, name: icon, size: 24, type: type })) }));
40
+ }, style: { minHeight: 48, minWidth: 48 }, testID: testID, children: loading ? (_jsx(ActivityIndicator, { color: resolvedColor, size: "small" })) : (_jsx(Icon, { color: resolvedColor, name: icon, size: 24 })) }));
41
41
  }
42
42
  export default IconButton;
@@ -1,21 +1,12 @@
1
+ import { type ConfirmationProps } from "./Dialog.Confirmation.js";
1
2
  import { type ReactNode } from "react";
2
- type ConfirmationVariant = "dialog" | "bottomSheet";
3
- interface IConfirmationOptions {
4
- variant?: ConfirmationVariant;
5
- title?: string;
6
- message?: string;
7
- confirmText?: string;
8
- cancelText?: string;
9
- onConfirm?: () => void | Promise<void>;
10
- onCancel?: () => void | Promise<void>;
11
- render?: (close: () => void) => ReactNode;
12
- }
3
+ export type { NativeConfirmationOptions } from "./requestConfirmation.js";
4
+ export { requestConfirmation } from "./requestConfirmation.js";
5
+ export type ConfirmationOptions = Omit<ConfirmationProps, "visible" | "onClose">;
13
6
  type ConfirmationContextValue = {
14
- show: (opts: IConfirmationOptions) => void;
15
- hide: () => void;
7
+ show: (options: ConfirmationOptions) => void;
16
8
  };
17
9
  export declare function ConfirmationProvider({ children }: {
18
10
  children: ReactNode;
19
11
  }): import("react").JSX.Element;
20
12
  export declare function useConfirmationContext(): ConfirmationContextValue;
21
- export {};
@@ -1,62 +1,22 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "nativewind/jsx-runtime";
2
- import { BottomSheet } from "./BottomSheet/index.js";
3
- import ConfirmationDialog from "./Dialog.Confirmation.js";
4
- import { BaseButton } from "../primitives/BaseButton.js";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import Confirmation from "./Dialog.Confirmation.js";
5
3
  import { createContext, useCallback, useContext, useMemo, useState, } from "react";
6
- const DEFAULT_STATE = {
7
- isVisible: false,
8
- variant: "dialog",
9
- title: "Confirmación",
10
- message: "¿Estás seguro de que quieres continuar?",
11
- confirmText: "Confirmar",
12
- cancelText: "Cancelar",
13
- onConfirm: undefined,
14
- onCancel: undefined,
15
- render: undefined,
16
- };
4
+ export { requestConfirmation } from "./requestConfirmation.js";
17
5
  const ConfirmationContext = createContext(null);
18
6
  export function ConfirmationProvider({ children }) {
19
- const [state, setState] = useState(DEFAULT_STATE);
20
- const show = useCallback((opts) => {
21
- setState((prev) => ({
22
- ...prev,
23
- isVisible: true,
24
- variant: opts.variant ?? DEFAULT_STATE.variant,
25
- title: opts.title ?? DEFAULT_STATE.title,
26
- message: opts.message ?? DEFAULT_STATE.message,
27
- confirmText: opts.confirmText ?? DEFAULT_STATE.confirmText,
28
- cancelText: opts.cancelText ?? DEFAULT_STATE.cancelText,
29
- onConfirm: opts.onConfirm,
30
- onCancel: opts.onCancel,
31
- render: opts.render,
32
- }));
7
+ const [options, setOptions] = useState();
8
+ const [visible, setVisible] = useState(false);
9
+ const show = useCallback(({ title = "Confirmación", message = "¿Estás seguro de que quieres continuar?", confirmText = "Confirmar", cancelText = "Cancelar", destructive = false, ...next }) => {
10
+ setOptions({ title, message, confirmText, cancelText, destructive, ...next });
11
+ setVisible(true);
33
12
  }, []);
34
- const hide = useCallback(() => {
35
- setState(DEFAULT_STATE);
36
- }, []);
37
- const value = useMemo(() => ({ show, hide }), [show, hide]);
38
- const handleConfirm = async () => {
39
- try {
40
- await state.onConfirm?.();
41
- }
42
- finally {
43
- hide();
44
- }
45
- };
46
- const handleCancel = async () => {
47
- try {
48
- await state.onCancel?.();
49
- }
50
- finally {
51
- hide();
52
- }
53
- };
54
- return (_jsxs(ConfirmationContext.Provider, { value: value, children: [children, state.variant === "dialog" && (_jsx(ConfirmationDialog, { auxTitle: state.title, cancelButtonText: state.cancelText, confirmButtonText: state.confirmText, onCancel: handleCancel, onConfirm: handleConfirm, subtitle: state.message, visible: state.isVisible })), state.variant === "bottomSheet" && (_jsx(BottomSheet.Root, { onClose: handleCancel, visible: state.isVisible, children: state.render ? (state.render(hide)) : (_jsxs(_Fragment, { children: [_jsx(BottomSheet.Header, { children: _jsx(BottomSheet.Title, { subtitle: state.message, children: state.title }) }), _jsxs(BottomSheet.Actions, { layout: "horizontal", children: [_jsx(BaseButton, { className: "flex-1", onPress: handleCancel, title: state.cancelText ?? "", variant: "outline" }), _jsx(BaseButton, { className: "flex-1", onPress: handleConfirm, title: state.confirmText ?? "", variant: "primary" })] })] })) }))] }));
13
+ const value = useMemo(() => ({ show }), [show]);
14
+ return (_jsxs(ConfirmationContext.Provider, { value: value, children: [children, options ? (_jsx(Confirmation, { ...options, onClose: () => setVisible(false), visible: visible })) : null] }));
55
15
  }
56
16
  export function useConfirmationContext() {
57
17
  const context = useContext(ConfirmationContext);
58
18
  if (!context) {
59
- return { show: () => { }, hide: () => { } };
19
+ throw new Error("useConfirmationContext requires ConfirmationProvider.");
60
20
  }
61
21
  return context;
62
22
  }
@@ -1,18 +1,19 @@
1
- export interface ActionSheetAction {
1
+ import type { IconName } from "../Display/icon-map.js";
2
+ export type ActionSheetAction = {
2
3
  label: string;
3
- icon?: string;
4
+ icon?: IconName;
4
5
  destructive?: boolean;
5
6
  disabled?: boolean;
6
- onPress: () => void;
7
- }
8
- export interface ActionSheetProps {
7
+ onPress: () => void | Promise<void>;
8
+ };
9
+ export type ActionSheetProps = {
9
10
  visible: boolean;
10
11
  onClose: () => void;
11
- actions: ActionSheetAction[];
12
+ actions: readonly ActionSheetAction[];
12
13
  title?: string;
13
14
  message?: string;
14
15
  cancelLabel?: string;
15
16
  testID?: string;
16
- }
17
+ };
17
18
  declare function ActionSheet({ visible, onClose, actions, title, message, cancelLabel, testID, }: ActionSheetProps): import("react").JSX.Element;
18
19
  export default ActionSheet;
@@ -1,28 +1,54 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import Sheet from "./Dialog.Sheet.js";
2
3
  import Icon from "../Display/Display.Icon.js";
3
4
  import Pressable from "../primitives/Pressable.js";
4
- import { themeToVars, useTheme } from "../theme/index.js";
5
+ import { useTheme } from "../theme/index.js";
5
6
  import { cn } from "../theme/tw.js";
6
7
  import { Typography } from "../Typography/index.js";
7
- import { useSheetTranslation } from "../utils/useSheetTranslation.js";
8
- import { vars } from "nativewind";
9
- import { Animated, Modal, ScrollView, View } from "react-native";
10
- import { useSafeAreaInsets } from "react-native-safe-area-context";
8
+ import { useRef, useState } from "react";
9
+ import { ActivityIndicator, View } from "react-native";
11
10
  function ActionSheet({ visible, onClose, actions, title, message, cancelLabel = "Cancelar", testID, }) {
12
11
  const theme = useTheme();
13
- const insets = useSafeAreaInsets();
14
- const { translateY } = useSheetTranslation(visible);
15
- return (_jsx(Modal, { animationType: "none", onRequestClose: onClose, presentationStyle: "overFullScreen", transparent: true, visible: visible, children: _jsxs(View, { className: "flex-1 justify-end", testID: testID, children: [_jsx(View, { accessibilityElementsHidden: true, className: "absolute inset-0 bg-black/50", importantForAccessibility: "no-hide-descendants", onResponderRelease: onClose, onStartShouldSetResponder: () => true }), _jsxs(Animated.View, { accessibilityViewIsModal: true, onAccessibilityEscape: onClose, style: [
16
- vars(themeToVars(theme)),
17
- {
18
- transform: [{ translateY }],
19
- maxHeight: "90%",
20
- paddingHorizontal: 12,
21
- paddingBottom: insets.bottom + 12,
22
- },
23
- ], children: [title || message ? (_jsxs(View, { className: "rounded-xl mb-2 px-4 py-3 items-center", style: { backgroundColor: theme.bg.card }, children: [title ? (_jsx(Typography.Text, { accessibilityRole: "header", className: "text-text-primary", semibold: true, children: title })) : null, message ? (_jsx(Typography.Text, { className: "text-text-secondary text-sm", variant: "bodySmall", children: message })) : null] })) : null, _jsx(ScrollView, { bounces: false, className: "rounded-xl overflow-hidden mb-2", style: { backgroundColor: theme.bg.card }, children: actions.map((action, index) => (_jsxs(Pressable, { accessibilityLabel: action.label, accessibilityRole: "button", accessibilityState: { disabled: action.disabled }, className: cn("flex-row items-center justify-center gap-2 px-4 py-4", index < actions.length - 1 && "border-b border-border-light"), disabled: action.disabled, onPress: () => {
24
- onClose();
25
- action.onPress();
26
- }, style: { minHeight: 48 }, children: [action.icon ? (_jsx(Icon, { color: action.destructive ? theme.danger : theme.icon.primary, name: action.icon, size: 20 })) : null, _jsx(Typography.Text, { className: cn(action.destructive ? "text-danger" : "text-text-primary"), semibold: true, children: action.label })] }, action.label))) }), _jsx(Pressable, { accessibilityLabel: cancelLabel, accessibilityRole: "button", className: "rounded-xl px-4 py-4 items-center", onPress: onClose, style: { backgroundColor: theme.bg.card, minHeight: 48 }, children: _jsx(Typography.Text, { className: "text-text-accent", semibold: true, children: cancelLabel }) })] })] }) }));
12
+ const saving = useRef(false);
13
+ const [pending, setPending] = useState();
14
+ const [error, setError] = useState();
15
+ const close = () => {
16
+ if (saving.current)
17
+ return;
18
+ setError(undefined);
19
+ onClose();
20
+ };
21
+ const choose = async (action, index) => {
22
+ if (action.disabled || saving.current)
23
+ return;
24
+ saving.current = true;
25
+ setPending(index);
26
+ setError(undefined);
27
+ try {
28
+ await action.onPress();
29
+ onClose();
30
+ }
31
+ catch (cause) {
32
+ setError(cause instanceof Error && cause.message.trim()
33
+ ? cause.message
34
+ : "No se pudo completar la acción. Vuelve a intentarlo.");
35
+ }
36
+ finally {
37
+ saving.current = false;
38
+ setPending(undefined);
39
+ }
40
+ };
41
+ return (_jsxs(Sheet, { dismissible: pending === undefined, footer: _jsx(Pressable, { accessibilityLabel: cancelLabel, accessibilityRole: "button", accessibilityState: { disabled: pending !== undefined }, disabled: pending !== undefined, onPress: close, style: { minHeight: 48, justifyContent: "center", alignItems: "center" }, children: _jsx(Typography.Text, { className: "text-text-accent", semibold: true, children: cancelLabel }) }), onClose: close, testID: testID, title: title, visible: visible, children: [message ? (_jsx(Typography.Text, { className: "pb-3 text-text-secondary", children: message })) : null, actions.map((action, index) => (_jsxs(Pressable, { accessibilityLabel: action.label, accessibilityRole: "button", accessibilityState: {
42
+ disabled: !!action.disabled || pending !== undefined,
43
+ busy: pending === index,
44
+ }, className: cn("flex-row items-center gap-3 py-3", index < actions.length - 1 && "border-b border-border-light"), disabled: action.disabled || pending !== undefined, onPress: () => void choose(action, index), style: { minHeight: 48 }, children: [action.icon ? (_jsx(Icon, { color: action.disabled
45
+ ? theme.icon.disabled
46
+ : action.destructive
47
+ ? theme.danger
48
+ : theme.icon.primary, name: action.icon, size: 20 })) : null, _jsx(Typography.Text, { className: cn("flex-1", action.disabled
49
+ ? "text-text-disabled"
50
+ : action.destructive
51
+ ? "text-danger"
52
+ : "text-text-primary"), semibold: true, children: action.label }), pending === index ? _jsx(ActivityIndicator, { color: theme.primary }) : null] }, `${action.label}-${index}`))), !actions.length ? (_jsx(Typography.Text, { children: "No hay acciones disponibles." })) : null, error ? (_jsx(View, { className: "pt-3", children: _jsx(Typography.Text, { accessibilityLiveRegion: "polite", accessibilityRole: "alert", className: "text-danger", selectable: true, children: error }) })) : null] }));
27
53
  }
28
54
  export default ActionSheet;
@@ -1,12 +1,15 @@
1
- import React from "react";
2
- export interface Props {
3
- auxTitle?: string;
4
- subtitle?: string;
5
- confirmButtonText?: string;
6
- cancelButtonText?: string;
1
+ import { type ReactNode } from "react";
2
+ export type ConfirmationProps = {
7
3
  visible: boolean;
8
- onCancel: () => void;
9
- onConfirm: () => void;
10
- }
11
- declare const ConfirmationDialog: React.FC<Props>;
12
- export default ConfirmationDialog;
4
+ onClose: () => void;
5
+ onConfirm: () => void | Promise<void>;
6
+ title?: string;
7
+ message?: string;
8
+ confirmText?: string;
9
+ cancelText?: string;
10
+ destructive?: boolean;
11
+ children?: ReactNode;
12
+ testID?: string;
13
+ };
14
+ declare function Confirmation({ visible, onClose, onConfirm, title, message, confirmText, cancelText, destructive, children, testID, }: ConfirmationProps): import("react").JSX.Element;
15
+ export default Confirmation;