@mrmeg/expo-ui 0.6.1 → 0.7.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/LLM_USAGE.md +9 -6
- package/README.md +11 -7
- package/dist/components/Accordion.js +21 -16
- package/dist/components/AnimatedView.d.ts +1 -1
- package/dist/components/AnimatedView.js +2 -2
- package/dist/components/Badge.d.ts +3 -2
- package/dist/components/Badge.js +4 -3
- package/dist/components/BottomSheet.js +31 -29
- package/dist/components/BottomSheetKeyboard.d.ts +7 -0
- package/dist/components/BottomSheetKeyboard.js +35 -0
- package/dist/components/Button.d.ts +55 -13
- package/dist/components/Button.js +72 -28
- package/dist/components/Card.js +8 -10
- package/dist/components/Checkbox.js +22 -25
- package/dist/components/Collapsible.js +3 -7
- package/dist/components/Dialog.js +1 -1
- package/dist/components/DismissKeyboard.js +3 -3
- package/dist/components/Drawer.js +21 -10
- package/dist/components/DropdownMenu.d.ts +3 -2
- package/dist/components/DropdownMenu.js +29 -29
- package/dist/components/EmptyState.js +1 -1
- package/dist/components/InputOTP.js +16 -40
- package/dist/components/Notification.js +106 -27
- package/dist/components/Popover.js +1 -1
- package/dist/components/Progress.d.ts +2 -2
- package/dist/components/Progress.js +36 -34
- package/dist/components/RadioGroup.js +22 -20
- package/dist/components/Select.js +30 -20
- package/dist/components/Skeleton.js +6 -6
- package/dist/components/Slider.js +90 -97
- package/dist/components/StyledText.context.d.ts +6 -0
- package/dist/components/StyledText.context.js +5 -0
- package/dist/components/StyledText.d.ts +7 -58
- package/dist/components/StyledText.js +8 -28
- package/dist/components/Switch.js +30 -26
- package/dist/components/Tabs.d.ts +23 -3
- package/dist/components/Tabs.js +39 -17
- package/dist/components/TextInput.d.ts +6 -2
- package/dist/components/TextInput.js +6 -7
- package/dist/components/Toggle.js +12 -7
- package/dist/components/ToggleGroup.js +17 -11
- package/dist/components/Tooltip.js +1 -1
- package/dist/hooks/useDimensions.js +25 -26
- package/dist/hooks/useReduceMotion.d.ts +5 -1
- package/dist/hooks/useReduceMotion.js +46 -41
- package/dist/hooks/useResources.js +6 -1
- package/dist/hooks/useScalePress.d.ts +6 -5
- package/dist/hooks/useScalePress.js +25 -21
- package/dist/hooks/useStaggeredEntrance.d.ts +9 -8
- package/dist/hooks/useStaggeredEntrance.js +48 -21
- package/dist/state/globalUIStore.d.ts +23 -16
- package/dist/state/themeColorScope.js +3 -3
- package/llms-full.md +5 -6
- package/llms.txt +2 -2
- package/package.json +8 -6
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useMemo, useState } from "react";
|
|
3
|
-
import { Pressable, StyleSheet, View, Platform, ActivityIndicator, } from "react-native";
|
|
4
|
-
import Animated from "react-native-reanimated";
|
|
2
|
+
import { use, useCallback, useMemo, useState } from "react";
|
|
3
|
+
import { Pressable, StyleSheet, View, Platform, ActivityIndicator, Animated, } from "react-native";
|
|
5
4
|
import { spacing } from "../constants/spacing.js";
|
|
6
|
-
import { StyledText
|
|
5
|
+
import { StyledText } from "./StyledText.js";
|
|
6
|
+
import { Icon } from "./Icon.js";
|
|
7
|
+
import { TextColorContext, TextSelectabilityContext, TextStyleContext } from "./StyledText.context";
|
|
7
8
|
import { fontFamilies } from "../constants/fonts.js";
|
|
8
9
|
import { palette } from "../constants/colors.js";
|
|
9
10
|
import { useTheme } from "../hooks/useTheme.js";
|
|
@@ -43,28 +44,32 @@ const getNativeHitSlop = (sizeConfig) => Math.ceil(Math.max(0, spacing.touchTarg
|
|
|
43
44
|
*
|
|
44
45
|
* Usage:
|
|
45
46
|
* ```tsx
|
|
46
|
-
* //
|
|
47
|
-
* <Button onPress={handler}
|
|
48
|
-
* <SansSerifBoldText>Click Me</SansSerifBoldText>
|
|
49
|
-
* </Button>
|
|
47
|
+
* // Simplest path — plain label via the `text` prop
|
|
48
|
+
* <Button onPress={handler} text="Click Me" />
|
|
50
49
|
*
|
|
51
50
|
* // Different variants
|
|
52
|
-
* <Button preset="outline" onPress={handler}
|
|
53
|
-
* <Button preset="ghost" onPress={handler}
|
|
54
|
-
* <Button preset="destructive" onPress={handler}
|
|
51
|
+
* <Button preset="outline" onPress={handler} text="Outline" />
|
|
52
|
+
* <Button preset="ghost" onPress={handler} text="Ghost" />
|
|
53
|
+
* <Button preset="destructive" onPress={handler} text="Delete" />
|
|
55
54
|
*
|
|
56
55
|
* // Different sizes
|
|
57
|
-
* <Button size="sm" onPress={handler}
|
|
58
|
-
* <Button size="lg" onPress={handler}
|
|
56
|
+
* <Button size="sm" onPress={handler} text="Small" />
|
|
57
|
+
* <Button size="lg" onPress={handler} text="Large" />
|
|
59
58
|
*
|
|
60
59
|
* // Loading state
|
|
61
|
-
* <Button loading onPress={handler}
|
|
60
|
+
* <Button loading onPress={handler} text="Processing..." />
|
|
62
61
|
*
|
|
63
62
|
* // Full width
|
|
64
|
-
* <Button fullWidth onPress={handler}
|
|
63
|
+
* <Button fullWidth onPress={handler} text="Submit" />
|
|
64
|
+
*
|
|
65
|
+
* // Composed content — icon + label via subcomponents
|
|
66
|
+
* <Button onPress={handler}>
|
|
67
|
+
* <Button.Icon name="heart" />
|
|
68
|
+
* <Button.Text>Like</Button.Text>
|
|
69
|
+
* </Button>
|
|
65
70
|
* ```
|
|
66
71
|
*/
|
|
67
|
-
|
|
72
|
+
function ButtonRoot(props) {
|
|
68
73
|
const { tx, text, txOptions, style: styleOverride, pressedStyle: pressedStyleOverride, textStyle: textStyleOverride, pressedTextStyle: pressedTextStyleOverride, disabledTextStyle: disabledTextStyleOverride, children, RightAccessory, LeftAccessory, disabled, disabledStyle: disabledStyleOverride, withShadow = false, preset = "default", size = "md", loading = false, fullWidth = false, onFocus, onBlur, onPressIn, onPressOut, ...rest } = props;
|
|
69
74
|
const { theme, getContrastingColor, getFocusRingStyle, getShadowStyle } = useTheme();
|
|
70
75
|
const styles = useMemo(() => createStyles(theme, size), [theme, size]);
|
|
@@ -115,11 +120,11 @@ export function Button(props) {
|
|
|
115
120
|
haptic: false,
|
|
116
121
|
scaleTo: preset === "link" ? 1 : 0.97,
|
|
117
122
|
});
|
|
118
|
-
const
|
|
123
|
+
const showFocusRing = (event) => {
|
|
119
124
|
setFocused(true);
|
|
120
125
|
onFocus?.(event);
|
|
121
126
|
};
|
|
122
|
-
const
|
|
127
|
+
const hideFocusRing = (event) => {
|
|
123
128
|
setFocused(false);
|
|
124
129
|
onBlur?.(event);
|
|
125
130
|
};
|
|
@@ -144,7 +149,7 @@ export function Button(props) {
|
|
|
144
149
|
return nextWidth;
|
|
145
150
|
});
|
|
146
151
|
}, [fullWidth, loading]);
|
|
147
|
-
return (_jsx(TextColorContext.Provider, { value: textColor, children: _jsx(TextSelectabilityContext.Provider, { value: false, children: _jsx(TextStyleContext.Provider, { value: styles.text, children: _jsx(Pressable, { accessibilityRole: "button", accessibilityState: { disabled: !!isDisabled, busy: loading }, ...rest, onPressIn: handlePressIn, onPressOut: handlePressOut, onFocus:
|
|
152
|
+
return (_jsx(TextColorContext.Provider, { value: textColor, children: _jsx(TextSelectabilityContext.Provider, { value: false, children: _jsx(TextStyleContext.Provider, { value: styles.text, children: _jsx(Pressable, { accessibilityRole: "button", accessibilityState: { disabled: !!isDisabled, busy: loading }, ...rest, onPressIn: handlePressIn, onPressOut: handlePressOut, onFocus: showFocusRing, onBlur: hideFocusRing, style: { alignSelf: fullWidth ? "stretch" : flattenedStyle?.alignSelf ?? "flex-start" }, hitSlop: rest.hitSlop ?? (Platform.OS === "web" ? undefined : getNativeHitSlop(sizeConfig)), disabled: isDisabled, children: (state) => (_jsx(Animated.View, { style: scaleStyle, children: _jsxs(View, { style: [
|
|
148
153
|
styles.button,
|
|
149
154
|
preset === "default" && styles.buttonDefault,
|
|
150
155
|
preset === "outline" && styles.buttonOutline,
|
|
@@ -170,15 +175,42 @@ export function Button(props) {
|
|
|
170
175
|
state.pressed && pressedTextStyleOverride,
|
|
171
176
|
isDisabled && disabledTextStyleOverride,
|
|
172
177
|
textStyleOverride,
|
|
173
|
-
] })) :
|
|
174
|
-
//
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
178
|
+
] })) : (
|
|
179
|
+
// Children render as-is. For text content, use the explicit
|
|
180
|
+
// <Button.Text> subcomponent so it inherits control typography
|
|
181
|
+
// via context instead of relying on a runtime `typeof` check.
|
|
182
|
+
children ?? null), !!RightAccessory && (_jsx(RightAccessory, { style: styles.rightAccessory, pressableState: state, disabled: isDisabled }))] })] }) })) }) }) }) }));
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Button.Text
|
|
186
|
+
* Text content for a Button. Inherits the button's control typography, color,
|
|
187
|
+
* and non-selectable behavior from context, so callers state their intent
|
|
188
|
+
* explicitly instead of relying on the component to inspect `typeof children`.
|
|
189
|
+
*
|
|
190
|
+
* ```tsx
|
|
191
|
+
* <Button onPress={save}>
|
|
192
|
+
* <Button.Text>Save</Button.Text>
|
|
193
|
+
* </Button>
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
function ButtonText(props) {
|
|
197
|
+
return _jsx(StyledText, { ...props });
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Button.Icon
|
|
201
|
+
* Icon content for a Button. Defaults its color to the button's text color
|
|
202
|
+
* (from context) and is decorative by default, since the label conveys meaning.
|
|
203
|
+
*
|
|
204
|
+
* ```tsx
|
|
205
|
+
* <Button onPress={like}>
|
|
206
|
+
* <Button.Icon name="heart" />
|
|
207
|
+
* <Button.Text>Like</Button.Text>
|
|
208
|
+
* </Button>
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
function ButtonIcon(props) {
|
|
212
|
+
const contextColor = use(TextColorContext);
|
|
213
|
+
return _jsx(Icon, { decorative: true, color: contextColor, ...props });
|
|
182
214
|
}
|
|
183
215
|
const createStyles = (theme, size) => {
|
|
184
216
|
const sizeConfig = SIZE_CONFIGS[size];
|
|
@@ -263,3 +295,15 @@ const createStyles = (theme, size) => {
|
|
|
263
295
|
},
|
|
264
296
|
});
|
|
265
297
|
};
|
|
298
|
+
/**
|
|
299
|
+
* Button with explicit subcomponents.
|
|
300
|
+
* - `Button.Text` for label text (inherits control typography)
|
|
301
|
+
* - `Button.Icon` for icons (inherits the button's text color)
|
|
302
|
+
*
|
|
303
|
+
* The `tx`/`text` props remain the simplest path for plain labels.
|
|
304
|
+
*/
|
|
305
|
+
const Button = Object.assign(ButtonRoot, {
|
|
306
|
+
Text: ButtonText,
|
|
307
|
+
Icon: ButtonIcon,
|
|
308
|
+
});
|
|
309
|
+
export { Button, ButtonText, ButtonIcon };
|
package/dist/components/Card.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createContext,
|
|
3
|
-
import { View, Pressable, StyleSheet, Platform } from "react-native";
|
|
4
|
-
import Animated from "react-native-reanimated";
|
|
2
|
+
import { createContext, use } from "react";
|
|
3
|
+
import { View, Pressable, StyleSheet, Platform, Animated } from "react-native";
|
|
5
4
|
import { StyledText } from "./StyledText.js";
|
|
6
5
|
import { useTheme } from "../hooks/useTheme.js";
|
|
7
6
|
import { useScalePress } from "../hooks/useScalePress.js";
|
|
@@ -30,13 +29,12 @@ import { spacing } from "../constants/spacing.js";
|
|
|
30
29
|
*/
|
|
31
30
|
const CardContext = createContext(null);
|
|
32
31
|
function useCardContext() {
|
|
33
|
-
const ctx =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
return ctx;
|
|
32
|
+
const ctx = use(CardContext);
|
|
33
|
+
// useTheme must run unconditionally (Rules of Hooks); createCardStyles is a
|
|
34
|
+
// plain function, so the `??` keeps the fallback styles lazy.
|
|
35
|
+
const { theme } = useTheme();
|
|
36
|
+
// Fallback for standalone usage without a Card parent.
|
|
37
|
+
return ctx ?? { theme, styles: createCardStyles(theme) };
|
|
40
38
|
}
|
|
41
39
|
function Card({ children, style: styleOverride, variant = "default", onPress, disabled }) {
|
|
42
40
|
const { theme, getShadowStyle } = useTheme();
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect } from "react";
|
|
3
|
-
import { View, StyleSheet, Pressable, Platform } from "react-native";
|
|
4
|
-
import Animated, { useSharedValue, useAnimatedStyle, withTiming, useReducedMotion, } from "react-native-reanimated";
|
|
2
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
3
|
+
import { View, StyleSheet, Pressable, Platform, Animated } from "react-native";
|
|
5
4
|
import { Icon } from "./Icon.js";
|
|
6
5
|
import { StyledText } from "./StyledText.js";
|
|
7
6
|
import { useTheme } from "../hooks/useTheme.js";
|
|
8
7
|
import { spacing } from "../constants/spacing.js";
|
|
9
8
|
import { hapticLight } from "../lib/haptics.js";
|
|
9
|
+
import { useReducedMotion } from "../hooks/useReduceMotion.js";
|
|
10
10
|
import * as CheckboxPrimitive from "@rn-primitives/checkbox";
|
|
11
11
|
const DEFAULT_HIT_SLOP = 8;
|
|
12
12
|
const SIZE_CONFIGS = {
|
|
@@ -19,30 +19,24 @@ function Checkbox({ size = "md", label, indeterminate = false, error = false, st
|
|
|
19
19
|
const reduceMotion = useReducedMotion();
|
|
20
20
|
const sizeConfig = SIZE_CONFIGS[size];
|
|
21
21
|
// Simple fast opacity for the checkmark icon
|
|
22
|
-
const checkOpacity =
|
|
22
|
+
const checkOpacity = useRef(new Animated.Value(checked || indeterminate ? 1 : 0)).current;
|
|
23
23
|
const isVisuallyChecked = !!checked || indeterminate;
|
|
24
|
+
const animateCheckOpacity = useCallback((nextVisible) => {
|
|
25
|
+
Animated.timing(checkOpacity, {
|
|
26
|
+
toValue: nextVisible ? 1 : 0,
|
|
27
|
+
duration: reduceMotion ? 0 : 60,
|
|
28
|
+
useNativeDriver: true,
|
|
29
|
+
}).start();
|
|
30
|
+
}, [checkOpacity, reduceMotion]);
|
|
24
31
|
useEffect(() => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
checkOpacity.value = withTiming(isVisuallyChecked ? 1 : 0, { duration: 60 });
|
|
30
|
-
}
|
|
31
|
-
}, [checkOpacity, isVisuallyChecked, reduceMotion]);
|
|
32
|
+
animateCheckOpacity(isVisuallyChecked);
|
|
33
|
+
}, [animateCheckOpacity, isVisuallyChecked]);
|
|
32
34
|
const wrappedOnCheckedChange = (next) => {
|
|
33
35
|
if (next)
|
|
34
36
|
hapticLight();
|
|
35
|
-
|
|
36
|
-
checkOpacity.value = withTiming(next ? 1 : 0, { duration: 0 });
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
checkOpacity.value = withTiming(next ? 1 : 0, { duration: 60 });
|
|
40
|
-
}
|
|
37
|
+
animateCheckOpacity(next);
|
|
41
38
|
onCheckedChange?.(next);
|
|
42
39
|
};
|
|
43
|
-
const checkAnimatedStyle = useAnimatedStyle(() => ({
|
|
44
|
-
opacity: checkOpacity.value,
|
|
45
|
-
}));
|
|
46
40
|
// Dynamic border color with sufficient contrast against background
|
|
47
41
|
const borderColor = error
|
|
48
42
|
? theme.colors.destructive
|
|
@@ -52,14 +46,11 @@ function Checkbox({ size = "md", label, indeterminate = false, error = false, st
|
|
|
52
46
|
// Flatten style override for web compatibility
|
|
53
47
|
const flattenedStyle = styleOverride ? StyleSheet.flatten(styleOverride) : undefined;
|
|
54
48
|
const checkboxElement = (_jsx(CheckboxPrimitive.Root, { ...props, checked: checked, onCheckedChange: wrappedOnCheckedChange, disabled: disabled, style: {
|
|
49
|
+
...styles.box,
|
|
55
50
|
borderColor,
|
|
56
51
|
backgroundColor: isVisuallyChecked ? theme.colors.primary : theme.colors.background,
|
|
57
|
-
borderRadius: spacing.radiusSm,
|
|
58
|
-
borderWidth: 1,
|
|
59
52
|
width: sizeConfig.size,
|
|
60
53
|
height: sizeConfig.size,
|
|
61
|
-
justifyContent: "center",
|
|
62
|
-
alignItems: "center",
|
|
63
54
|
opacity: disabled ? 0.5 : 1,
|
|
64
55
|
...(Platform.OS === "web" && { cursor: disabled ? "not-allowed" : "pointer" }),
|
|
65
56
|
...(flattenedStyle || {}),
|
|
@@ -69,7 +60,7 @@ function Checkbox({ size = "md", label, indeterminate = false, error = false, st
|
|
|
69
60
|
}, accessibilityLabel: label, children: _jsx(CheckboxPrimitive.Indicator, { style: {
|
|
70
61
|
justifyContent: "center",
|
|
71
62
|
alignItems: "center",
|
|
72
|
-
}, children: _jsx(Animated.View, { style:
|
|
63
|
+
}, children: _jsx(Animated.View, { style: { opacity: checkOpacity }, children: indeterminate ? (_jsx(Icon, { name: "minus", size: sizeConfig.iconSize, color: theme.colors.primaryForeground })) : (_jsx(Icon, { name: "check", size: sizeConfig.iconSize, color: theme.colors.primaryForeground })) }) }) }));
|
|
73
64
|
// If no label, return just the checkbox
|
|
74
65
|
if (!label) {
|
|
75
66
|
return checkboxElement;
|
|
@@ -86,6 +77,12 @@ function Checkbox({ size = "md", label, indeterminate = false, error = false, st
|
|
|
86
77
|
], children: [label, required && (_jsx(StyledText, { selectable: false, style: [styles.required, { color: theme.colors.destructive }], children: " *" }))] }) })] }));
|
|
87
78
|
}
|
|
88
79
|
const styles = StyleSheet.create({
|
|
80
|
+
box: {
|
|
81
|
+
borderRadius: spacing.radiusSm,
|
|
82
|
+
borderWidth: 1,
|
|
83
|
+
justifyContent: "center",
|
|
84
|
+
alignItems: "center",
|
|
85
|
+
},
|
|
89
86
|
container: {
|
|
90
87
|
flexDirection: "row",
|
|
91
88
|
alignItems: "center",
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import { TextClassContext, TextSelectabilityContext } from "./StyledText.js";
|
|
2
|
+
import { Platform, StyleSheet, View } from "react-native";
|
|
3
|
+
import { TextClassContext, TextSelectabilityContext } from "./StyledText.context";
|
|
5
4
|
import { spacing } from "../constants/spacing.js";
|
|
6
5
|
import { useTheme } from "../hooks/useTheme.js";
|
|
7
6
|
import * as CollapsiblePrimitive from "@rn-primitives/collapsible";
|
|
@@ -26,11 +25,8 @@ function CollapsibleTrigger({ style: styleOverride, ...props }) {
|
|
|
26
25
|
} }) }) }));
|
|
27
26
|
}
|
|
28
27
|
function CollapsibleContent({ forceMount, style: styleOverride, children, ...props }) {
|
|
29
|
-
|
|
30
|
-
const fadeAnim = React.useRef(new Animated.Value(1)).current;
|
|
31
|
-
return (_jsx(TextClassContext.Provider, { value: "", children: _jsx(CollapsiblePrimitive.Content, { ...props, forceMount: forceMount, children: _jsx(Animated.View, { style: {
|
|
28
|
+
return (_jsx(TextClassContext.Provider, { value: "", children: _jsx(CollapsiblePrimitive.Content, { ...props, forceMount: forceMount, children: _jsx(View, { style: {
|
|
32
29
|
overflow: "hidden",
|
|
33
|
-
opacity: fadeAnim,
|
|
34
30
|
...(styleOverride && typeof styleOverride !== "function"
|
|
35
31
|
? StyleSheet.flatten(styleOverride)
|
|
36
32
|
: {}),
|
|
@@ -5,7 +5,7 @@ import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens";
|
|
|
5
5
|
import * as DialogPrimitive from "@rn-primitives/dialog";
|
|
6
6
|
import * as AlertDialogPrimitive from "@rn-primitives/alert-dialog";
|
|
7
7
|
import { AnimatedView } from "./AnimatedView.js";
|
|
8
|
-
import { TextClassContext, TextColorContext } from "./StyledText.
|
|
8
|
+
import { TextClassContext, TextColorContext } from "./StyledText.context";
|
|
9
9
|
import { StyledText } from "./StyledText.js";
|
|
10
10
|
import { useTheme } from "../hooks/useTheme.js";
|
|
11
11
|
import { spacing } from "../constants/spacing.js";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Pressable, Keyboard, Platform, KeyboardAvoidingView, ScrollView } from "react-native";
|
|
3
|
+
const handleDismissKeyboard = () => Platform.OS !== "web" && Keyboard.dismiss();
|
|
3
4
|
/**
|
|
4
5
|
* @returns Wrapper for a view that dismisses the keyboard when tapped outside of a text input
|
|
5
6
|
*/
|
|
6
7
|
export const DismissKeyboard = ({ children, style, avoidKeyboard = true, scrollable = true }) => {
|
|
7
|
-
const handlePress = () => Platform.OS !== "web" && Keyboard.dismiss();
|
|
8
8
|
const content = scrollable ? (_jsx(ScrollView, { style: { flex: 1 }, contentContainerStyle: { flexGrow: 1, justifyContent: "center" }, keyboardShouldPersistTaps: "handled", showsVerticalScrollIndicator: false, children: children })) : (children);
|
|
9
9
|
if (!avoidKeyboard) {
|
|
10
|
-
return (_jsx(Pressable, { onPress:
|
|
10
|
+
return (_jsx(Pressable, { onPress: handleDismissKeyboard, accessible: false, style: { flex: 1 }, children: content }));
|
|
11
11
|
}
|
|
12
|
-
return (_jsx(KeyboardAvoidingView, { style: [{ flex: 1, width: "100%" }, style], behavior: Platform.OS === "ios" ? "padding" : "height", keyboardVerticalOffset: Platform.OS === "ios" ? 0 : 0, children: _jsx(Pressable, { onPress:
|
|
12
|
+
return (_jsx(KeyboardAvoidingView, { style: [{ flex: 1, width: "100%" }, style], behavior: Platform.OS === "ios" ? "padding" : "height", keyboardVerticalOffset: Platform.OS === "ios" ? 0 : 0, children: _jsx(Pressable, { onPress: handleDismissKeyboard, accessible: false, style: { flex: 1 }, children: content }) }));
|
|
13
13
|
};
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React, { createContext,
|
|
3
|
-
import { View, Pressable, Animated, StyleSheet, Platform,
|
|
2
|
+
import React, { createContext, use, useState, useReducer, useRef } from "react";
|
|
3
|
+
import { View, Pressable, Animated, StyleSheet, Platform, PanResponder, } from "react-native";
|
|
4
4
|
import { Portal } from "@rn-primitives/portal";
|
|
5
5
|
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens";
|
|
6
6
|
import { Pressable as SlotPressable } from "@rn-primitives/slot";
|
|
7
7
|
import { useTheme } from "../hooks/useTheme.js";
|
|
8
|
+
import { useDimensions } from "../hooks/useDimensions.js";
|
|
8
9
|
import { shouldUseNativeDriver } from "../lib/animations.js";
|
|
9
10
|
import { spacing } from "../constants/spacing.js";
|
|
10
|
-
import { TextColorContext, TextClassContext } from "./StyledText.
|
|
11
|
+
import { TextColorContext, TextClassContext } from "./StyledText.context";
|
|
11
12
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
12
13
|
/**
|
|
13
14
|
* Drawer Component with Sub-components
|
|
@@ -50,7 +51,7 @@ const FullWindowOverlay = Platform.OS === "ios" ? RNFullWindowOverlay : React.Fr
|
|
|
50
51
|
// ============================================================================
|
|
51
52
|
const DrawerContext = createContext(null);
|
|
52
53
|
function useDrawerContext() {
|
|
53
|
-
const context =
|
|
54
|
+
const context = use(DrawerContext);
|
|
54
55
|
if (!context) {
|
|
55
56
|
throw new Error("Drawer components must be used within a Drawer");
|
|
56
57
|
}
|
|
@@ -59,13 +60,12 @@ function useDrawerContext() {
|
|
|
59
60
|
// ============================================================================
|
|
60
61
|
// Utility Functions
|
|
61
62
|
// ============================================================================
|
|
62
|
-
function parseWidth(width) {
|
|
63
|
+
function parseWidth(width, screenWidth) {
|
|
63
64
|
if (typeof width === "number") {
|
|
64
65
|
return width;
|
|
65
66
|
}
|
|
66
67
|
// Parse percentage string
|
|
67
68
|
const percentage = parseFloat(width) / 100;
|
|
68
|
-
const screenWidth = Dimensions.get("window").width;
|
|
69
69
|
return screenWidth * percentage;
|
|
70
70
|
}
|
|
71
71
|
function drawerReducer(state, action) {
|
|
@@ -103,7 +103,9 @@ function DrawerRoot({ open: controlledOpen, onOpenChange: controlledOnOpenChange
|
|
|
103
103
|
dispatch({ type: newOpen ? "OPEN" : "CLOSE" });
|
|
104
104
|
}
|
|
105
105
|
};
|
|
106
|
-
|
|
106
|
+
// useDimensions reacts to rotation / split-screen, unlike Dimensions.get.
|
|
107
|
+
const { width: screenWidth } = useDimensions();
|
|
108
|
+
const parsedWidth = parseWidth(width, screenWidth);
|
|
107
109
|
const contextValue = {
|
|
108
110
|
open,
|
|
109
111
|
onOpenChange,
|
|
@@ -148,10 +150,19 @@ function DrawerContent({ swipeEnabled = true, swipeThreshold = 0.3, velocityThre
|
|
|
148
150
|
const { open, onOpenChange, side, width, closeOnBackdropPress } = drawerContext;
|
|
149
151
|
const { theme, getShadowStyle } = useTheme();
|
|
150
152
|
const insets = useSafeAreaInsets();
|
|
151
|
-
// Animation values - initialize
|
|
153
|
+
// Animation values - initialize lazily so the Animated.Value is allocated
|
|
154
|
+
// once on first render instead of being rebuilt and discarded every render.
|
|
152
155
|
const closedPosition = side === "left" ? -width : width;
|
|
153
|
-
const
|
|
154
|
-
|
|
156
|
+
const translateXRef = useRef(null);
|
|
157
|
+
if (translateXRef.current === null) {
|
|
158
|
+
translateXRef.current = new Animated.Value(open ? 0 : closedPosition);
|
|
159
|
+
}
|
|
160
|
+
const translateX = translateXRef.current;
|
|
161
|
+
const backdropOpacityRef = useRef(null);
|
|
162
|
+
if (backdropOpacityRef.current === null) {
|
|
163
|
+
backdropOpacityRef.current = new Animated.Value(open ? 1 : 0);
|
|
164
|
+
}
|
|
165
|
+
const backdropOpacity = backdropOpacityRef.current;
|
|
155
166
|
// Track if drawer is actually visible (for unmounting after close animation)
|
|
156
167
|
const [isVisible, setIsVisible] = useState(open);
|
|
157
168
|
// Track what we last animated to - persists across renders
|
|
@@ -115,9 +115,10 @@ declare function DropdownMenuSeparator({ style: styleOverride, ...props }: Dropd
|
|
|
115
115
|
* Text component for displaying keyboard shortcuts
|
|
116
116
|
*/
|
|
117
117
|
interface DropdownMenuShortcutProps {
|
|
118
|
-
children
|
|
118
|
+
children?: React.ReactNode;
|
|
119
|
+
text?: string;
|
|
119
120
|
style?: TextStyle;
|
|
120
121
|
}
|
|
121
|
-
declare function DropdownMenuShortcut({ style: styleOverride, ...props }: DropdownMenuShortcutProps): import("react/jsx-runtime").JSX.Element;
|
|
122
|
+
declare function DropdownMenuShortcut({ children, text, style: styleOverride, ...props }: DropdownMenuShortcutProps): import("react/jsx-runtime").JSX.Element;
|
|
122
123
|
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, };
|
|
123
124
|
export type { DropdownMenuTriggerProps, DropdownMenuSubTriggerProps, DropdownMenuSubContentProps, DropdownMenuContentProps, DropdownMenuItemProps, DropdownMenuCheckboxItemProps, DropdownMenuRadioItemProps, DropdownMenuLabelProps, DropdownMenuSeparatorProps, DropdownMenuShortcutProps, };
|
|
@@ -3,7 +3,7 @@ import * as React from "react";
|
|
|
3
3
|
import { Platform, StyleSheet, Text, View } from "react-native";
|
|
4
4
|
import { Icon } from "./Icon.js";
|
|
5
5
|
import { AnimatedView } from "./AnimatedView.js";
|
|
6
|
-
import { TextClassContext, TextSelectabilityContext } from "./StyledText.
|
|
6
|
+
import { TextClassContext, TextSelectabilityContext } from "./StyledText.context";
|
|
7
7
|
import { useTheme } from "../hooks/useTheme.js";
|
|
8
8
|
import { spacing } from "../constants/spacing.js";
|
|
9
9
|
import * as DropdownMenuPrimitive from "@rn-primitives/dropdown-menu";
|
|
@@ -91,14 +91,7 @@ function DropdownMenuContent({ side, align = "start", sideOffset = 4, portalHost
|
|
|
91
91
|
function DropdownMenuItem({ inset = false, variant = "default", style: styleOverride, ...props }) {
|
|
92
92
|
const { theme } = useTheme();
|
|
93
93
|
return (_jsx(TextClassContext.Provider, { value: "", children: _jsx(TextSelectabilityContext.Provider, { value: false, children: _jsx(DropdownMenuPrimitive.Item, { ...props, style: {
|
|
94
|
-
|
|
95
|
-
flexDirection: "row",
|
|
96
|
-
alignItems: "center",
|
|
97
|
-
gap: spacing.sm,
|
|
98
|
-
borderRadius: spacing.radiusSm,
|
|
99
|
-
paddingHorizontal: spacing.sm,
|
|
100
|
-
paddingVertical: Platform.select({ web: spacing.xs, default: spacing.sm }),
|
|
101
|
-
backgroundColor: "transparent",
|
|
94
|
+
...styles.item,
|
|
102
95
|
...(Platform.OS === "web" && {
|
|
103
96
|
cursor: props.disabled ? "not-allowed" : "pointer",
|
|
104
97
|
outlineStyle: "none",
|
|
@@ -114,15 +107,7 @@ function DropdownMenuItem({ inset = false, variant = "default", style: styleOver
|
|
|
114
107
|
function DropdownMenuCheckboxItem({ children, style: styleOverride, ...props }) {
|
|
115
108
|
const { theme } = useTheme();
|
|
116
109
|
return (_jsx(TextClassContext.Provider, { value: "", children: _jsx(TextSelectabilityContext.Provider, { value: false, children: _jsxs(DropdownMenuPrimitive.CheckboxItem, { ...props, style: {
|
|
117
|
-
|
|
118
|
-
flexDirection: "row",
|
|
119
|
-
alignItems: "center",
|
|
120
|
-
gap: spacing.sm,
|
|
121
|
-
borderRadius: spacing.radiusSm,
|
|
122
|
-
paddingVertical: Platform.select({ web: spacing.xs, default: spacing.sm }),
|
|
123
|
-
paddingLeft: spacing.xl,
|
|
124
|
-
paddingRight: spacing.sm,
|
|
125
|
-
backgroundColor: "transparent",
|
|
110
|
+
...styles.indicatorItem,
|
|
126
111
|
...(Platform.OS === "web" && {
|
|
127
112
|
cursor: props.disabled ? "not-allowed" : "pointer",
|
|
128
113
|
outlineStyle: "none",
|
|
@@ -144,15 +129,7 @@ function DropdownMenuCheckboxItem({ children, style: styleOverride, ...props })
|
|
|
144
129
|
function DropdownMenuRadioItem({ children, style: styleOverride, ...props }) {
|
|
145
130
|
const { theme } = useTheme();
|
|
146
131
|
return (_jsx(TextClassContext.Provider, { value: "", children: _jsx(TextSelectabilityContext.Provider, { value: false, children: _jsxs(DropdownMenuPrimitive.RadioItem, { ...props, style: {
|
|
147
|
-
|
|
148
|
-
flexDirection: "row",
|
|
149
|
-
alignItems: "center",
|
|
150
|
-
gap: spacing.sm,
|
|
151
|
-
borderRadius: spacing.radiusSm,
|
|
152
|
-
paddingVertical: Platform.select({ web: spacing.xs, default: spacing.sm }),
|
|
153
|
-
paddingLeft: spacing.xl,
|
|
154
|
-
paddingRight: spacing.sm,
|
|
155
|
-
backgroundColor: "transparent",
|
|
132
|
+
...styles.indicatorItem,
|
|
156
133
|
...(Platform.OS === "web" && {
|
|
157
134
|
cursor: props.disabled ? "not-allowed" : "pointer",
|
|
158
135
|
outlineStyle: "none",
|
|
@@ -203,7 +180,7 @@ function DropdownMenuSeparator({ style: styleOverride, ...props }) {
|
|
|
203
180
|
: {}),
|
|
204
181
|
} }));
|
|
205
182
|
}
|
|
206
|
-
function DropdownMenuShortcut({ style: styleOverride, ...props }) {
|
|
183
|
+
function DropdownMenuShortcut({ children, text, style: styleOverride, ...props }) {
|
|
207
184
|
const { theme } = useTheme();
|
|
208
185
|
return (_jsx(Text, { ...props, style: [
|
|
209
186
|
{
|
|
@@ -215,6 +192,29 @@ function DropdownMenuShortcut({ style: styleOverride, ...props }) {
|
|
|
215
192
|
userSelect: "none",
|
|
216
193
|
},
|
|
217
194
|
styleOverride,
|
|
218
|
-
] }));
|
|
195
|
+
], children: text ?? children }));
|
|
219
196
|
}
|
|
197
|
+
const styles = StyleSheet.create({
|
|
198
|
+
item: {
|
|
199
|
+
position: "relative",
|
|
200
|
+
flexDirection: "row",
|
|
201
|
+
alignItems: "center",
|
|
202
|
+
gap: spacing.sm,
|
|
203
|
+
borderRadius: spacing.radiusSm,
|
|
204
|
+
paddingHorizontal: spacing.sm,
|
|
205
|
+
paddingVertical: Platform.select({ web: spacing.xs, default: spacing.sm }),
|
|
206
|
+
backgroundColor: "transparent",
|
|
207
|
+
},
|
|
208
|
+
indicatorItem: {
|
|
209
|
+
position: "relative",
|
|
210
|
+
flexDirection: "row",
|
|
211
|
+
alignItems: "center",
|
|
212
|
+
gap: spacing.sm,
|
|
213
|
+
borderRadius: spacing.radiusSm,
|
|
214
|
+
paddingVertical: Platform.select({ web: spacing.xs, default: spacing.sm }),
|
|
215
|
+
paddingLeft: spacing.xl,
|
|
216
|
+
paddingRight: spacing.sm,
|
|
217
|
+
backgroundColor: "transparent",
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
220
|
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, };
|
|
@@ -26,7 +26,7 @@ import { spacing } from "../constants/spacing.js";
|
|
|
26
26
|
export function EmptyState({ icon, iconSize = 48, title, description, actionLabel, onAction, actionPreset = "default", style, children, }) {
|
|
27
27
|
const { theme } = useTheme();
|
|
28
28
|
const styles = useMemo(() => createStyles(theme), [theme]);
|
|
29
|
-
return (_jsxs(View, { style: [styles.container, style], children: [!!icon && (_jsx(View, { style: styles.iconWrapper, children: _jsx(Icon, { name: icon, size: iconSize, color: theme.colors.mutedForeground }) })), _jsx(SansSerifBoldText, { selectable: false, style: styles.title, children: title }), !!description && (_jsx(SansSerifText, { selectable: false, style: styles.description, children: description })), children, !!actionLabel && onAction && (_jsx(Button, { preset: actionPreset, onPress: onAction, style: styles.action
|
|
29
|
+
return (_jsxs(View, { style: [styles.container, style], children: [!!icon && (_jsx(View, { style: styles.iconWrapper, children: _jsx(Icon, { name: icon, size: iconSize, color: theme.colors.mutedForeground }) })), _jsx(SansSerifBoldText, { selectable: false, style: styles.title, children: title }), !!description && (_jsx(SansSerifText, { selectable: false, style: styles.description, children: description })), children, !!actionLabel && onAction && (_jsx(Button, { preset: actionPreset, onPress: onAction, text: actionLabel, style: styles.action }))] }));
|
|
30
30
|
}
|
|
31
31
|
const createStyles = (theme) => StyleSheet.create({
|
|
32
32
|
container: {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import { useMemo, useRef, useState, useCallback } from "react";
|
|
3
3
|
import { View, TextInput as RNTextInput, Pressable, StyleSheet, Platform, } from "react-native";
|
|
4
|
-
import Animated, { useSharedValue, useAnimatedStyle, withTiming, useReducedMotion, } from "react-native-reanimated";
|
|
5
4
|
import { useTheme } from "../hooks/useTheme.js";
|
|
6
5
|
import { spacing } from "../constants/spacing.js";
|
|
7
6
|
import { fontFamilies } from "../constants/fonts.js";
|
|
@@ -11,7 +10,6 @@ const CELL_HEIGHT = 40;
|
|
|
11
10
|
const CELL_FONT_SIZE = 20;
|
|
12
11
|
const CELL_FONT_WEIGHT = "600";
|
|
13
12
|
const BULLET = "\u2022";
|
|
14
|
-
const ANIM_DURATION = 60;
|
|
15
13
|
/**
|
|
16
14
|
* OTP/verification code input with individual character cells.
|
|
17
15
|
*
|
|
@@ -30,7 +28,6 @@ const ANIM_DURATION = 60;
|
|
|
30
28
|
*/
|
|
31
29
|
function InputOTP({ length = 6, value = "", onChangeText, onComplete, error = false, errorText, disabled = false, autoFocus = false, secureTextEntry = false, inputMode = "numeric", style: styleOverride, }) {
|
|
32
30
|
const { theme } = useTheme();
|
|
33
|
-
const reduceMotion = useReducedMotion();
|
|
34
31
|
const inputRef = useRef(null);
|
|
35
32
|
const [focused, setFocused] = useState(false);
|
|
36
33
|
const styles = useMemo(() => createStyles(theme), [theme]);
|
|
@@ -61,13 +58,13 @@ function InputOTP({ length = 6, value = "", onChangeText, onComplete, error = fa
|
|
|
61
58
|
return;
|
|
62
59
|
}
|
|
63
60
|
}, [value]);
|
|
64
|
-
const
|
|
61
|
+
const markOtpFocused = useCallback(() => {
|
|
65
62
|
setFocused(true);
|
|
66
63
|
}, []);
|
|
67
|
-
const
|
|
64
|
+
const markOtpBlurred = useCallback(() => {
|
|
68
65
|
setFocused(false);
|
|
69
66
|
}, []);
|
|
70
|
-
return (_jsxs(View, { style: StyleSheet.flatten([styles.container, styleOverride]), children: [_jsx(RNTextInput, { ref: inputRef, value: value, onChangeText: handleChangeText, onKeyPress: handleKeyPress, onFocus:
|
|
67
|
+
return (_jsxs(View, { style: StyleSheet.flatten([styles.container, styleOverride]), children: [_jsx(RNTextInput, { ref: inputRef, value: value, onChangeText: handleChangeText, onKeyPress: handleKeyPress, onFocus: markOtpFocused, onBlur: markOtpBlurred, maxLength: length, autoFocus: autoFocus, editable: !disabled, inputMode: inputMode, autoComplete: "one-time-code", textContentType: "oneTimeCode", caretHidden: true, style: styles.hiddenInput, accessibilityLabel: "Verification code input", importantForAccessibility: "yes" }), _jsx(View, { style: styles.cellRow, children: Array.from({ length }, (_, index) => {
|
|
71
68
|
const char = value[index] ?? "";
|
|
72
69
|
const isActive = focused && index === activeIndex;
|
|
73
70
|
const displayChar = char
|
|
@@ -75,52 +72,31 @@ function InputOTP({ length = 6, value = "", onChangeText, onComplete, error = fa
|
|
|
75
72
|
? BULLET
|
|
76
73
|
: char
|
|
77
74
|
: "";
|
|
78
|
-
return (_jsx(OTPCell, { index: index, total: length, char: displayChar, isActive: isActive, hasError: hasError, disabled: disabled, theme: theme,
|
|
79
|
-
}) }), !!errorText && (_jsx(StyledText, { style:
|
|
75
|
+
return (_jsx(OTPCell, { index: index, total: length, char: displayChar, isActive: isActive, hasError: hasError, disabled: disabled, theme: theme, onPress: focusInput }, index));
|
|
76
|
+
}) }), !!errorText && (_jsx(StyledText, { style: styles.errorText, children: errorText }))] }));
|
|
80
77
|
}
|
|
81
|
-
function OTPCell({ index, total, char, isActive, hasError, disabled, theme,
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
function OTPCell({ index, total, char, isActive, hasError, disabled, theme, onPress, }) {
|
|
79
|
+
// borderWidth is a layout property — animating it forces a JS-thread layout
|
|
80
|
+
// pass every frame. The 1↔2px and color changes read as instant for OTP
|
|
81
|
+
// cells, so compute both during render.
|
|
82
|
+
const borderWidth = isActive && !hasError ? 2 : 1;
|
|
83
|
+
const borderColor = hasError
|
|
84
84
|
? theme.colors.destructive
|
|
85
85
|
: isActive
|
|
86
86
|
? theme.colors.primary
|
|
87
|
-
: theme.colors.border
|
|
88
|
-
|
|
89
|
-
React.useEffect(() => {
|
|
90
|
-
const duration = reduceMotion ? 0 : ANIM_DURATION;
|
|
91
|
-
const targetWidth = isActive && !hasError ? 2 : 1;
|
|
92
|
-
const targetColor = hasError
|
|
93
|
-
? theme.colors.destructive
|
|
94
|
-
: isActive
|
|
95
|
-
? theme.colors.primary
|
|
96
|
-
: theme.colors.border;
|
|
97
|
-
borderWidth.value = withTiming(targetWidth, { duration });
|
|
98
|
-
borderColor.value = withTiming(targetColor, { duration });
|
|
99
|
-
}, [
|
|
100
|
-
isActive,
|
|
101
|
-
hasError,
|
|
102
|
-
theme.colors.destructive,
|
|
103
|
-
theme.colors.primary,
|
|
104
|
-
theme.colors.border,
|
|
105
|
-
reduceMotion,
|
|
106
|
-
borderWidth,
|
|
107
|
-
borderColor,
|
|
108
|
-
]);
|
|
109
|
-
const animatedStyle = useAnimatedStyle(() => ({
|
|
110
|
-
borderWidth: borderWidth.value,
|
|
111
|
-
borderColor: borderColor.value,
|
|
112
|
-
}));
|
|
113
|
-
return (_jsx(Pressable, { onPress: onPress, disabled: disabled, accessibilityRole: "button", accessibilityLabel: `Digit ${index + 1} of ${total}`, accessibilityState: { disabled }, children: _jsx(Animated.View, { style: [
|
|
87
|
+
: theme.colors.border;
|
|
88
|
+
return (_jsx(Pressable, { onPress: onPress, disabled: disabled, accessibilityRole: "button", accessibilityLabel: `Digit ${index + 1} of ${total}`, accessibilityState: { disabled }, children: _jsx(View, { style: [
|
|
114
89
|
{
|
|
115
90
|
width: CELL_WIDTH,
|
|
116
91
|
height: CELL_HEIGHT,
|
|
117
92
|
borderRadius: spacing.radiusMd,
|
|
93
|
+
borderWidth,
|
|
118
94
|
justifyContent: "center",
|
|
119
95
|
alignItems: "center",
|
|
120
96
|
backgroundColor: "transparent",
|
|
121
97
|
opacity: disabled ? 0.5 : 1,
|
|
98
|
+
borderColor,
|
|
122
99
|
},
|
|
123
|
-
animatedStyle,
|
|
124
100
|
], children: _jsx(StyledText, { selectable: false, style: {
|
|
125
101
|
fontSize: CELL_FONT_SIZE,
|
|
126
102
|
fontWeight: CELL_FONT_WEIGHT,
|