@mrmeg/expo-ui 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LLM_USAGE.md +4 -5
- package/README.md +6 -6
- 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 +50 -25
- 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/themeColorScope.js +3 -3
- package/llms-full.md +4 -5
- package/llms.txt +2 -2
- package/package.json +1 -4
|
@@ -1,25 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Text as RNText, TextProps as RNTextProps
|
|
3
|
-
/**
|
|
4
|
-
* TextClassContext provides className context for nested text components
|
|
5
|
-
* Used by @rn-primitives to apply consistent styling through the component tree
|
|
6
|
-
*/
|
|
7
|
-
export declare const TextClassContext: React.Context<string | undefined>;
|
|
8
|
-
/**
|
|
9
|
-
* TextColorContext provides color context for nested text components
|
|
10
|
-
* Allows parent components (like Button) to override text color for all children
|
|
11
|
-
*/
|
|
12
|
-
export declare const TextColorContext: React.Context<string | undefined>;
|
|
13
|
-
/**
|
|
14
|
-
* TextStyleContext allows controls such as Button to pass sizing typography to
|
|
15
|
-
* nested StyledText children without forcing consumers to use the `text` prop.
|
|
16
|
-
*/
|
|
17
|
-
export declare const TextStyleContext: React.Context<StyleProp<TextStyle>>;
|
|
18
|
-
/**
|
|
19
|
-
* Allows interactive controls to disable text selection for nested StyledText
|
|
20
|
-
* without changing the package-wide default for readable content.
|
|
21
|
-
*/
|
|
22
|
-
export declare const TextSelectabilityContext: React.Context<boolean | undefined>;
|
|
1
|
+
import { type Ref } from "react";
|
|
2
|
+
import { Text as RNText, TextProps as RNTextProps } from "react-native";
|
|
23
3
|
/**
|
|
24
4
|
* Font size variants following the DM Sans / DM Serif Display scale
|
|
25
5
|
*/
|
|
@@ -71,6 +51,10 @@ export type TextProps = RNTextProps & {
|
|
|
71
51
|
* as well as explicitly setting locale or translation fallbacks.
|
|
72
52
|
*/
|
|
73
53
|
txOptions?: object;
|
|
54
|
+
/**
|
|
55
|
+
* Forwarded ref to the underlying RNText element.
|
|
56
|
+
*/
|
|
57
|
+
ref?: Ref<RNText>;
|
|
74
58
|
};
|
|
75
59
|
/**
|
|
76
60
|
* Base Text component with theme and variant support
|
|
@@ -85,42 +69,7 @@ export type TextProps = RNTextProps & {
|
|
|
85
69
|
* chrome such as button labels, tabs, badges, and field labels
|
|
86
70
|
* - numberOfLines and ellipsizeMode support from RN TextProps
|
|
87
71
|
*/
|
|
88
|
-
export declare
|
|
89
|
-
/**
|
|
90
|
-
* Font variant - serif (DM Serif Display) or sans-serif (DM Sans)
|
|
91
|
-
*/
|
|
92
|
-
variant?: "serif" | "sansSerif";
|
|
93
|
-
/**
|
|
94
|
-
* Font weight - light, regular, medium, semibold, bold
|
|
95
|
-
*/
|
|
96
|
-
fontWeight?: FontWeight;
|
|
97
|
-
/**
|
|
98
|
-
* Font size variant
|
|
99
|
-
*/
|
|
100
|
-
size?: FontSize;
|
|
101
|
-
/**
|
|
102
|
-
* Semantic variant - automatically sets size and weight
|
|
103
|
-
* Overrides individual size/fontWeight if provided
|
|
104
|
-
*/
|
|
105
|
-
semantic?: SemanticVariant;
|
|
106
|
-
/**
|
|
107
|
-
* Text alignment
|
|
108
|
-
*/
|
|
109
|
-
align?: TextAlign;
|
|
110
|
-
/**
|
|
111
|
-
* Text which is looked up via i18n.
|
|
112
|
-
*/
|
|
113
|
-
tx?: string;
|
|
114
|
-
/**
|
|
115
|
-
* The text to display directly, or as fallback text when `tx` is provided.
|
|
116
|
-
*/
|
|
117
|
-
text?: string;
|
|
118
|
-
/**
|
|
119
|
-
* Optional options to pass to i18n. Useful for interpolation
|
|
120
|
-
* as well as explicitly setting locale or translation fallbacks.
|
|
121
|
-
*/
|
|
122
|
-
txOptions?: object;
|
|
123
|
-
} & React.RefAttributes<RNText>>;
|
|
72
|
+
export declare function StyledText(props: TextProps): import("react/jsx-runtime").JSX.Element;
|
|
124
73
|
/**
|
|
125
74
|
* Serif Text Component
|
|
126
75
|
* Uses serif font family (DM Serif Display)
|
|
@@ -1,29 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import { use } from "react";
|
|
3
3
|
import { Text as RNText, StyleSheet } from "react-native";
|
|
4
4
|
import { useTheme } from "../hooks/useTheme.js";
|
|
5
5
|
import { fontFamilies } from "../constants/fonts.js";
|
|
6
6
|
import { translateText } from "../lib/i18n.js";
|
|
7
|
-
|
|
8
|
-
* TextClassContext provides className context for nested text components
|
|
9
|
-
* Used by @rn-primitives to apply consistent styling through the component tree
|
|
10
|
-
*/
|
|
11
|
-
export const TextClassContext = React.createContext(undefined);
|
|
12
|
-
/**
|
|
13
|
-
* TextColorContext provides color context for nested text components
|
|
14
|
-
* Allows parent components (like Button) to override text color for all children
|
|
15
|
-
*/
|
|
16
|
-
export const TextColorContext = React.createContext(undefined);
|
|
17
|
-
/**
|
|
18
|
-
* TextStyleContext allows controls such as Button to pass sizing typography to
|
|
19
|
-
* nested StyledText children without forcing consumers to use the `text` prop.
|
|
20
|
-
*/
|
|
21
|
-
export const TextStyleContext = React.createContext(undefined);
|
|
22
|
-
/**
|
|
23
|
-
* Allows interactive controls to disable text selection for nested StyledText
|
|
24
|
-
* without changing the package-wide default for readable content.
|
|
25
|
-
*/
|
|
26
|
-
export const TextSelectabilityContext = React.createContext(undefined);
|
|
7
|
+
import { TextColorContext, TextSelectabilityContext, TextStyleContext } from "./StyledText.context";
|
|
27
8
|
const FONT_SIZES = {
|
|
28
9
|
xs: 11,
|
|
29
10
|
sm: 12,
|
|
@@ -83,13 +64,13 @@ const getFontFamilyWeight = (weight) => {
|
|
|
83
64
|
* chrome such as button labels, tabs, badges, and field labels
|
|
84
65
|
* - numberOfLines and ellipsizeMode support from RN TextProps
|
|
85
66
|
*/
|
|
86
|
-
export
|
|
87
|
-
const { tx, text, txOptions, style, variant = "sansSerif", fontWeight, size, semantic, align, selectable, children, ...otherProps } = props;
|
|
67
|
+
export function StyledText(props) {
|
|
68
|
+
const { tx, text, txOptions, style, variant = "sansSerif", fontWeight, size, semantic, align, selectable, children, ref, ...otherProps } = props;
|
|
88
69
|
const { theme } = useTheme();
|
|
89
70
|
// Check if there's a color override from parent context (e.g., Button)
|
|
90
|
-
const contextColor =
|
|
91
|
-
const contextTextStyle =
|
|
92
|
-
const contextSelectable =
|
|
71
|
+
const contextColor = use(TextColorContext);
|
|
72
|
+
const contextTextStyle = use(TextStyleContext);
|
|
73
|
+
const contextSelectable = use(TextSelectabilityContext);
|
|
93
74
|
const resolvedSelectable = selectable ?? contextSelectable ?? true;
|
|
94
75
|
// Use context color if provided, otherwise use theme default
|
|
95
76
|
const color = contextColor ?? theme.colors.text;
|
|
@@ -134,8 +115,7 @@ export const StyledText = forwardRef((props, ref) => {
|
|
|
134
115
|
resolvedContextTextStyle,
|
|
135
116
|
style,
|
|
136
117
|
], selectable: resolvedSelectable, ...otherProps, children: content }));
|
|
137
|
-
}
|
|
138
|
-
StyledText.displayName = "StyledText";
|
|
118
|
+
}
|
|
139
119
|
/**
|
|
140
120
|
* Serif Text Component
|
|
141
121
|
* Uses serif font family (DM Serif Display)
|
|
@@ -6,9 +6,9 @@ import { useTheme } from "../hooks/useTheme.js";
|
|
|
6
6
|
import { hapticLight } from "../lib/haptics.js";
|
|
7
7
|
import * as SwitchPrimitives from "@rn-primitives/switch";
|
|
8
8
|
import { useCallback, useEffect, useRef } from "react";
|
|
9
|
-
import { ActivityIndicator, Platform, StyleSheet, View } from "react-native";
|
|
10
|
-
import Animated, { useSharedValue, useAnimatedStyle, withTiming, interpolate, useReducedMotion, } from "react-native-reanimated";
|
|
9
|
+
import { ActivityIndicator, Platform, StyleSheet, View, Animated } from "react-native";
|
|
11
10
|
import { StyledText } from "./StyledText.js";
|
|
11
|
+
import { useReducedMotion } from "../hooks/useReduceMotion.js";
|
|
12
12
|
const DEFAULT_HIT_SLOP = 8;
|
|
13
13
|
function Switch({ variant = "default", labelOn, labelOff, size = { width: 44, height: 24 }, thumbSize = 20, loading = false, style: styleOverride, ...props }) {
|
|
14
14
|
const { theme, getContrastingColor, getShadowStyle, withAlpha } = useTheme();
|
|
@@ -23,27 +23,31 @@ function Switch({ variant = "default", labelOn, labelOff, size = { width: 44, he
|
|
|
23
23
|
useEffect(() => {
|
|
24
24
|
hasMounted.current = true;
|
|
25
25
|
}, []);
|
|
26
|
-
// Single
|
|
27
|
-
const progress =
|
|
26
|
+
// Single animated value drives everything: 0 = off, 1 = on
|
|
27
|
+
const progress = useRef(new Animated.Value(props.checked ? 1 : 0)).current;
|
|
28
28
|
useEffect(() => {
|
|
29
29
|
const target = props.checked ? 1 : 0;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}, [props.checked, reduceMotion]);
|
|
30
|
+
Animated.timing(progress, {
|
|
31
|
+
toValue: target,
|
|
32
|
+
duration: reduceMotion ? 0 : 120,
|
|
33
|
+
useNativeDriver: true,
|
|
34
|
+
}).start();
|
|
35
|
+
}, [props.checked, reduceMotion, progress]);
|
|
37
36
|
// Thumb slides from left to right with equal inset on every side.
|
|
38
37
|
const thumbInset = Math.max(2, (size.height - thumbSize) / 2);
|
|
39
38
|
const thumbTravel = Math.max(0, size.width - thumbSize - thumbInset * 2);
|
|
40
39
|
const labelGap = spacing.xs;
|
|
41
40
|
const labelHorizontalInset = spacing.xs;
|
|
42
|
-
const thumbAnimatedStyle =
|
|
41
|
+
const thumbAnimatedStyle = {
|
|
43
42
|
transform: [
|
|
44
|
-
{
|
|
43
|
+
{
|
|
44
|
+
translateX: progress.interpolate({
|
|
45
|
+
inputRange: [0, 1],
|
|
46
|
+
outputRange: [0, thumbTravel],
|
|
47
|
+
}),
|
|
48
|
+
},
|
|
45
49
|
],
|
|
46
|
-
}
|
|
50
|
+
};
|
|
47
51
|
const isIOS = variant === "ios";
|
|
48
52
|
const checkedColor = isIOS ? "#34C759" : theme.colors.accent;
|
|
49
53
|
const uncheckedColor = theme.dark ? withAlpha(palette.white, 0.12) : theme.colors.input;
|
|
@@ -84,15 +88,10 @@ function Switch({ variant = "default", labelOn, labelOff, size = { width: 44, he
|
|
|
84
88
|
borderColor: trackBorderColor,
|
|
85
89
|
pointerEvents: "none",
|
|
86
90
|
} }), labelOn && !isIOS && (_jsx(View, { style: {
|
|
87
|
-
|
|
88
|
-
top: 0,
|
|
89
|
-
bottom: 0,
|
|
91
|
+
...styles.label,
|
|
90
92
|
left: labelHorizontalInset,
|
|
91
93
|
right: thumbInset + thumbSize + labelGap,
|
|
92
|
-
justifyContent: "center",
|
|
93
|
-
alignItems: "center",
|
|
94
94
|
opacity: props.checked ? 1 : 0,
|
|
95
|
-
pointerEvents: "none",
|
|
96
95
|
}, children: _jsx(StyledText, { selectable: false, style: {
|
|
97
96
|
fontFamily: fontFamilies.sansSerif.bold,
|
|
98
97
|
fontSize: labelFontSize,
|
|
@@ -113,15 +112,10 @@ function Switch({ variant = "default", labelOn, labelOff, size = { width: 44, he
|
|
|
113
112
|
},
|
|
114
113
|
thumbAnimatedStyle,
|
|
115
114
|
], children: loading && (_jsx(ActivityIndicator, { size: "small", color: thumbIndicatorColor })) }) }), labelOff && !isIOS && (_jsx(View, { style: {
|
|
116
|
-
|
|
117
|
-
top: 0,
|
|
118
|
-
bottom: 0,
|
|
115
|
+
...styles.label,
|
|
119
116
|
left: thumbInset + thumbSize + labelGap,
|
|
120
117
|
right: labelHorizontalInset,
|
|
121
|
-
justifyContent: "center",
|
|
122
|
-
alignItems: "center",
|
|
123
118
|
opacity: props.checked ? 0 : 1,
|
|
124
|
-
pointerEvents: "none",
|
|
125
119
|
}, children: _jsx(StyledText, { selectable: false, style: {
|
|
126
120
|
fontFamily: fontFamilies.sansSerif.bold,
|
|
127
121
|
fontSize: labelFontSize,
|
|
@@ -129,4 +123,14 @@ function Switch({ variant = "default", labelOn, labelOff, size = { width: 44, he
|
|
|
129
123
|
userSelect: "none",
|
|
130
124
|
}, children: labelOff }) }))] }));
|
|
131
125
|
}
|
|
126
|
+
const styles = StyleSheet.create({
|
|
127
|
+
label: {
|
|
128
|
+
position: "absolute",
|
|
129
|
+
top: 0,
|
|
130
|
+
bottom: 0,
|
|
131
|
+
justifyContent: "center",
|
|
132
|
+
alignItems: "center",
|
|
133
|
+
pointerEvents: "none",
|
|
134
|
+
},
|
|
135
|
+
});
|
|
132
136
|
export { Switch };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import * as React from "react";
|
|
1
2
|
import { type StyleProp, type ViewStyle } from "react-native";
|
|
2
3
|
import * as TabsPrimitive from "@rn-primitives/tabs";
|
|
4
|
+
import { StyledText } from "./StyledText";
|
|
3
5
|
import { type IconName } from "./Icon";
|
|
4
6
|
type TabsVariant = "underline" | "pill";
|
|
5
7
|
type TabsSize = "sm" | "md";
|
|
@@ -17,15 +19,33 @@ export interface TabsTriggerProps extends TabsPrimitive.TriggerProps {
|
|
|
17
19
|
style?: StyleProp<ViewStyle>;
|
|
18
20
|
}
|
|
19
21
|
declare function TabsTriggerInner({ icon, style, children, value, ...props }: TabsTriggerProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
-
|
|
22
|
+
/**
|
|
23
|
+
* TabsTrigger.Text
|
|
24
|
+
* Label text for a TabsTrigger. Reads the tab size from context and applies the
|
|
25
|
+
* matching font size, so callers state their intent explicitly instead of
|
|
26
|
+
* relying on the trigger to inspect `typeof children`. Text color is inherited
|
|
27
|
+
* from the trigger via TextColorContext.
|
|
28
|
+
*
|
|
29
|
+
* ```tsx
|
|
30
|
+
* <Tabs.Trigger value="account">
|
|
31
|
+
* <Tabs.Trigger.Text>Account</Tabs.Trigger.Text>
|
|
32
|
+
* </Tabs.Trigger>
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
declare function TabsTriggerText({ style, ...props }: React.ComponentProps<typeof StyledText>): import("react/jsx-runtime").JSX.Element;
|
|
21
36
|
export interface TabsContentProps extends TabsPrimitive.ContentProps {
|
|
22
37
|
style?: StyleProp<ViewStyle>;
|
|
23
38
|
}
|
|
24
39
|
declare function TabsContent({ style, children, ...props }: TabsContentProps): import("react/jsx-runtime").JSX.Element;
|
|
40
|
+
declare const TabsTriggerCompound: typeof TabsTriggerInner & {
|
|
41
|
+
Text: typeof TabsTriggerText;
|
|
42
|
+
};
|
|
25
43
|
declare const Tabs: typeof TabsRoot & {
|
|
26
44
|
List: typeof TabsList;
|
|
27
|
-
Trigger: typeof TabsTriggerInner
|
|
45
|
+
Trigger: typeof TabsTriggerInner & {
|
|
46
|
+
Text: typeof TabsTriggerText;
|
|
47
|
+
};
|
|
28
48
|
Content: typeof TabsContent;
|
|
29
49
|
};
|
|
30
|
-
export { Tabs, TabsList, TabsTrigger, TabsContent, };
|
|
50
|
+
export { Tabs, TabsList, TabsTriggerCompound as TabsTrigger, TabsTriggerText, TabsContent, };
|
|
31
51
|
export type { TabsVariant, TabsSize, };
|
package/dist/components/Tabs.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import { Platform, StyleSheet, View } from "react-native";
|
|
3
|
+
import { Animated, Platform, StyleSheet, View } from "react-native";
|
|
4
4
|
import * as TabsPrimitive from "@rn-primitives/tabs";
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
5
|
+
import { StyledText } from "./StyledText.js";
|
|
6
|
+
import { TextClassContext, TextColorContext } from "./StyledText.context";
|
|
7
7
|
import { Icon } from "./Icon.js";
|
|
8
8
|
import { useTheme } from "../hooks/useTheme.js";
|
|
9
|
+
import { useReducedMotion } from "../hooks/useReduceMotion.js";
|
|
9
10
|
import { spacing } from "../constants/spacing.js";
|
|
10
11
|
// ============================================================================
|
|
11
12
|
// Size configs
|
|
@@ -29,10 +30,11 @@ const TabsContext = React.createContext({
|
|
|
29
30
|
size: "md",
|
|
30
31
|
});
|
|
31
32
|
function useTabsContext() {
|
|
32
|
-
return React.
|
|
33
|
+
return React.use(TabsContext);
|
|
33
34
|
}
|
|
34
35
|
function TabsRoot({ variant = "underline", size = "md", children, ...props }) {
|
|
35
|
-
|
|
36
|
+
const contextValue = React.useMemo(() => ({ variant, size }), [variant, size]);
|
|
37
|
+
return (_jsx(TabsContext.Provider, { value: contextValue, children: _jsx(TabsPrimitive.Root, { ...props, children: children }) }));
|
|
36
38
|
}
|
|
37
39
|
function TabsList({ style, children, ...props }) {
|
|
38
40
|
const { theme } = useTheme();
|
|
@@ -62,15 +64,14 @@ function TabsTriggerInner({ icon, style, children, value, ...props }) {
|
|
|
62
64
|
// Determine selected state by comparing trigger value with root value
|
|
63
65
|
const rootContext = TabsPrimitive.useRootContext();
|
|
64
66
|
const isSelected = rootContext.value === value;
|
|
65
|
-
const activeOpacity =
|
|
67
|
+
const activeOpacity = React.useRef(new Animated.Value(isSelected ? 1 : 0)).current;
|
|
66
68
|
React.useEffect(() => {
|
|
67
|
-
activeOpacity
|
|
68
|
-
|
|
69
|
-
:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}));
|
|
69
|
+
Animated.timing(activeOpacity, {
|
|
70
|
+
toValue: isSelected ? 1 : 0,
|
|
71
|
+
duration: reduceMotion ? 0 : 200,
|
|
72
|
+
useNativeDriver: true,
|
|
73
|
+
}).start();
|
|
74
|
+
}, [isSelected, reduceMotion, activeOpacity]);
|
|
74
75
|
const textColor = isDisabled
|
|
75
76
|
? theme.colors.mutedForeground
|
|
76
77
|
: isSelected
|
|
@@ -92,7 +93,7 @@ function TabsTriggerInner({ icon, style, children, value, ...props }) {
|
|
|
92
93
|
borderRadius: spacing.radiusSm,
|
|
93
94
|
...getShadowStyle("subtle"),
|
|
94
95
|
} : {};
|
|
95
|
-
return (_jsx(TextColorContext.Provider, { value: textColor, children: _jsx(TextClassContext.Provider, { value: "", children: _jsxs(TabsPrimitive.Trigger, { value: value, style: StyleSheet.flatten([triggerBaseStyle, pillActiveStyle, style]), ...props, children: [_jsxs(View, { style: triggerContentStyles.container, children: [icon && (_jsx(Icon, { name: icon, size: sizeConfig.iconSize, color: textColor, decorative: true })),
|
|
96
|
+
return (_jsx(TextColorContext.Provider, { value: textColor, children: _jsx(TextClassContext.Provider, { value: "", children: _jsxs(TabsPrimitive.Trigger, { value: value, style: StyleSheet.flatten([triggerBaseStyle, pillActiveStyle, style]), ...props, children: [_jsxs(View, { style: triggerContentStyles.container, children: [icon && (_jsx(Icon, { name: icon, size: sizeConfig.iconSize, color: textColor, decorative: true })), children] }), variant === "underline" && (_jsx(Animated.View, { style: [
|
|
96
97
|
{
|
|
97
98
|
position: "absolute",
|
|
98
99
|
bottom: 0,
|
|
@@ -101,10 +102,28 @@ function TabsTriggerInner({ icon, style, children, value, ...props }) {
|
|
|
101
102
|
height: 2,
|
|
102
103
|
backgroundColor: theme.colors.foreground,
|
|
103
104
|
},
|
|
104
|
-
|
|
105
|
+
{ opacity: activeOpacity },
|
|
105
106
|
] }))] }) }) }));
|
|
106
107
|
}
|
|
107
108
|
const TabsTrigger = TabsTriggerInner;
|
|
109
|
+
/**
|
|
110
|
+
* TabsTrigger.Text
|
|
111
|
+
* Label text for a TabsTrigger. Reads the tab size from context and applies the
|
|
112
|
+
* matching font size, so callers state their intent explicitly instead of
|
|
113
|
+
* relying on the trigger to inspect `typeof children`. Text color is inherited
|
|
114
|
+
* from the trigger via TextColorContext.
|
|
115
|
+
*
|
|
116
|
+
* ```tsx
|
|
117
|
+
* <Tabs.Trigger value="account">
|
|
118
|
+
* <Tabs.Trigger.Text>Account</Tabs.Trigger.Text>
|
|
119
|
+
* </Tabs.Trigger>
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
function TabsTriggerText({ style, ...props }) {
|
|
123
|
+
const { size } = useTabsContext();
|
|
124
|
+
const { fontSize } = SIZE_CONFIGS[size];
|
|
125
|
+
return _jsx(StyledText, { selectable: false, style: [{ fontSize }, style], ...props });
|
|
126
|
+
}
|
|
108
127
|
function TabsContent({ style, children, ...props }) {
|
|
109
128
|
return (_jsx(TabsPrimitive.Content, { style: StyleSheet.flatten([{ marginTop: spacing.md }, style]), ...props, children: children }));
|
|
110
129
|
}
|
|
@@ -122,9 +141,12 @@ const triggerContentStyles = StyleSheet.create({
|
|
|
122
141
|
// ============================================================================
|
|
123
142
|
// Compound Export
|
|
124
143
|
// ============================================================================
|
|
144
|
+
const TabsTriggerCompound = Object.assign(TabsTrigger, {
|
|
145
|
+
Text: TabsTriggerText,
|
|
146
|
+
});
|
|
125
147
|
const Tabs = Object.assign(TabsRoot, {
|
|
126
148
|
List: TabsList,
|
|
127
|
-
Trigger:
|
|
149
|
+
Trigger: TabsTriggerCompound,
|
|
128
150
|
Content: TabsContent,
|
|
129
151
|
});
|
|
130
|
-
export { Tabs, TabsList, TabsTrigger, TabsContent, };
|
|
152
|
+
export { Tabs, TabsList, TabsTriggerCompound as TabsTrigger, TabsTriggerText, TabsContent, };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type ReactNode, type Ref } from "react";
|
|
2
2
|
import { TextInput as RNTextInput, ViewStyle, TextStyle, TextInputProps, StyleProp } from "react-native";
|
|
3
3
|
/**
|
|
4
4
|
* Size variants for TextInput
|
|
@@ -9,6 +9,10 @@ export type TextInputSize = "sm" | "md" | "lg";
|
|
|
9
9
|
*/
|
|
10
10
|
export type TextInputVariant = "outline" | "filled" | "underlined";
|
|
11
11
|
interface TextInputCustomProps extends TextInputProps {
|
|
12
|
+
/**
|
|
13
|
+
* Forwarded ref to the underlying RNTextInput element.
|
|
14
|
+
*/
|
|
15
|
+
ref?: Ref<RNTextInput>;
|
|
12
16
|
/**
|
|
13
17
|
* Visual variant
|
|
14
18
|
* @default "outline"
|
|
@@ -116,5 +120,5 @@ interface TextInputCustomProps extends TextInputProps {
|
|
|
116
120
|
* />
|
|
117
121
|
* ```
|
|
118
122
|
*/
|
|
119
|
-
export declare
|
|
123
|
+
export declare function TextInput({ variant, size, label, helperText, errorText, error, required, rows, showSecureEntryToggle, leftElement, rightElement, clearable, wrapperStyle, focusedStyle, forceLight, secureTextEntry, inputMode, style, onChangeText, onFocus, onBlur, value, multiline, editable, ref, ...rest }: TextInputCustomProps): import("react/jsx-runtime").JSX.Element;
|
|
120
124
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import { useMemo, useState } from "react";
|
|
3
3
|
import { StyleSheet, TextInput as RNTextInput, Platform, View, Pressable, } from "react-native";
|
|
4
4
|
import { useTheme } from "../hooks/useTheme.js";
|
|
5
5
|
import { spacing } from "../constants/spacing.js";
|
|
@@ -70,7 +70,7 @@ const SIZE_CONFIGS = {
|
|
|
70
70
|
* />
|
|
71
71
|
* ```
|
|
72
72
|
*/
|
|
73
|
-
export
|
|
73
|
+
export function TextInput({ variant = "outline", size = "md", label, helperText, errorText, error, required, rows, showSecureEntryToggle, leftElement, rightElement, clearable = false, wrapperStyle, focusedStyle, forceLight, secureTextEntry, inputMode, style, onChangeText, onFocus, onBlur, value, multiline, editable = true, ref, ...rest }) {
|
|
74
74
|
const { theme, getContrastingColor, getFocusRingStyle } = useTheme();
|
|
75
75
|
const styles = useMemo(() => createStyles(theme, variant, size), [theme, variant, size]);
|
|
76
76
|
const [focused, setFocused] = useState(false);
|
|
@@ -116,18 +116,18 @@ export const TextInput = React.forwardRef(({ variant = "outline", size = "md", l
|
|
|
116
116
|
? "#1f2937"
|
|
117
117
|
: getContrastingColor(backgroundColor === "transparent" ? theme.colors.background : backgroundColor, theme.colors.text, palette.white);
|
|
118
118
|
const shouldScroll = multiline && rest.scrollEnabled !== false && contentHeight > 100;
|
|
119
|
-
const
|
|
119
|
+
const showInputFocusRing = (e) => {
|
|
120
120
|
setFocused(true);
|
|
121
121
|
onFocus?.(e);
|
|
122
122
|
};
|
|
123
|
-
const
|
|
123
|
+
const hideInputFocusRing = (e) => {
|
|
124
124
|
setFocused(false);
|
|
125
125
|
onBlur?.(e);
|
|
126
126
|
};
|
|
127
127
|
const togglePasswordVisible = () => {
|
|
128
128
|
setPasswordVisible(v => !v);
|
|
129
129
|
};
|
|
130
|
-
return (_jsxs(View, { style: wrapperStyle, children: [!!label && (_jsx(View, { style: styles.labelContainer, children: _jsxs(StyledText, { selectable: false, style: styles.label, children: [label, required && _jsx(StyledText, { selectable: false, style: styles.required, children: " *" })] }) })), _jsxs(View, { style: [styles.wrapper, focused && getFocusRingStyle()], children: [leftElement && _jsx(View, { style: styles.leftElement, children: leftElement }), _jsx(RNTextInput, { ref: ref, ...rest, editable: editable, inputMode: inputMode || "text", multiline: multiline, numberOfLines: rows, secureTextEntry: secureTextEntry && !passwordVisible, onChangeText: inputMode === "numeric" ? handleNumericChange : handleTextChange, onFocus:
|
|
130
|
+
return (_jsxs(View, { style: wrapperStyle, children: [!!label && (_jsx(View, { style: styles.labelContainer, children: _jsxs(StyledText, { selectable: false, style: styles.label, children: [label, required && _jsx(StyledText, { selectable: false, style: styles.required, children: " *" })] }) })), _jsxs(View, { style: [styles.wrapper, focused && getFocusRingStyle()], children: [leftElement && _jsx(View, { style: styles.leftElement, children: leftElement }), _jsx(RNTextInput, { ref: ref, ...rest, editable: editable, inputMode: inputMode || "text", multiline: multiline, numberOfLines: rows, secureTextEntry: secureTextEntry && !passwordVisible, onChangeText: inputMode === "numeric" ? handleNumericChange : handleTextChange, onFocus: showInputFocusRing, onBlur: hideInputFocusRing, onContentSizeChange: (e) => setContentHeight(e.nativeEvent.contentSize.height), scrollEnabled: shouldScroll, placeholderTextColor: theme.colors.textDim, style: [
|
|
131
131
|
styles.input,
|
|
132
132
|
{
|
|
133
133
|
backgroundColor,
|
|
@@ -156,8 +156,7 @@ export const TextInput = React.forwardRef(({ variant = "outline", size = "md", l
|
|
|
156
156
|
styles.helperText,
|
|
157
157
|
hasError && styles.errorText,
|
|
158
158
|
], children: errorText || helperText }))] }));
|
|
159
|
-
}
|
|
160
|
-
TextInput.displayName = "TextInput";
|
|
159
|
+
}
|
|
161
160
|
const createStyles = (theme, variant, size) => StyleSheet.create({
|
|
162
161
|
wrapper: {
|
|
163
162
|
width: "100%",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { Icon } from "./Icon.js";
|
|
4
|
-
import { TextClassContext, TextColorContext, TextSelectabilityContext } from "./StyledText.
|
|
4
|
+
import { TextClassContext, TextColorContext, TextSelectabilityContext } from "./StyledText.context";
|
|
5
5
|
import { useTheme } from "../hooks/useTheme.js";
|
|
6
6
|
import { spacing } from "../constants/spacing.js";
|
|
7
7
|
import * as TogglePrimitive from "@rn-primitives/toggle";
|
|
@@ -102,15 +102,11 @@ function Toggle({ variant = "default", size = "default", shape = "default", load
|
|
|
102
102
|
const isDisabled = props.disabled || loading;
|
|
103
103
|
const children = props.children;
|
|
104
104
|
return (_jsx(TextColorContext.Provider, { value: textColor, children: _jsx(TextClassContext.Provider, { value: "", children: _jsx(TogglePrimitive.Root, { ...props, disabled: isDisabled, style: {
|
|
105
|
-
|
|
106
|
-
alignItems: "center",
|
|
107
|
-
justifyContent: "center",
|
|
108
|
-
gap: spacing.sm,
|
|
105
|
+
...styles.root,
|
|
109
106
|
height: sizeConfig.height,
|
|
110
107
|
minWidth: sizeConfig.minWidth,
|
|
111
108
|
paddingHorizontal: iconOnly ? sizeConfig.height / 2 - sizeConfig.iconSize / 2 : sizeConfig.paddingHorizontal,
|
|
112
109
|
borderRadius: getBorderRadius(),
|
|
113
|
-
borderWidth: 1,
|
|
114
110
|
// Base variant styles
|
|
115
111
|
...(variant === "default" && !props.pressed && {
|
|
116
112
|
backgroundColor: "transparent",
|
|
@@ -146,7 +142,16 @@ function Toggle({ variant = "default", size = "default", shape = "default", load
|
|
|
146
142
|
}, children: loading ? (_jsx(TextSelectabilityContext.Provider, { value: false, children: _jsx(ActivityIndicator, { size: "small", color: textColor }) })) : typeof children === "function" ? ((state) => (_jsx(TextSelectabilityContext.Provider, { value: false, children: children(state) }))) : (_jsx(TextSelectabilityContext.Provider, { value: false, children: children })) }) }) }));
|
|
147
143
|
}
|
|
148
144
|
function ToggleIcon({ name, size, color }) {
|
|
149
|
-
const contextColor = React.
|
|
145
|
+
const contextColor = React.use(TextColorContext);
|
|
150
146
|
return _jsx(Icon, { name: name, size: size || spacing.iconMd, color: color || contextColor });
|
|
151
147
|
}
|
|
148
|
+
const styles = StyleSheet.create({
|
|
149
|
+
root: {
|
|
150
|
+
flexDirection: "row",
|
|
151
|
+
alignItems: "center",
|
|
152
|
+
justifyContent: "center",
|
|
153
|
+
gap: spacing.sm,
|
|
154
|
+
borderWidth: 1,
|
|
155
|
+
},
|
|
156
|
+
});
|
|
152
157
|
export { Toggle, ToggleIcon };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Icon } from "./Icon.js";
|
|
3
|
-
import { TextClassContext, TextColorContext, TextSelectabilityContext } from "./StyledText.
|
|
3
|
+
import { TextClassContext, TextColorContext, TextSelectabilityContext } from "./StyledText.context";
|
|
4
4
|
import { spacing } from "../constants/spacing.js";
|
|
5
5
|
import { useTheme } from "../hooks/useTheme.js";
|
|
6
6
|
import * as ToggleGroupPrimitive from "@rn-primitives/toggle-group";
|
|
7
7
|
import * as React from "react";
|
|
8
|
-
import { Platform } from "react-native";
|
|
8
|
+
import { Platform, StyleSheet } from "react-native";
|
|
9
9
|
const DEFAULT_HIT_SLOP = 8;
|
|
10
10
|
// Size configurations (same as Toggle)
|
|
11
11
|
const TOGGLE_GROUP_SIZES = {
|
|
@@ -33,7 +33,7 @@ const TOGGLE_GROUP_SIZES = {
|
|
|
33
33
|
};
|
|
34
34
|
const ToggleGroupContext = React.createContext(null);
|
|
35
35
|
function useToggleGroupContext() {
|
|
36
|
-
const context = React.
|
|
36
|
+
const context = React.use(ToggleGroupContext);
|
|
37
37
|
if (context === null) {
|
|
38
38
|
throw new Error("ToggleGroup compound components cannot be rendered outside the ToggleGroup component");
|
|
39
39
|
}
|
|
@@ -65,6 +65,7 @@ function useToggleGroupContext() {
|
|
|
65
65
|
*/
|
|
66
66
|
function ToggleGroup({ variant = "default", size = "default", children, ...props }) {
|
|
67
67
|
const { theme } = useTheme();
|
|
68
|
+
const contextValue = React.useMemo(() => ({ variant, size }), [variant, size]);
|
|
68
69
|
// Count valid children for first/last detection
|
|
69
70
|
const childrenArray = React.Children.toArray(children);
|
|
70
71
|
const validChildren = childrenArray.filter((child) => React.isValidElement(child) && child.type === ToggleGroupItem);
|
|
@@ -97,7 +98,7 @@ function ToggleGroup({ variant = "default", size = "default", children, ...props
|
|
|
97
98
|
...(Platform.OS === "web" && {
|
|
98
99
|
width: "fit-content",
|
|
99
100
|
}),
|
|
100
|
-
}, children: _jsx(ToggleGroupContext.Provider, { value:
|
|
101
|
+
}, children: _jsx(ToggleGroupContext.Provider, { value: contextValue, children: enhancedChildren }) }));
|
|
101
102
|
}
|
|
102
103
|
/**
|
|
103
104
|
* ToggleGroupItem Component
|
|
@@ -124,15 +125,10 @@ function ToggleGroupItem({ isFirst = false, isLast = false, children, ...props }
|
|
|
124
125
|
? getContrastingColor(itemBgColor, theme.colors.foreground, theme.colors.background)
|
|
125
126
|
: theme.colors.foreground;
|
|
126
127
|
return (_jsx(TextColorContext.Provider, { value: textColor, children: _jsx(TextClassContext.Provider, { value: "", children: _jsx(ToggleGroupPrimitive.Item, { ...props, style: {
|
|
127
|
-
|
|
128
|
-
alignItems: "center",
|
|
129
|
-
justifyContent: "center",
|
|
130
|
-
gap: spacing.sm,
|
|
128
|
+
...styles.item,
|
|
131
129
|
height: sizeConfig.height,
|
|
132
130
|
minWidth: sizeConfig.minWidth,
|
|
133
131
|
paddingHorizontal: sizeConfig.paddingHorizontal,
|
|
134
|
-
borderWidth: 1,
|
|
135
|
-
flexShrink: 0,
|
|
136
132
|
// Base variant styles
|
|
137
133
|
...(context.variant === "default" && !isSelected && {
|
|
138
134
|
backgroundColor: "transparent",
|
|
@@ -184,7 +180,17 @@ function ToggleGroupItem({ isFirst = false, isLast = false, children, ...props }
|
|
|
184
180
|
}, hitSlop: DEFAULT_HIT_SLOP, children: typeof children === "function" ? ((state) => (_jsx(TextSelectabilityContext.Provider, { value: false, children: children(state) }))) : (_jsx(TextSelectabilityContext.Provider, { value: false, children: children })) }) }) }));
|
|
185
181
|
}
|
|
186
182
|
function ToggleGroupIcon({ name, size, color }) {
|
|
187
|
-
const contextColor = React.
|
|
183
|
+
const contextColor = React.use(TextColorContext);
|
|
188
184
|
return _jsx(Icon, { name: name, size: size || spacing.iconMd, color: color || contextColor });
|
|
189
185
|
}
|
|
186
|
+
const styles = StyleSheet.create({
|
|
187
|
+
item: {
|
|
188
|
+
flexDirection: "row",
|
|
189
|
+
alignItems: "center",
|
|
190
|
+
justifyContent: "center",
|
|
191
|
+
gap: spacing.sm,
|
|
192
|
+
borderWidth: 1,
|
|
193
|
+
flexShrink: 0,
|
|
194
|
+
},
|
|
195
|
+
});
|
|
190
196
|
export { ToggleGroup, ToggleGroupIcon, ToggleGroupItem };
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { Platform, StyleSheet, View } from "react-native";
|
|
4
4
|
import { AnimatedView } from "./AnimatedView.js";
|
|
5
|
-
import { TextClassContext, TextColorContext, TextSelectabilityContext } from "./StyledText.
|
|
5
|
+
import { TextClassContext, TextColorContext, TextSelectabilityContext } from "./StyledText.context";
|
|
6
6
|
import { useTheme } from "../hooks/useTheme.js";
|
|
7
7
|
import { spacing } from "../constants/spacing.js";
|
|
8
8
|
import * as TooltipPrimitive from "@rn-primitives/tooltip";
|