@sincpro/mobile-ui 1.0.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/README.md +701 -231
  2. package/dist/Actions/Actions.Button.d.ts +15 -12
  3. package/dist/Actions/Actions.Button.js +25 -14
  4. package/dist/Actions/Actions.Dock.d.ts +20 -0
  5. package/dist/Actions/Actions.Dock.js +89 -0
  6. package/dist/Actions/Actions.FAB.d.ts +2 -1
  7. package/dist/Actions/Actions.FAB.js +5 -3
  8. package/dist/Actions/Actions.FilterButton.d.ts +10 -2
  9. package/dist/Actions/Actions.FilterButton.js +21 -6
  10. package/dist/Actions/Actions.IconButton.d.ts +4 -2
  11. package/dist/Actions/Actions.IconButton.js +7 -3
  12. package/dist/Actions/index.d.ts +6 -2
  13. package/dist/Actions/index.js +4 -1
  14. package/dist/Dialog/BottomSheet/BottomSheet.Root.js +22 -34
  15. package/dist/Dialog/Dialog.ActionSheet.d.ts +2 -1
  16. package/dist/Dialog/Dialog.ActionSheet.js +17 -21
  17. package/dist/Dialog/Dialog.Confirmation.js +8 -6
  18. package/dist/Dialog/Dialog.EditValue.js +3 -1
  19. package/dist/Dialog/Dialog.NativeSheet.d.ts +10 -0
  20. package/dist/Dialog/Dialog.NativeSheet.js +21 -0
  21. package/dist/Dialog/Dialog.Sheet.d.ts +5 -1
  22. package/dist/Dialog/Dialog.Sheet.js +30 -31
  23. package/dist/Display/Display.Accordion.js +7 -3
  24. package/dist/Display/Display.AmountSummary.d.ts +28 -0
  25. package/dist/Display/Display.AmountSummary.js +25 -0
  26. package/dist/Display/Display.Carousel.d.ts +27 -4
  27. package/dist/Display/Display.Carousel.js +112 -12
  28. package/dist/Display/Display.CopyableText.d.ts +9 -2
  29. package/dist/Display/Display.CopyableText.js +75 -7
  30. package/dist/Display/Display.ExpoImage.d.ts +8 -0
  31. package/dist/Display/Display.ExpoImage.js +15 -0
  32. package/dist/Display/Display.Icon.js +2 -1
  33. package/dist/Display/Display.Image.d.ts +10 -6
  34. package/dist/Display/Display.Image.js +39 -6
  35. package/dist/Display/Display.KeyValue.d.ts +11 -4
  36. package/dist/Display/Display.KeyValue.js +6 -4
  37. package/dist/Display/Display.ListGroup.d.ts +23 -0
  38. package/dist/Display/Display.ListGroup.js +17 -0
  39. package/dist/Display/Display.ListItem.d.ts +15 -2
  40. package/dist/Display/Display.ListItem.js +25 -8
  41. package/dist/Display/Display.Logo.d.ts +2 -2
  42. package/dist/Display/Display.Logo.js +3 -2
  43. package/dist/Display/Display.MediaCard.d.ts +2 -2
  44. package/dist/Display/Display.MediaEmbed.d.ts +2 -2
  45. package/dist/Display/Display.ProductCard.d.ts +2 -2
  46. package/dist/Display/Display.SectionHeader.d.ts +11 -1
  47. package/dist/Display/Display.SectionHeader.js +11 -2
  48. package/dist/Display/Display.Story.d.ts +3 -3
  49. package/dist/Display/Display.Story.js +5 -4
  50. package/dist/Display/Display.StoryTray.d.ts +4 -4
  51. package/dist/Display/Display.StoryTray.js +3 -2
  52. package/dist/Display/Display.StoryViewer.d.ts +3 -3
  53. package/dist/Display/Display.StoryViewer.js +6 -3
  54. package/dist/Display/Display.SwipeableRow.d.ts +3 -1
  55. package/dist/Display/Display.SwipeableRow.js +3 -3
  56. package/dist/Display/index.d.ts +13 -0
  57. package/dist/Display/index.js +6 -0
  58. package/dist/Feedback/Feedback.Banner.js +1 -1
  59. package/dist/Feedback/Feedback.DomainSplashScreen.d.ts +13 -4
  60. package/dist/Feedback/Feedback.DomainSplashScreen.js +68 -53
  61. package/dist/Feedback/Feedback.EmptyState.d.ts +2 -2
  62. package/dist/Feedback/Feedback.EmptyState.js +3 -2
  63. package/dist/Feedback/Feedback.Error.d.ts +2 -1
  64. package/dist/Feedback/Feedback.Error.js +4 -3
  65. package/dist/Feedback/Feedback.Loading.d.ts +6 -2
  66. package/dist/Feedback/Feedback.Loading.js +2 -2
  67. package/dist/Feedback/Feedback.Progress.d.ts +6 -3
  68. package/dist/Feedback/Feedback.Progress.js +29 -19
  69. package/dist/Feedback/Feedback.ProgressCircle.d.ts +3 -2
  70. package/dist/Feedback/Feedback.ProgressCircle.js +10 -7
  71. package/dist/Feedback/Feedback.Skeleton.d.ts +4 -4
  72. package/dist/Feedback/Feedback.Skeleton.js +15 -3
  73. package/dist/Feedback/Feedback.Spinner.d.ts +3 -3
  74. package/dist/Feedback/Feedback.Spinner.js +40 -9
  75. package/dist/Feedback/Feedback.Splash.d.ts +2 -2
  76. package/dist/Feedback/Feedback.Splash.js +5 -3
  77. package/dist/Feedback/Toast.context.d.ts +64 -3
  78. package/dist/Feedback/Toast.context.js +112 -31
  79. package/dist/Feedback/index.d.ts +15 -1
  80. package/dist/Feedback/index.js +4 -1
  81. package/dist/Form/Form.Checkbox.d.ts +3 -1
  82. package/dist/Form/Form.Checkbox.js +2 -2
  83. package/dist/Form/Form.DatePicker.js +3 -2
  84. package/dist/Form/Form.Dropdown.d.ts +15 -3
  85. package/dist/Form/Form.Dropdown.js +21 -6
  86. package/dist/Form/Form.ErrorSummary.d.ts +23 -0
  87. package/dist/Form/Form.ErrorSummary.js +53 -0
  88. package/dist/Form/Form.FilterButton.d.ts +1 -1
  89. package/dist/Form/Form.Input.d.ts +4 -3
  90. package/dist/Form/Form.Input.js +2 -2
  91. package/dist/Form/Form.KeyboardForm.d.ts +11 -0
  92. package/dist/Form/Form.KeyboardForm.js +9 -0
  93. package/dist/Form/Form.KeyboardToolbar.d.ts +21 -0
  94. package/dist/Form/Form.KeyboardToolbar.js +70 -0
  95. package/dist/Form/Form.Select.d.ts +2 -1
  96. package/dist/Form/Form.Select.js +26 -26
  97. package/dist/Form/index.d.ts +9 -0
  98. package/dist/Form/index.js +6 -0
  99. package/dist/Navigation/Navigation.AppBar.js +8 -6
  100. package/dist/Navigation/Navigation.BottomNav.js +10 -5
  101. package/dist/Navigation/Navigation.Header.d.ts +2 -2
  102. package/dist/Navigation/Navigation.Menu.js +10 -3
  103. package/dist/Navigation/Navigation.RowItem.d.ts +1 -1
  104. package/dist/Navigation/Navigation.RowItem.js +2 -2
  105. package/dist/Navigation/Navigation.Search.d.ts +41 -0
  106. package/dist/Navigation/Navigation.Search.js +47 -0
  107. package/dist/Navigation/Navigation.SearchBar.d.ts +5 -1
  108. package/dist/Navigation/Navigation.SearchBar.js +25 -7
  109. package/dist/Navigation/Navigation.SearchSuggestions.d.ts +14 -0
  110. package/dist/Navigation/Navigation.SearchSuggestions.js +7 -0
  111. package/dist/Navigation/Navigation.SegmentedControl.d.ts +11 -1
  112. package/dist/Navigation/Navigation.SegmentedControl.js +29 -15
  113. package/dist/Navigation/index.d.ts +13 -0
  114. package/dist/Navigation/index.js +4 -0
  115. package/dist/Typography/Typography.Heading.js +1 -1
  116. package/dist/Typography/Typography.SelectableText.d.ts +10 -0
  117. package/dist/Typography/Typography.SelectableText.js +16 -0
  118. package/dist/Typography/Typography.Text.js +4 -20
  119. package/dist/Typography/index.d.ts +3 -0
  120. package/dist/Typography/index.js +3 -0
  121. package/dist/branding.d.ts +3 -2
  122. package/dist/layouts/AuthContainer.d.ts +4 -1
  123. package/dist/layouts/AuthContainer.js +3 -3
  124. package/dist/primitives/BaseButton.d.ts +15 -12
  125. package/dist/primitives/BaseButton.js +21 -9
  126. package/dist/theme/typography.d.ts +16 -1
  127. package/dist/theme/typography.js +19 -0
  128. package/dist/utils/criteria.d.ts +56 -0
  129. package/dist/utils/criteria.js +99 -0
  130. package/dist/utils/image.d.ts +3 -0
  131. package/dist/utils/image.js +13 -0
  132. package/dist/utils/keys.d.ts +1 -0
  133. package/dist/utils/keys.js +7 -0
  134. package/dist/utils/progress.d.ts +1 -0
  135. package/dist/utils/progress.js +5 -0
  136. package/dist/utils/reorder.d.ts +4 -0
  137. package/dist/utils/reorder.js +29 -0
  138. package/dist/utils/useReducedMotion.d.ts +1 -0
  139. package/dist/utils/useReducedMotion.js +43 -0
  140. package/dist/utils/useSheetTranslation.d.ts +5 -0
  141. package/dist/utils/useSheetTranslation.js +23 -0
  142. package/dist/views/AppSplashView.d.ts +3 -2
  143. package/dist/views/AppSplashView.js +3 -2
  144. package/dist/views/AuthFormView.d.ts +6 -3
  145. package/dist/views/AuthFormView.js +2 -2
  146. package/dist/views/FormViewV2.d.ts +9 -4
  147. package/dist/views/FormViewV2.js +35 -16
  148. package/dist/views/ListViewV2.context.d.ts +3 -1
  149. package/dist/views/ListViewV2.context.js +41 -21
  150. package/dist/views/ListViewV2.d.ts +18 -7
  151. package/dist/views/ListViewV2.js +69 -35
  152. package/dist/views/types/IListView.js +6 -5
  153. package/dist/widgets/CollectionOptions.d.ts +15 -0
  154. package/dist/widgets/CollectionOptions.js +129 -0
  155. package/dist/widgets/FilterBar.d.ts +21 -0
  156. package/dist/widgets/FilterBar.js +14 -0
  157. package/dist/widgets/FilterBuilder.d.ts +10 -0
  158. package/dist/widgets/FilterBuilder.js +115 -0
  159. package/dist/widgets/HomeHeader.d.ts +2 -2
  160. package/dist/widgets/PromoModal.js +3 -1
  161. package/dist/widgets/QueryChoice.d.ts +10 -0
  162. package/dist/widgets/QueryChoice.js +13 -0
  163. package/dist/widgets/ReorderableList.d.ts +51 -0
  164. package/dist/widgets/ReorderableList.js +138 -0
  165. package/dist/widgets/ScreenHeader.d.ts +2 -2
  166. package/dist/widgets/index.d.ts +4 -0
  167. package/dist/widgets/index.js +3 -0
  168. package/package.json +18 -1
@@ -1,32 +1,28 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
2
  import Icon from "../Display/Display.Icon.js";
3
3
  import Pressable from "../primitives/Pressable.js";
4
- import { theme } from "../theme/index.js";
4
+ import { themeToVars, useTheme } from "../theme/index.js";
5
5
  import { cn } from "../theme/tw.js";
6
6
  import { Typography } from "../Typography/index.js";
7
- import { useEffect, useRef } from "react";
8
- import { Animated, Modal, Pressable as RNPressable, View } from "react-native";
7
+ import { useSheetTranslation } from "../utils/useSheetTranslation.js";
8
+ import { vars } from "nativewind";
9
+ import { Animated, Modal, ScrollView, View } from "react-native";
9
10
  import { useSafeAreaInsets } from "react-native-safe-area-context";
10
- function ActionSheet({ visible, onClose, actions, title, message, cancelLabel = "Cancelar", }) {
11
+ function ActionSheet({ visible, onClose, actions, title, message, cancelLabel = "Cancelar", testID, }) {
12
+ const theme = useTheme();
11
13
  const insets = useSafeAreaInsets();
12
- // eslint-disable-next-line react-hooks/refs
13
- const translateY = useRef(new Animated.Value(800)).current;
14
- useEffect(() => {
15
- if (visible) {
16
- Animated.spring(translateY, {
17
- toValue: 0,
18
- useNativeDriver: true,
19
- damping: 20,
20
- stiffness: 300,
21
- }).start();
22
- }
23
- else {
24
- translateY.setValue(800);
25
- }
26
- }, [visible, translateY]);
27
- return (_jsx(Modal, { animationType: "none", onRequestClose: onClose, presentationStyle: "overFullScreen", transparent: true, visible: visible, children: _jsx(RNPressable, { className: "flex-1 justify-end bg-black/50", onPress: onClose, children: _jsx(Animated.View, { style: { transform: [{ translateY }] }, children: _jsxs(RNPressable, { style: { paddingHorizontal: 12, paddingBottom: insets.bottom + 12 }, 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, { 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(View, { className: "rounded-xl overflow-hidden mb-2", style: { backgroundColor: theme.bg.card }, children: actions.map((action, index) => (_jsxs(Pressable, { 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: () => {
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: () => {
28
24
  onClose();
29
25
  action.onPress();
30
- }, 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, { accessibilityRole: "button", className: "rounded-xl px-4 py-4 items-center", onPress: onClose, style: { backgroundColor: theme.bg.card }, children: _jsx(Typography.Text, { className: "text-text-accent", semibold: true, children: cancelLabel }) })] }) }) }) }));
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 }) })] })] }) }));
31
27
  }
32
28
  export default ActionSheet;
@@ -2,12 +2,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
2
  import { BaseButton } from "../primitives/BaseButton.js";
3
3
  import { theme } from "../theme/index.js";
4
4
  import Typography from "../Typography/Typography.Text.js";
5
+ import { useReducedMotion } from "../utils/useReducedMotion.js";
5
6
  import { Modal, TouchableWithoutFeedback, View } from "react-native";
6
- const ConfirmationDialog = ({ visible, onCancel, onConfirm, auxTitle = "cambios", subtitle = "¿Estás seguro de que quieres descartar los cambios realizados?", confirmButtonText = "Sí", cancelButtonText = "No, continuar aquí", }) => {
7
- return (_jsx(Modal, { animationType: "fade", onRequestClose: onCancel, presentationStyle: "overFullScreen", transparent: true, visible: visible, children: _jsx(TouchableWithoutFeedback, { onPress: onCancel, children: _jsx(View, { className: "flex-1 justify-center items-center p-6 bg-black/50", children: _jsx(TouchableWithoutFeedback, { children: _jsxs(View, { className: "p-3 w-full max-w-xs mx-auto", style: {
8
- borderRadius: 16,
9
- backgroundColor: theme.bg.popover,
10
- ...theme.shadow.md,
11
- }, children: [_jsx(Typography, { className: "mb-1.5 text-sm", variant: "body", children: _jsx(Typography, { semibold: true, variant: "body", children: auxTitle }) }), _jsx(Typography, { className: "mb-3 text-xs leading-tight", variant: "body", children: subtitle }), _jsxs(View, { className: "flex-row gap-2", children: [_jsx(BaseButton, { className: "flex-1", onPress: onCancel, size: "small", title: cancelButtonText, variant: "outlineBlack" }), _jsx(BaseButton, { className: "flex-1", onPress: onConfirm, size: "small", title: confirmButtonText, variant: "primary" })] })] }) }) }) }) }));
7
+ const ConfirmationDialog = ({ visible, onCancel, onConfirm, auxTitle = "Descartar cambios", subtitle = "¿Quieres descartar los cambios realizados?", confirmButtonText = "Descartar cambios", cancelButtonText = "Seguir editando", }) => {
8
+ const reducedMotion = useReducedMotion();
9
+ return (_jsx(Modal, { animationType: reducedMotion ? "none" : "fade", onRequestClose: onCancel, presentationStyle: "overFullScreen", transparent: true, visible: visible, children: _jsxs(View, { className: "flex-1 justify-center items-center p-6 bg-black/50", children: [_jsx(TouchableWithoutFeedback, { accessible: false, onPress: onCancel, children: _jsx(View, { className: "absolute inset-0" }) }), _jsxs(View, { accessibilityViewIsModal: true, className: "p-3 w-full max-w-xs mx-auto", onAccessibilityEscape: onCancel, style: {
10
+ borderRadius: 16,
11
+ backgroundColor: theme.bg.popover,
12
+ ...theme.shadow.md,
13
+ }, children: [_jsx(Typography, { accessibilityRole: "header", className: "mb-1.5 text-sm", variant: "body", children: _jsx(Typography, { semibold: true, variant: "body", children: auxTitle }) }), _jsx(Typography, { className: "mb-3 text-xs leading-tight", variant: "body", children: subtitle }), _jsxs(View, { className: "flex-row gap-2", children: [_jsx(BaseButton, { className: "flex-1", onPress: onCancel, size: "small", title: cancelButtonText, variant: "outlineBlack" }), _jsx(BaseButton, { className: "flex-1", onPress: onConfirm, size: "small", title: confirmButtonText, variant: "primary" })] })] })] }) }));
12
14
  };
13
15
  export default ConfirmationDialog;
@@ -1,16 +1,18 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
2
  import { theme } from "../theme/index.js";
3
3
  import { Typography } from "../Typography/index.js";
4
+ import { useReducedMotion } from "../utils/useReducedMotion.js";
4
5
  import { useEffect, useRef } from "react";
5
6
  import { FlatList, Modal, TextInput, TouchableOpacity, View, } from "react-native";
6
7
  function EditableValueModal({ visible, title, value, onChangeValue, onConfirm, onCancel, placeholder, helperText, chips, keyboardType = "decimal-pad", rightAdornment, confirmLabel = "Aplicar", cancelLabel = "Cancelar", autoFocus = true, testID, }) {
7
8
  const inputRef = useRef(null);
9
+ const reducedMotion = useReducedMotion();
8
10
  useEffect(() => {
9
11
  if (visible && autoFocus) {
10
12
  const t = setTimeout(() => inputRef.current?.focus(), 60);
11
13
  return () => clearTimeout(t);
12
14
  }
13
15
  }, [visible, autoFocus]);
14
- return (_jsx(Modal, { animationType: "fade", onRequestClose: onCancel, presentationStyle: "overFullScreen", transparent: true, visible: visible, children: _jsx(View, { className: "flex-1 justify-center p-6 bg-black/40", children: _jsxs(View, { className: "p-4", style: { borderRadius: 16, backgroundColor: theme.bg.popover, ...theme.shadow.md }, testID: testID, children: [_jsx(Typography.Text, { className: "mb-3", semibold: true, variant: "h6", children: title }), _jsxs(View, { className: "flex-row items-center", children: [_jsx(TextInput, { className: "flex-1 border border-input rounded-lg p-2.5 text-lg text-text-primary", keyboardType: keyboardType, onChangeText: onChangeValue, onSubmitEditing: onConfirm, placeholder: placeholder, placeholderTextColor: theme.text.tertiary, ref: inputRef, returnKeyType: "done", selectionColor: theme.primary, underlineColorAndroid: "transparent", value: value }), rightAdornment && _jsx(View, { className: "ml-2", children: rightAdornment })] }), helperText ? (_jsx(View, { className: "mt-2", children: typeof helperText === "string" ? (_jsx(Typography.Text, { className: "opacity-70", variant: "bodySmall", children: helperText })) : (helperText) })) : null, chips && chips.length > 0 && (_jsx(FlatList, { contentContainerClassName: "gap-2 py-3", data: chips, horizontal: true, keyExtractor: (c, i) => c.key || `${c.label}-${i}`, renderItem: ({ item }) => (_jsx(TouchableOpacity, { className: "px-3 py-1.5 bg-accent/10 rounded-2xl border border-accent/30", onPress: item.onPress, children: _jsx(Typography.Text, { semibold: true, variant: "bodySmall", children: item.label }) })) })), _jsxs(View, { className: "flex-row justify-end gap-3", children: [_jsx(TouchableOpacity, { className: "px-4 py-2.5 rounded-lg bg-bg-hover", onPress: onCancel, children: _jsx(Typography.Text, { semibold: true, variant: "body", children: cancelLabel }) }), _jsx(TouchableOpacity, { className: "px-4 py-2.5 rounded-lg bg-accent", onPress: onConfirm, children: _jsx(Typography.Text, { className: "text-text-inverse", semibold: true, variant: "body", children: confirmLabel }) })] })] }) }) }));
16
+ return (_jsx(Modal, { animationType: reducedMotion ? "none" : "fade", onRequestClose: onCancel, presentationStyle: "overFullScreen", transparent: true, visible: visible, children: _jsx(View, { className: "flex-1 justify-center p-6 bg-black/40", children: _jsxs(View, { accessibilityViewIsModal: true, className: "p-4", onAccessibilityEscape: onCancel, style: { borderRadius: 16, backgroundColor: theme.bg.popover, ...theme.shadow.md }, testID: testID, children: [_jsx(Typography.Text, { className: "mb-3", semibold: true, variant: "h6", children: title }), _jsxs(View, { className: "flex-row items-center", children: [_jsx(TextInput, { accessibilityLabel: title, className: "flex-1 border border-input rounded-lg p-2.5 text-lg text-text-primary", keyboardType: keyboardType, onChangeText: onChangeValue, onSubmitEditing: onConfirm, placeholder: placeholder, placeholderTextColor: theme.text.tertiary, ref: inputRef, returnKeyType: "done", selectionColor: theme.primary, underlineColorAndroid: "transparent", value: value }), rightAdornment && _jsx(View, { className: "ml-2", children: rightAdornment })] }), helperText ? (_jsx(View, { className: "mt-2", children: typeof helperText === "string" ? (_jsx(Typography.Text, { className: "opacity-70", variant: "bodySmall", children: helperText })) : (helperText) })) : null, chips && chips.length > 0 && (_jsx(FlatList, { contentContainerClassName: "gap-2 py-3", data: chips, horizontal: true, keyExtractor: (c, i) => c.key || `${c.label}-${i}`, renderItem: ({ item }) => (_jsx(TouchableOpacity, { className: "px-3 py-1.5 bg-accent/10 rounded-2xl border border-accent/30", onPress: item.onPress, children: _jsx(Typography.Text, { semibold: true, variant: "bodySmall", children: item.label }) })) })), _jsxs(View, { className: "flex-row justify-end gap-3", children: [_jsx(TouchableOpacity, { className: "px-4 py-2.5 rounded-lg bg-bg-hover", onPress: onCancel, children: _jsx(Typography.Text, { semibold: true, variant: "body", children: cancelLabel }) }), _jsx(TouchableOpacity, { className: "px-4 py-2.5 rounded-lg bg-accent", onPress: onConfirm, children: _jsx(Typography.Text, { className: "text-text-inverse", semibold: true, variant: "body", children: confirmLabel }) })] })] }) }) }));
15
17
  }
16
18
  export default EditableValueModal;
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from "react";
2
+ export interface NativeSheetProps {
3
+ visible: boolean;
4
+ onClose: () => void;
5
+ title: string;
6
+ children: ReactNode;
7
+ testID?: string;
8
+ }
9
+ declare function NativeSheet({ visible, onClose, title, children, testID }: NativeSheetProps): import("react").JSX.Element;
10
+ export default NativeSheet;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import BottomSheet, { BottomSheetView } from "@expo/ui/community/bottom-sheet";
3
+ import Pressable from "../primitives/Pressable.js";
4
+ import { themeToVars, useTheme } from "../theme/index.js";
5
+ import { Typography } from "../Typography/index.js";
6
+ import { vars } from "nativewind";
7
+ import { ScrollView, View } from "react-native";
8
+ const SNAP_POINTS = ["60%", "90%"];
9
+ function NativeSheet({ visible, onClose, title, children, testID }) {
10
+ const theme = useTheme();
11
+ return (_jsx(BottomSheet, { backgroundStyle: { backgroundColor: theme.bg.card }, enablePanDownToClose: true, index: visible ? 0 : -1, onClose: () => {
12
+ if (visible)
13
+ onClose();
14
+ }, snapPoints: SNAP_POINTS, children: _jsx(BottomSheetView, { style: { flex: 1 }, children: _jsxs(View, { accessibilityViewIsModal: true, onAccessibilityEscape: onClose, style: [vars(themeToVars(theme)), { flex: 1, padding: 16 }], testID: testID, children: [_jsxs(View, { className: "flex-row items-center gap-3", children: [_jsx(Typography.Heading, { className: "flex-1", variant: "h4", children: title }), _jsx(Pressable, { accessibilityLabel: "Cerrar", onPress: onClose, style: {
15
+ minHeight: 48,
16
+ minWidth: 48,
17
+ justifyContent: "center",
18
+ alignItems: "center",
19
+ }, children: _jsx(Typography.Text, { className: "text-text-accent", variant: "bodySmall", children: "Cerrar" }) })] }), _jsx(ScrollView, { contentContainerStyle: { paddingVertical: 12 }, keyboardShouldPersistTaps: "handled", children: children })] }) }) }));
20
+ }
21
+ export default NativeSheet;
@@ -10,11 +10,15 @@ export interface SheetProps {
10
10
  visible: boolean;
11
11
  onClose: () => void;
12
12
  children: ReactNode;
13
+ footer?: ReactNode;
14
+ footerClassName?: string;
13
15
  title?: string;
14
16
  /** Max sheet height (fraction of the screen). Default 0.9. */
15
17
  maxHeightRatio?: number;
16
18
  /** Allow closing by tapping the dim backdrop. Default `false` (handle/button only). */
17
19
  dismissOnBackdrop?: boolean;
20
+ /** Show a visible close button in the sheet header. */
21
+ showCloseButton?: boolean;
18
22
  testID?: string;
19
23
  }
20
24
  /**
@@ -22,5 +26,5 @@ export interface SheetProps {
22
26
  * **swipe-down to close** (handle only). By default it does NOT close on backdrop tap
23
27
  * (`dismissOnBackdrop`). No provider or native deps. Import via subpath `Dialog/Sheet`.
24
28
  */
25
- declare function Sheet({ visible, onClose, children, title, maxHeightRatio, dismissOnBackdrop, testID, }: SheetProps): import("react").JSX.Element;
29
+ declare function Sheet({ visible, onClose, children, footer, footerClassName, title, maxHeightRatio, dismissOnBackdrop, showCloseButton, testID, }: SheetProps): import("react").JSX.Element;
26
30
  export default Sheet;
@@ -3,9 +3,12 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "nativewind/js
3
3
  // This file uses the canonical RN patterns: useRef(new Animated.Value()).current and PanResponder
4
4
  // created inside useState. Those hooks rules warn — they're not bugs here. Documented at the
5
5
  // eslint config level (see eslint.config.js:30-33).
6
- import { theme } from "../theme/index.js";
6
+ import Pressable from "../primitives/Pressable.js";
7
+ import { themeToVars, useTheme } from "../theme/index.js";
7
8
  import { Typography } from "../Typography/index.js";
8
- import { useEffect, useRef, useState } from "react";
9
+ import { useSheetTranslation } from "../utils/useSheetTranslation.js";
10
+ import { vars } from "nativewind";
11
+ import { useRef, useState } from "react";
9
12
  import { Animated, Modal, PanResponder, ScrollView, View } from "react-native";
10
13
  import { useSafeAreaInsets } from "react-native-safe-area-context";
11
14
  /**
@@ -20,49 +23,45 @@ export function SheetProvider({ children }) {
20
23
  * **swipe-down to close** (handle only). By default it does NOT close on backdrop tap
21
24
  * (`dismissOnBackdrop`). No provider or native deps. Import via subpath `Dialog/Sheet`.
22
25
  */
23
- function Sheet({ visible, onClose, children, title, maxHeightRatio = 0.9, dismissOnBackdrop = false, testID, }) {
26
+ function Sheet({ visible, onClose, children, footer, footerClassName, title, maxHeightRatio = 0.9, dismissOnBackdrop = false, showCloseButton = false, testID, }) {
27
+ const theme = useTheme();
24
28
  const insets = useSafeAreaInsets();
25
- const translateY = useRef(new Animated.Value(800)).current;
26
- const onCloseRef = useRef(onClose);
27
- onCloseRef.current = onClose;
28
- useEffect(() => {
29
- if (visible) {
30
- Animated.spring(translateY, {
31
- toValue: 0,
32
- useNativeDriver: true,
33
- damping: 20,
34
- stiffness: 300,
35
- }).start();
36
- }
37
- else {
38
- translateY.setValue(800);
39
- }
40
- }, [visible, translateY]);
29
+ const { translateY, reducedMotion } = useSheetTranslation(visible);
30
+ const gesture = useRef({ onClose, reducedMotion });
31
+ gesture.current = { onClose, reducedMotion };
41
32
  const [pan] = useState(() => PanResponder.create({
42
33
  onStartShouldSetPanResponder: () => true,
43
34
  onMoveShouldSetPanResponder: (_, g) => g.dy > 2,
35
+ onPanResponderGrant: () => translateY.stopAnimation(),
44
36
  onPanResponderMove: (_, g) => {
45
37
  if (g.dy > 0)
46
38
  translateY.setValue(g.dy);
47
39
  },
48
40
  onPanResponderRelease: (_, g) => {
49
41
  if (g.dy > 100 || g.vy > 0.5) {
50
- Animated.timing(translateY, {
51
- toValue: 800,
52
- duration: 200,
53
- useNativeDriver: true,
54
- }).start(() => onCloseRef.current());
42
+ translateY.stopAnimation();
43
+ gesture.current.onClose();
55
44
  }
56
45
  else {
57
- Animated.spring(translateY, { toValue: 0, useNativeDriver: true }).start();
46
+ if (gesture.current.reducedMotion)
47
+ translateY.setValue(0);
48
+ else
49
+ Animated.spring(translateY, { toValue: 0, useNativeDriver: true }).start();
58
50
  }
59
51
  },
60
52
  }));
61
- 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, { className: "absolute inset-0 bg-black/50", onResponderRelease: dismissOnBackdrop ? onClose : undefined, onStartShouldSetResponder: () => dismissOnBackdrop }), _jsxs(Animated.View, { className: "rounded-t-3xl", style: {
62
- backgroundColor: theme.bg.card,
63
- paddingBottom: insets.bottom + 12,
64
- maxHeight: `${Math.round(maxHeightRatio * 100)}%`,
65
- transform: [{ translateY }],
66
- }, children: [_jsx(View, { className: "items-center pt-3 pb-2", ...pan.panHandlers, children: _jsx(View, { className: "w-12 h-1.5 rounded-full bg-border-strong" }) }), title ? (_jsx(Typography.Text, { className: "text-text-primary px-4 pb-1", semibold: true, variant: "subtitle", children: title })) : null, _jsx(ScrollView, { contentContainerStyle: { paddingHorizontal: 16, paddingTop: 4 }, children: children })] })] }) }));
53
+ 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, { className: "absolute inset-0 bg-black/50", onResponderRelease: dismissOnBackdrop ? onClose : undefined, onStartShouldSetResponder: () => dismissOnBackdrop }), _jsxs(Animated.View, { accessibilityViewIsModal: true, className: "rounded-t-3xl", importantForAccessibility: "yes", onAccessibilityEscape: onClose, style: [
54
+ vars(themeToVars(theme)),
55
+ {
56
+ backgroundColor: theme.bg.card,
57
+ paddingBottom: insets.bottom + 12,
58
+ maxHeight: `${Math.round(maxHeightRatio * 100)}%`,
59
+ transform: [{ translateY }],
60
+ },
61
+ ], children: [_jsx(View, { accessibilityElementsHidden: true, className: "items-center pt-3 pb-2", ...pan.panHandlers, children: _jsx(View, { className: "w-12 h-1.5 rounded-full bg-border-strong" }) }), title || showCloseButton ? (_jsxs(View, { className: "flex-row items-center px-4 pb-1", children: [title ? (_jsx(Typography.Text, { accessibilityRole: "header", className: "flex-1 text-text-primary", semibold: true, variant: "subtitle", children: title })) : null, showCloseButton ? (_jsx(Pressable, { accessibilityLabel: "Cerrar", accessibilityRole: "button", className: "items-center justify-center rounded-lg", hitSlop: 8, onPress: onClose, style: { minWidth: 48, minHeight: 48 }, children: _jsx(Typography.Text, { className: "text-text-accent", variant: "bodySmall", children: "Cerrar" }) })) : null] })) : null, _jsx(ScrollView, { contentContainerStyle: {
62
+ paddingHorizontal: 16,
63
+ paddingTop: 4,
64
+ paddingBottom: footer ? 12 : 0,
65
+ }, style: { flexShrink: 1 }, children: children }), footer ? (_jsx(View, { className: `border-t border-border-light px-4 pt-3 ${footerClassName ?? ""}`, children: footer })) : null] })] }) }));
67
66
  }
68
67
  export default Sheet;
@@ -1,18 +1,22 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
2
  import Icon from "./Display.Icon.js";
3
3
  import Pressable from "../primitives/Pressable.js";
4
- import { theme } from "../theme/index.js";
4
+ import { useTheme } from "../theme/index.js";
5
5
  import { cn } from "../theme/tw.js";
6
6
  import { Typography } from "../Typography/index.js";
7
+ import { useReducedMotion } from "../utils/useReducedMotion.js";
7
8
  import { useState } from "react";
8
9
  import { LayoutAnimation, View } from "react-native";
9
10
  function AccordionItem({ title, children, defaultOpen = false, className, testID, }) {
10
11
  const [open, setOpen] = useState(defaultOpen);
12
+ const theme = useTheme();
13
+ const reducedMotion = useReducedMotion();
11
14
  const toggle = () => {
12
- LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
15
+ if (!reducedMotion)
16
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
13
17
  setOpen((prev) => !prev);
14
18
  };
15
- return (_jsxs(View, { className: cn("border-b border-border-default", className), testID: testID, children: [_jsxs(Pressable, { accessibilityRole: "button", accessibilityState: { expanded: open }, className: "flex-row items-center justify-between py-3", onPress: toggle, children: [_jsx(Typography.Text, { className: "flex-1 text-text-primary", semibold: true, children: title }), _jsx(View, { style: { transform: [{ rotate: open ? "180deg" : "0deg" }] }, children: _jsx(Icon, { color: theme.icon.secondary, name: "chevron-down", size: 18 }) })] }), open ? _jsx(View, { className: "pb-3", children: children }) : null] }));
19
+ return (_jsxs(View, { className: cn("border-b border-border-default", className), testID: testID, children: [_jsxs(Pressable, { accessibilityLabel: title, accessibilityRole: "button", accessibilityState: { expanded: open }, className: "flex-row items-center justify-between py-3", onPress: toggle, style: { minHeight: 48 }, children: [_jsx(Typography.Text, { className: "flex-1 text-text-primary", semibold: true, children: title }), _jsx(View, { style: { transform: [{ rotate: open ? "180deg" : "0deg" }] }, children: _jsx(Icon, { color: theme.icon.secondary, name: "chevron-down", size: 18 }) })] }), open ? _jsx(View, { className: "pb-3", children: children }) : null] }));
16
20
  }
17
21
  function AccordionRoot({ children, className }) {
18
22
  return _jsx(View, { className: cn("bg-bg-card rounded-lg", className), children: children });
@@ -0,0 +1,28 @@
1
+ import { type ReactNode } from "react";
2
+ export interface AmountSummaryRootProps {
3
+ children: ReactNode;
4
+ currencySymbol: string;
5
+ variant?: "card" | "plain";
6
+ className?: string;
7
+ testID?: string;
8
+ }
9
+ declare function Root({ children, currencySymbol, variant, className, testID, }: AmountSummaryRootProps): import("react").JSX.Element;
10
+ export interface AmountSummaryRowProps {
11
+ label: string;
12
+ value: number | string | null | undefined;
13
+ currencySymbol?: string;
14
+ prefix?: string;
15
+ className?: string;
16
+ labelClassName?: string;
17
+ valueClassName?: string;
18
+ testID?: string;
19
+ }
20
+ declare function Row({ label, value, currencySymbol, prefix, className, labelClassName, valueClassName, testID, }: AmountSummaryRowProps): import("react").JSX.Element;
21
+ export type AmountSummaryTotalProps = AmountSummaryRowProps;
22
+ declare function Total(props: AmountSummaryTotalProps): import("react").JSX.Element;
23
+ declare const AmountSummary: {
24
+ Root: typeof Root;
25
+ Row: typeof Row;
26
+ Total: typeof Total;
27
+ };
28
+ export default AmountSummary;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "nativewind/jsx-runtime";
2
+ import { cn } from "../theme/tw.js";
3
+ import { Typography } from "../Typography/index.js";
4
+ import { createContext, useContext } from "react";
5
+ import { View } from "react-native";
6
+ import Monetary from "./Display.Monetary";
7
+ const AmountSummaryContext = createContext(undefined);
8
+ function Root({ children, currencySymbol, variant = "card", className, testID, }) {
9
+ return (_jsx(AmountSummaryContext.Provider, { value: { currencySymbol }, children: _jsx(View, { className: cn("gap-1", variant === "card"
10
+ ? "rounded-xl border border-border-light bg-bg-card px-4 py-2"
11
+ : "", className), testID: testID, children: children }) }));
12
+ }
13
+ function Row({ label, value, currencySymbol, prefix, className, labelClassName, valueClassName, testID, }) {
14
+ const context = useContext(AmountSummaryContext);
15
+ const resolvedCurrencySymbol = currencySymbol ?? context?.currencySymbol;
16
+ if (resolvedCurrencySymbol === undefined) {
17
+ throw new Error("AmountSummary.Row requires a currencySymbol or an AmountSummary.Root.");
18
+ }
19
+ return (_jsxs(View, { className: cn("flex-row items-center justify-between gap-3 py-1", className), testID: testID, children: [_jsx(Typography.Text, { className: cn("flex-1 text-text-secondary", labelClassName), children: label }), _jsx(Monetary, { className: cn("text-text-primary", valueClassName), currencySymbol: resolvedCurrencySymbol, prefix: prefix, semibold: true, textVariant: "body", value: value })] }));
20
+ }
21
+ function Total(props) {
22
+ return (_jsxs(_Fragment, { children: [_jsx(View, { className: "my-1 border-t border-border-light" }), _jsx(Row, { ...props, className: cn("py-2", props.className), labelClassName: cn("text-text-primary font-semibold", props.labelClassName), valueClassName: cn("text-text-primary", props.valueClassName) })] }));
23
+ }
24
+ const AmountSummary = { Root, Row, Total };
25
+ export default AmountSummary;
@@ -1,6 +1,17 @@
1
- import { type ReactNode } from "react";
2
- export interface CarouselProps<T> {
3
- data: T[];
1
+ import { Typography } from "../Typography/index.js";
2
+ import { type ComponentProps, type ReactNode } from "react";
3
+ import { type StyleProp, type ViewProps, type ViewStyle } from "react-native";
4
+ export interface CarouselControlsProps {
5
+ index: number;
6
+ count: number;
7
+ canPrevious: boolean;
8
+ canNext: boolean;
9
+ previous: () => void;
10
+ next: () => void;
11
+ goTo: (index: number) => void;
12
+ }
13
+ export interface CarouselProps<T> extends Omit<ViewProps, "children"> {
14
+ data: readonly T[];
4
15
  renderItem: (item: T, index: number) => ReactNode;
5
16
  keyExtractor: (item: T, index: number) => string;
6
17
  itemWidth: number;
@@ -8,6 +19,18 @@ export interface CarouselProps<T> {
8
19
  showDots?: boolean;
9
20
  className?: string;
10
21
  testID?: string;
22
+ index?: number;
23
+ onIndexChange?: (index: number) => void;
24
+ previousAccessibilityLabel?: string;
25
+ nextAccessibilityLabel?: string;
26
+ formatPageLabel?: (index: number, count: number) => string;
27
+ renderControls?: (props: CarouselControlsProps) => ReactNode;
28
+ controlsClassName?: string;
29
+ paginationTextProps?: ComponentProps<typeof Typography.Text>;
30
+ contentContainerStyle?: StyleProp<ViewStyle>;
31
+ itemStyle?: StyleProp<ViewStyle>;
32
+ dotStyle?: StyleProp<ViewStyle>;
33
+ activeDotStyle?: StyleProp<ViewStyle>;
11
34
  }
12
- declare function Carousel<T>({ data, renderItem, keyExtractor, itemWidth, gap, showDots, className, testID, }: CarouselProps<T>): import("react").JSX.Element;
35
+ declare function Carousel<T>({ data, renderItem, keyExtractor, itemWidth, gap, showDots, className, testID, index: controlledIndex, onIndexChange, previousAccessibilityLabel, nextAccessibilityLabel, formatPageLabel, renderControls, controlsClassName, paginationTextProps, contentContainerStyle, itemStyle, dotStyle, activeDotStyle, ...viewProps }: CarouselProps<T>): import("react").JSX.Element;
13
36
  export default Carousel;
@@ -1,19 +1,119 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
- import { theme } from "../theme/index.js";
3
- import { cn } from "../theme/tw.js";
4
- import { useState } from "react";
2
+ import IconButton from "../Actions/Actions.IconButton.js";
3
+ import { useTheme } from "../theme/index.js";
4
+ import { Typography } from "../Typography/index.js";
5
+ import { uniqueKeys } from "../utils/keys.js";
6
+ import { useReducedMotion } from "../utils/useReducedMotion.js";
7
+ import { useEffect, useRef, useState } from "react";
5
8
  import { ScrollView, View, } from "react-native";
6
- function Carousel({ data, renderItem, keyExtractor, itemWidth, gap = 12, showDots = true, className, testID, }) {
9
+ function Carousel({ data, renderItem, keyExtractor, itemWidth, gap = 12, showDots = true, className, testID, index: controlledIndex, onIndexChange, previousAccessibilityLabel = "Elemento anterior", nextAccessibilityLabel = "Elemento siguiente", formatPageLabel = (index, count) => `Elemento ${index + 1} de ${count}`, renderControls, controlsClassName, paginationTextProps, contentContainerStyle, itemStyle, dotStyle, activeDotStyle, ...viewProps }) {
10
+ if (!Number.isFinite(itemWidth) ||
11
+ itemWidth <= 0 ||
12
+ !Number.isFinite(gap) ||
13
+ gap < 0 ||
14
+ !Number.isFinite(itemWidth + gap))
15
+ throw new RangeError("Carousel requires a positive finite itemWidth and a non-negative finite gap.");
16
+ if (controlledIndex !== undefined &&
17
+ (!Number.isInteger(controlledIndex) || controlledIndex < 0))
18
+ throw new RangeError("Carousel index must be a non-negative integer.");
19
+ if (controlledIndex !== undefined && !onIndexChange)
20
+ throw new Error("Carousel requires onIndexChange when index is controlled.");
21
+ const keys = uniqueKeys(data, keyExtractor, "Carousel");
22
+ const theme = useTheme();
23
+ const reducedMotion = useReducedMotion();
7
24
  const [active, setActive] = useState(0);
25
+ const [viewportWidth, setViewportWidth] = useState(0);
26
+ const [scrollRevision, setScrollRevision] = useState(0);
27
+ const scrollRef = useRef(null);
28
+ const mounted = useRef(false);
8
29
  const snap = itemWidth + gap;
9
- const onScroll = (e) => {
10
- const index = Math.round(e.nativeEvent.contentOffset.x / snap);
11
- if (index !== active)
12
- setActive(index);
30
+ const current = Math.min(controlledIndex ?? active, Math.max(0, data.length - 1));
31
+ const nativePosition = useRef({ index: 0, snap, count: data.length, width: 0 });
32
+ if (controlledIndex === undefined && active !== current)
33
+ setActive(current);
34
+ useEffect(() => {
35
+ const previous = nativePosition.current;
36
+ if (previous.index !== current ||
37
+ previous.snap !== snap ||
38
+ previous.width !== viewportWidth) {
39
+ scrollRef.current?.scrollTo({
40
+ x: current * snap,
41
+ y: 0,
42
+ animated: mounted.current &&
43
+ !reducedMotion &&
44
+ previous.snap === snap &&
45
+ previous.count === data.length &&
46
+ previous.width === viewportWidth,
47
+ });
48
+ }
49
+ nativePosition.current = {
50
+ index: current,
51
+ snap,
52
+ count: data.length,
53
+ width: viewportWidth,
54
+ };
55
+ mounted.current = true;
56
+ if (controlledIndex === undefined &&
57
+ previous.count > data.length &&
58
+ previous.index !== current) {
59
+ onIndexChange?.(current);
60
+ }
61
+ }, [
62
+ active,
63
+ current,
64
+ controlledIndex,
65
+ data.length,
66
+ onIndexChange,
67
+ reducedMotion,
68
+ snap,
69
+ viewportWidth,
70
+ scrollRevision,
71
+ ]);
72
+ const changeIndex = (next) => {
73
+ setActive(next);
74
+ if (next !== current)
75
+ onIndexChange?.(next);
13
76
  };
14
- return (_jsxs(View, { className: className, testID: testID, children: [_jsx(ScrollView, { decelerationRate: "fast", horizontal: true, onMomentumScrollEnd: onScroll, showsHorizontalScrollIndicator: false, snapToInterval: snap, children: data.map((item, index) => (_jsx(View, { style: { width: itemWidth, marginRight: index < data.length - 1 ? gap : 0 }, children: renderItem(item, index) }, keyExtractor(item, index)))) }), showDots && data.length > 1 ? (_jsx(View, { className: "flex-row justify-center gap-1.5 mt-3", children: data.map((item, index) => (_jsx(View, { className: cn("h-1.5 rounded-full"), style: {
15
- width: index === active ? 16 : 6,
16
- backgroundColor: index === active ? theme.primary : theme.border.strong,
17
- } }, keyExtractor(item, index)))) })) : null] }));
77
+ const goTo = (next) => {
78
+ if (!Number.isInteger(next) || next < 0 || next >= data.length)
79
+ throw new RangeError("Carousel goTo requires an index within the current data.");
80
+ changeIndex(next);
81
+ };
82
+ const onScroll = (event) => {
83
+ const offset = event.nativeEvent.contentOffset.x;
84
+ if (!Number.isFinite(offset))
85
+ throw new RangeError("Carousel received a non-finite scroll offset.");
86
+ const next = Math.max(0, Math.min(Math.round(offset / snap), data.length - 1));
87
+ nativePosition.current = { index: next, snap, count: data.length, width: viewportWidth };
88
+ changeIndex(next);
89
+ if (controlledIndex !== undefined)
90
+ setScrollRevision((revision) => revision + 1);
91
+ };
92
+ const controls = {
93
+ index: current,
94
+ count: data.length,
95
+ canPrevious: current > 0,
96
+ canNext: current < data.length - 1,
97
+ previous: () => {
98
+ if (current > 0)
99
+ goTo(current - 1);
100
+ },
101
+ next: () => {
102
+ if (current < data.length - 1)
103
+ goTo(current + 1);
104
+ },
105
+ goTo,
106
+ };
107
+ return (_jsxs(View, { ...viewProps, className: className, testID: testID, children: [_jsxs(ScrollView, { contentContainerStyle: contentContainerStyle, decelerationRate: "fast", horizontal: true, onLayout: (event) => setViewportWidth(event.nativeEvent.layout.width), onMomentumScrollEnd: onScroll, ref: scrollRef, showsHorizontalScrollIndicator: false, snapToInterval: snap, testID: testID ? `${testID}-scroll` : undefined, children: [data.map((item, index) => (_jsx(View, { style: [
108
+ itemStyle,
109
+ { width: itemWidth, marginRight: index < data.length - 1 ? gap : 0 },
110
+ ], children: renderItem(item, index) }, keys[index]))), data.length > 0 ? (_jsx(View, { accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", style: { width: Math.max(0, viewportWidth - itemWidth) }, testID: testID ? `${testID}-end-spacing` : undefined })) : null] }), showDots && data.length > 1 ? (_jsx(View, { accessibilityElementsHidden: true, className: "flex-row justify-center gap-1.5 mt-3", importantForAccessibility: "no-hide-descendants", children: data.map((_item, index) => (_jsx(View, { className: "h-1.5 rounded-full", style: [
111
+ {
112
+ width: index === current ? 16 : 6,
113
+ backgroundColor: index === current ? theme.primary : theme.border.strong,
114
+ },
115
+ dotStyle,
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] }));
18
118
  }
19
119
  export default Carousel;
@@ -1,4 +1,4 @@
1
- type CopyableTextProps = {
1
+ export type CopyableTextProps = ({
2
2
  value: string;
3
3
  getValue?: never;
4
4
  label?: string;
@@ -8,6 +8,13 @@ type CopyableTextProps = {
8
8
  getValue: () => string;
9
9
  label?: string;
10
10
  className?: string;
11
+ }) & {
12
+ accessibilityLabel?: string;
13
+ testID?: string;
14
+ disabled?: boolean;
15
+ onCopied?: (value: string) => void;
16
+ onCopyError?: (error: Error) => void;
17
+ selectable?: boolean;
11
18
  };
12
- declare function CopyableText({ value, getValue, label, className }: CopyableTextProps): import("react").JSX.Element;
19
+ declare function CopyableText({ value, getValue, label, className, accessibilityLabel, testID, disabled, onCopied, onCopyError, selectable, }: CopyableTextProps): import("react").JSX.Element;
13
20
  export default CopyableText;