@sincpro/mobile-ui 0.3.0 → 0.3.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/dist/Dialog/BottomSheet/BottomSheet.Root.d.ts +2 -2
- package/dist/Dialog/BottomSheet/BottomSheet.Root.js +2 -2
- package/dist/Dialog/Dialog.Confirmation.js +5 -1
- package/dist/Dialog/Dialog.EditValue.js +1 -1
- package/dist/Display/Display.GradientSurface.d.ts +116 -0
- package/dist/Display/Display.GradientSurface.js +62 -0
- package/dist/Display/Display.KeyValue.js +1 -1
- package/dist/Display/Display.ListItem.d.ts +5 -1
- package/dist/Display/Display.ListItem.js +6 -2
- package/dist/Display/Display.Monetary.js +1 -1
- package/dist/Display/Display.Pattern.d.ts +26 -0
- package/dist/Display/Display.Pattern.js +27 -0
- package/dist/Display/Display.PriceTag.js +3 -3
- package/dist/Display/Display.Stat.js +1 -1
- package/dist/Display/Display.StatCard.js +1 -1
- package/dist/Display/index.d.ts +4 -0
- package/dist/Display/index.js +4 -0
- package/dist/Feedback/Feedback.DomainSplashScreen.js +3 -2
- package/dist/Feedback/Feedback.Loading.d.ts +6 -0
- package/dist/Feedback/Feedback.Loading.js +7 -1
- package/dist/Feedback/Feedback.Spinner.js +2 -1
- package/dist/Feedback/Feedback.Splash.d.ts +7 -1
- package/dist/Feedback/Feedback.Splash.js +11 -2
- package/dist/Feedback/Toast.context.d.ts +8 -1
- package/dist/Feedback/Toast.context.js +25 -23
- package/dist/Form/Form.Button.d.ts +4 -4
- package/dist/Form/Form.Button.js +19 -19
- package/dist/Form/Form.Checkbox.js +1 -1
- package/dist/Form/Form.Input.js +1 -1
- package/dist/Form/Form.InputField.js +1 -1
- package/dist/Form/Form.Switch.js +1 -1
- package/dist/Navigation/Navigation.AppBar.d.ts +18 -2
- package/dist/Navigation/Navigation.AppBar.js +48 -9
- package/dist/Navigation/Navigation.BottomNav.d.ts +33 -5
- package/dist/Navigation/Navigation.BottomNav.js +90 -17
- package/dist/Navigation/Navigation.SearchBar.js +7 -1
- package/dist/Navigation/Navigation.TabIcon.d.ts +4 -0
- package/dist/Navigation/Navigation.TabIcon.js +4 -0
- package/dist/Navigation/Navigation.TabItem.d.ts +5 -0
- package/dist/Navigation/Navigation.TabItem.js +5 -0
- package/dist/Navigation/index.d.ts +2 -2
- package/dist/Typography/Typography.Text.d.ts +1 -1
- package/dist/Typography/Typography.Text.js +16 -6
- package/dist/icons/OdooIcon.d.ts +5 -0
- package/dist/icons/OdooIcon.js +5 -0
- package/dist/icons/PrinterIcon.d.ts +5 -0
- package/dist/icons/PrinterIcon.js +5 -0
- package/dist/layouts/AuthContainer.js +2 -2
- package/dist/theme/dark.d.ts +3 -3
- package/dist/theme/dark.js +54 -53
- package/dist/theme/default.js +1 -0
- package/dist/theme/index.d.ts +1 -0
- package/dist/theme/index.js +1 -0
- package/dist/theme/types.d.ts +1 -0
- package/dist/theme/typography.d.ts +117 -60
- package/dist/theme/typography.js +115 -29
- package/dist/utils/timezone.d.ts +5 -0
- package/dist/views/FormViewV2.d.ts +2 -1
- package/dist/views/FormViewV2.js +8 -4
- package/dist/views/ListViewV2.context.d.ts +5 -1
- package/dist/views/ListViewV2.context.js +3 -1
- package/dist/views/ListViewV2.d.ts +7 -2
- package/dist/views/ListViewV2.js +18 -20
- package/dist/widgets/GeoPermissionCard.d.ts +5 -0
- package/dist/widgets/GeoPermissionCard.js +8 -3
- package/dist/widgets/JSONViewer.js +3 -6
- package/dist/widgets/TimeZoneSelector.d.ts +5 -0
- package/dist/widgets/TimeZoneSelector.js +6 -1
- package/dist/widgets/TimeZoneSelectorModal.d.ts +5 -0
- package/dist/widgets/TimeZoneSelectorModal.js +8 -3
- package/package.json +12 -7
|
@@ -6,26 +6,18 @@ import { tv } from "../theme/tw";
|
|
|
6
6
|
import { Typography } from "../Typography";
|
|
7
7
|
import { createContext, useCallback, useContext, useMemo, useRef, useState, } from "react";
|
|
8
8
|
import { ActivityIndicator, View } from "react-native";
|
|
9
|
-
import Animated, { FadeInDown, FadeOutDown } from "react-native-reanimated";
|
|
9
|
+
import Animated, { FadeInDown, FadeInUp, FadeOutDown, FadeOutUp, } from "react-native-reanimated";
|
|
10
10
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
11
11
|
const ToastContext = createContext(null);
|
|
12
12
|
const DEFAULT_DURATION = 3500;
|
|
13
|
+
// iOS-style card: rounded, hairline border + soft shadow, neutral surface. The variant is
|
|
14
|
+
// conveyed by the colored icon (no left-accent stripe — it doesn't read well on iOS).
|
|
13
15
|
const toast = tv({
|
|
14
16
|
slots: {
|
|
15
|
-
root: "flex-row items-center gap-3 rounded-
|
|
17
|
+
root: "flex-row items-center gap-3 rounded-2xl px-4 py-3 bg-bg-card border border-border-light shadow-lg",
|
|
16
18
|
title: "text-sm font-semibold text-text-primary",
|
|
17
19
|
message: "text-sm text-text-secondary",
|
|
18
20
|
},
|
|
19
|
-
variants: {
|
|
20
|
-
variant: {
|
|
21
|
-
info: { root: "border-info" },
|
|
22
|
-
success: { root: "border-success" },
|
|
23
|
-
warning: { root: "border-warning" },
|
|
24
|
-
danger: { root: "border-danger" },
|
|
25
|
-
loading: { root: "border-primary" },
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
defaultVariants: { variant: "info" },
|
|
29
21
|
});
|
|
30
22
|
const iconByVariant = {
|
|
31
23
|
info: { name: "information-circle", color: theme.info },
|
|
@@ -33,15 +25,19 @@ const iconByVariant = {
|
|
|
33
25
|
warning: { name: "warning", color: theme.warning },
|
|
34
26
|
danger: { name: "close-circle", color: theme.danger },
|
|
35
27
|
};
|
|
36
|
-
function ToastItem({ entry, onHide }) {
|
|
28
|
+
function ToastItem({ entry, onHide, position, }) {
|
|
37
29
|
const variant = entry.variant ?? "info";
|
|
38
|
-
const styles = toast(
|
|
39
|
-
|
|
30
|
+
const styles = toast();
|
|
31
|
+
const entering = position === "top" ? FadeInDown : FadeInUp;
|
|
32
|
+
const exiting = position === "top" ? FadeOutUp : FadeOutDown;
|
|
33
|
+
const resolvedDuration = entry.duration ?? (variant === "loading" ? 0 : DEFAULT_DURATION);
|
|
34
|
+
const showClose = entry.dismissible ?? resolvedDuration === 0;
|
|
35
|
+
return (_jsxs(Animated.View, { accessibilityLiveRegion: "polite", accessible: true, className: styles.root(), entering: entering.duration(220), exiting: exiting.duration(180), children: [variant === "loading" ? (_jsx(ActivityIndicator, { color: theme.primary, size: "small" })) : (_jsx(Icon, { color: iconByVariant[variant].color, name: iconByVariant[variant].name, size: 22 })), _jsxs(View, { className: "flex-1", children: [entry.title ? (_jsx(Typography.Text, { className: styles.title(), children: entry.title })) : null, _jsx(Typography.Text, { className: styles.message(), children: entry.message })] }), entry.action ? (_jsx(Pressable, { accessibilityLabel: entry.action.label, onPress: () => {
|
|
40
36
|
entry.action?.onPress();
|
|
41
37
|
onHide(entry.id);
|
|
42
|
-
}, children: _jsx(Typography.Text, { className: "text-primary text-sm font-semibold", children: entry.action.label }) })) : null] }));
|
|
38
|
+
}, children: _jsx(Typography.Text, { className: "text-primary text-sm font-semibold", children: entry.action.label }) })) : null, showClose ? (_jsx(Pressable, { accessibilityLabel: "Cerrar", hitSlop: 8, onPress: () => onHide(entry.id), children: _jsx(Icon, { color: theme.text.tertiary, name: "close", size: 18 }) })) : null] }));
|
|
43
39
|
}
|
|
44
|
-
export function ToastProvider({ children }) {
|
|
40
|
+
export function ToastProvider({ children, position = "bottom", }) {
|
|
45
41
|
const insets = useSafeAreaInsets();
|
|
46
42
|
const [toasts, setToasts] = useState([]);
|
|
47
43
|
const counter = useRef(0);
|
|
@@ -65,11 +61,17 @@ export function ToastProvider({ children }) {
|
|
|
65
61
|
}
|
|
66
62
|
}, [clearTimer, hide]);
|
|
67
63
|
const show = useCallback((options) => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
let id = options.id;
|
|
65
|
+
if (!id) {
|
|
66
|
+
counter.current += 1;
|
|
67
|
+
id = `toast-${counter.current}`;
|
|
68
|
+
}
|
|
69
|
+
const toastId = id;
|
|
70
|
+
setToasts((prev) => prev.some((t) => t.id === toastId)
|
|
71
|
+
? prev.map((t) => (t.id === toastId ? { ...t, ...options, id: toastId } : t))
|
|
72
|
+
: [...prev, { ...options, id: toastId }]);
|
|
73
|
+
scheduleDismiss(toastId, options.variant, options.duration);
|
|
74
|
+
return toastId;
|
|
73
75
|
}, [scheduleDismiss]);
|
|
74
76
|
const update = useCallback((id, options) => {
|
|
75
77
|
setToasts((prev) => prev.map((t) => (t.id === id ? { ...t, ...options } : t)));
|
|
@@ -108,7 +110,7 @@ export function ToastProvider({ children }) {
|
|
|
108
110
|
promise,
|
|
109
111
|
};
|
|
110
112
|
}, [show, update, hide]);
|
|
111
|
-
return (_jsxs(ToastContext.Provider, { value: value, children: [children, _jsx(View, { className: "absolute left-0 right-0 px-4 gap-2 z-toast", pointerEvents: "box-none", style: { bottom: insets.bottom + 12 }, children: toasts.map((entry) => (_jsx(ToastItem, { entry: entry, onHide: hide }, entry.id))) })] }));
|
|
113
|
+
return (_jsxs(ToastContext.Provider, { value: value, children: [children, _jsx(View, { className: "absolute left-0 right-0 px-4 gap-2 z-toast", pointerEvents: "box-none", style: position === "top" ? { top: insets.top + 12 } : { bottom: insets.bottom + 12 }, children: toasts.map((entry) => (_jsx(ToastItem, { entry: entry, onHide: hide, position: position }, entry.id))) })] }));
|
|
112
114
|
}
|
|
113
115
|
export function useToast() {
|
|
114
116
|
const ctx = useContext(ToastContext);
|
|
@@ -21,7 +21,7 @@ declare const button: import("tailwind-variants").TVReturnType<{
|
|
|
21
21
|
disabled: {
|
|
22
22
|
true: string;
|
|
23
23
|
};
|
|
24
|
-
}, undefined, "rounded-
|
|
24
|
+
}, undefined, "rounded-[13px] items-center justify-center flex-row min-h-[44px]", {
|
|
25
25
|
variant: {
|
|
26
26
|
primary: string;
|
|
27
27
|
accent: string;
|
|
@@ -63,7 +63,7 @@ declare const button: import("tailwind-variants").TVReturnType<{
|
|
|
63
63
|
disabled: {
|
|
64
64
|
true: string;
|
|
65
65
|
};
|
|
66
|
-
}, undefined, "rounded-
|
|
66
|
+
}, undefined, "rounded-[13px] items-center justify-center flex-row min-h-[44px]", unknown, unknown, undefined>>;
|
|
67
67
|
declare const buttonIcon: import("tailwind-variants").TVReturnType<{
|
|
68
68
|
variant: {
|
|
69
69
|
primary: string;
|
|
@@ -85,7 +85,7 @@ declare const buttonIcon: import("tailwind-variants").TVReturnType<{
|
|
|
85
85
|
disabled: {
|
|
86
86
|
true: string;
|
|
87
87
|
};
|
|
88
|
-
}, undefined, "rounded-
|
|
88
|
+
}, undefined, "rounded-[13px] items-center justify-center aspect-square", {
|
|
89
89
|
variant: {
|
|
90
90
|
primary: string;
|
|
91
91
|
accent: string;
|
|
@@ -127,7 +127,7 @@ declare const buttonIcon: import("tailwind-variants").TVReturnType<{
|
|
|
127
127
|
disabled: {
|
|
128
128
|
true: string;
|
|
129
129
|
};
|
|
130
|
-
}, undefined, "rounded-
|
|
130
|
+
}, undefined, "rounded-[13px] items-center justify-center aspect-square", unknown, unknown, undefined>>;
|
|
131
131
|
export interface ButtonProps extends VariantProps<typeof button> {
|
|
132
132
|
title: string;
|
|
133
133
|
onPress?: () => void;
|
package/dist/Form/Form.Button.js
CHANGED
|
@@ -12,13 +12,13 @@ import { runOnJS } from "react-native-reanimated";
|
|
|
12
12
|
// STYLE VARIANTS
|
|
13
13
|
// ============================================================================
|
|
14
14
|
const button = tv({
|
|
15
|
-
base: "rounded-
|
|
15
|
+
base: "rounded-[13px] items-center justify-center flex-row min-h-[44px]",
|
|
16
16
|
variants: {
|
|
17
17
|
variant: {
|
|
18
18
|
primary: "shadow-sm",
|
|
19
19
|
accent: "shadow-sm",
|
|
20
20
|
cta: "shadow-sm",
|
|
21
|
-
secondary: "bg-bg-
|
|
21
|
+
secondary: "bg-bg-muted",
|
|
22
22
|
outline: "bg-transparent border border-primary",
|
|
23
23
|
outlineDanger: "bg-transparent border border-danger",
|
|
24
24
|
outlineBlack: "bg-transparent border border-border-strong",
|
|
@@ -41,13 +41,13 @@ const button = tv({
|
|
|
41
41
|
},
|
|
42
42
|
});
|
|
43
43
|
const buttonIcon = tv({
|
|
44
|
-
base: "rounded-
|
|
44
|
+
base: "rounded-[13px] items-center justify-center aspect-square",
|
|
45
45
|
variants: {
|
|
46
46
|
variant: {
|
|
47
47
|
primary: "shadow-sm",
|
|
48
48
|
accent: "shadow-sm",
|
|
49
49
|
cta: "shadow-sm",
|
|
50
|
-
secondary: "bg-bg-
|
|
50
|
+
secondary: "bg-bg-muted",
|
|
51
51
|
outline: "bg-transparent border border-primary",
|
|
52
52
|
outlineDanger: "bg-transparent border border-danger",
|
|
53
53
|
outlineBlack: "bg-transparent border border-border-strong",
|
|
@@ -73,9 +73,9 @@ const textColor = tv({
|
|
|
73
73
|
variants: {
|
|
74
74
|
variant: {
|
|
75
75
|
primary: "text-white",
|
|
76
|
-
accent: "text-
|
|
77
|
-
cta: "text-
|
|
78
|
-
secondary: "text-primary",
|
|
76
|
+
accent: "text-text-on-accent",
|
|
77
|
+
cta: "text-text-on-accent",
|
|
78
|
+
secondary: "text-text-primary",
|
|
79
79
|
outline: "text-primary",
|
|
80
80
|
outlineDanger: "text-danger",
|
|
81
81
|
outlineBlack: "text-text-primary",
|
|
@@ -96,12 +96,12 @@ const textColor = tv({
|
|
|
96
96
|
{
|
|
97
97
|
variant: "accent",
|
|
98
98
|
disabled: true,
|
|
99
|
-
className: "text-
|
|
99
|
+
className: "text-text-on-accent",
|
|
100
100
|
},
|
|
101
101
|
{
|
|
102
102
|
variant: "cta",
|
|
103
103
|
disabled: true,
|
|
104
|
-
className: "text-
|
|
104
|
+
className: "text-text-on-accent",
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
variant: "transparent",
|
|
@@ -142,7 +142,7 @@ function getLoadingColorDynamic(variant) {
|
|
|
142
142
|
primary: theme.text.inverse,
|
|
143
143
|
accent: theme.text.inverse,
|
|
144
144
|
cta: theme.text.inverse,
|
|
145
|
-
secondary: theme.primary,
|
|
145
|
+
secondary: theme.text.primary,
|
|
146
146
|
outline: theme.primary,
|
|
147
147
|
outlineDanger: theme.danger,
|
|
148
148
|
outlineBlack: theme.text.primary,
|
|
@@ -156,9 +156,9 @@ function getIconColorDynamic(variant, disabled) {
|
|
|
156
156
|
if (disabled) {
|
|
157
157
|
const disabledMap = {
|
|
158
158
|
primary: theme.icon.inverse + "70",
|
|
159
|
-
accent: theme.
|
|
160
|
-
cta: theme.
|
|
161
|
-
secondary: theme.
|
|
159
|
+
accent: theme.text.onAccent + "70",
|
|
160
|
+
cta: theme.text.onAccent + "70",
|
|
161
|
+
secondary: theme.text.disabled,
|
|
162
162
|
outline: theme.icon.disabled,
|
|
163
163
|
outlineDanger: theme.danger + "7f",
|
|
164
164
|
outlineBlack: theme.text.primary + "7f",
|
|
@@ -170,9 +170,9 @@ function getIconColorDynamic(variant, disabled) {
|
|
|
170
170
|
}
|
|
171
171
|
const enabledMap = {
|
|
172
172
|
primary: theme.icon.inverse,
|
|
173
|
-
accent: theme.
|
|
174
|
-
cta: theme.
|
|
175
|
-
secondary: theme.primary,
|
|
173
|
+
accent: theme.text.onAccent,
|
|
174
|
+
cta: theme.text.onAccent,
|
|
175
|
+
secondary: theme.text.primary,
|
|
176
176
|
outline: theme.primary,
|
|
177
177
|
outlineDanger: theme.danger,
|
|
178
178
|
outlineBlack: theme.text.primary,
|
|
@@ -239,7 +239,7 @@ const Button = ({ title, onPress, confirm, variant = "primary", size = "medium",
|
|
|
239
239
|
if (variant === "primary" || variant === "accent" || variant === "cta") {
|
|
240
240
|
const gradientColors = variant === "primary" ? theme.gradient.primary : theme.gradient.accent;
|
|
241
241
|
return (_jsx(LinearGradient, { colors: gradientColors, end: { x: 1, y: 1 }, start: { x: 0, y: 0 }, style: {
|
|
242
|
-
borderRadius:
|
|
242
|
+
borderRadius: 13,
|
|
243
243
|
opacity: disabled ? 0.7 : 1,
|
|
244
244
|
}, children: _jsx(TouchableOpacity, { activeOpacity: 0.8, disabled: disabled || loading, onPress: handlePress, children: _jsx(View, { className: button({ size, disabled }), style: { backgroundColor: "transparent" }, children: loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsxs(_Fragment, { children: [icon ? _jsx(View, { className: "mr-2", children: icon }) : null, _jsx(Typography.Text, { allowFontScaling: false, bold: true, className: textColorClass, ellipsizeMode: "tail", numberOfLines: 1, variant: textVariantValue, children: title })] })) }) }) }));
|
|
245
245
|
}
|
|
@@ -269,8 +269,8 @@ const ButtonIcon = ({ icon, onPress, confirm, variant = "primary", size = "mediu
|
|
|
269
269
|
}
|
|
270
270
|
}, [onPress, confirm, show, hide]);
|
|
271
271
|
if (variant === "primary") {
|
|
272
|
-
return (_jsx(LinearGradient, { colors:
|
|
273
|
-
borderRadius:
|
|
272
|
+
return (_jsx(LinearGradient, { colors: theme.gradient.primary, end: { x: 1, y: 1 }, start: { x: 0, y: 0 }, style: {
|
|
273
|
+
borderRadius: 13,
|
|
274
274
|
opacity: disabled ? 0.7 : 1,
|
|
275
275
|
}, children: _jsx(TouchableOpacity, { activeOpacity: 0.8, disabled: disabled || loading, onPress: handlePress, children: _jsx(View, { className: buttonIcon({ size, disabled }), style: { backgroundColor: "transparent" }, children: loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsx(Display.Icon, { color: iconColorValue, name: icon, size: iconSizeValue })) }) }) }));
|
|
276
276
|
}
|
|
@@ -25,6 +25,6 @@ function RowCheckBox({ mainText, subText, rightText, rightComponent, leftCompone
|
|
|
25
25
|
}
|
|
26
26
|
return null;
|
|
27
27
|
};
|
|
28
|
-
return (_jsxs(Pressable, { className: cn("flex-row justify-between items-center", disabled && "opacity-50", className), disabled: disabled, onPress: handlePress, children: [_jsx(View, { children: renderLeftComponent() }), _jsxs(View, { className: "flex-row items-center", children: [renderRightComponent(), _jsx(Checkbox, { className: "ml-2", color: theme.
|
|
28
|
+
return (_jsxs(Pressable, { className: cn("flex-row justify-between items-center", disabled && "opacity-50", className), disabled: disabled, onPress: handlePress, children: [_jsx(View, { children: renderLeftComponent() }), _jsxs(View, { className: "flex-row items-center", children: [renderRightComponent(), _jsx(Checkbox, { className: "ml-2", color: theme.accent, disabled: disabled, pointerEvents: "none", value: isChecked })] })] }));
|
|
29
29
|
}
|
|
30
30
|
export default RowCheckBox;
|
package/dist/Form/Form.Input.js
CHANGED
package/dist/Form/Form.Switch.js
CHANGED
|
@@ -4,6 +4,6 @@ import { cn } from "../theme/tw";
|
|
|
4
4
|
import { Typography } from "../Typography";
|
|
5
5
|
import { Switch as RNSwitch, View } from "react-native";
|
|
6
6
|
function Switch({ value, onValueChange, label, disabled = false, className, testID, }) {
|
|
7
|
-
return (_jsxs(View, { className: cn("flex-row items-center justify-between gap-3", className), children: [label ? (_jsx(Typography.Text, { className: cn("flex-1 text-text-primary", disabled && "text-text-disabled"), children: label })) : null, _jsx(RNSwitch, { accessibilityLabel: label, accessibilityRole: "switch", accessibilityState: { checked: value, disabled }, disabled: disabled, ios_backgroundColor: theme.bg.muted, onValueChange: onValueChange, testID: testID, thumbColor: theme.bg.card, trackColor: { false: theme.bg.muted, true: theme.
|
|
7
|
+
return (_jsxs(View, { className: cn("flex-row items-center justify-between gap-3", className), children: [label ? (_jsx(Typography.Text, { className: cn("flex-1 text-text-primary", disabled && "text-text-disabled"), children: label })) : null, _jsx(RNSwitch, { accessibilityLabel: label, accessibilityRole: "switch", accessibilityState: { checked: value, disabled }, disabled: disabled, ios_backgroundColor: theme.bg.muted, onValueChange: onValueChange, testID: testID, thumbColor: theme.bg.card, trackColor: { false: theme.bg.muted, true: theme.accent }, value: value })] }));
|
|
8
8
|
}
|
|
9
9
|
export default Switch;
|
|
@@ -6,13 +6,17 @@ export interface AppBarActionProps {
|
|
|
6
6
|
accessibilityLabel?: string;
|
|
7
7
|
/** `tinted` highlights (primary action); `plain` is the neutral contained default. */
|
|
8
8
|
tone?: "plain" | "tinted";
|
|
9
|
+
/** Render for a dark surface (translucent light button + light icon). */
|
|
10
|
+
onDark?: boolean;
|
|
11
|
+
/** Drop the contained surface → bare icon (iOS-style plain back/action). */
|
|
12
|
+
bare?: boolean;
|
|
9
13
|
testID?: string;
|
|
10
14
|
}
|
|
11
15
|
/**
|
|
12
16
|
* "Contained" icon-button for the AppBar: circular touch area with a subtle surface
|
|
13
17
|
* (merge of Material 3 state-layer + iOS 26 contained button). Optional badge.
|
|
14
18
|
*/
|
|
15
|
-
declare function AppBarAction({ icon, onPress, badge, accessibilityLabel, tone, testID, }: AppBarActionProps): import("react").JSX.Element;
|
|
19
|
+
declare function AppBarAction({ icon, onPress, badge, accessibilityLabel, tone, onDark, bare, testID, }: AppBarActionProps): import("react").JSX.Element;
|
|
16
20
|
export interface AppBarProps {
|
|
17
21
|
title?: string;
|
|
18
22
|
subtitle?: string;
|
|
@@ -21,6 +25,18 @@ export interface AppBarProps {
|
|
|
21
25
|
actions?: ReactNode;
|
|
22
26
|
subheader?: ReactNode;
|
|
23
27
|
variant?: "default" | "large" | "center";
|
|
28
|
+
/**
|
|
29
|
+
* Surface tone:
|
|
30
|
+
* - `default` — tokenized light card.
|
|
31
|
+
* - `dark` — always-dark hero (light text + light contained actions).
|
|
32
|
+
* - `gradient` — brand-accent gradient hero for greeting headers ("Buenos días, …"),
|
|
33
|
+
* with dark text via `text.onAccent` (reads `theme.gradient.accent`).
|
|
34
|
+
*/
|
|
35
|
+
tone?: "default" | "dark" | "gradient";
|
|
36
|
+
/** [large variant] Show a left vertical accent bar beside the large title. */
|
|
37
|
+
accentBar?: boolean;
|
|
38
|
+
/** Back button style: `contained` (circular surface) or `plain` (bare iOS chevron). */
|
|
39
|
+
backVariant?: "contained" | "plain";
|
|
24
40
|
border?: boolean;
|
|
25
41
|
/** Reserves the status-bar space (insets.top). `false` to embed/demo. */
|
|
26
42
|
safeArea?: boolean;
|
|
@@ -33,7 +49,7 @@ export interface AppBarProps {
|
|
|
33
49
|
* a `subheader` slot (for SearchBar/Segmented below the title), and a `large` variant.
|
|
34
50
|
* Replaces the gradient look of `Navigation.Header`/`ScreenHeader`.
|
|
35
51
|
*/
|
|
36
|
-
declare function AppBar({ title, subtitle, onBack, leading, actions, subheader, variant, border, safeArea, className, testID, }: AppBarProps): import("react").JSX.Element;
|
|
52
|
+
declare function AppBar({ title, subtitle, onBack, leading, actions, subheader, variant, tone, accentBar, backVariant, border, safeArea, className, testID, }: AppBarProps): import("react").JSX.Element;
|
|
37
53
|
declare const AppBarWithAction: typeof AppBar & {
|
|
38
54
|
Action: typeof AppBarAction;
|
|
39
55
|
};
|
|
@@ -1,21 +1,36 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
|
+
import GradientSurface from "../Display/Display.GradientSurface";
|
|
2
3
|
import Icon from "../Display/Display.Icon";
|
|
3
4
|
import Pressable from "../primitives/Pressable";
|
|
4
5
|
import { theme } from "../theme";
|
|
5
6
|
import { cn } from "../theme/tw";
|
|
6
7
|
import { Typography } from "../Typography";
|
|
7
|
-
import { View } from "react-native";
|
|
8
|
+
import { StyleSheet, View } from "react-native";
|
|
8
9
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
10
|
+
/** Fallback for the `tone="dark"` hero surface when the theme has no `bg.inverse` token. */
|
|
11
|
+
const HERO_DARK_FALLBACK = "#0E1714";
|
|
9
12
|
/**
|
|
10
13
|
* "Contained" icon-button for the AppBar: circular touch area with a subtle surface
|
|
11
14
|
* (merge of Material 3 state-layer + iOS 26 contained button). Optional badge.
|
|
12
15
|
*/
|
|
13
|
-
function AppBarAction({ icon, onPress, badge, accessibilityLabel, tone = "plain", testID, }) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
function AppBarAction({ icon, onPress, badge, accessibilityLabel, tone = "plain", onDark = false, bare = false, testID, }) {
|
|
17
|
+
const bg = bare
|
|
18
|
+
? "transparent"
|
|
19
|
+
: onDark
|
|
20
|
+
? "rgba(255,255,255,0.14)"
|
|
21
|
+
: tone === "tinted"
|
|
22
|
+
? theme.bg.accent
|
|
23
|
+
: theme.bg.card;
|
|
24
|
+
const iconColor = tone === "tinted" ? theme.primary : onDark ? theme.icon.inverse : theme.icon.primary;
|
|
25
|
+
// Plain (white) squircle gets a hairline so it reads as a contained button on a light bar.
|
|
26
|
+
const showBorder = !bare && !onDark && tone !== "tinted";
|
|
27
|
+
return (_jsxs(Pressable, { accessibilityLabel: accessibilityLabel, accessibilityRole: "button", className: "items-center justify-center", hitSlop: 6, onPress: onPress, style: {
|
|
28
|
+
width: 40,
|
|
29
|
+
height: 40,
|
|
30
|
+
borderRadius: 12,
|
|
31
|
+
backgroundColor: bg,
|
|
32
|
+
...(showBorder ? { borderWidth: 1, borderColor: theme.border.light } : null),
|
|
33
|
+
}, testID: testID, children: [_jsx(Icon, { color: iconColor, name: icon, size: bare ? 24 : 20 }), badge != null ? (_jsx(View, { className: "absolute bg-danger rounded-full min-w-[16px] h-4 px-1 items-center justify-center", style: { top: -2, right: -2, borderWidth: 2, borderColor: theme.bg.card }, children: _jsx(Typography.Text, { style: { color: theme.text.inverse, fontSize: 9, lineHeight: 11 }, children: badge }) })) : null] }));
|
|
19
34
|
}
|
|
20
35
|
/**
|
|
21
36
|
* Flat, modern top bar (merge of Material 3 + iOS 26): tokenized surface, left-aligned
|
|
@@ -23,10 +38,34 @@ function AppBarAction({ icon, onPress, badge, accessibilityLabel, tone = "plain"
|
|
|
23
38
|
* a `subheader` slot (for SearchBar/Segmented below the title), and a `large` variant.
|
|
24
39
|
* Replaces the gradient look of `Navigation.Header`/`ScreenHeader`.
|
|
25
40
|
*/
|
|
26
|
-
function AppBar({ title, subtitle, onBack, leading, actions, subheader, variant = "default", border = true, safeArea = true, className, testID, }) {
|
|
41
|
+
function AppBar({ title, subtitle, onBack, leading, actions, subheader, variant = "default", tone = "default", accentBar = false, backVariant = "contained", border = true, safeArea = true, className, testID, }) {
|
|
27
42
|
const insets = useSafeAreaInsets();
|
|
28
43
|
const centered = variant === "center";
|
|
29
|
-
|
|
44
|
+
const large = variant === "large";
|
|
45
|
+
const dark = tone === "dark";
|
|
46
|
+
const gradient = tone === "gradient";
|
|
47
|
+
// Greeting/balance hero (large, no back/leading): drop the empty top toolbar row and put
|
|
48
|
+
// the action(s) inline beside the big title — avoids a tall dead zone above the title.
|
|
49
|
+
const largeInlineActions = large && !onBack && !leading;
|
|
50
|
+
// Both `dark` and `gradient` are colored surfaces: no card bg, no hairline, light/dark text.
|
|
51
|
+
const colored = dark || gradient;
|
|
52
|
+
const onColoredText = gradient ? theme.text.onAccent : theme.text.inverse;
|
|
53
|
+
const titleStyle = colored ? { color: onColoredText } : undefined;
|
|
54
|
+
const subtitleStyle = gradient
|
|
55
|
+
? { color: theme.text.onAccent, opacity: 0.72 }
|
|
56
|
+
: dark
|
|
57
|
+
? { color: "rgba(255,255,255,0.7)" }
|
|
58
|
+
: undefined;
|
|
59
|
+
return (_jsxs(View, { className: cn(colored ? "" : "bg-bg-card", !colored && border && "border-b border-border-light", className), style: {
|
|
60
|
+
paddingTop: safeArea ? insets.top : 0,
|
|
61
|
+
...(dark ? { backgroundColor: theme.bg.inverse ?? HERO_DARK_FALLBACK } : null),
|
|
62
|
+
}, testID: testID, children: [gradient ? (_jsx(GradientSurface, { colors: theme.gradient.accent, padding: "none", radius: "none", style: StyleSheet.absoluteFill })) : null, largeInlineActions ? null : (_jsxs(View, { className: "flex-row items-center px-3 gap-2 min-h-[56px]", children: [_jsxs(View, { className: "flex-row items-center gap-2", style: { minWidth: 38 }, children: [onBack ? (_jsx(AppBarAction, { accessibilityLabel: "Volver", bare: backVariant === "plain", icon: "chevron-back", onDark: dark, onPress: onBack })) : null, leading] }), !large ? (_jsxs(View, { className: cn("flex-1", centered && "items-center"), children: [title ? (_jsx(Typography.Text, { className: colored ? "" : "text-text-primary", numberOfLines: 1, semibold: true, style: titleStyle, variant: "subtitle", children: title })) : null, subtitle ? (_jsx(Typography.Text, { className: colored ? "" : "text-text-secondary", numberOfLines: 1, style: subtitleStyle, variant: "caption", children: subtitle })) : null] })) : (_jsx(View, { className: "flex-1" })), _jsx(View, { className: "flex-row items-center justify-end gap-2", style: { minWidth: 38 }, children: actions })] })), large && title ? (_jsxs(View, { className: cn("flex-row px-4 pb-3", largeInlineActions ? "pt-2" : "pt-0.5"), children: [accentBar ? (_jsx(View, { style: {
|
|
63
|
+
width: 4,
|
|
64
|
+
marginRight: 10,
|
|
65
|
+
marginVertical: 2,
|
|
66
|
+
borderRadius: 999,
|
|
67
|
+
backgroundColor: theme.accent,
|
|
68
|
+
} })) : null, _jsxs(View, { className: "flex-1", children: [_jsx(Typography.Text, { className: colored ? "" : "text-text-primary", style: titleStyle, variant: "h2", children: title }), subtitle ? (_jsx(Typography.Text, { className: colored ? "" : "text-text-secondary", style: subtitleStyle, variant: "bodySmall", children: subtitle })) : null] }), largeInlineActions && actions ? (_jsx(View, { className: "flex-row items-center justify-end gap-2 pl-2", children: actions })) : null] })) : null, subheader ? _jsx(View, { className: "px-3 pb-3 pt-0.5", children: subheader }) : null] }));
|
|
30
69
|
}
|
|
31
70
|
const AppBarWithAction = Object.assign(AppBar, { Action: AppBarAction });
|
|
32
71
|
export default AppBarWithAction;
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import type { CustomIconProps } from "../icons/props";
|
|
2
|
+
import type { ComponentType } from "react";
|
|
1
3
|
export interface BottomNavItem {
|
|
2
4
|
key: string;
|
|
3
5
|
label?: string;
|
|
4
|
-
|
|
6
|
+
/** Icon font name (Display.Icon). Provide this OR `customIcon`. */
|
|
7
|
+
icon?: string;
|
|
5
8
|
activeIcon?: string;
|
|
9
|
+
/** Brand/custom SVG icon component. Takes precedence over `icon` (for non-font brand marks). */
|
|
10
|
+
customIcon?: ComponentType<CustomIconProps>;
|
|
6
11
|
badge?: string | number;
|
|
7
12
|
}
|
|
8
13
|
export interface BottomNavCenterAction {
|
|
@@ -10,20 +15,43 @@ export interface BottomNavCenterAction {
|
|
|
10
15
|
onPress: () => void;
|
|
11
16
|
accessibilityLabel?: string;
|
|
12
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Active-tab indicator style:
|
|
20
|
+
* - `none`: color only.
|
|
21
|
+
* - `pill`: filled round pill behind the icon.
|
|
22
|
+
* - `pill-text`: extended pill with icon + label inside (inactive = icon only).
|
|
23
|
+
* - `bar`: top accent bar over the active item (label below).
|
|
24
|
+
* - `glow`: glowing dot under the active icon (pairs with `tone="dark"`).
|
|
25
|
+
*/
|
|
26
|
+
export type BottomNavIndicator = "none" | "pill" | "pill-text" | "bar" | "glow";
|
|
13
27
|
export interface BottomNavProps {
|
|
14
28
|
items: BottomNavItem[];
|
|
15
29
|
value: string;
|
|
16
30
|
onChange: (key: string) => void;
|
|
31
|
+
/** Show the text label under each icon. `false` = icon-only. */
|
|
17
32
|
showLabels?: boolean;
|
|
33
|
+
/** Elevated center FAB (delivery/social apps). Splits items left/right around it. */
|
|
18
34
|
centerAction?: BottomNavCenterAction;
|
|
19
|
-
/**
|
|
35
|
+
/** Reserve the bottom inset (insets.bottom). Auto-disabled for `floating`/`dark`. */
|
|
20
36
|
safeArea?: boolean;
|
|
37
|
+
/** `bar` = full-width docked bar; `floating` = self-contained rounded pill (shadow, margins). */
|
|
38
|
+
shape?: "bar" | "floating";
|
|
39
|
+
/** Surface tone. `dark` renders a dark rounded bar (pairs with `glow`). */
|
|
40
|
+
tone?: "light" | "dark";
|
|
41
|
+
/** Active-tab indicator style (see BottomNavIndicator). */
|
|
42
|
+
indicator?: BottomNavIndicator;
|
|
43
|
+
/** [light bar only] Top hairline + card surface. */
|
|
44
|
+
bordered?: boolean;
|
|
45
|
+
/** Token for the active color. `primary` (default) or `accent` (e.g. green CTA brand). */
|
|
46
|
+
activeColor?: "primary" | "accent";
|
|
21
47
|
className?: string;
|
|
22
48
|
testID?: string;
|
|
23
49
|
}
|
|
24
50
|
/**
|
|
25
|
-
*
|
|
26
|
-
* `
|
|
51
|
+
* The single bottom-navigation component. Extensible by props, not by forking:
|
|
52
|
+
* `shape` (bar/floating), `tone` (light/dark), `indicator` (none/pill/pill-text/bar/glow),
|
|
53
|
+
* `showLabels`, `centerAction` (FAB), `activeColor`. Replaces the legacy `TabNavigatorLayout`.
|
|
27
54
|
*/
|
|
28
|
-
declare function BottomNav({ items, value, onChange, showLabels, centerAction, safeArea, className, testID, }: BottomNavProps): import("react").JSX.Element;
|
|
55
|
+
declare function BottomNav({ items, value, onChange, showLabels, centerAction, safeArea, shape, tone, indicator, bordered, activeColor, className, testID, }: BottomNavProps): import("react").JSX.Element;
|
|
29
56
|
export default BottomNav;
|
|
57
|
+
export { BottomNav };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "nativewind/jsx-runtime";
|
|
2
2
|
import Icon from "../Display/Display.Icon";
|
|
3
3
|
import Pressable from "../primitives/Pressable";
|
|
4
4
|
import { theme } from "../theme";
|
|
@@ -6,30 +6,103 @@ import { cn } from "../theme/tw";
|
|
|
6
6
|
import { Typography } from "../Typography";
|
|
7
7
|
import { View } from "react-native";
|
|
8
8
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
9
|
+
const DARK_SURFACE = "#15181B";
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
-
* `
|
|
11
|
+
* The single bottom-navigation component. Extensible by props, not by forking:
|
|
12
|
+
* `shape` (bar/floating), `tone` (light/dark), `indicator` (none/pill/pill-text/bar/glow),
|
|
13
|
+
* `showLabels`, `centerAction` (FAB), `activeColor`. Replaces the legacy `TabNavigatorLayout`.
|
|
12
14
|
*/
|
|
13
|
-
function BottomNav({ items, value, onChange, showLabels = true, centerAction, safeArea = true, className, testID, }) {
|
|
15
|
+
function BottomNav({ items, value, onChange, showLabels = true, centerAction, safeArea = true, shape = "bar", tone = "light", indicator = "none", bordered = true, activeColor = "primary", className, testID, }) {
|
|
14
16
|
const insets = useSafeAreaInsets();
|
|
15
|
-
const
|
|
17
|
+
const dark = tone === "dark";
|
|
18
|
+
const rounded = shape === "floating" || dark;
|
|
19
|
+
const bottomPad = rounded ? 6 : safeArea ? insets.bottom : 0;
|
|
20
|
+
const activeTint = activeColor === "accent" ? theme.accent : theme.primary;
|
|
21
|
+
const onActive = activeColor === "accent" ? theme.text.onAccent : theme.icon.inverse;
|
|
22
|
+
const inactiveColor = dark ? "rgba(255,255,255,0.55)" : theme.icon.secondary;
|
|
23
|
+
const surfaceColor = dark ? DARK_SURFACE : theme.bg.card;
|
|
24
|
+
const containerStyle = rounded
|
|
25
|
+
? {
|
|
26
|
+
borderRadius: 32,
|
|
27
|
+
paddingBottom: bottomPad,
|
|
28
|
+
paddingTop: 6,
|
|
29
|
+
paddingHorizontal: 6,
|
|
30
|
+
minHeight: 64,
|
|
31
|
+
backgroundColor: surfaceColor,
|
|
32
|
+
...theme.shadow.lg,
|
|
33
|
+
}
|
|
34
|
+
: {
|
|
35
|
+
paddingBottom: bottomPad,
|
|
36
|
+
minHeight: 56,
|
|
37
|
+
backgroundColor: surfaceColor,
|
|
38
|
+
...(bordered ? { borderTopWidth: 1, borderTopColor: theme.border.light } : null),
|
|
39
|
+
};
|
|
40
|
+
const renderIconNode = (item, active, color) => {
|
|
41
|
+
const node = item.customIcon ? (_jsx(Icon, { color: color, customIcon: item.customIcon, size: 24, type: "custom" })) : (_jsx(Icon, { color: color, name: (active ? (item.activeIcon ?? item.icon) : item.icon) ?? "", size: 24 }));
|
|
42
|
+
return (_jsxs(View, { children: [node, item.badge != null ? (_jsx(View, { className: "absolute bg-danger rounded-full min-w-[16px] h-4 px-1 items-center justify-center", style: { top: -4, right: -10, borderWidth: 2, borderColor: surfaceColor }, children: _jsx(Typography.Text, { style: { color: theme.text.inverse, fontSize: 9, lineHeight: 11 }, children: item.badge }) })) : null] }));
|
|
43
|
+
};
|
|
16
44
|
const renderItem = (item) => {
|
|
17
45
|
const active = item.key === value;
|
|
18
|
-
|
|
19
|
-
|
|
46
|
+
// Variant A — extended pill with text (active = horizontal pill, inactive = icon only).
|
|
47
|
+
if (indicator === "pill-text") {
|
|
48
|
+
if (active) {
|
|
49
|
+
return (_jsxs(Pressable, { accessibilityLabel: item.label ?? item.key, accessibilityRole: "tab", accessibilityState: { selected: true }, className: "flex-row items-center px-4 py-2", onPress: () => onChange(item.key), style: { borderRadius: 999, backgroundColor: activeTint }, children: [renderIconNode(item, true, onActive), item.label ? (_jsx(Typography.Text, { semibold: true, style: { color: onActive, fontSize: 13, marginLeft: 8 }, children: item.label })) : null] }, item.key));
|
|
50
|
+
}
|
|
51
|
+
return (_jsx(Pressable, { accessibilityLabel: item.label ?? item.key, accessibilityRole: "tab", className: "items-center justify-center px-3 py-2", onPress: () => onChange(item.key), children: renderIconNode(item, false, inactiveColor) }, item.key));
|
|
52
|
+
}
|
|
53
|
+
// Variants none / pill / bar / glow — vertical icon + optional indicator + optional label.
|
|
54
|
+
const inPill = indicator === "pill" && active;
|
|
55
|
+
const iconColor = inPill
|
|
56
|
+
? onActive
|
|
57
|
+
: active
|
|
58
|
+
? indicator === "glow"
|
|
59
|
+
? activeTint
|
|
60
|
+
: activeTint
|
|
61
|
+
: inactiveColor;
|
|
62
|
+
const labelColor = active ? (dark ? "#FFFFFF" : activeTint) : inactiveColor;
|
|
63
|
+
return (_jsxs(Pressable, { accessibilityLabel: item.label ?? item.key, accessibilityRole: "tab", accessibilityState: { selected: active }, className: "flex-1 items-center justify-center py-1.5", onPress: () => onChange(item.key), children: [indicator === "bar" && active ? (_jsx(View, { style: {
|
|
64
|
+
position: "absolute",
|
|
65
|
+
top: 0,
|
|
66
|
+
width: 26,
|
|
67
|
+
height: 3,
|
|
68
|
+
borderRadius: 999,
|
|
69
|
+
backgroundColor: activeTint,
|
|
70
|
+
} })) : null, _jsx(View, { className: inPill ? "px-5 py-1.5" : undefined, style: inPill ? { borderRadius: 999, backgroundColor: activeTint } : undefined, children: renderIconNode(item, active, iconColor) }), indicator === "glow" && active ? (_jsx(View, { style: {
|
|
71
|
+
width: 5,
|
|
72
|
+
height: 5,
|
|
73
|
+
borderRadius: 999,
|
|
74
|
+
marginTop: 4,
|
|
75
|
+
backgroundColor: activeTint,
|
|
76
|
+
shadowColor: activeTint,
|
|
77
|
+
shadowOpacity: 0.9,
|
|
78
|
+
shadowRadius: 5,
|
|
79
|
+
elevation: 4,
|
|
80
|
+
} })) : null, showLabels && item.label && indicator !== "glow" ? (_jsx(Typography.Text, { numberOfLines: 1, semibold: active, style: { color: labelColor, fontSize: 11, marginTop: 3 }, children: item.label })) : null] }, item.key));
|
|
20
81
|
};
|
|
82
|
+
const renderCenterFab = () => (_jsx(Pressable, { accessibilityLabel: centerAction?.accessibilityLabel ?? "Acción", accessibilityRole: "button", className: "items-center justify-center", onPress: centerAction?.onPress, style: {
|
|
83
|
+
width: 52,
|
|
84
|
+
height: 52,
|
|
85
|
+
borderRadius: 18,
|
|
86
|
+
marginTop: -20,
|
|
87
|
+
marginHorizontal: 6,
|
|
88
|
+
backgroundColor: activeTint,
|
|
89
|
+
// Glow halo en el color de acento (iOS shadow + Android elevation).
|
|
90
|
+
shadowColor: activeTint,
|
|
91
|
+
shadowOffset: { width: 0, height: 4 },
|
|
92
|
+
shadowOpacity: 0.55,
|
|
93
|
+
shadowRadius: 14,
|
|
94
|
+
elevation: 10,
|
|
95
|
+
}, children: _jsx(Icon, { color: onActive, name: centerAction?.icon ?? "add", size: 26 }) }));
|
|
96
|
+
let content;
|
|
21
97
|
if (centerAction) {
|
|
22
98
|
const mid = Math.ceil(items.length / 2);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
height: 52,
|
|
28
|
-
marginTop: -20,
|
|
29
|
-
marginHorizontal: 6,
|
|
30
|
-
...theme.shadow.md,
|
|
31
|
-
}, children: _jsx(Icon, { color: theme.icon.inverse, name: centerAction.icon, size: 26 }) }), right.map(renderItem)] }));
|
|
99
|
+
content = (_jsxs(_Fragment, { children: [items.slice(0, mid).map(renderItem), renderCenterFab(), items.slice(mid).map(renderItem)] }));
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
content = items.map(renderItem);
|
|
32
103
|
}
|
|
33
|
-
|
|
104
|
+
const justify = indicator === "pill-text" ? "justify-around" : "";
|
|
105
|
+
return (_jsx(View, { className: cn("flex-row items-center", justify, className), style: containerStyle, testID: testID, children: content }));
|
|
34
106
|
}
|
|
35
107
|
export default BottomNav;
|
|
108
|
+
export { BottomNav };
|