getlotui 0.1.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.
Files changed (128) hide show
  1. package/README.md +78 -0
  2. package/dist/bin.d.ts +2 -0
  3. package/dist/bin.js +5 -0
  4. package/dist/commands/add.d.ts +1 -0
  5. package/dist/commands/add.js +37 -0
  6. package/dist/commands/init.d.ts +1 -0
  7. package/dist/commands/init.js +93 -0
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.js +22 -0
  10. package/dist/templates/expo/Accordion.d.ts +14 -0
  11. package/dist/templates/expo/Accordion.js +118 -0
  12. package/dist/templates/expo/Accordion.tsx +152 -0
  13. package/dist/templates/expo/AlertDialog.d.ts +12 -0
  14. package/dist/templates/expo/AlertDialog.js +126 -0
  15. package/dist/templates/expo/AlertDialog.tsx +147 -0
  16. package/dist/templates/expo/Avatar.d.ts +8 -0
  17. package/dist/templates/expo/Avatar.js +81 -0
  18. package/dist/templates/expo/Avatar.tsx +78 -0
  19. package/dist/templates/expo/Badge.d.ts +6 -0
  20. package/dist/templates/expo/Badge.js +60 -0
  21. package/dist/templates/expo/Badge.tsx +67 -0
  22. package/dist/templates/expo/Button.d.ts +9 -0
  23. package/dist/templates/expo/Button.js +37 -0
  24. package/dist/templates/expo/Button.tsx +53 -0
  25. package/dist/templates/expo/Dropdown.d.ts +12 -0
  26. package/dist/templates/expo/Dropdown.js +91 -0
  27. package/dist/templates/expo/Dropdown.tsx +100 -0
  28. package/dist/templates/expo/Input.d.ts +11 -0
  29. package/dist/templates/expo/Input.js +43 -0
  30. package/dist/templates/expo/Input.tsx +67 -0
  31. package/dist/templates/expo/Toast.d.ts +16 -0
  32. package/dist/templates/expo/Toast.js +142 -0
  33. package/dist/templates/expo/Toast.tsx +161 -0
  34. package/dist/templates/expo/utils.d.ts +4 -0
  35. package/dist/templates/expo/utils.js +10 -0
  36. package/dist/templates/expo/utils.ts +8 -0
  37. package/dist/templates/flutter/Accordion.dart +142 -0
  38. package/dist/templates/flutter/Alert.dart +96 -0
  39. package/dist/templates/flutter/AlertDialog.dart +175 -0
  40. package/dist/templates/flutter/Avatar.dart +82 -0
  41. package/dist/templates/flutter/Badge.dart +89 -0
  42. package/dist/templates/flutter/Button.dart +116 -0
  43. package/dist/templates/flutter/Card.dart +91 -0
  44. package/dist/templates/flutter/Input.dart +73 -0
  45. package/dist/templates/flutter/Text.dart +87 -0
  46. package/dist/templates/flutter/utils.dart +13 -0
  47. package/dist/templates/templates/expo/Button.tsx +50 -0
  48. package/dist/templates/templates/expo/Input.tsx +67 -0
  49. package/dist/templates/web/Accordion.d.ts +7 -0
  50. package/dist/templates/web/Accordion.js +59 -0
  51. package/dist/templates/web/Accordion.tsx +64 -0
  52. package/dist/templates/web/Alert.d.ts +9 -0
  53. package/dist/templates/web/Alert.js +64 -0
  54. package/dist/templates/web/Alert.tsx +71 -0
  55. package/dist/templates/web/AlertDialog.d.ts +14 -0
  56. package/dist/templates/web/AlertDialog.js +85 -0
  57. package/dist/templates/web/AlertDialog.tsx +164 -0
  58. package/dist/templates/web/Avatar.d.ts +6 -0
  59. package/dist/templates/web/Avatar.js +50 -0
  60. package/dist/templates/web/Avatar.tsx +51 -0
  61. package/dist/templates/web/Badge.d.ts +9 -0
  62. package/dist/templates/web/Badge.js +59 -0
  63. package/dist/templates/web/Badge.tsx +38 -0
  64. package/dist/templates/web/Button.d.ts +10 -0
  65. package/dist/templates/web/Button.js +70 -0
  66. package/dist/templates/web/Button.tsx +60 -0
  67. package/dist/templates/web/Card.d.ts +9 -0
  68. package/dist/templates/web/Card.js +65 -0
  69. package/dist/templates/web/Card.tsx +92 -0
  70. package/dist/templates/web/Dropdown.d.ts +27 -0
  71. package/dist/templates/web/Dropdown.js +95 -0
  72. package/dist/templates/web/Dropdown.tsx +198 -0
  73. package/dist/templates/web/Input.d.ts +3 -0
  74. package/dist/templates/web/Input.js +41 -0
  75. package/dist/templates/web/Input.tsx +21 -0
  76. package/dist/templates/web/Tabs.d.ts +7 -0
  77. package/dist/templates/web/Tabs.js +55 -0
  78. package/dist/templates/web/Tabs.tsx +66 -0
  79. package/dist/templates/web/Toast.d.ts +15 -0
  80. package/dist/templates/web/Toast.js +75 -0
  81. package/dist/templates/web/Toast.tsx +126 -0
  82. package/dist/templates/web/utils.d.ts +2 -0
  83. package/dist/templates/web/utils.js +8 -0
  84. package/dist/templates/web/utils.ts +6 -0
  85. package/dist/utils/detect.d.ts +19 -0
  86. package/dist/utils/detect.js +90 -0
  87. package/dist/utils/fs.d.ts +5 -0
  88. package/dist/utils/fs.js +35 -0
  89. package/getlotui.config.json +4 -0
  90. package/package.json +31 -0
  91. package/src/bin.ts +5 -0
  92. package/src/commands/add.ts +50 -0
  93. package/src/commands/init.ts +108 -0
  94. package/src/index.ts +23 -0
  95. package/src/templates/expo/Accordion.tsx +152 -0
  96. package/src/templates/expo/AlertDialog.tsx +147 -0
  97. package/src/templates/expo/Avatar.tsx +78 -0
  98. package/src/templates/expo/Badge.tsx +67 -0
  99. package/src/templates/expo/Button.tsx +53 -0
  100. package/src/templates/expo/Dropdown.tsx +100 -0
  101. package/src/templates/expo/Input.tsx +67 -0
  102. package/src/templates/expo/Toast.tsx +161 -0
  103. package/src/templates/expo/utils.ts +8 -0
  104. package/src/templates/flutter/Accordion.dart +142 -0
  105. package/src/templates/flutter/Alert.dart +96 -0
  106. package/src/templates/flutter/AlertDialog.dart +175 -0
  107. package/src/templates/flutter/Avatar.dart +82 -0
  108. package/src/templates/flutter/Badge.dart +89 -0
  109. package/src/templates/flutter/Button.dart +116 -0
  110. package/src/templates/flutter/Card.dart +91 -0
  111. package/src/templates/flutter/Input.dart +73 -0
  112. package/src/templates/flutter/Text.dart +87 -0
  113. package/src/templates/flutter/utils.dart +13 -0
  114. package/src/templates/web/Accordion.tsx +64 -0
  115. package/src/templates/web/Alert.tsx +71 -0
  116. package/src/templates/web/AlertDialog.tsx +164 -0
  117. package/src/templates/web/Avatar.tsx +51 -0
  118. package/src/templates/web/Badge.tsx +38 -0
  119. package/src/templates/web/Button.tsx +60 -0
  120. package/src/templates/web/Card.tsx +92 -0
  121. package/src/templates/web/Dropdown.tsx +198 -0
  122. package/src/templates/web/Input.tsx +21 -0
  123. package/src/templates/web/Tabs.tsx +66 -0
  124. package/src/templates/web/Toast.tsx +126 -0
  125. package/src/templates/web/utils.ts +6 -0
  126. package/src/utils/detect.ts +81 -0
  127. package/src/utils/fs.ts +32 -0
  128. package/tsconfig.json +17 -0
@@ -0,0 +1,100 @@
1
+ import React, { useState } from "react";
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ Pressable,
7
+ Modal,
8
+ Dimensions,
9
+ FlatList,
10
+ } from "react-native";
11
+
12
+ export interface DropdownItem {
13
+ label: string;
14
+ value: string;
15
+ icon?: React.ReactNode;
16
+ }
17
+
18
+ export interface DropdownProps {
19
+ trigger: React.ReactNode;
20
+ items: DropdownItem[];
21
+ onSelect: (value: string) => void;
22
+ }
23
+
24
+ export const Dropdown = ({ trigger, items, onSelect }: DropdownProps) => {
25
+ const [open, setOpen] = useState(false);
26
+ const [layout, setLayout] = useState({ x: 0, y: 0, width: 0, height: 0 });
27
+
28
+ const onTriggerLayout = (event: any) => {
29
+ // In a real implementation, we'd use measure() to get page position
30
+ // For the template, we'll simplify
31
+ };
32
+
33
+ return (
34
+ <View>
35
+ <Pressable onPress={() => setOpen(true)} onLayout={onTriggerLayout}>
36
+ {trigger}
37
+ </Pressable>
38
+
39
+ <Modal
40
+ visible={open}
41
+ transparent
42
+ animationType="none"
43
+ onRequestClose={() => setOpen(false)}
44
+ >
45
+ <Pressable style={styles.overlay} onPress={() => setOpen(false)}>
46
+ <View style={styles.menu}>
47
+ {items.map((item) => (
48
+ <Pressable
49
+ key={item.value}
50
+ style={styles.item}
51
+ onPress={() => {
52
+ onSelect(item.value);
53
+ setOpen(false);
54
+ }}
55
+ >
56
+ {item.icon && <View style={styles.icon}>{item.icon}</View>}
57
+ <Text style={styles.label}>{item.label}</Text>
58
+ </Pressable>
59
+ ))}
60
+ </View>
61
+ </Pressable>
62
+ </Modal>
63
+ </View>
64
+ );
65
+ };
66
+
67
+ const styles = StyleSheet.create({
68
+ overlay: {
69
+ flex: 1,
70
+ backgroundColor: "transparent",
71
+ justifyContent: "center", // Should be positioned near trigger
72
+ alignItems: "center",
73
+ },
74
+ menu: {
75
+ backgroundColor: "white",
76
+ borderRadius: 8,
77
+ padding: 4,
78
+ minWidth: 160,
79
+ shadowColor: "#000",
80
+ shadowOffset: { width: 0, height: 4 },
81
+ shadowOpacity: 0.1,
82
+ shadowRadius: 8,
83
+ elevation: 8,
84
+ borderWidth: 1,
85
+ borderColor: "#e4e4e7",
86
+ },
87
+ item: {
88
+ flexDirection: "row",
89
+ alignItems: "center",
90
+ padding: 8,
91
+ borderRadius: 4,
92
+ },
93
+ icon: {
94
+ marginRight: 8,
95
+ },
96
+ label: {
97
+ fontSize: 14,
98
+ color: "#18181b",
99
+ },
100
+ });
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ type InputProps = {
3
+ label?: string;
4
+ placeholder?: string;
5
+ value: string;
6
+ onChangeText: (text: string) => void;
7
+ secureTextEntry?: boolean;
8
+ disabled?: boolean;
9
+ };
10
+ export declare const Input: React.FC<InputProps>;
11
+ export {};
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Input = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const react_native_1 = require("react-native");
9
+ const Input = ({ label, placeholder, value, onChangeText, secureTextEntry = false, disabled = false, }) => {
10
+ const colors = {
11
+ text: "#111827", // gray-900
12
+ border: "#d1d5db", // gray-300
13
+ muted: "#9ca3af", // gray-400
14
+ };
15
+ return (react_1.default.createElement(react_native_1.View, { style: styles.container },
16
+ label && (react_1.default.createElement(react_native_1.Text, { style: [styles.label, { color: colors.text }] }, label)),
17
+ react_1.default.createElement(react_native_1.TextInput, { style: [
18
+ styles.input,
19
+ { borderColor: colors.border, color: colors.text },
20
+ disabled && styles.disabled,
21
+ ], placeholder: placeholder, placeholderTextColor: colors.muted, value: value, onChangeText: onChangeText, secureTextEntry: secureTextEntry, editable: !disabled })));
22
+ };
23
+ exports.Input = Input;
24
+ const styles = react_native_1.StyleSheet.create({
25
+ container: {
26
+ marginVertical: 8,
27
+ },
28
+ label: {
29
+ marginBottom: 4,
30
+ fontSize: 14,
31
+ fontWeight: "500",
32
+ },
33
+ input: {
34
+ borderWidth: 1,
35
+ borderRadius: 4,
36
+ paddingHorizontal: 12,
37
+ paddingVertical: 8,
38
+ fontSize: 16,
39
+ },
40
+ disabled: {
41
+ opacity: 0.6,
42
+ },
43
+ });
@@ -0,0 +1,67 @@
1
+ import React from "react";
2
+ import { TextInput, StyleSheet, View, Text } from "react-native";
3
+
4
+ type InputProps = {
5
+ label?: string;
6
+ placeholder?: string;
7
+ value: string;
8
+ onChangeText: (text: string) => void;
9
+ secureTextEntry?: boolean;
10
+ disabled?: boolean;
11
+ };
12
+
13
+ export const Input: React.FC<InputProps> = ({
14
+ label,
15
+ placeholder,
16
+ value,
17
+ onChangeText,
18
+ secureTextEntry = false,
19
+ disabled = false,
20
+ }) => {
21
+ const colors = {
22
+ text: "#111827", // gray-900
23
+ border: "#d1d5db", // gray-300
24
+ muted: "#9ca3af", // gray-400
25
+ };
26
+ return (
27
+ <View style={styles.container}>
28
+ {label && (
29
+ <Text style={[styles.label, { color: colors.text }]}>{label}</Text>
30
+ )}
31
+ <TextInput
32
+ style={[
33
+ styles.input,
34
+ { borderColor: colors.border, color: colors.text },
35
+ disabled && styles.disabled,
36
+ ]}
37
+ placeholder={placeholder}
38
+ placeholderTextColor={colors.muted}
39
+ value={value}
40
+ onChangeText={onChangeText}
41
+ secureTextEntry={secureTextEntry}
42
+ editable={!disabled}
43
+ />
44
+ </View>
45
+ );
46
+ };
47
+
48
+ const styles = StyleSheet.create({
49
+ container: {
50
+ marginVertical: 8,
51
+ },
52
+ label: {
53
+ marginBottom: 4,
54
+ fontSize: 14,
55
+ fontWeight: "500",
56
+ },
57
+ input: {
58
+ borderWidth: 1,
59
+ borderRadius: 4,
60
+ paddingHorizontal: 12,
61
+ paddingVertical: 8,
62
+ fontSize: 16,
63
+ },
64
+ disabled: {
65
+ opacity: 0.6,
66
+ },
67
+ });
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ type ToastType = "default" | "success" | "error";
3
+ interface Toast {
4
+ id: string;
5
+ title: string;
6
+ description?: string;
7
+ type?: ToastType;
8
+ }
9
+ interface ToastContextType {
10
+ toast: (toast: Omit<Toast, "id">) => void;
11
+ }
12
+ export declare const ToastProvider: ({ children }: {
13
+ children: React.ReactNode;
14
+ }) => React.JSX.Element;
15
+ export declare const useToast: () => ToastContextType;
16
+ export {};
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.useToast = exports.ToastProvider = void 0;
37
+ const react_1 = __importStar(require("react"));
38
+ const react_native_1 = require("react-native");
39
+ const ToastContext = (0, react_1.createContext)(undefined);
40
+ const ToastProvider = ({ children }) => {
41
+ const [toasts, setToasts] = (0, react_1.useState)([]);
42
+ const toast = ({ title, description, type = "default", }) => {
43
+ const id = Math.random().toString(36).substr(2, 9);
44
+ setToasts((prev) => [...prev, { id, title, description, type }]);
45
+ };
46
+ const removeToast = (id) => {
47
+ setToasts((prev) => prev.filter((t) => t.id !== id));
48
+ };
49
+ return (react_1.default.createElement(ToastContext.Provider, { value: { toast } },
50
+ children,
51
+ react_1.default.createElement(react_native_1.View, { style: styles.container, pointerEvents: "box-none" }, toasts.map((t) => (react_1.default.createElement(ToastItem, { key: t.id, toast: t, onRemove: () => removeToast(t.id) }))))));
52
+ };
53
+ exports.ToastProvider = ToastProvider;
54
+ const useToast = () => {
55
+ const context = (0, react_1.useContext)(ToastContext);
56
+ if (!context)
57
+ throw new Error("useToast must be used within ToastProvider");
58
+ return context;
59
+ };
60
+ exports.useToast = useToast;
61
+ const ToastItem = ({ toast, onRemove, }) => {
62
+ const opacity = (0, react_1.useState)(new react_native_1.Animated.Value(0))[0];
63
+ const translateY = (0, react_1.useState)(new react_native_1.Animated.Value(20))[0];
64
+ (0, react_1.useEffect)(() => {
65
+ react_native_1.Animated.parallel([
66
+ react_native_1.Animated.timing(opacity, {
67
+ toValue: 1,
68
+ duration: 300,
69
+ useNativeDriver: true,
70
+ }),
71
+ react_native_1.Animated.timing(translateY, {
72
+ toValue: 0,
73
+ duration: 300,
74
+ useNativeDriver: true,
75
+ }),
76
+ ]).start();
77
+ const timer = setTimeout(() => {
78
+ react_native_1.Animated.parallel([
79
+ react_native_1.Animated.timing(opacity, {
80
+ toValue: 0,
81
+ duration: 300,
82
+ useNativeDriver: true,
83
+ }),
84
+ react_native_1.Animated.timing(translateY, {
85
+ toValue: 20,
86
+ duration: 300,
87
+ useNativeDriver: true,
88
+ }),
89
+ ]).start(() => onRemove());
90
+ }, 3000);
91
+ return () => clearTimeout(timer);
92
+ }, []);
93
+ const bgColors = {
94
+ default: "white",
95
+ success: "#f0fdf4",
96
+ error: "#fef2f2",
97
+ };
98
+ return (react_1.default.createElement(react_native_1.Animated.View, { style: [
99
+ styles.toast,
100
+ {
101
+ opacity,
102
+ transform: [{ translateY }],
103
+ backgroundColor: bgColors[toast.type || "default"],
104
+ },
105
+ ] },
106
+ react_1.default.createElement(react_native_1.View, null,
107
+ react_1.default.createElement(react_native_1.Text, { style: styles.title }, toast.title),
108
+ toast.description && (react_1.default.createElement(react_native_1.Text, { style: styles.description }, toast.description)))));
109
+ };
110
+ const styles = react_native_1.StyleSheet.create({
111
+ container: {
112
+ position: "absolute",
113
+ bottom: 40,
114
+ left: 20,
115
+ right: 20,
116
+ alignItems: "center",
117
+ },
118
+ toast: {
119
+ width: "100%",
120
+ maxWidth: 400,
121
+ padding: 16,
122
+ borderRadius: 8,
123
+ marginBottom: 8,
124
+ shadowColor: "#000",
125
+ shadowOffset: { width: 0, height: 2 },
126
+ shadowOpacity: 0.1,
127
+ shadowRadius: 4,
128
+ elevation: 3,
129
+ borderWidth: 1,
130
+ borderColor: "#e4e4e7",
131
+ },
132
+ title: {
133
+ fontSize: 14,
134
+ fontWeight: "600",
135
+ color: "#18181b",
136
+ },
137
+ description: {
138
+ fontSize: 12,
139
+ color: "#71717a",
140
+ marginTop: 2,
141
+ },
142
+ });
@@ -0,0 +1,161 @@
1
+ import React, { useState, useEffect, createContext, useContext } from "react";
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ Animated,
7
+ Dimensions,
8
+ Pressable,
9
+ } from "react-native";
10
+
11
+ type ToastType = "default" | "success" | "error";
12
+
13
+ interface Toast {
14
+ id: string;
15
+ title: string;
16
+ description?: string;
17
+ type?: ToastType;
18
+ }
19
+
20
+ interface ToastContextType {
21
+ toast: (toast: Omit<Toast, "id">) => void;
22
+ }
23
+
24
+ const ToastContext = createContext<ToastContextType | undefined>(undefined);
25
+
26
+ export const ToastProvider = ({ children }: { children: React.ReactNode }) => {
27
+ const [toasts, setToasts] = useState<Toast[]>([]);
28
+
29
+ const toast = ({
30
+ title,
31
+ description,
32
+ type = "default",
33
+ }: Omit<Toast, "id">) => {
34
+ const id = Math.random().toString(36).substr(2, 9);
35
+ setToasts((prev) => [...prev, { id, title, description, type }]);
36
+ };
37
+
38
+ const removeToast = (id: string) => {
39
+ setToasts((prev) => prev.filter((t) => t.id !== id));
40
+ };
41
+
42
+ return (
43
+ <ToastContext.Provider value={{ toast }}>
44
+ {children}
45
+ <View style={styles.container} pointerEvents="box-none">
46
+ {toasts.map((t) => (
47
+ <ToastItem key={t.id} toast={t} onRemove={() => removeToast(t.id)} />
48
+ ))}
49
+ </View>
50
+ </ToastContext.Provider>
51
+ );
52
+ };
53
+
54
+ export const useToast = () => {
55
+ const context = useContext(ToastContext);
56
+ if (!context) throw new Error("useToast must be used within ToastProvider");
57
+ return context;
58
+ };
59
+
60
+ const ToastItem = ({
61
+ toast,
62
+ onRemove,
63
+ }: {
64
+ toast: Toast;
65
+ onRemove: () => void;
66
+ }) => {
67
+ const opacity = useState(new Animated.Value(0))[0];
68
+ const translateY = useState(new Animated.Value(20))[0];
69
+
70
+ useEffect(() => {
71
+ Animated.parallel([
72
+ Animated.timing(opacity, {
73
+ toValue: 1,
74
+ duration: 300,
75
+ useNativeDriver: true,
76
+ }),
77
+ Animated.timing(translateY, {
78
+ toValue: 0,
79
+ duration: 300,
80
+ useNativeDriver: true,
81
+ }),
82
+ ]).start();
83
+
84
+ const timer = setTimeout(() => {
85
+ Animated.parallel([
86
+ Animated.timing(opacity, {
87
+ toValue: 0,
88
+ duration: 300,
89
+ useNativeDriver: true,
90
+ }),
91
+ Animated.timing(translateY, {
92
+ toValue: 20,
93
+ duration: 300,
94
+ useNativeDriver: true,
95
+ }),
96
+ ]).start(() => onRemove());
97
+ }, 3000);
98
+
99
+ return () => clearTimeout(timer);
100
+ }, []);
101
+
102
+ const bgColors = {
103
+ default: "white",
104
+ success: "#f0fdf4",
105
+ error: "#fef2f2",
106
+ };
107
+
108
+ return (
109
+ <Animated.View
110
+ style={[
111
+ styles.toast,
112
+ {
113
+ opacity,
114
+ transform: [{ translateY }],
115
+ backgroundColor: bgColors[toast.type || "default"],
116
+ },
117
+ ]}
118
+ >
119
+ <View>
120
+ <Text style={styles.title}>{toast.title}</Text>
121
+ {toast.description && (
122
+ <Text style={styles.description}>{toast.description}</Text>
123
+ )}
124
+ </View>
125
+ </Animated.View>
126
+ );
127
+ };
128
+
129
+ const styles = StyleSheet.create({
130
+ container: {
131
+ position: "absolute",
132
+ bottom: 40,
133
+ left: 20,
134
+ right: 20,
135
+ alignItems: "center",
136
+ },
137
+ toast: {
138
+ width: "100%",
139
+ maxWidth: 400,
140
+ padding: 16,
141
+ borderRadius: 8,
142
+ marginBottom: 8,
143
+ shadowColor: "#000",
144
+ shadowOffset: { width: 0, height: 2 },
145
+ shadowOpacity: 0.1,
146
+ shadowRadius: 4,
147
+ elevation: 3,
148
+ borderWidth: 1,
149
+ borderColor: "#e4e4e7",
150
+ },
151
+ title: {
152
+ fontSize: 14,
153
+ fontWeight: "600",
154
+ color: "#18181b",
155
+ },
156
+ description: {
157
+ fontSize: 12,
158
+ color: "#71717a",
159
+ marginTop: 2,
160
+ },
161
+ });
@@ -0,0 +1,4 @@
1
+ /**
2
+ * A utility to merge React Native styles.
3
+ */
4
+ export declare function cn(...inputs: any[]): any;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cn = cn;
4
+ const react_native_1 = require("react-native");
5
+ /**
6
+ * A utility to merge React Native styles.
7
+ */
8
+ function cn(...inputs) {
9
+ return react_native_1.StyleSheet.flatten(inputs.filter(Boolean));
10
+ }
@@ -0,0 +1,8 @@
1
+ import { StyleSheet } from "react-native";
2
+
3
+ /**
4
+ * A utility to merge React Native styles.
5
+ */
6
+ export function cn(...inputs: any[]) {
7
+ return StyleSheet.flatten(inputs.filter(Boolean));
8
+ }