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