@timmbr/native-ui 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Actionsheet/Actionsheet.d.ts +12 -0
- package/dist/components/Actionsheet/Actionsheet.d.ts.map +1 -0
- package/dist/components/Actionsheet/Actionsheet.js +116 -0
- package/dist/components/Actionsheet/Actionsheet.styles.d.ts +43 -0
- package/dist/components/Actionsheet/Actionsheet.styles.d.ts.map +1 -0
- package/dist/components/Actionsheet/Actionsheet.styles.js +25 -0
- package/dist/components/Actionsheet/Actionsheet.types.d.ts +39 -0
- package/dist/components/Actionsheet/Actionsheet.types.d.ts.map +1 -0
- package/dist/components/Actionsheet/index.d.ts +4 -0
- package/dist/components/Actionsheet/index.d.ts.map +1 -0
- package/dist/components/Avatar/Avatar.d.ts +8 -0
- package/dist/components/Avatar/Avatar.d.ts.map +1 -0
- package/dist/components/Avatar/Avatar.js +66 -0
- package/dist/components/Avatar/Avatar.styles.d.ts +25 -0
- package/dist/components/Avatar/Avatar.styles.d.ts.map +1 -0
- package/dist/components/Avatar/Avatar.styles.js +29 -0
- package/dist/components/Avatar/Avatar.types.d.ts +21 -0
- package/dist/components/Avatar/Avatar.types.d.ts.map +1 -0
- package/dist/components/Avatar/index.d.ts +4 -0
- package/dist/components/Avatar/index.d.ts.map +1 -0
- package/dist/components/Badge/Badge.d.ts +7 -0
- package/dist/components/Badge/Badge.d.ts.map +1 -0
- package/dist/components/Badge/Badge.js +73 -0
- package/dist/components/Badge/Badge.styles.d.ts +15 -0
- package/dist/components/Badge/Badge.styles.d.ts.map +1 -0
- package/dist/components/Badge/Badge.styles.js +71 -0
- package/dist/components/Badge/Badge.types.d.ts +25 -0
- package/dist/components/Badge/Badge.types.d.ts.map +1 -0
- package/dist/components/Badge/index.d.ts +4 -0
- package/dist/components/Badge/index.d.ts.map +1 -0
- package/dist/components/Box/Box.d.ts +4 -0
- package/dist/components/Box/Box.d.ts.map +1 -0
- package/dist/components/Box/Box.js +26 -0
- package/dist/components/Box/Box.styles.d.ts +7 -0
- package/dist/components/Box/Box.styles.d.ts.map +1 -0
- package/dist/components/Box/Box.styles.js +7 -0
- package/dist/components/Box/Box.types.d.ts +8 -0
- package/dist/components/Box/Box.types.d.ts.map +1 -0
- package/dist/components/Box/index.d.ts +4 -0
- package/dist/components/Box/index.d.ts.map +1 -0
- package/dist/components/Button/Button.d.ts +9 -0
- package/dist/components/Button/Button.d.ts.map +1 -0
- package/dist/components/Button/Button.js +169 -0
- package/dist/components/Button/Button.styles.d.ts +30 -0
- package/dist/components/Button/Button.styles.d.ts.map +1 -0
- package/dist/components/Button/Button.styles.js +106 -0
- package/dist/components/Button/Button.types.d.ts +41 -0
- package/dist/components/Button/Button.types.d.ts.map +1 -0
- package/dist/components/Button/index.d.ts +4 -0
- package/dist/components/Button/index.d.ts.map +1 -0
- package/dist/components/Card/Card.d.ts +8 -0
- package/dist/components/Card/Card.d.ts.map +1 -0
- package/dist/components/Card/Card.js +50 -0
- package/dist/components/Card/Card.styles.d.ts +26 -0
- package/dist/components/Card/Card.styles.d.ts.map +1 -0
- package/dist/components/Card/Card.styles.js +32 -0
- package/dist/components/Card/Card.types.d.ts +26 -0
- package/dist/components/Card/Card.types.d.ts.map +1 -0
- package/dist/components/Card/index.d.ts +4 -0
- package/dist/components/Card/index.d.ts.map +1 -0
- package/dist/components/Divider/Divider.d.ts +4 -0
- package/dist/components/Divider/Divider.d.ts.map +1 -0
- package/dist/components/Divider/Divider.js +22 -0
- package/dist/components/Divider/Divider.styles.d.ts +7 -0
- package/dist/components/Divider/Divider.styles.d.ts.map +1 -0
- package/dist/components/Divider/Divider.styles.js +16 -0
- package/dist/components/Divider/Divider.types.d.ts +7 -0
- package/dist/components/Divider/Divider.types.d.ts.map +1 -0
- package/dist/components/Divider/index.d.ts +4 -0
- package/dist/components/Divider/index.d.ts.map +1 -0
- package/dist/components/HStack/HStack.d.ts +4 -0
- package/dist/components/HStack/HStack.d.ts.map +1 -0
- package/dist/components/HStack/HStack.js +21 -0
- package/dist/components/HStack/HStack.styles.d.ts +8 -0
- package/dist/components/HStack/HStack.styles.d.ts.map +1 -0
- package/dist/components/HStack/HStack.styles.js +28 -0
- package/dist/components/HStack/HStack.types.d.ts +9 -0
- package/dist/components/HStack/HStack.types.d.ts.map +1 -0
- package/dist/components/HStack/index.d.ts +4 -0
- package/dist/components/HStack/index.d.ts.map +1 -0
- package/dist/components/Heading/Heading.d.ts +4 -0
- package/dist/components/Heading/Heading.d.ts.map +1 -0
- package/dist/components/Heading/Heading.js +22 -0
- package/dist/components/Heading/Heading.styles.d.ts +8 -0
- package/dist/components/Heading/Heading.styles.d.ts.map +1 -0
- package/dist/components/Heading/Heading.styles.js +28 -0
- package/dist/components/Heading/Heading.types.d.ts +10 -0
- package/dist/components/Heading/Heading.types.d.ts.map +1 -0
- package/dist/components/Heading/index.d.ts +4 -0
- package/dist/components/Heading/index.d.ts.map +1 -0
- package/dist/components/Icon/Icon.d.ts +4 -0
- package/dist/components/Icon/Icon.d.ts.map +1 -0
- package/dist/components/Icon/Icon.js +37 -0
- package/dist/components/Icon/Icon.styles.d.ts +7 -0
- package/dist/components/Icon/Icon.styles.d.ts.map +1 -0
- package/dist/components/Icon/Icon.styles.js +20 -0
- package/dist/components/Icon/Icon.types.d.ts +9 -0
- package/dist/components/Icon/Icon.types.d.ts.map +1 -0
- package/dist/components/Icon/index.d.ts +4 -0
- package/dist/components/Icon/index.d.ts.map +1 -0
- package/dist/components/Input/Input.d.ts +8 -0
- package/dist/components/Input/Input.d.ts.map +1 -0
- package/dist/components/Input/Input.js +105 -0
- package/dist/components/Input/Input.styles.d.ts +23 -0
- package/dist/components/Input/Input.styles.d.ts.map +1 -0
- package/dist/components/Input/Input.styles.js +56 -0
- package/dist/components/Input/Input.types.d.ts +32 -0
- package/dist/components/Input/Input.types.d.ts.map +1 -0
- package/dist/components/Input/index.d.ts +4 -0
- package/dist/components/Input/index.d.ts.map +1 -0
- package/dist/components/Modal/Modal.d.ts +11 -0
- package/dist/components/Modal/Modal.d.ts.map +1 -0
- package/dist/components/Modal/Modal.js +102 -0
- package/dist/components/Modal/Modal.styles.d.ts +43 -0
- package/dist/components/Modal/Modal.styles.d.ts.map +1 -0
- package/dist/components/Modal/Modal.styles.js +37 -0
- package/dist/components/Modal/Modal.types.d.ts +38 -0
- package/dist/components/Modal/Modal.types.d.ts.map +1 -0
- package/dist/components/Modal/index.d.ts +4 -0
- package/dist/components/Modal/index.d.ts.map +1 -0
- package/dist/components/Pressable/Pressable.d.ts +4 -0
- package/dist/components/Pressable/Pressable.d.ts.map +1 -0
- package/dist/components/Pressable/Pressable.js +43 -0
- package/dist/components/Pressable/Pressable.styles.d.ts +7 -0
- package/dist/components/Pressable/Pressable.styles.d.ts.map +1 -0
- package/dist/components/Pressable/Pressable.styles.js +16 -0
- package/dist/components/Pressable/Pressable.types.d.ts +9 -0
- package/dist/components/Pressable/Pressable.types.d.ts.map +1 -0
- package/dist/components/Pressable/index.d.ts +4 -0
- package/dist/components/Pressable/index.d.ts.map +1 -0
- package/dist/components/Spinner/Spinner.d.ts +4 -0
- package/dist/components/Spinner/Spinner.d.ts.map +1 -0
- package/dist/components/Spinner/Spinner.js +52 -0
- package/dist/components/Spinner/Spinner.styles.d.ts +7 -0
- package/dist/components/Spinner/Spinner.styles.d.ts.map +1 -0
- package/dist/components/Spinner/Spinner.styles.js +20 -0
- package/dist/components/Spinner/Spinner.types.d.ts +8 -0
- package/dist/components/Spinner/Spinner.types.d.ts.map +1 -0
- package/dist/components/Spinner/index.d.ts +4 -0
- package/dist/components/Spinner/index.d.ts.map +1 -0
- package/dist/components/Switch/Switch.d.ts +4 -0
- package/dist/components/Switch/Switch.d.ts.map +1 -0
- package/dist/components/Switch/Switch.js +56 -0
- package/dist/components/Switch/Switch.styles.d.ts +16 -0
- package/dist/components/Switch/Switch.styles.d.ts.map +1 -0
- package/dist/components/Switch/Switch.styles.js +50 -0
- package/dist/components/Switch/Switch.types.d.ts +11 -0
- package/dist/components/Switch/Switch.types.d.ts.map +1 -0
- package/dist/components/Switch/index.d.ts +4 -0
- package/dist/components/Switch/index.d.ts.map +1 -0
- package/dist/components/Tabs/Tabs.d.ts +9 -0
- package/dist/components/Tabs/Tabs.d.ts.map +1 -0
- package/dist/components/Tabs/Tabs.js +109 -0
- package/dist/components/Tabs/Tabs.styles.d.ts +31 -0
- package/dist/components/Tabs/Tabs.styles.d.ts.map +1 -0
- package/dist/components/Tabs/Tabs.styles.js +37 -0
- package/dist/components/Tabs/Tabs.types.d.ts +31 -0
- package/dist/components/Tabs/Tabs.types.d.ts.map +1 -0
- package/dist/components/Tabs/index.d.ts +4 -0
- package/dist/components/Tabs/index.d.ts.map +1 -0
- package/dist/components/Text/Text.d.ts +4 -0
- package/dist/components/Text/Text.d.ts.map +1 -0
- package/dist/components/Text/Text.js +29 -0
- package/dist/components/Text/Text.styles.d.ts +9 -0
- package/dist/components/Text/Text.styles.d.ts.map +1 -0
- package/dist/components/Text/Text.styles.js +35 -0
- package/dist/components/Text/Text.types.d.ts +11 -0
- package/dist/components/Text/Text.types.d.ts.map +1 -0
- package/dist/components/Text/index.d.ts +4 -0
- package/dist/components/Text/index.d.ts.map +1 -0
- package/dist/components/Textarea/Textarea.d.ts +6 -0
- package/dist/components/Textarea/Textarea.d.ts.map +1 -0
- package/dist/components/Textarea/Textarea.js +66 -0
- package/dist/components/Textarea/Textarea.styles.d.ts +16 -0
- package/dist/components/Textarea/Textarea.styles.d.ts.map +1 -0
- package/dist/components/Textarea/Textarea.styles.js +47 -0
- package/dist/components/Textarea/Textarea.types.d.ts +20 -0
- package/dist/components/Textarea/Textarea.types.d.ts.map +1 -0
- package/dist/components/Textarea/index.d.ts +4 -0
- package/dist/components/Textarea/index.d.ts.map +1 -0
- package/dist/components/Toast/Toast.d.ts +7 -0
- package/dist/components/Toast/Toast.d.ts.map +1 -0
- package/dist/components/Toast/Toast.js +39 -0
- package/dist/components/Toast/Toast.styles.d.ts +20 -0
- package/dist/components/Toast/Toast.styles.d.ts.map +1 -0
- package/dist/components/Toast/Toast.styles.js +42 -0
- package/dist/components/Toast/Toast.types.d.ts +22 -0
- package/dist/components/Toast/Toast.types.d.ts.map +1 -0
- package/dist/components/Toast/index.d.ts +4 -0
- package/dist/components/Toast/index.d.ts.map +1 -0
- package/dist/components/VStack/VStack.d.ts +4 -0
- package/dist/components/VStack/VStack.d.ts.map +1 -0
- package/dist/components/VStack/VStack.js +21 -0
- package/dist/components/VStack/VStack.styles.d.ts +8 -0
- package/dist/components/VStack/VStack.styles.d.ts.map +1 -0
- package/dist/components/VStack/VStack.styles.js +28 -0
- package/dist/components/VStack/VStack.types.d.ts +9 -0
- package/dist/components/VStack/VStack.types.d.ts.map +1 -0
- package/dist/components/VStack/index.d.ts +4 -0
- package/dist/components/VStack/index.d.ts.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/provider/TimmbrNativeProvider.d.ts +21 -0
- package/dist/provider/TimmbrNativeProvider.d.ts.map +1 -0
- package/dist/provider/TimmbrNativeProvider.js +110 -0
- package/dist/theme/config.d.ts +360 -0
- package/dist/theme/config.d.ts.map +1 -0
- package/dist/theme/config.js +129 -0
- package/package.json +70 -3
- package/src/native/index.tsx +799 -0
- package/README.md +0 -3
|
@@ -0,0 +1,799 @@
|
|
|
1
|
+
import React, { createContext, useContext, useState, useMemo } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text as RNText,
|
|
5
|
+
Pressable as RNPressable,
|
|
6
|
+
TextInput,
|
|
7
|
+
Modal as RNModal,
|
|
8
|
+
Switch as RNSwitch,
|
|
9
|
+
ActivityIndicator,
|
|
10
|
+
ViewProps,
|
|
11
|
+
TextProps,
|
|
12
|
+
PressableProps,
|
|
13
|
+
TextInputProps,
|
|
14
|
+
SwitchProps,
|
|
15
|
+
ActivityIndicatorProps,
|
|
16
|
+
ModalProps,
|
|
17
|
+
useColorScheme,
|
|
18
|
+
} from 'react-native';
|
|
19
|
+
|
|
20
|
+
export type ThemeMode = 'light' | 'dark' | 'system';
|
|
21
|
+
export type ResolvedTheme = 'light' | 'dark';
|
|
22
|
+
|
|
23
|
+
export interface TimmbrNativeContextValue {
|
|
24
|
+
mode: ThemeMode;
|
|
25
|
+
resolvedTheme: ResolvedTheme;
|
|
26
|
+
setMode: (mode: ThemeMode) => void;
|
|
27
|
+
toggleTheme: () => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const TimmbrNativeContext = createContext<TimmbrNativeContextValue>({
|
|
31
|
+
mode: 'system',
|
|
32
|
+
resolvedTheme: 'light',
|
|
33
|
+
setMode: () => {},
|
|
34
|
+
toggleTheme: () => {},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export const useTimmbrNative = (): TimmbrNativeContextValue => useContext(TimmbrNativeContext);
|
|
38
|
+
export const useTimmbrTheme = useTimmbrNative;
|
|
39
|
+
|
|
40
|
+
export interface TimmbrNativeProviderProps {
|
|
41
|
+
mode?: ThemeMode;
|
|
42
|
+
defaultMode?: ThemeMode;
|
|
43
|
+
children: React.ReactNode;
|
|
44
|
+
className?: string;
|
|
45
|
+
colorMode?: ThemeMode;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const TimmbrNativeProvider: React.FC<TimmbrNativeProviderProps> = ({
|
|
49
|
+
mode: controlledMode,
|
|
50
|
+
defaultMode = 'system',
|
|
51
|
+
colorMode,
|
|
52
|
+
children,
|
|
53
|
+
className = '',
|
|
54
|
+
}) => {
|
|
55
|
+
const systemScheme = useColorScheme();
|
|
56
|
+
const [uncontrolledMode, setUncontrolledMode] = useState<ThemeMode>(
|
|
57
|
+
colorMode ?? defaultMode
|
|
58
|
+
);
|
|
59
|
+
const currentMode = controlledMode ?? colorMode ?? uncontrolledMode;
|
|
60
|
+
|
|
61
|
+
const resolvedTheme: ResolvedTheme = useMemo(() => {
|
|
62
|
+
if (currentMode === 'system') {
|
|
63
|
+
return systemScheme === 'dark' ? 'dark' : 'light';
|
|
64
|
+
}
|
|
65
|
+
return currentMode;
|
|
66
|
+
}, [currentMode, systemScheme]);
|
|
67
|
+
|
|
68
|
+
const setMode = (newMode: ThemeMode) => {
|
|
69
|
+
setUncontrolledMode(newMode);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const toggleTheme = () => {
|
|
73
|
+
setUncontrolledMode((prev) => {
|
|
74
|
+
const active = prev === 'system' ? (systemScheme === 'dark' ? 'dark' : 'light') : prev;
|
|
75
|
+
return active === 'dark' ? 'light' : 'dark';
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const contextValue = useMemo(
|
|
80
|
+
() => ({
|
|
81
|
+
mode: currentMode,
|
|
82
|
+
resolvedTheme,
|
|
83
|
+
setMode,
|
|
84
|
+
toggleTheme,
|
|
85
|
+
}),
|
|
86
|
+
[currentMode, resolvedTheme]
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<TimmbrNativeContext.Provider value={contextValue}>
|
|
91
|
+
<View
|
|
92
|
+
className={`flex-1 ${resolvedTheme === 'dark' ? 'dark bg-background' : 'bg-background'} ${className}`}
|
|
93
|
+
>
|
|
94
|
+
{children}
|
|
95
|
+
</View>
|
|
96
|
+
</TimmbrNativeContext.Provider>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export interface BoxProps extends ViewProps {
|
|
101
|
+
className?: string;
|
|
102
|
+
}
|
|
103
|
+
export const Box: React.FC<BoxProps> = ({ className = '', style, children, ...props }) => (
|
|
104
|
+
<View className={className} style={style} {...props}>
|
|
105
|
+
{children}
|
|
106
|
+
</View>
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
export interface StackProps extends ViewProps {
|
|
110
|
+
className?: string;
|
|
111
|
+
space?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
112
|
+
reversed?: boolean;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const spaceGapMap: Record<string, string> = {
|
|
116
|
+
xs: 'gap-1',
|
|
117
|
+
sm: 'gap-2',
|
|
118
|
+
md: 'gap-3',
|
|
119
|
+
lg: 'gap-4',
|
|
120
|
+
xl: 'gap-6',
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const VStack: React.FC<StackProps> = ({
|
|
124
|
+
space,
|
|
125
|
+
className = '',
|
|
126
|
+
reversed,
|
|
127
|
+
children,
|
|
128
|
+
...props
|
|
129
|
+
}) => {
|
|
130
|
+
const gapClass = space ? spaceGapMap[space] : '';
|
|
131
|
+
const dirClass = reversed ? 'flex-col-reverse' : 'flex-col';
|
|
132
|
+
return (
|
|
133
|
+
<View className={`${dirClass} ${gapClass} ${className}`} {...props}>
|
|
134
|
+
{children}
|
|
135
|
+
</View>
|
|
136
|
+
);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export const HStack: React.FC<StackProps> = ({
|
|
140
|
+
space,
|
|
141
|
+
className = '',
|
|
142
|
+
reversed,
|
|
143
|
+
children,
|
|
144
|
+
...props
|
|
145
|
+
}) => {
|
|
146
|
+
const gapClass = space ? spaceGapMap[space] : '';
|
|
147
|
+
const dirClass = reversed ? 'flex-row-reverse' : 'flex-row items-center';
|
|
148
|
+
return (
|
|
149
|
+
<View className={`${dirClass} ${gapClass} ${className}`} {...props}>
|
|
150
|
+
{children}
|
|
151
|
+
</View>
|
|
152
|
+
);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export interface TextPropsWithClass extends TextProps {
|
|
156
|
+
className?: string;
|
|
157
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const textSizeMap: Record<string, string> = {
|
|
161
|
+
xs: 'text-xs',
|
|
162
|
+
sm: 'text-sm',
|
|
163
|
+
md: 'text-base',
|
|
164
|
+
lg: 'text-lg',
|
|
165
|
+
xl: 'text-xl',
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export const Text: React.FC<TextPropsWithClass> = ({
|
|
169
|
+
size,
|
|
170
|
+
className = '',
|
|
171
|
+
children,
|
|
172
|
+
...props
|
|
173
|
+
}) => {
|
|
174
|
+
const sizeClass = size ? textSizeMap[size] : '';
|
|
175
|
+
return (
|
|
176
|
+
<RNText className={`text-foreground font-sans ${sizeClass} ${className}`} {...props}>
|
|
177
|
+
{children}
|
|
178
|
+
</RNText>
|
|
179
|
+
);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export interface HeadingProps extends TextProps {
|
|
183
|
+
className?: string;
|
|
184
|
+
size?: 'sm' | 'md' | 'lg' | 'xl' | '2xl';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const headingSizeMap: Record<string, string> = {
|
|
188
|
+
sm: 'text-lg',
|
|
189
|
+
md: 'text-xl',
|
|
190
|
+
lg: 'text-2xl',
|
|
191
|
+
xl: 'text-3xl',
|
|
192
|
+
'2xl': 'text-4xl',
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export const Heading: React.FC<HeadingProps> = ({
|
|
196
|
+
size = 'lg',
|
|
197
|
+
className = '',
|
|
198
|
+
children,
|
|
199
|
+
...props
|
|
200
|
+
}) => {
|
|
201
|
+
return (
|
|
202
|
+
<RNText
|
|
203
|
+
className={`font-title font-bold text-foreground ${headingSizeMap[size]} ${className}`}
|
|
204
|
+
{...props}
|
|
205
|
+
>
|
|
206
|
+
{children}
|
|
207
|
+
</RNText>
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export interface ButtonProps extends Omit<PressableProps, 'children'> {
|
|
212
|
+
className?: string;
|
|
213
|
+
variant?: 'solid' | 'outline' | 'link';
|
|
214
|
+
action?: 'primary' | 'secondary' | 'positive' | 'negative' | 'default';
|
|
215
|
+
size?: 'sm' | 'md' | 'lg';
|
|
216
|
+
isDisabled?: boolean;
|
|
217
|
+
isLoading?: boolean;
|
|
218
|
+
children?: React.ReactNode;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const buttonVariantStyles: Record<string, Record<string, string>> = {
|
|
222
|
+
solid: {
|
|
223
|
+
primary: 'bg-primary border-primary',
|
|
224
|
+
secondary: 'bg-secondary border-border',
|
|
225
|
+
positive: 'bg-success border-success',
|
|
226
|
+
negative: 'bg-destructive border-destructive',
|
|
227
|
+
default: 'bg-secondary border-border',
|
|
228
|
+
},
|
|
229
|
+
outline: {
|
|
230
|
+
primary: 'bg-transparent border-primary',
|
|
231
|
+
secondary: 'bg-transparent border-border',
|
|
232
|
+
positive: 'bg-transparent border-success',
|
|
233
|
+
negative: 'bg-transparent border-destructive',
|
|
234
|
+
default: 'bg-transparent border-border',
|
|
235
|
+
},
|
|
236
|
+
link: {
|
|
237
|
+
primary: 'bg-transparent border-transparent',
|
|
238
|
+
secondary: 'bg-transparent border-transparent',
|
|
239
|
+
positive: 'bg-transparent border-transparent',
|
|
240
|
+
negative: 'bg-transparent border-transparent',
|
|
241
|
+
default: 'bg-transparent border-transparent',
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const buttonSizeStyles: Record<string, string> = {
|
|
246
|
+
sm: 'px-3 py-1.5 rounded-md min-h-[36px]',
|
|
247
|
+
md: 'px-4 py-2.5 rounded-lg min-h-[44px]',
|
|
248
|
+
lg: 'px-6 py-3.5 rounded-xl min-h-[52px]',
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
export const Button: React.FC<ButtonProps> = ({
|
|
252
|
+
variant = 'solid',
|
|
253
|
+
action = 'primary',
|
|
254
|
+
size = 'md',
|
|
255
|
+
isDisabled = false,
|
|
256
|
+
isLoading = false,
|
|
257
|
+
disabled,
|
|
258
|
+
className = '',
|
|
259
|
+
children,
|
|
260
|
+
...props
|
|
261
|
+
}) => {
|
|
262
|
+
const isActuallyDisabled = isDisabled || isLoading || disabled;
|
|
263
|
+
const variantClass = buttonVariantStyles[variant]?.[action] || buttonVariantStyles.solid.primary;
|
|
264
|
+
const sizeClass = buttonSizeStyles[size] || buttonSizeStyles.md;
|
|
265
|
+
const disabledClass = isActuallyDisabled ? 'opacity-50' : 'active:opacity-85';
|
|
266
|
+
|
|
267
|
+
return (
|
|
268
|
+
<RNPressable
|
|
269
|
+
disabled={isActuallyDisabled}
|
|
270
|
+
className={`flex-row items-center justify-center border ${variantClass} ${sizeClass} ${disabledClass} ${className}`}
|
|
271
|
+
{...props}
|
|
272
|
+
>
|
|
273
|
+
{isLoading && <ActivityIndicator color="#FFFFFF" className="mr-2" size="small" />}
|
|
274
|
+
{children}
|
|
275
|
+
</RNPressable>
|
|
276
|
+
);
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
export interface ButtonTextProps extends TextPropsWithClass {
|
|
280
|
+
action?: 'primary' | 'secondary' | 'positive' | 'negative' | 'default';
|
|
281
|
+
variant?: 'solid' | 'outline' | 'link';
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export const ButtonText: React.FC<ButtonTextProps> = ({
|
|
285
|
+
className = '',
|
|
286
|
+
children,
|
|
287
|
+
...props
|
|
288
|
+
}) => (
|
|
289
|
+
<RNText
|
|
290
|
+
className={`text-center font-semibold font-sans text-sm text-primary-foreground ${className}`}
|
|
291
|
+
{...props}
|
|
292
|
+
>
|
|
293
|
+
{children}
|
|
294
|
+
</RNText>
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
export interface ButtonSpinnerProps extends ActivityIndicatorProps {
|
|
298
|
+
className?: string;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export const ButtonSpinner: React.FC<ButtonSpinnerProps> = ({
|
|
302
|
+
color = '#FFFFFF',
|
|
303
|
+
size = 'small',
|
|
304
|
+
className = 'mr-2',
|
|
305
|
+
...props
|
|
306
|
+
}) => <ActivityIndicator color={color} size={size} className={className} {...props} />;
|
|
307
|
+
|
|
308
|
+
export interface BadgeProps extends ViewProps {
|
|
309
|
+
className?: string;
|
|
310
|
+
variant?: 'solid' | 'outline';
|
|
311
|
+
action?: 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info' | 'muted';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const badgeActionStyles: Record<string, Record<string, string>> = {
|
|
315
|
+
solid: {
|
|
316
|
+
primary: 'bg-primary border-primary',
|
|
317
|
+
secondary: 'bg-secondary border-border',
|
|
318
|
+
success: 'bg-emerald-600 border-emerald-600',
|
|
319
|
+
warning: 'bg-amber-600 border-amber-600',
|
|
320
|
+
error: 'bg-destructive border-destructive',
|
|
321
|
+
info: 'bg-blue-600 border-blue-600',
|
|
322
|
+
muted: 'bg-muted border-border',
|
|
323
|
+
},
|
|
324
|
+
outline: {
|
|
325
|
+
primary: 'bg-transparent border-primary',
|
|
326
|
+
secondary: 'bg-transparent border-border',
|
|
327
|
+
success: 'bg-transparent border-emerald-600',
|
|
328
|
+
warning: 'bg-transparent border-amber-600',
|
|
329
|
+
error: 'bg-transparent border-destructive',
|
|
330
|
+
info: 'bg-transparent border-blue-600',
|
|
331
|
+
muted: 'bg-transparent border-border',
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
export const Badge: React.FC<BadgeProps> = ({
|
|
336
|
+
variant = 'solid',
|
|
337
|
+
action = 'primary',
|
|
338
|
+
className = '',
|
|
339
|
+
children,
|
|
340
|
+
...props
|
|
341
|
+
}) => {
|
|
342
|
+
const badgeClass = badgeActionStyles[variant]?.[action] || badgeActionStyles.solid.primary;
|
|
343
|
+
return (
|
|
344
|
+
<View
|
|
345
|
+
className={`px-2.5 py-0.5 rounded-full border items-center justify-center flex-row ${badgeClass} ${className}`}
|
|
346
|
+
{...props}
|
|
347
|
+
>
|
|
348
|
+
{children}
|
|
349
|
+
</View>
|
|
350
|
+
);
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
export const BadgeText: React.FC<TextPropsWithClass> = ({
|
|
354
|
+
className = '',
|
|
355
|
+
children,
|
|
356
|
+
...props
|
|
357
|
+
}) => (
|
|
358
|
+
<RNText
|
|
359
|
+
className={`text-xs font-semibold font-sans text-primary-foreground ${className}`}
|
|
360
|
+
{...props}
|
|
361
|
+
>
|
|
362
|
+
{children}
|
|
363
|
+
</RNText>
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
export const Card: React.FC<BoxProps> = ({ className = '', children, ...props }) => (
|
|
367
|
+
<View
|
|
368
|
+
className={`rounded-xl p-4 bg-card border border-border shadow-sm ${className}`}
|
|
369
|
+
{...props}
|
|
370
|
+
>
|
|
371
|
+
{children}
|
|
372
|
+
</View>
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
export interface InputProps extends ViewProps {
|
|
376
|
+
className?: string;
|
|
377
|
+
isInvalid?: boolean;
|
|
378
|
+
isDisabled?: boolean;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export const Input: React.FC<InputProps> = ({
|
|
382
|
+
isInvalid = false,
|
|
383
|
+
isDisabled = false,
|
|
384
|
+
className = '',
|
|
385
|
+
children,
|
|
386
|
+
...props
|
|
387
|
+
}) => {
|
|
388
|
+
const stateClass = isInvalid
|
|
389
|
+
? 'border-destructive'
|
|
390
|
+
: isDisabled
|
|
391
|
+
? 'border-border opacity-50'
|
|
392
|
+
: 'border-border';
|
|
393
|
+
return (
|
|
394
|
+
<View
|
|
395
|
+
className={`flex-row items-center rounded-lg border bg-background px-3 py-2 min-h-[44px] ${stateClass} ${className}`}
|
|
396
|
+
{...props}
|
|
397
|
+
>
|
|
398
|
+
{children}
|
|
399
|
+
</View>
|
|
400
|
+
);
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
export interface InputFieldProps extends TextInputProps {
|
|
404
|
+
className?: string;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export const InputField: React.FC<InputFieldProps> = ({
|
|
408
|
+
className = '',
|
|
409
|
+
placeholderTextColor = '#6D6D6D',
|
|
410
|
+
...props
|
|
411
|
+
}) => (
|
|
412
|
+
<TextInput
|
|
413
|
+
className={`flex-1 text-foreground placeholder:text-muted-foreground font-sans text-base py-1 ${className}`}
|
|
414
|
+
placeholderTextColor={placeholderTextColor}
|
|
415
|
+
{...props}
|
|
416
|
+
/>
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
export const Textarea: React.FC<InputProps> = ({
|
|
420
|
+
isInvalid = false,
|
|
421
|
+
isDisabled = false,
|
|
422
|
+
className = '',
|
|
423
|
+
children,
|
|
424
|
+
...props
|
|
425
|
+
}) => {
|
|
426
|
+
const stateClass = isInvalid
|
|
427
|
+
? 'border-destructive'
|
|
428
|
+
: isDisabled
|
|
429
|
+
? 'border-border opacity-50'
|
|
430
|
+
: 'border-border';
|
|
431
|
+
return (
|
|
432
|
+
<View
|
|
433
|
+
className={`rounded-lg border bg-background p-3 min-h-[100px] ${stateClass} ${className}`}
|
|
434
|
+
{...props}
|
|
435
|
+
>
|
|
436
|
+
{children}
|
|
437
|
+
</View>
|
|
438
|
+
);
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
export const TextareaInput: React.FC<InputFieldProps> = ({
|
|
442
|
+
className = '',
|
|
443
|
+
placeholderTextColor = '#6D6D6D',
|
|
444
|
+
...props
|
|
445
|
+
}) => (
|
|
446
|
+
<TextInput
|
|
447
|
+
multiline
|
|
448
|
+
textAlignVertical="top"
|
|
449
|
+
className={`flex-1 text-foreground placeholder:text-muted-foreground font-sans text-base ${className}`}
|
|
450
|
+
placeholderTextColor={placeholderTextColor}
|
|
451
|
+
{...props}
|
|
452
|
+
/>
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
interface TabsContextValue {
|
|
456
|
+
activeTab: string;
|
|
457
|
+
setActiveTab: (tab: string) => void;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const TabsContext = createContext<TabsContextValue>({
|
|
461
|
+
activeTab: '',
|
|
462
|
+
setActiveTab: () => {},
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
export interface TabsProps extends ViewProps {
|
|
466
|
+
defaultValue?: string;
|
|
467
|
+
value?: string;
|
|
468
|
+
onValueChange?: (value: string) => void;
|
|
469
|
+
className?: string;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
export const Tabs: React.FC<TabsProps> = ({
|
|
473
|
+
defaultValue = '',
|
|
474
|
+
value: controlledValue,
|
|
475
|
+
onValueChange,
|
|
476
|
+
className = '',
|
|
477
|
+
children,
|
|
478
|
+
...props
|
|
479
|
+
}) => {
|
|
480
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
|
|
481
|
+
const activeTab = controlledValue ?? uncontrolledValue;
|
|
482
|
+
|
|
483
|
+
const setActiveTab = (newTab: string) => {
|
|
484
|
+
setUncontrolledValue(newTab);
|
|
485
|
+
onValueChange?.(newTab);
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
return (
|
|
489
|
+
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
|
|
490
|
+
<View className={`w-full ${className}`} {...props}>
|
|
491
|
+
{children}
|
|
492
|
+
</View>
|
|
493
|
+
</TabsContext.Provider>
|
|
494
|
+
);
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
export const TabsList: React.FC<BoxProps> = ({ className = '', children, ...props }) => (
|
|
498
|
+
<View
|
|
499
|
+
className={`flex-row rounded-lg bg-secondary p-1 border border-border ${className}`}
|
|
500
|
+
{...props}
|
|
501
|
+
>
|
|
502
|
+
{children}
|
|
503
|
+
</View>
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
export interface TabsTriggerProps extends PressableProps {
|
|
507
|
+
value: string;
|
|
508
|
+
className?: string;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
export const TabsTrigger: React.FC<TabsTriggerProps> = ({
|
|
512
|
+
value,
|
|
513
|
+
className = '',
|
|
514
|
+
children,
|
|
515
|
+
...props
|
|
516
|
+
}) => {
|
|
517
|
+
const { activeTab, setActiveTab } = useContext(TabsContext);
|
|
518
|
+
const isActive = activeTab === value;
|
|
519
|
+
|
|
520
|
+
return (
|
|
521
|
+
<RNPressable
|
|
522
|
+
onPress={() => setActiveTab(value)}
|
|
523
|
+
className={`flex-1 py-2 px-3 rounded-md items-center justify-center ${
|
|
524
|
+
isActive ? 'bg-card shadow-sm border border-border' : 'opacity-70'
|
|
525
|
+
} ${className}`}
|
|
526
|
+
{...props}
|
|
527
|
+
>
|
|
528
|
+
{children}
|
|
529
|
+
</RNPressable>
|
|
530
|
+
);
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
export const TabsTriggerText: React.FC<TextPropsWithClass> = ({
|
|
534
|
+
className = '',
|
|
535
|
+
children,
|
|
536
|
+
...props
|
|
537
|
+
}) => (
|
|
538
|
+
<RNText
|
|
539
|
+
className={`text-sm font-semibold text-foreground font-sans ${className}`}
|
|
540
|
+
{...props}
|
|
541
|
+
>
|
|
542
|
+
{children}
|
|
543
|
+
</RNText>
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
export interface TabsContentProps extends ViewProps {
|
|
547
|
+
value: string;
|
|
548
|
+
className?: string;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
export const TabsContent: React.FC<TabsContentProps> = ({
|
|
552
|
+
value,
|
|
553
|
+
className = '',
|
|
554
|
+
children,
|
|
555
|
+
...props
|
|
556
|
+
}) => {
|
|
557
|
+
const { activeTab } = useContext(TabsContext);
|
|
558
|
+
if (activeTab !== value) return null;
|
|
559
|
+
|
|
560
|
+
return (
|
|
561
|
+
<View className={`mt-3 ${className}`} {...props}>
|
|
562
|
+
{children}
|
|
563
|
+
</View>
|
|
564
|
+
);
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
export interface ActionsheetProps {
|
|
568
|
+
isOpen: boolean;
|
|
569
|
+
onClose: () => void;
|
|
570
|
+
children: React.ReactNode;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
export const Actionsheet: React.FC<ActionsheetProps> = ({ isOpen, onClose, children }) => (
|
|
574
|
+
<RNModal
|
|
575
|
+
visible={isOpen}
|
|
576
|
+
transparent
|
|
577
|
+
animationType="fade"
|
|
578
|
+
onRequestClose={onClose}
|
|
579
|
+
>
|
|
580
|
+
<View className="flex-1 justify-end">
|
|
581
|
+
{children}
|
|
582
|
+
</View>
|
|
583
|
+
</RNModal>
|
|
584
|
+
);
|
|
585
|
+
|
|
586
|
+
export const ActionsheetBackdrop: React.FC<PressableProps & { className?: string }> = ({
|
|
587
|
+
onPress,
|
|
588
|
+
className = '',
|
|
589
|
+
}) => (
|
|
590
|
+
<RNPressable
|
|
591
|
+
onPress={onPress}
|
|
592
|
+
className={`absolute inset-0 bg-black/60 ${className}`}
|
|
593
|
+
/>
|
|
594
|
+
);
|
|
595
|
+
|
|
596
|
+
export const ActionsheetContent: React.FC<BoxProps> = ({
|
|
597
|
+
className = '',
|
|
598
|
+
children,
|
|
599
|
+
...props
|
|
600
|
+
}) => (
|
|
601
|
+
<View
|
|
602
|
+
className={`bg-card rounded-t-3xl p-5 border-t border-border shadow-lg max-h-[85%] ${className}`}
|
|
603
|
+
{...props}
|
|
604
|
+
>
|
|
605
|
+
{children}
|
|
606
|
+
</View>
|
|
607
|
+
);
|
|
608
|
+
|
|
609
|
+
export const ActionsheetDragIndicator: React.FC<BoxProps> = ({
|
|
610
|
+
className = '',
|
|
611
|
+
...props
|
|
612
|
+
}) => (
|
|
613
|
+
<View className="items-center pb-3">
|
|
614
|
+
<View className={`w-12 h-1.5 rounded-full bg-muted ${className}`} {...props} />
|
|
615
|
+
</View>
|
|
616
|
+
);
|
|
617
|
+
|
|
618
|
+
export interface ActionsheetItemProps extends PressableProps {
|
|
619
|
+
className?: string;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
export const ActionsheetItem: React.FC<ActionsheetItemProps> = ({
|
|
623
|
+
className = '',
|
|
624
|
+
children,
|
|
625
|
+
...props
|
|
626
|
+
}) => (
|
|
627
|
+
<RNPressable
|
|
628
|
+
className={`py-3.5 px-4 rounded-xl active:bg-secondary flex-row items-center ${className}`}
|
|
629
|
+
{...props}
|
|
630
|
+
>
|
|
631
|
+
{children}
|
|
632
|
+
</RNPressable>
|
|
633
|
+
);
|
|
634
|
+
|
|
635
|
+
export const ActionsheetItemText: React.FC<TextPropsWithClass> = ({
|
|
636
|
+
className = '',
|
|
637
|
+
children,
|
|
638
|
+
...props
|
|
639
|
+
}) => (
|
|
640
|
+
<RNText className={`text-base font-sans text-foreground ${className}`} {...props}>
|
|
641
|
+
{children}
|
|
642
|
+
</RNText>
|
|
643
|
+
);
|
|
644
|
+
|
|
645
|
+
export interface CustomModalProps extends ModalProps {
|
|
646
|
+
isOpen?: boolean;
|
|
647
|
+
onClose?: () => void;
|
|
648
|
+
className?: string;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
export const Modal: React.FC<CustomModalProps> = ({
|
|
652
|
+
isOpen = false,
|
|
653
|
+
visible,
|
|
654
|
+
onClose,
|
|
655
|
+
children,
|
|
656
|
+
...props
|
|
657
|
+
}) => (
|
|
658
|
+
<RNModal
|
|
659
|
+
visible={isOpen || visible}
|
|
660
|
+
transparent
|
|
661
|
+
animationType="fade"
|
|
662
|
+
onRequestClose={onClose}
|
|
663
|
+
{...props}
|
|
664
|
+
>
|
|
665
|
+
<View className="flex-1 items-center justify-center bg-black/60 p-4">
|
|
666
|
+
{children}
|
|
667
|
+
</View>
|
|
668
|
+
</RNModal>
|
|
669
|
+
);
|
|
670
|
+
|
|
671
|
+
export interface CustomSwitchProps extends SwitchProps {
|
|
672
|
+
className?: string;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
export const Switch: React.FC<CustomSwitchProps> = ({
|
|
676
|
+
trackColor = { false: '#DDD5CA', true: '#C0643A' },
|
|
677
|
+
thumbColor = '#FFFFFF',
|
|
678
|
+
className = '',
|
|
679
|
+
...props
|
|
680
|
+
}) => (
|
|
681
|
+
<RNSwitch
|
|
682
|
+
trackColor={trackColor}
|
|
683
|
+
thumbColor={thumbColor}
|
|
684
|
+
className={className}
|
|
685
|
+
{...props}
|
|
686
|
+
/>
|
|
687
|
+
);
|
|
688
|
+
|
|
689
|
+
export const Spinner: React.FC<ButtonSpinnerProps> = ({
|
|
690
|
+
color = '#C0643A',
|
|
691
|
+
size = 'small',
|
|
692
|
+
className = '',
|
|
693
|
+
...props
|
|
694
|
+
}) => <ActivityIndicator color={color} size={size} className={className} {...props} />;
|
|
695
|
+
|
|
696
|
+
export interface AvatarProps extends ViewProps {
|
|
697
|
+
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
698
|
+
className?: string;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
const avatarSizeMap: Record<string, string> = {
|
|
702
|
+
sm: 'w-8 h-8 rounded-full',
|
|
703
|
+
md: 'w-10 h-10 rounded-full',
|
|
704
|
+
lg: 'w-14 h-14 rounded-full',
|
|
705
|
+
xl: 'w-20 h-20 rounded-full',
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
export const Avatar: React.FC<AvatarProps> = ({
|
|
709
|
+
size = 'md',
|
|
710
|
+
className = '',
|
|
711
|
+
children,
|
|
712
|
+
...props
|
|
713
|
+
}) => (
|
|
714
|
+
<View
|
|
715
|
+
className={`items-center justify-center bg-secondary border border-border overflow-hidden ${avatarSizeMap[size]} ${className}`}
|
|
716
|
+
{...props}
|
|
717
|
+
>
|
|
718
|
+
{children}
|
|
719
|
+
</View>
|
|
720
|
+
);
|
|
721
|
+
|
|
722
|
+
export const AvatarFallbackText: React.FC<TextPropsWithClass> = ({
|
|
723
|
+
className = '',
|
|
724
|
+
children,
|
|
725
|
+
...props
|
|
726
|
+
}) => (
|
|
727
|
+
<RNText
|
|
728
|
+
className={`font-bold font-sans text-secondary-foreground text-sm ${className}`}
|
|
729
|
+
{...props}
|
|
730
|
+
>
|
|
731
|
+
{children}
|
|
732
|
+
</RNText>
|
|
733
|
+
);
|
|
734
|
+
|
|
735
|
+
export const Divider: React.FC<BoxProps> = ({ className = '', ...props }) => (
|
|
736
|
+
<View className={`h-px bg-border my-2 w-full ${className}`} {...props} />
|
|
737
|
+
);
|
|
738
|
+
|
|
739
|
+
export const Pressable: React.FC<PressableProps & { className?: string }> = ({
|
|
740
|
+
className = '',
|
|
741
|
+
children,
|
|
742
|
+
...props
|
|
743
|
+
}) => (
|
|
744
|
+
<RNPressable className={className} {...props}>
|
|
745
|
+
{children}
|
|
746
|
+
</RNPressable>
|
|
747
|
+
);
|
|
748
|
+
|
|
749
|
+
export interface ToastProps extends ViewProps {
|
|
750
|
+
action?: 'success' | 'warning' | 'error' | 'info';
|
|
751
|
+
title?: string;
|
|
752
|
+
description?: string;
|
|
753
|
+
className?: string;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
export const Toast: React.FC<ToastProps> = ({
|
|
757
|
+
action = 'info',
|
|
758
|
+
title,
|
|
759
|
+
description,
|
|
760
|
+
className = '',
|
|
761
|
+
children,
|
|
762
|
+
...props
|
|
763
|
+
}) => {
|
|
764
|
+
const borderColors: Record<string, string> = {
|
|
765
|
+
success: 'border-emerald-600',
|
|
766
|
+
warning: 'border-amber-600',
|
|
767
|
+
error: 'border-destructive',
|
|
768
|
+
info: 'border-blue-600',
|
|
769
|
+
};
|
|
770
|
+
return (
|
|
771
|
+
<View
|
|
772
|
+
className={`p-4 rounded-xl bg-card border-l-4 shadow-md ${borderColors[action]} ${className}`}
|
|
773
|
+
{...props}
|
|
774
|
+
>
|
|
775
|
+
{title && <RNText className="font-bold text-foreground">{title}</RNText>}
|
|
776
|
+
{description && <RNText className="text-sm text-muted-foreground mt-1">{description}</RNText>}
|
|
777
|
+
{children}
|
|
778
|
+
</View>
|
|
779
|
+
);
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
export interface IconProps {
|
|
783
|
+
as?: React.ComponentType<{ size?: number; color?: string; className?: string }>;
|
|
784
|
+
size?: number | string;
|
|
785
|
+
color?: string;
|
|
786
|
+
className?: string;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
export const Icon: React.FC<IconProps> = ({
|
|
790
|
+
as: Component,
|
|
791
|
+
size = 20,
|
|
792
|
+
color = 'currentColor',
|
|
793
|
+
className = '',
|
|
794
|
+
}) => {
|
|
795
|
+
if (!Component) return null;
|
|
796
|
+
const numSize = typeof size === 'number' ? size : parseInt(size, 10) || 20;
|
|
797
|
+
return <Component size={numSize} color={color} className={className} />;
|
|
798
|
+
};
|
|
799
|
+
|