@nativetail/ui 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,101 @@
1
+ import { BlurView } from "expo-blur";
2
+ import { cn, Pressable, Text, useTw, View } from "@nativetail/core";
3
+ import { useEffect } from "react";
4
+ import { create } from "zustand";
5
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
6
+ import { AnimatePresence } from "moti";
7
+ import { Blur } from "../blur";
8
+ type ToastType = {
9
+ message: string;
10
+ content?: string;
11
+ id: string;
12
+ timeout?: number;
13
+ position?: "top" | "bottom";
14
+ containerClassName?: string;
15
+ };
16
+ type InsertToastType = Omit<ToastType, "id">;
17
+ type ToastStore = {
18
+ toasts: ToastType[];
19
+ addToast: (toast: ToastType) => void;
20
+ removeToast: (id: string) => void;
21
+ };
22
+ const useToastState = create<ToastStore>((set) => ({
23
+ toasts: [],
24
+ addToast: (toast) => set((state) => ({ toasts: [...state.toasts, toast] })),
25
+ removeToast: (id) =>
26
+ set((state) => ({
27
+ toasts: state.toasts.filter((toast) => toast.id !== id),
28
+ })),
29
+ }));
30
+ let timeouts = new Map<string, NodeJS.Timeout>();
31
+ export const showToast = (toast: InsertToastType) => {
32
+ const id = Math.random().toString(36).substring(7);
33
+ useToastState.getState().addToast({ ...toast, id });
34
+ return id;
35
+ };
36
+ export function Toaster() {
37
+ const toasts = useToastState((state) => state.toasts);
38
+ return (
39
+ <AnimatePresence exitBeforeEnter>
40
+ {toasts.map((toast, index) => (
41
+ <Toast key={toast.id} index={index} {...toast} />
42
+ ))}
43
+ </AnimatePresence>
44
+ );
45
+ }
46
+
47
+ const Toast = (
48
+ toast: ToastType & {
49
+ index: number;
50
+ }
51
+ ) => {
52
+ const tw = useTw();
53
+ useEffect(() => {
54
+ const id = setTimeout(() => {
55
+ useToastState.getState().removeToast(toast.id);
56
+ }, toast.timeout || 5000);
57
+ timeouts.set(toast.id, id);
58
+ return () => {
59
+ clearTimeout(timeouts.get(toast.id)!);
60
+ timeouts.delete(toast.id);
61
+ };
62
+ }, [toast.id]);
63
+ const safeInsets = useSafeAreaInsets();
64
+ return (
65
+ <View
66
+ className={cn(
67
+ "absolute w-full top-0 left-0 items-center justify-center z-50 ",
68
+ toast.position === "top"
69
+ ? `top-[${safeInsets.top + 10}px]`
70
+ : `bottom-[${safeInsets.bottom + 10}px]`,
71
+ toast.containerClassName
72
+ )}
73
+ animated
74
+ >
75
+ <Pressable
76
+ onPress={() => {
77
+ useToastState.getState().removeToast(toast.id);
78
+ }}
79
+ className="w-full items-center justify-center active:scale-95 scale-100 px-4"
80
+ >
81
+ <View
82
+ className={cn(
83
+ `bg-card/95 border border-muted/15 px-6 py-3 in:opacity-0 opacity-100 in:-translate-y-16 out:-translate-y-16 out:opacity-0 in:scale-0 scale-100 out:scale-0 rounded-full overflow-hidden max-w-sm w-full `,
84
+ `translate-y-0`
85
+ )}
86
+ animated
87
+ >
88
+ <Blur
89
+ style={tw`absolute top-0 left-0 rounded-xl flex-1 bg-card/50 rounded-full`}
90
+ />
91
+ <Text className="font-medium text-[16px] text-foreground">
92
+ {toast.message}
93
+ </Text>
94
+ {toast.content && (
95
+ <Text className="text-sm text-muted">{toast.content}</Text>
96
+ )}
97
+ </View>
98
+ </Pressable>
99
+ </View>
100
+ );
101
+ };
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./components";
@@ -0,0 +1,25 @@
1
+ module.exports = {
2
+ theme: {
3
+ screens: {
4
+ sm: "380px",
5
+ md: "420px",
6
+ lg: "680px",
7
+ // or maybe name them after devices for `tablet:flex-row`
8
+ tablet: "1024px",
9
+ },
10
+ extend: {
11
+ colors: {
12
+ primary: "#43D386",
13
+ secondary: '#EBB461',
14
+ background: '#F2F2F2',
15
+ card: '#fff',
16
+ foreground: '#000',
17
+ muted: '#383737',
18
+ success: '#4CAF50',
19
+ danger: '#F44336',
20
+ warning: '#FFC107',
21
+ info: '#00BCD4',
22
+ },
23
+ },
24
+ },
25
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "strict": false,
4
+ "esModuleInterop": true,
5
+ "noUncheckedIndexedAccess": true,
6
+ "noImplicitOverride": true,
7
+ "skipLibCheck": true,
8
+ "rootDir": "./",
9
+ "outDir": "./dist/esm",
10
+ "lib": ["ES2019"],
11
+ "target": "ES2019",
12
+ "declaration": true,
13
+ "moduleResolution": "node",
14
+ "jsx": "react-jsx",
15
+ "stripInternal": true
16
+ },
17
+ "exclude": ["./dist", "**/*.spec.ts"]
18
+ }