@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.
- package/README.md +701 -231
- package/dist/Actions/Actions.Button.d.ts +15 -12
- package/dist/Actions/Actions.Button.js +25 -14
- package/dist/Actions/Actions.Dock.d.ts +20 -0
- package/dist/Actions/Actions.Dock.js +89 -0
- package/dist/Actions/Actions.FAB.d.ts +2 -1
- package/dist/Actions/Actions.FAB.js +5 -3
- package/dist/Actions/Actions.FilterButton.d.ts +10 -2
- package/dist/Actions/Actions.FilterButton.js +21 -6
- package/dist/Actions/Actions.IconButton.d.ts +4 -2
- package/dist/Actions/Actions.IconButton.js +7 -3
- package/dist/Actions/index.d.ts +6 -2
- package/dist/Actions/index.js +4 -1
- package/dist/Dialog/BottomSheet/BottomSheet.Root.js +22 -34
- package/dist/Dialog/Dialog.ActionSheet.d.ts +2 -1
- package/dist/Dialog/Dialog.ActionSheet.js +17 -21
- package/dist/Dialog/Dialog.Confirmation.js +8 -6
- package/dist/Dialog/Dialog.EditValue.js +3 -1
- package/dist/Dialog/Dialog.NativeSheet.d.ts +10 -0
- package/dist/Dialog/Dialog.NativeSheet.js +21 -0
- package/dist/Dialog/Dialog.Sheet.d.ts +5 -1
- package/dist/Dialog/Dialog.Sheet.js +30 -31
- package/dist/Display/Display.Accordion.js +7 -3
- package/dist/Display/Display.AmountSummary.d.ts +28 -0
- package/dist/Display/Display.AmountSummary.js +25 -0
- package/dist/Display/Display.Carousel.d.ts +27 -4
- package/dist/Display/Display.Carousel.js +112 -12
- package/dist/Display/Display.CopyableText.d.ts +9 -2
- package/dist/Display/Display.CopyableText.js +75 -7
- package/dist/Display/Display.ExpoImage.d.ts +8 -0
- package/dist/Display/Display.ExpoImage.js +15 -0
- package/dist/Display/Display.Icon.js +2 -1
- package/dist/Display/Display.Image.d.ts +10 -6
- package/dist/Display/Display.Image.js +39 -6
- package/dist/Display/Display.KeyValue.d.ts +11 -4
- package/dist/Display/Display.KeyValue.js +6 -4
- package/dist/Display/Display.ListGroup.d.ts +23 -0
- package/dist/Display/Display.ListGroup.js +17 -0
- package/dist/Display/Display.ListItem.d.ts +15 -2
- package/dist/Display/Display.ListItem.js +25 -8
- package/dist/Display/Display.Logo.d.ts +2 -2
- package/dist/Display/Display.Logo.js +3 -2
- package/dist/Display/Display.MediaCard.d.ts +2 -2
- package/dist/Display/Display.MediaEmbed.d.ts +2 -2
- package/dist/Display/Display.ProductCard.d.ts +2 -2
- package/dist/Display/Display.SectionHeader.d.ts +11 -1
- package/dist/Display/Display.SectionHeader.js +11 -2
- package/dist/Display/Display.Story.d.ts +3 -3
- package/dist/Display/Display.Story.js +5 -4
- package/dist/Display/Display.StoryTray.d.ts +4 -4
- package/dist/Display/Display.StoryTray.js +3 -2
- package/dist/Display/Display.StoryViewer.d.ts +3 -3
- package/dist/Display/Display.StoryViewer.js +6 -3
- package/dist/Display/Display.SwipeableRow.d.ts +3 -1
- package/dist/Display/Display.SwipeableRow.js +3 -3
- package/dist/Display/index.d.ts +13 -0
- package/dist/Display/index.js +6 -0
- package/dist/Feedback/Feedback.Banner.js +1 -1
- package/dist/Feedback/Feedback.DomainSplashScreen.d.ts +13 -4
- package/dist/Feedback/Feedback.DomainSplashScreen.js +68 -53
- package/dist/Feedback/Feedback.EmptyState.d.ts +2 -2
- package/dist/Feedback/Feedback.EmptyState.js +3 -2
- package/dist/Feedback/Feedback.Error.d.ts +2 -1
- package/dist/Feedback/Feedback.Error.js +4 -3
- package/dist/Feedback/Feedback.Loading.d.ts +6 -2
- package/dist/Feedback/Feedback.Loading.js +2 -2
- package/dist/Feedback/Feedback.Progress.d.ts +6 -3
- package/dist/Feedback/Feedback.Progress.js +29 -19
- package/dist/Feedback/Feedback.ProgressCircle.d.ts +3 -2
- package/dist/Feedback/Feedback.ProgressCircle.js +10 -7
- package/dist/Feedback/Feedback.Skeleton.d.ts +4 -4
- package/dist/Feedback/Feedback.Skeleton.js +15 -3
- package/dist/Feedback/Feedback.Spinner.d.ts +3 -3
- package/dist/Feedback/Feedback.Spinner.js +40 -9
- package/dist/Feedback/Feedback.Splash.d.ts +2 -2
- package/dist/Feedback/Feedback.Splash.js +5 -3
- package/dist/Feedback/Toast.context.d.ts +64 -3
- package/dist/Feedback/Toast.context.js +112 -31
- package/dist/Feedback/index.d.ts +15 -1
- package/dist/Feedback/index.js +4 -1
- package/dist/Form/Form.Checkbox.d.ts +3 -1
- package/dist/Form/Form.Checkbox.js +2 -2
- package/dist/Form/Form.DatePicker.js +3 -2
- package/dist/Form/Form.Dropdown.d.ts +15 -3
- package/dist/Form/Form.Dropdown.js +21 -6
- package/dist/Form/Form.ErrorSummary.d.ts +23 -0
- package/dist/Form/Form.ErrorSummary.js +53 -0
- package/dist/Form/Form.FilterButton.d.ts +1 -1
- package/dist/Form/Form.Input.d.ts +4 -3
- package/dist/Form/Form.Input.js +2 -2
- package/dist/Form/Form.KeyboardForm.d.ts +11 -0
- package/dist/Form/Form.KeyboardForm.js +9 -0
- package/dist/Form/Form.KeyboardToolbar.d.ts +21 -0
- package/dist/Form/Form.KeyboardToolbar.js +70 -0
- package/dist/Form/Form.Select.d.ts +2 -1
- package/dist/Form/Form.Select.js +26 -26
- package/dist/Form/index.d.ts +9 -0
- package/dist/Form/index.js +6 -0
- package/dist/Navigation/Navigation.AppBar.js +8 -6
- package/dist/Navigation/Navigation.BottomNav.js +10 -5
- package/dist/Navigation/Navigation.Header.d.ts +2 -2
- package/dist/Navigation/Navigation.Menu.js +10 -3
- package/dist/Navigation/Navigation.RowItem.d.ts +1 -1
- package/dist/Navigation/Navigation.RowItem.js +2 -2
- package/dist/Navigation/Navigation.Search.d.ts +41 -0
- package/dist/Navigation/Navigation.Search.js +47 -0
- package/dist/Navigation/Navigation.SearchBar.d.ts +5 -1
- package/dist/Navigation/Navigation.SearchBar.js +25 -7
- package/dist/Navigation/Navigation.SearchSuggestions.d.ts +14 -0
- package/dist/Navigation/Navigation.SearchSuggestions.js +7 -0
- package/dist/Navigation/Navigation.SegmentedControl.d.ts +11 -1
- package/dist/Navigation/Navigation.SegmentedControl.js +29 -15
- package/dist/Navigation/index.d.ts +13 -0
- package/dist/Navigation/index.js +4 -0
- package/dist/Typography/Typography.Heading.js +1 -1
- package/dist/Typography/Typography.SelectableText.d.ts +10 -0
- package/dist/Typography/Typography.SelectableText.js +16 -0
- package/dist/Typography/Typography.Text.js +4 -20
- package/dist/Typography/index.d.ts +3 -0
- package/dist/Typography/index.js +3 -0
- package/dist/branding.d.ts +3 -2
- package/dist/layouts/AuthContainer.d.ts +4 -1
- package/dist/layouts/AuthContainer.js +3 -3
- package/dist/primitives/BaseButton.d.ts +15 -12
- package/dist/primitives/BaseButton.js +21 -9
- package/dist/theme/typography.d.ts +16 -1
- package/dist/theme/typography.js +19 -0
- package/dist/utils/criteria.d.ts +56 -0
- package/dist/utils/criteria.js +99 -0
- package/dist/utils/image.d.ts +3 -0
- package/dist/utils/image.js +13 -0
- package/dist/utils/keys.d.ts +1 -0
- package/dist/utils/keys.js +7 -0
- package/dist/utils/progress.d.ts +1 -0
- package/dist/utils/progress.js +5 -0
- package/dist/utils/reorder.d.ts +4 -0
- package/dist/utils/reorder.js +29 -0
- package/dist/utils/useReducedMotion.d.ts +1 -0
- package/dist/utils/useReducedMotion.js +43 -0
- package/dist/utils/useSheetTranslation.d.ts +5 -0
- package/dist/utils/useSheetTranslation.js +23 -0
- package/dist/views/AppSplashView.d.ts +3 -2
- package/dist/views/AppSplashView.js +3 -2
- package/dist/views/AuthFormView.d.ts +6 -3
- package/dist/views/AuthFormView.js +2 -2
- package/dist/views/FormViewV2.d.ts +9 -4
- package/dist/views/FormViewV2.js +35 -16
- package/dist/views/ListViewV2.context.d.ts +3 -1
- package/dist/views/ListViewV2.context.js +41 -21
- package/dist/views/ListViewV2.d.ts +18 -7
- package/dist/views/ListViewV2.js +69 -35
- package/dist/views/types/IListView.js +6 -5
- package/dist/widgets/CollectionOptions.d.ts +15 -0
- package/dist/widgets/CollectionOptions.js +129 -0
- package/dist/widgets/FilterBar.d.ts +21 -0
- package/dist/widgets/FilterBar.js +14 -0
- package/dist/widgets/FilterBuilder.d.ts +10 -0
- package/dist/widgets/FilterBuilder.js +115 -0
- package/dist/widgets/HomeHeader.d.ts +2 -2
- package/dist/widgets/PromoModal.js +3 -1
- package/dist/widgets/QueryChoice.d.ts +10 -0
- package/dist/widgets/QueryChoice.js +13 -0
- package/dist/widgets/ReorderableList.d.ts +51 -0
- package/dist/widgets/ReorderableList.js +138 -0
- package/dist/widgets/ScreenHeader.d.ts +2 -2
- package/dist/widgets/index.d.ts +4 -0
- package/dist/widgets/index.js +3 -0
- 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 {
|
|
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 {
|
|
8
|
-
import {
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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 = "¿
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
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 {
|
|
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 =
|
|
26
|
-
const
|
|
27
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
duration: 200,
|
|
53
|
-
useNativeDriver: true,
|
|
54
|
-
}).start(() => onCloseRef.current());
|
|
42
|
+
translateY.stopAnimation();
|
|
43
|
+
gesture.current.onClose();
|
|
55
44
|
}
|
|
56
45
|
else {
|
|
57
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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 {
|
|
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
|
-
|
|
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 {
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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;
|