@nativetail/ui 0.1.2 → 0.1.3
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/package.json +4 -3
- package/src/components/actions-sheet/index.tsx +1 -1
- package/src/components/alert-dialog/index.tsx +27 -6
- package/src/components/button/index.tsx +26 -21
- package/src/components/card/card.tsx +72 -0
- package/src/components/card/index.ts +3 -0
- package/src/components/card/info-card.tsx +113 -0
- package/src/components/card/stat-card.tsx +46 -0
- package/src/components/check/index.tsx +43 -0
- package/src/components/collapsible/index.tsx +23 -0
- package/src/components/counter/index.tsx +4 -12
- package/src/components/index.ts +4 -0
- package/src/components/indicator/index.tsx +15 -0
- package/src/components/input/phone-input.tsx +2 -1
- package/src/components/input/show-password.tsx +4 -13
- package/src/components/loader-dialog/index.tsx +35 -0
- package/src/components/select/multi-select.tsx +4 -12
- package/src/components/select/select.tsx +12 -15
- package/src/components/switch/index.tsx +3 -2
- package/src/components/toast/index.tsx +13 -29
- package/tailwind.config.js +6 -2
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@nativetail/ui",
|
3
|
-
"version": "0.1.
|
3
|
+
"version": "0.1.3",
|
4
4
|
"description": "",
|
5
5
|
"main": "src/index.ts",
|
6
6
|
"scripts": {},
|
@@ -27,24 +27,25 @@
|
|
27
27
|
},
|
28
28
|
"dependencies": {
|
29
29
|
"@hookform/resolvers": "^3.6.0",
|
30
|
-
"@nativetail/core": "^0.0.
|
30
|
+
"@nativetail/core": "^0.0.5",
|
31
31
|
"@shopify/flash-list": "^1.7.0",
|
32
32
|
"countries-list": "^3.1.0",
|
33
33
|
"expo-blur": "^13.0.2",
|
34
34
|
"expo-linear-gradient": "~13.0.2",
|
35
|
+
"lucide-react-native": "^0.427.0",
|
35
36
|
"moti": "^0.29.0",
|
36
37
|
"react": "18.2.0",
|
37
38
|
"react-hook-form": "^7.51.0",
|
38
39
|
"react-native": "0.74.3",
|
39
40
|
"react-native-actions-sheet": "^0.9.6",
|
40
41
|
"react-native-gesture-handler": "^2.17.1",
|
42
|
+
"react-native-mask-text": "^0.14.2",
|
41
43
|
"react-native-reanimated": "~3.10.1",
|
42
44
|
"react-native-safe-area-context": "4.10.1",
|
43
45
|
"tailwind-merge": "^2.3.0",
|
44
46
|
"zustand": "^4.5.2"
|
45
47
|
},
|
46
48
|
"devDependencies": {
|
47
|
-
"metro-react-native-babel-preset": "^0.77.0",
|
48
49
|
"tailwindcss": "^3.4.4"
|
49
50
|
},
|
50
51
|
"private": false
|
@@ -86,7 +86,7 @@ const ActionSheetItem = ({
|
|
86
86
|
<Button
|
87
87
|
variant="link"
|
88
88
|
className={cn(
|
89
|
-
"w-full items-center opacity-100 active:opacity-50 text-
|
89
|
+
"w-full items-center opacity-100 active:opacity-50 text-sm h-14 text-blue-500 rounded-none border-b ",
|
90
90
|
last ? "border-transparent" : "border-muted/15",
|
91
91
|
className
|
92
92
|
)}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Text, View } from "@nativetail/core";
|
1
|
+
import { cn, Text, View } from "@nativetail/core";
|
2
2
|
import { forwardRef } from "react";
|
3
3
|
import { Button } from "../button";
|
4
4
|
import { Dialog, DialogMethods } from "../dialog";
|
@@ -10,11 +10,26 @@ export type AlertDialogProps = {
|
|
10
10
|
title?: string;
|
11
11
|
description?: string;
|
12
12
|
useBlur?: boolean;
|
13
|
+
confirmClassName?: string;
|
14
|
+
cancelClassName?: string;
|
15
|
+
confirmText?: string;
|
16
|
+
cancelText?: string;
|
13
17
|
};
|
14
18
|
export type AlertDialogRef = DialogMethods;
|
15
19
|
export const AlertDialog = forwardRef<DialogMethods, AlertDialogProps>(
|
16
20
|
function AlertDialog(
|
17
|
-
{
|
21
|
+
{
|
22
|
+
containerClassName,
|
23
|
+
onConfirm,
|
24
|
+
confirmText = "Confirm",
|
25
|
+
cancelText = "Cancel",
|
26
|
+
cancelClassName,
|
27
|
+
confirmClassName,
|
28
|
+
title,
|
29
|
+
description,
|
30
|
+
useBlur,
|
31
|
+
onCancel,
|
32
|
+
},
|
18
33
|
ref
|
19
34
|
) {
|
20
35
|
return (
|
@@ -39,17 +54,23 @@ export const AlertDialog = forwardRef<DialogMethods, AlertDialogProps>(
|
|
39
54
|
<View className="flex-row items-center border-t border-muted/15">
|
40
55
|
<Button
|
41
56
|
variant="link"
|
42
|
-
className=
|
57
|
+
className={cn(
|
58
|
+
"flex-1 active:opacity-75 text-foreground rounded-none",
|
59
|
+
cancelClassName
|
60
|
+
)}
|
43
61
|
onPress={onCancel}
|
44
62
|
>
|
45
|
-
|
63
|
+
{cancelText}
|
46
64
|
</Button>
|
47
65
|
<Button
|
48
66
|
variant="link"
|
49
|
-
className=
|
67
|
+
className={cn(
|
68
|
+
"flex-1 border active:opacity-75 border-transparent rounded-none border-l-muted/15",
|
69
|
+
cancelClassName
|
70
|
+
)}
|
50
71
|
onPress={onConfirm}
|
51
72
|
>
|
52
|
-
|
73
|
+
{confirmText}
|
53
74
|
</Button>
|
54
75
|
</View>
|
55
76
|
</Dialog>
|
@@ -8,6 +8,7 @@ import {
|
|
8
8
|
mergeClasses,
|
9
9
|
Pressable,
|
10
10
|
separateClasses,
|
11
|
+
useColor,
|
11
12
|
useTw,
|
12
13
|
VariantProps,
|
13
14
|
} from "@nativetail/core";
|
@@ -15,11 +16,11 @@ import { MotiPressableProps } from "moti/interactions";
|
|
15
16
|
import { useComponentTheme } from "../../utils/component-theme";
|
16
17
|
|
17
18
|
const buttonVariants = cva(
|
18
|
-
"flex-row gap-2 items-center justify-center rounded text-sm font-medium hover:opacity-90 active:opacity-
|
19
|
+
"flex-row gap-2 items-center justify-center rounded text-sm font-medium hover:opacity-90 active:opacity-50 duration-30 opacity-100 select-none",
|
19
20
|
{
|
20
21
|
variants: {
|
21
22
|
variant: {
|
22
|
-
default: "bg-primary
|
23
|
+
default: "bg-primary text-primary-foreground ",
|
23
24
|
destructive: "bg-red-500 text-foreground ",
|
24
25
|
outline: "border border-muted/15 text-foreground bg-black/0 ",
|
25
26
|
secondary: "bg-secondary text-foreground ",
|
@@ -84,9 +85,10 @@ export type ButtonProps = MotiPressableProps &
|
|
84
85
|
const Button = (passedProps: ButtonProps) => {
|
85
86
|
const componentTheme = useComponentTheme();
|
86
87
|
const buttonProps = componentTheme?.Button || {};
|
87
|
-
|
88
|
+
let {
|
88
89
|
text,
|
89
90
|
children,
|
91
|
+
|
90
92
|
isLoading,
|
91
93
|
disabled,
|
92
94
|
variant,
|
@@ -101,6 +103,7 @@ const Button = (passedProps: ButtonProps) => {
|
|
101
103
|
...buttonProps,
|
102
104
|
...passedProps,
|
103
105
|
};
|
106
|
+
children = children || text;
|
104
107
|
const tw = useTw();
|
105
108
|
const className = cn(buttonProps.className, passedProps.className);
|
106
109
|
|
@@ -128,24 +131,26 @@ const Button = (passedProps: ButtonProps) => {
|
|
128
131
|
const { textClasses } = separateClasses(variantClass);
|
129
132
|
|
130
133
|
return (
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
134
|
+
<>
|
135
|
+
<Pressable
|
136
|
+
disabled={disabled || loading}
|
137
|
+
className={variantClass}
|
138
|
+
{...props}
|
139
|
+
>
|
140
|
+
{leftElement}
|
141
|
+
{loading && (
|
142
|
+
<ActivityIndicator
|
143
|
+
className={mergeClasses(
|
144
|
+
"mr-2 h-5 w-5 text-primary-foreground ",
|
145
|
+
textClasses,
|
146
|
+
loadingIndicatorClassName
|
147
|
+
)}
|
148
|
+
/>
|
149
|
+
)}
|
150
|
+
{children}
|
151
|
+
{rightElement}
|
152
|
+
</Pressable>
|
153
|
+
</>
|
149
154
|
);
|
150
155
|
};
|
151
156
|
export { Button };
|
@@ -0,0 +1,72 @@
|
|
1
|
+
import { cn, Text, useTw, View } from "@nativetail/core";
|
2
|
+
import { Pressable } from "react-native";
|
3
|
+
import { Button, ButtonProps } from "../button";
|
4
|
+
|
5
|
+
type CardProps = {
|
6
|
+
renderHeader?: () => React.ReactNode;
|
7
|
+
title?: string;
|
8
|
+
subtitle?: string;
|
9
|
+
description?: string;
|
10
|
+
titleClassname?: string;
|
11
|
+
subtitleClassName?: string;
|
12
|
+
descriptionClassName?: string;
|
13
|
+
containerClassName?: string;
|
14
|
+
contentClassName?: string;
|
15
|
+
buttonProps?: ButtonProps;
|
16
|
+
onPress?: () => void;
|
17
|
+
renderFooter?: () => React.ReactNode;
|
18
|
+
renderContent?: () => React.ReactNode;
|
19
|
+
};
|
20
|
+
export function Card({
|
21
|
+
description,
|
22
|
+
descriptionClassName,
|
23
|
+
renderHeader,
|
24
|
+
subtitle,
|
25
|
+
subtitleClassName,
|
26
|
+
title,
|
27
|
+
titleClassname,
|
28
|
+
contentClassName,
|
29
|
+
buttonProps,
|
30
|
+
onPress,
|
31
|
+
renderFooter,
|
32
|
+
containerClassName,
|
33
|
+
...props
|
34
|
+
}: CardProps) {
|
35
|
+
const tw = useTw();
|
36
|
+
const renderContent = () => {
|
37
|
+
if (props.renderContent) {
|
38
|
+
return props.renderContent();
|
39
|
+
}
|
40
|
+
return (
|
41
|
+
<View className={cn("p-1 gap-1", contentClassName)}>
|
42
|
+
<Text className={cn("text-lg font-medium", titleClassname)}>
|
43
|
+
{title}
|
44
|
+
</Text>
|
45
|
+
<Text className={cn("text-sm ", subtitleClassName)}>{subtitle}</Text>
|
46
|
+
<Text
|
47
|
+
className={cn(
|
48
|
+
"text-xs text-muted tracking-widest",
|
49
|
+
descriptionClassName
|
50
|
+
)}
|
51
|
+
>
|
52
|
+
{description}
|
53
|
+
</Text>
|
54
|
+
{buttonProps && <Button {...buttonProps} />}
|
55
|
+
</View>
|
56
|
+
);
|
57
|
+
};
|
58
|
+
return (
|
59
|
+
<Pressable
|
60
|
+
style={tw.style(
|
61
|
+
"bg-card p-3 rounded-xl border border-muted/15",
|
62
|
+
containerClassName
|
63
|
+
)}
|
64
|
+
onPress={onPress}
|
65
|
+
disabled={!onPress}
|
66
|
+
>
|
67
|
+
{renderHeader && renderHeader()}
|
68
|
+
{renderContent()}
|
69
|
+
{renderFooter && renderFooter()}
|
70
|
+
</Pressable>
|
71
|
+
);
|
72
|
+
}
|
@@ -0,0 +1,113 @@
|
|
1
|
+
import { cn, Pressable, Text, useColor, View } from "@nativetail/core";
|
2
|
+
import { EllipsisVertical } from "lucide-react-native";
|
3
|
+
import { useRef } from "react";
|
4
|
+
import {
|
5
|
+
ActionSheet,
|
6
|
+
ActionSheetProps,
|
7
|
+
ActionSheetRef,
|
8
|
+
} from "../actions-sheet";
|
9
|
+
|
10
|
+
type InfoCardProps = {
|
11
|
+
containerClassname?: string;
|
12
|
+
renderIcon?: () => React.ReactNode;
|
13
|
+
renderContent?: () => React.ReactNode;
|
14
|
+
contentClassName?: string;
|
15
|
+
title?: string;
|
16
|
+
subtitle?: string;
|
17
|
+
titleClassname?: string;
|
18
|
+
subtitleClassName?: string;
|
19
|
+
description?: string;
|
20
|
+
descriptionClassName?: string;
|
21
|
+
actions?: ActionSheetProps["options"];
|
22
|
+
renderRight?: () => React.ReactNode;
|
23
|
+
onPress?: () => void;
|
24
|
+
dotsClassname?: string;
|
25
|
+
textContentClassname?: string;
|
26
|
+
};
|
27
|
+
export function InfoCard({
|
28
|
+
containerClassname,
|
29
|
+
renderIcon,
|
30
|
+
subtitle,
|
31
|
+
title,
|
32
|
+
titleClassname,
|
33
|
+
subtitleClassName,
|
34
|
+
description,
|
35
|
+
descriptionClassName,
|
36
|
+
actions,
|
37
|
+
onPress,
|
38
|
+
dotsClassname,
|
39
|
+
contentClassName,
|
40
|
+
textContentClassname,
|
41
|
+
...props
|
42
|
+
}: InfoCardProps) {
|
43
|
+
const actionSheetRef = useRef<ActionSheetRef>(null);
|
44
|
+
const renderRight = () => {
|
45
|
+
if (props.renderRight) {
|
46
|
+
return props.renderRight();
|
47
|
+
}
|
48
|
+
if (actions)
|
49
|
+
return (
|
50
|
+
<View className="flex-row gap-1">
|
51
|
+
<>
|
52
|
+
<Pressable
|
53
|
+
className={cn(
|
54
|
+
"p-1.5 w-7 h-7 items-center justify-center border rounded-full border-muted/15 active:scale-95 scale-100",
|
55
|
+
dotsClassname
|
56
|
+
)}
|
57
|
+
onPress={() => {
|
58
|
+
actionSheetRef.current?.show();
|
59
|
+
}}
|
60
|
+
>
|
61
|
+
<EllipsisVertical color={useColor("foreground")} />
|
62
|
+
</Pressable>
|
63
|
+
<ActionSheet
|
64
|
+
onCancel={() => {
|
65
|
+
actionSheetRef.current?.hide();
|
66
|
+
}}
|
67
|
+
options={actions.map((action) => ({
|
68
|
+
...action,
|
69
|
+
onPress: () => {
|
70
|
+
actionSheetRef.current?.hide();
|
71
|
+
action.onPress();
|
72
|
+
},
|
73
|
+
}))}
|
74
|
+
ref={actionSheetRef}
|
75
|
+
/>
|
76
|
+
</>
|
77
|
+
</View>
|
78
|
+
);
|
79
|
+
};
|
80
|
+
const renderContent = () => {
|
81
|
+
if (props.renderContent) {
|
82
|
+
return props.renderContent();
|
83
|
+
}
|
84
|
+
return (
|
85
|
+
<View className={textContentClassname}>
|
86
|
+
<Text className={cn("text-sm font-medium", titleClassname)}>
|
87
|
+
{title}
|
88
|
+
</Text>
|
89
|
+
<Text className={cn("text-[13px] text-muted", subtitleClassName)}>
|
90
|
+
{subtitle}
|
91
|
+
</Text>
|
92
|
+
<Text className={cn("text-xs text-muted/65", descriptionClassName)}>
|
93
|
+
{description}
|
94
|
+
</Text>
|
95
|
+
</View>
|
96
|
+
);
|
97
|
+
};
|
98
|
+
return (
|
99
|
+
<Pressable
|
100
|
+
className={cn(
|
101
|
+
"flex-row items-center w-full justify-between ",
|
102
|
+
containerClassname
|
103
|
+
)}
|
104
|
+
disabled={!onPress}
|
105
|
+
>
|
106
|
+
<View className={cn("flex-row gap-2 items-center p-2", contentClassName)}>
|
107
|
+
{renderIcon && renderIcon()}
|
108
|
+
{renderContent()}
|
109
|
+
</View>
|
110
|
+
{renderRight()}
|
111
|
+
</Pressable>
|
112
|
+
);
|
113
|
+
}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import { cn, View } from "@nativetail/core";
|
2
|
+
|
3
|
+
type StatCardProps = {
|
4
|
+
renderIcon?: () => React.ReactNode;
|
5
|
+
renderTitle?: () => React.ReactNode;
|
6
|
+
renderValue?: () => React.ReactNode;
|
7
|
+
title?: string;
|
8
|
+
value?: string;
|
9
|
+
Icon?: React.ReactNode;
|
10
|
+
containerClassName?: string;
|
11
|
+
};
|
12
|
+
export function StatCard(props: StatCardProps) {
|
13
|
+
const renderIcon = () => {
|
14
|
+
if (props.Icon) return <View className="mb-2">{props.Icon}</View>;
|
15
|
+
if (!props.renderIcon) return null;
|
16
|
+
return props.renderIcon();
|
17
|
+
};
|
18
|
+
const renderTitle = () => {
|
19
|
+
if (!props.renderTitle)
|
20
|
+
return <View className="text-xs text-muted/80">{props.title}</View>;
|
21
|
+
return props.renderTitle();
|
22
|
+
};
|
23
|
+
const renderValue = () => {
|
24
|
+
if (!props.renderValue)
|
25
|
+
return (
|
26
|
+
<View className="text-xl font-medium text-foreground">
|
27
|
+
{props.value}
|
28
|
+
</View>
|
29
|
+
);
|
30
|
+
return props.renderValue();
|
31
|
+
};
|
32
|
+
return (
|
33
|
+
<View
|
34
|
+
className={cn(
|
35
|
+
"p-3 rounded-xl gap-0.5 bg-card border justify-between border-muted/15",
|
36
|
+
props.containerClassName
|
37
|
+
)}
|
38
|
+
>
|
39
|
+
{renderIcon()}
|
40
|
+
<View className="gap-0.5">
|
41
|
+
{renderValue()}
|
42
|
+
{renderTitle()}
|
43
|
+
</View>
|
44
|
+
</View>
|
45
|
+
);
|
46
|
+
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import { cn, Pressable, useColor } from "@nativetail/core";
|
2
|
+
import { CheckIcon } from "lucide-react-native";
|
3
|
+
|
4
|
+
type CheckProps = {
|
5
|
+
checked: boolean;
|
6
|
+
onChange: (checked: boolean) => void;
|
7
|
+
className?: string;
|
8
|
+
activeClassName?: string;
|
9
|
+
inactiveCheckColor?: string;
|
10
|
+
activeCheckColor?: string;
|
11
|
+
};
|
12
|
+
export function Check({
|
13
|
+
checked,
|
14
|
+
onChange,
|
15
|
+
className,
|
16
|
+
activeClassName,
|
17
|
+
inactiveCheckColor,
|
18
|
+
activeCheckColor,
|
19
|
+
...props
|
20
|
+
}: CheckProps) {
|
21
|
+
return (
|
22
|
+
<Pressable
|
23
|
+
className={cn(
|
24
|
+
"border w-6 h-6 p-1 items-center justify-center rounded-lg border-muted/15 bg-card",
|
25
|
+
className,
|
26
|
+
checked ? "bg-primary " + activeClassName : ""
|
27
|
+
)}
|
28
|
+
aria-checked={checked}
|
29
|
+
accessibilityRole="switch"
|
30
|
+
aria-label="Check"
|
31
|
+
{...props}
|
32
|
+
onPress={() => onChange(!checked)}
|
33
|
+
>
|
34
|
+
<CheckIcon
|
35
|
+
color={useColor(
|
36
|
+
checked
|
37
|
+
? activeCheckColor || "card"
|
38
|
+
: inactiveCheckColor || "primary/35"
|
39
|
+
)}
|
40
|
+
/>
|
41
|
+
</Pressable>
|
42
|
+
);
|
43
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
type CollapsibleProps = {
|
2
|
+
onChange: (checked: boolean) => void;
|
3
|
+
className?: string;
|
4
|
+
};
|
5
|
+
function CollapsibleRoot({ onChange, className, ...props }: CollapsibleProps) {
|
6
|
+
return <></>;
|
7
|
+
}
|
8
|
+
type CollapsibleTriggerProps = {};
|
9
|
+
|
10
|
+
const CollapsibleTrigger = ({ ...props }: CollapsibleTriggerProps) => {
|
11
|
+
return <></>;
|
12
|
+
};
|
13
|
+
type CollapsibleContentProps = {};
|
14
|
+
|
15
|
+
const CollapsibleContent = ({ ...props }: CollapsibleContentProps) => {
|
16
|
+
return <></>;
|
17
|
+
};
|
18
|
+
|
19
|
+
export const Collapsible = {
|
20
|
+
Trigger: CollapsibleTrigger,
|
21
|
+
Content: CollapsibleContent,
|
22
|
+
Root: CollapsibleRoot,
|
23
|
+
};
|
@@ -1,6 +1,6 @@
|
|
1
|
-
import { TextInput, View, cn, useTw } from "@nativetail/core";
|
1
|
+
import { TextInput, View, cn, useColor, useTw } from "@nativetail/core";
|
2
|
+
import { Minus, Plus } from "lucide-react-native";
|
2
3
|
import { useCallback, useRef } from "react";
|
3
|
-
import { Iconify } from "react-native-iconify";
|
4
4
|
import { Button } from "../button";
|
5
5
|
export type CounterProps = {
|
6
6
|
value: number;
|
@@ -48,11 +48,7 @@ export function Counter({
|
|
48
48
|
)}
|
49
49
|
>
|
50
50
|
<CounterButton disabled={!!(min && value <= min)} onPress={decrement}>
|
51
|
-
<
|
52
|
-
icon="ic:round-minus"
|
53
|
-
size={15}
|
54
|
-
color={tw.color("foreground")}
|
55
|
-
/>
|
51
|
+
<Minus size={15} color={useColor("foreground")} />
|
56
52
|
</CounterButton>
|
57
53
|
<View className="flex-1 h-full">
|
58
54
|
<TextInput
|
@@ -62,11 +58,7 @@ export function Counter({
|
|
62
58
|
/>
|
63
59
|
</View>
|
64
60
|
<CounterButton disabled={!!(max && value >= max)} onPress={increment}>
|
65
|
-
<
|
66
|
-
icon="ic:round-plus"
|
67
|
-
size={15}
|
68
|
-
color={tw.color("foreground")}
|
69
|
-
/>
|
61
|
+
<Plus size={15} color={useColor("foreground")} />
|
70
62
|
</CounterButton>
|
71
63
|
</View>
|
72
64
|
);
|
package/src/components/index.ts
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
import { cn, View } from "@nativetail/core";
|
2
|
+
|
3
|
+
type IndicatorProps = {
|
4
|
+
className?: string;
|
5
|
+
size?: number;
|
6
|
+
color?: string;
|
7
|
+
Icon?: React.ReactNode;
|
8
|
+
};
|
9
|
+
export function Indicator({ className, size, color, Icon }: IndicatorProps) {
|
10
|
+
return (
|
11
|
+
<View className={cn("w-4 h-4 rounded-full bg-primary", className)}>
|
12
|
+
{Icon}
|
13
|
+
</View>
|
14
|
+
);
|
15
|
+
}
|
@@ -8,6 +8,7 @@ import { Input, InputProps } from "./input";
|
|
8
8
|
|
9
9
|
import { countries, ICountry } from "countries-list";
|
10
10
|
import { FloatingInput } from "./floating-input";
|
11
|
+
import { ChevronDown } from "lucide-react-native";
|
11
12
|
type CountryType = ICountry & {
|
12
13
|
code: string;
|
13
14
|
};
|
@@ -97,7 +98,7 @@ const SelectCountry = ({
|
|
97
98
|
<Text>
|
98
99
|
<Text className="mr-1">{flag}</Text>+{selectedCountry.phone?.[0]}
|
99
100
|
</Text>
|
100
|
-
<
|
101
|
+
<ChevronDown size={24} color="gray" />
|
101
102
|
</Pressable>
|
102
103
|
<CountryBottomSheet
|
103
104
|
selectedCountry={selectedCountry}
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import { Pressable,
|
2
|
-
import {
|
1
|
+
import { Pressable, useColor } from "@nativetail/core";
|
2
|
+
import { Eye, EyeOff } from "lucide-react-native";
|
3
3
|
|
4
4
|
export default function ShowPassword({
|
5
5
|
showPassword,
|
@@ -8,24 +8,15 @@ export default function ShowPassword({
|
|
8
8
|
showPassword: boolean;
|
9
9
|
setShowPassword: (showPassword: boolean) => void;
|
10
10
|
}) {
|
11
|
-
const tw = useTw();
|
12
11
|
return (
|
13
12
|
<Pressable
|
14
13
|
onPress={() => setShowPassword(!showPassword)}
|
15
14
|
className="absolute right-2 bottom-2"
|
16
15
|
>
|
17
16
|
{showPassword ? (
|
18
|
-
<
|
19
|
-
icon="solar:eye-linear"
|
20
|
-
size={20}
|
21
|
-
color={tw.color("foreground")}
|
22
|
-
/>
|
17
|
+
<Eye size={20} color={useColor("foreground")} />
|
23
18
|
) : (
|
24
|
-
<
|
25
|
-
icon="solar:eye-closed-linear"
|
26
|
-
size={20}
|
27
|
-
color={tw.color("foreground")}
|
28
|
-
/>
|
19
|
+
<EyeOff size={20} color={useColor("foreground")} />
|
29
20
|
)}
|
30
21
|
</Pressable>
|
31
22
|
);
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import { ActivityIndicator, cn, useColor } from "@nativetail/core";
|
2
|
+
import { forwardRef } from "react";
|
3
|
+
import { Dialog, DialogMethods, DialogProps } from "../dialog";
|
4
|
+
|
5
|
+
export type LoaderProps = Omit<DialogProps, "children"> & {
|
6
|
+
activityIndicatorProps?: React.ComponentProps<typeof ActivityIndicator>;
|
7
|
+
children?: React.ReactNode;
|
8
|
+
};
|
9
|
+
|
10
|
+
export const Loader = forwardRef<DialogMethods, LoaderProps>(function Loader(
|
11
|
+
{ ...props },
|
12
|
+
ref
|
13
|
+
) {
|
14
|
+
return (
|
15
|
+
<Dialog
|
16
|
+
ref={ref}
|
17
|
+
closable={false}
|
18
|
+
{...props}
|
19
|
+
contentClassName={cn(
|
20
|
+
"items-center justify-center p-4 w-24 h-24",
|
21
|
+
props?.contentClassName
|
22
|
+
)}
|
23
|
+
>
|
24
|
+
{props?.children || (
|
25
|
+
<ActivityIndicator
|
26
|
+
size="large"
|
27
|
+
color={useColor("primary")}
|
28
|
+
{...props?.activityIndicatorProps}
|
29
|
+
/>
|
30
|
+
)}
|
31
|
+
</Dialog>
|
32
|
+
);
|
33
|
+
});
|
34
|
+
|
35
|
+
export type Loader = DialogMethods;
|
@@ -3,6 +3,7 @@ import {
|
|
3
3
|
Pressable,
|
4
4
|
PressableProps,
|
5
5
|
Text,
|
6
|
+
useColor,
|
6
7
|
useTw,
|
7
8
|
View,
|
8
9
|
} from "@nativetail/core";
|
@@ -10,6 +11,7 @@ import { Dropdown } from "../dropdown";
|
|
10
11
|
import { memo, useCallback, useMemo } from "react";
|
11
12
|
import { Iconify } from "react-native-iconify";
|
12
13
|
import { Control, Controller, Path } from "react-hook-form";
|
14
|
+
import { CheckCheck, CheckIcon, ChevronDown } from "lucide-react-native";
|
13
15
|
|
14
16
|
export type MultiSelectProps<T = Record<string, any>> = PressableProps & {
|
15
17
|
containerClassName?: string;
|
@@ -150,11 +152,7 @@ const SelectTrigger = memo(
|
|
150
152
|
{selectedOptions.length == 0 && placeholder && (
|
151
153
|
<Text className="text-muted">{placeholder}</Text>
|
152
154
|
)}
|
153
|
-
<
|
154
|
-
icon="solar:alt-arrow-down-outline"
|
155
|
-
size={20}
|
156
|
-
color={tw.color("foreground")}
|
157
|
-
/>
|
155
|
+
<ChevronDown size={20} color={useColor("foreground")} />
|
158
156
|
</Dropdown.Trigger>
|
159
157
|
|
160
158
|
{error && <Text className="text-sm text-danger">{error}</Text>}
|
@@ -195,13 +193,7 @@ const SelectItem = memo(
|
|
195
193
|
<Text className="text-sm text-foreground">{label}</Text>
|
196
194
|
{icon}
|
197
195
|
</View>
|
198
|
-
{isActive && (
|
199
|
-
<Iconify
|
200
|
-
icon="lucide:check"
|
201
|
-
size={16}
|
202
|
-
color={tw.color("foreground")}
|
203
|
-
/>
|
204
|
-
)}
|
196
|
+
{isActive && <CheckIcon size={16} color={useColor("foreground")} />}
|
205
197
|
</Dropdown.Item>
|
206
198
|
);
|
207
199
|
}
|
@@ -1,8 +1,15 @@
|
|
1
|
-
import {
|
2
|
-
|
1
|
+
import {
|
2
|
+
cn,
|
3
|
+
PressableProps,
|
4
|
+
Text,
|
5
|
+
useColor,
|
6
|
+
useTw,
|
7
|
+
View,
|
8
|
+
} from "@nativetail/core";
|
9
|
+
import { CheckCheck, CheckIcon, ChevronDown } from "lucide-react-native";
|
3
10
|
import { memo, useCallback, useMemo } from "react";
|
4
|
-
import { Iconify } from "react-native-iconify";
|
5
11
|
import { Control, Controller, Path } from "react-hook-form";
|
12
|
+
import { Dropdown } from "../dropdown";
|
6
13
|
|
7
14
|
export type SelectProps<T = Record<string, any>> = PressableProps & {
|
8
15
|
containerClassName?: string;
|
@@ -124,11 +131,7 @@ const SelectTrigger = memo(
|
|
124
131
|
{!selectedOption && placeholder && (
|
125
132
|
<Text className="text-muted">{placeholder}</Text>
|
126
133
|
)}
|
127
|
-
<
|
128
|
-
icon="solar:alt-arrow-down-outline"
|
129
|
-
size={20}
|
130
|
-
color={tw.color("foreground")}
|
131
|
-
/>
|
134
|
+
<ChevronDown size={20} color={useColor("foreground")} />
|
132
135
|
</Dropdown.Trigger>
|
133
136
|
{error && <Text className="text-sm text-danger">{error}</Text>}
|
134
137
|
</>
|
@@ -167,13 +170,7 @@ const SelectItem = memo(
|
|
167
170
|
<Text className="text-sm text-foreground">{label}</Text>
|
168
171
|
{icon}
|
169
172
|
</View>
|
170
|
-
{isActive && (
|
171
|
-
<Iconify
|
172
|
-
icon="lucide:check"
|
173
|
-
size={16}
|
174
|
-
color={tw.color("foreground")}
|
175
|
-
/>
|
176
|
-
)}
|
173
|
+
{isActive && <CheckIcon size={16} color={useColor("foreground")} />}
|
177
174
|
</Dropdown.Item>
|
178
175
|
);
|
179
176
|
}
|
@@ -8,7 +8,7 @@ import {
|
|
8
8
|
} from "@nativetail/core";
|
9
9
|
|
10
10
|
const switchVariants = cva(
|
11
|
-
"rounded-full bg-card justify-center border p-0.5 items-start",
|
11
|
+
"rounded-full bg-card justify-center border border-muted/15 p-0.5 items-start",
|
12
12
|
{
|
13
13
|
variants: {
|
14
14
|
size: {
|
@@ -51,7 +51,7 @@ export function Switch({
|
|
51
51
|
<Pressable
|
52
52
|
className={cn(
|
53
53
|
className,
|
54
|
-
checked ? "bg-primary
|
54
|
+
checked ? "bg-primary " + containerActiveClass : ""
|
55
55
|
)}
|
56
56
|
aria-checked={checked}
|
57
57
|
accessibilityRole="switch"
|
@@ -63,6 +63,7 @@ export function Switch({
|
|
63
63
|
className={cn(
|
64
64
|
`rounded-full bg-primary aspect-square h-full`,
|
65
65
|
indicatorClassName,
|
66
|
+
checked ? "bg-card" : "",
|
66
67
|
x
|
67
68
|
)}
|
68
69
|
animated
|
@@ -1,8 +1,13 @@
|
|
1
|
-
import { cn, Pressable, Text, useTw, View } from "@nativetail/core";
|
1
|
+
import { cn, Pressable, Text, useColor, useTw, View } from "@nativetail/core";
|
2
|
+
import {
|
3
|
+
CheckCircle2,
|
4
|
+
Info,
|
5
|
+
OctagonAlert,
|
6
|
+
OctagonX,
|
7
|
+
} from "lucide-react-native";
|
2
8
|
import { AnimatePresence } from "moti";
|
3
9
|
import { useCallback, useEffect, useState } from "react";
|
4
10
|
import { Modal } from "react-native";
|
5
|
-
import { Iconify } from "react-native-iconify";
|
6
11
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
7
12
|
import { create } from "zustand";
|
8
13
|
type ToastType = {
|
@@ -50,7 +55,6 @@ const useToastState = create<ToastStore>((set) => ({
|
|
50
55
|
let timeouts = new Map<string, NodeJS.Timeout>();
|
51
56
|
export const showToast = (toast: InsertToastType) => {
|
52
57
|
const id = Math.random().toString(36).substring(7);
|
53
|
-
console.log(id);
|
54
58
|
useToastState.getState().addToast({ ...toast, id });
|
55
59
|
return id;
|
56
60
|
};
|
@@ -108,8 +112,8 @@ const Toast = (
|
|
108
112
|
className={cn(
|
109
113
|
"absolute w-full h-full bg-black/15 left-0 justify-start z-50 gap-2",
|
110
114
|
toast.position.includes("top")
|
111
|
-
? `pt-[${safeInsets.top + 10}px]`
|
112
|
-
: `pb-[${safeInsets.bottom + 10}px]`
|
115
|
+
? `pt-[${safeInsets.top + 10}px] top-0`
|
116
|
+
: `pb-[${safeInsets.bottom + 10}px] bottom-0 justify-end`
|
113
117
|
)}
|
114
118
|
onPress={close}
|
115
119
|
>
|
@@ -140,30 +144,10 @@ const BaseToast = (
|
|
140
144
|
}, [toast.id]);
|
141
145
|
|
142
146
|
const Icons = {
|
143
|
-
success: (
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
color={tw.color("success")}
|
148
|
-
/>
|
149
|
-
),
|
150
|
-
danger: (
|
151
|
-
<Iconify icon="uis:times-circle" size={20} color={tw.color("danger")} />
|
152
|
-
),
|
153
|
-
info: (
|
154
|
-
<Iconify
|
155
|
-
icon="fluent:info-16-filled"
|
156
|
-
size={20}
|
157
|
-
color={tw.color("info")}
|
158
|
-
/>
|
159
|
-
),
|
160
|
-
warning: (
|
161
|
-
<Iconify
|
162
|
-
icon="fluent:warning-16-filled"
|
163
|
-
size={20}
|
164
|
-
color={tw.color("warning")}
|
165
|
-
/>
|
166
|
-
),
|
147
|
+
success: <CheckCircle2 size={20} color={useColor("success")} />,
|
148
|
+
danger: <OctagonX size={20} color={useColor("danger")} />,
|
149
|
+
info: <Info size={20} color={useColor("info")} />,
|
150
|
+
warning: <OctagonAlert size={20} color={useColor("warning")} />,
|
167
151
|
};
|
168
152
|
|
169
153
|
const Icon = toast.icon || Icons[toast.type];
|
package/tailwind.config.js
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
|
1
2
|
module.exports = {
|
2
3
|
theme: {
|
3
4
|
screens: {
|
@@ -9,7 +10,10 @@ module.exports = {
|
|
9
10
|
},
|
10
11
|
extend: {
|
11
12
|
colors: {
|
12
|
-
primary:
|
13
|
+
primary: {
|
14
|
+
DEFAULT: "#43D386",
|
15
|
+
foreground: "#000",
|
16
|
+
},
|
13
17
|
secondary: '#EBB461',
|
14
18
|
background: '#F2F2F2',
|
15
19
|
card: '#fff',
|
@@ -26,4 +30,4 @@ module.exports = {
|
|
26
30
|
}
|
27
31
|
},
|
28
32
|
},
|
29
|
-
};
|
33
|
+
};
|