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
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # Getting Started with GetLotUI
2
+
3
+ GetLotUI is a component scaffolding tool that copies customizable, source-code components directly into your project. It is designed to work seamlessly with Expo and React Native applications.
4
+
5
+ ## Prerequisites
6
+
7
+ - **Node.js** (v18 or higher)
8
+ - An existing **Expo** project (managed or bare workflow)
9
+
10
+ ## Installation
11
+
12
+ You don't need to install GetLotUI globally. You can use it directly via `npx`.
13
+
14
+ However, if you prefer to install it as a dev dependency in your project:
15
+
16
+ ```bash
17
+ pnpm add -D getlotui
18
+ # or
19
+ npm install -D getlotui
20
+ # or
21
+ yarn add -D getlotui
22
+ ```
23
+
24
+ ## Initialization
25
+
26
+ Run the `init` command to set up GetLotUI in your project. This will create a `getlotui.config.json` file in your root directory.
27
+
28
+ ```bash
29
+ npx getlotui init
30
+ ```
31
+
32
+ The CLI will:
33
+ 1. Detect your project type (e.g., Expo).
34
+ 2. Create `getlotui.config.json` with default settings:
35
+
36
+ ```json
37
+ {
38
+ "adapter": "expo",
39
+ "componentsDir": "components/ui"
40
+ }
41
+ ```
42
+
43
+ ## Adding Components
44
+
45
+ Once initialized, you can add components to your project. Use the `add` command followed by the component name.
46
+
47
+ ```bash
48
+ npx getlotui add button
49
+ npx getlotui add input
50
+ ```
51
+
52
+ This will:
53
+ 1. Download/Copy the component source code to your configured `componentsDir` (default: `components/ui`).
54
+ 2. Add any necessary utility files (if applicable in future updates).
55
+
56
+ Now you can import and use the component in your app:
57
+
58
+ ```tsx
59
+ import { Button } from '@/components/ui/Button';
60
+
61
+ export default function App() {
62
+ return (
63
+ <Button title="Click Me" onPress={() => console.log('Pressed')} variant="primary" />
64
+ );
65
+ }
66
+ ```
67
+
68
+ ## Customization
69
+
70
+ Since GetLotUI copies the **source code** into your project, you have full control.
71
+ - Open `components/ui/Button.tsx`.
72
+ - Modify styles, props, or behavior to match your design system.
73
+ - No vendor lock-in; it's your code now!
74
+
75
+ ## Troubleshooting
76
+
77
+ - **"getlotui.config.json not found"**: Run `npx getlotui init` first.
78
+ - **"Component already exists"**: The CLI won't overwrite existing files to prevent data loss. Rename your existing file or delete it if you want to replace it.
package/dist/bin.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/bin.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const index_1 = require("./index");
5
+ (0, index_1.runCLI)();
@@ -0,0 +1 @@
1
+ export declare function addCommand(component: string): Promise<void>;
@@ -0,0 +1,37 @@
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.addCommand = addCommand;
7
+ const fs_1 = require("fs");
8
+ const path_1 = __importDefault(require("path"));
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const fs_2 = require("../utils/fs");
11
+ async function addCommand(component) {
12
+ try {
13
+ const cwd = process.cwd();
14
+ const configPath = path_1.default.join(cwd, "getlotui.config.json");
15
+ // Ensure config exists
16
+ let config;
17
+ try {
18
+ const configContent = await fs_1.promises.readFile(configPath, "utf8");
19
+ config = JSON.parse(configContent);
20
+ }
21
+ catch {
22
+ console.error(chalk_1.default.red("getlotui.config.json not found. Run `getlotui init` first."));
23
+ return;
24
+ }
25
+ const adapter = config.adapter || "expo";
26
+ const extension = adapter === "flutter" ? "dart" : "tsx";
27
+ const componentsDir = path_1.default.resolve(cwd, config.componentsDir ||
28
+ (adapter === "flutter" ? "lib/components/ui" : "components/ui"));
29
+ const componentName = component.charAt(0).toUpperCase() + component.slice(1);
30
+ const templatePath = path_1.default.resolve(__dirname, "..", "templates", adapter, `${componentName}.${extension}`);
31
+ await (0, fs_2.copyComponent)(templatePath, path_1.default.join(componentsDir, `${componentName}.${extension}`));
32
+ console.log(chalk_1.default.green(`Component ${componentName} added to ${componentsDir}`));
33
+ }
34
+ catch (err) {
35
+ console.error(chalk_1.default.red("Failed to add component:"), err);
36
+ }
37
+ }
@@ -0,0 +1 @@
1
+ export declare function initCommand(): Promise<void>;
@@ -0,0 +1,93 @@
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.initCommand = initCommand;
7
+ const fs_1 = require("fs");
8
+ const path_1 = __importDefault(require("path"));
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const detect_1 = require("../utils/detect");
11
+ async function initCommand() {
12
+ try {
13
+ const cwd = process.cwd();
14
+ const configPath = path_1.default.join(cwd, "getlotui.config.json");
15
+ // Check if config already exists
16
+ try {
17
+ await fs_1.promises.access(configPath);
18
+ console.log(chalk_1.default.yellow("getlotui.config.json already exists. Skipping creation."));
19
+ return;
20
+ }
21
+ catch {
22
+ // file does not exist, continue
23
+ }
24
+ // Detect adapter
25
+ let adapter = "unknown";
26
+ if (await (0, detect_1.detectExpoProject)(cwd)) {
27
+ adapter = "expo";
28
+ }
29
+ else if (await (0, detect_1.detectFlutterProject)(cwd)) {
30
+ adapter = "flutter";
31
+ }
32
+ else if (await (0, detect_1.detectWebProject)(cwd)) {
33
+ adapter = "web";
34
+ }
35
+ // Detect package manager
36
+ const packageManager = await (0, detect_1.detectPackageManager)(cwd);
37
+ const defaultConfig = {
38
+ adapter,
39
+ packageManager,
40
+ componentsDir: adapter === "flutter" ? "lib/components/ui" : "components/ui",
41
+ themeDir: adapter === "flutter" ? "lib/theme" : "theme",
42
+ };
43
+ await fs_1.promises.writeFile(configPath, JSON.stringify(defaultConfig, null, 2), "utf8");
44
+ // Create theme directory
45
+ const fullThemeDir = path_1.default.join(cwd, defaultConfig.themeDir);
46
+ try {
47
+ await fs_1.promises.mkdir(fullThemeDir, { recursive: true });
48
+ const isFlutter = adapter === "flutter";
49
+ const themeFileName = isFlutter ? "config.dart" : "config.ts";
50
+ const themeFilePath = path_1.default.join(fullThemeDir, themeFileName);
51
+ const themeContent = isFlutter
52
+ ? `import 'package:flutter/material.dart';
53
+
54
+ class GetLotUITheme {
55
+ static const colors = {
56
+ 'primary': Color(0xFF6366F1),
57
+ 'background': Color(0xFFFFFFFF),
58
+ 'foreground': Color(0xFF0F172A),
59
+ };
60
+
61
+ static const radius = {
62
+ 'sm': 4.0,
63
+ 'md': 8.0,
64
+ 'lg': 12.0,
65
+ };
66
+ }`
67
+ : `export const theme = {
68
+ colors: {
69
+ primary: '#6366f1',
70
+ background: '#ffffff',
71
+ foreground: '#0f172a',
72
+ },
73
+ radius: {
74
+ sm: 4,
75
+ md: 8,
76
+ lg: 12,
77
+ },
78
+ spacing: {
79
+ unit: 8,
80
+ }
81
+ };`;
82
+ await fs_1.promises.writeFile(themeFilePath, themeContent, "utf8");
83
+ console.log(chalk_1.default.green(`Created ${defaultConfig.themeDir}/${themeFileName}`));
84
+ }
85
+ catch (err) {
86
+ console.warn(chalk_1.default.yellow("Could not create theme directory:"), err);
87
+ }
88
+ console.log(chalk_1.default.green(`Created getlotui.config.json with ${adapter} (${packageManager}) defaults.`));
89
+ }
90
+ catch (err) {
91
+ console.error(chalk_1.default.red("Failed to initialize GetLotUI config:"), err);
92
+ }
93
+ }
@@ -0,0 +1 @@
1
+ export declare function runCLI(): void;
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runCLI = runCLI;
4
+ const commander_1 = require("commander");
5
+ const init_1 = require("./commands/init");
6
+ const add_1 = require("./commands/add");
7
+ function runCLI() {
8
+ const program = new commander_1.Command();
9
+ program
10
+ .name("getlotui")
11
+ .description("GetLotUI CLI for scaffolding components")
12
+ .version("0.1.0");
13
+ program
14
+ .command("init")
15
+ .description("Initialize GetLotUI config")
16
+ .action(init_1.initCommand);
17
+ program
18
+ .command("add <component>")
19
+ .description("Add a UI component")
20
+ .action(add_1.addCommand);
21
+ program.parse(process.argv);
22
+ }
@@ -0,0 +1,14 @@
1
+ import React from "react";
2
+ interface AccordionProps {
3
+ type?: "single" | "multiple";
4
+ collapsible?: boolean;
5
+ defaultValue?: string | string[];
6
+ children?: React.ReactNode;
7
+ items: Array<{
8
+ value: string;
9
+ trigger: string;
10
+ content: string;
11
+ }>;
12
+ }
13
+ export declare const Accordion: React.FC<AccordionProps>;
14
+ export {};
@@ -0,0 +1,118 @@
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.Accordion = void 0;
37
+ const react_1 = __importStar(require("react"));
38
+ const react_native_1 = require("react-native");
39
+ if (react_native_1.Platform.OS === "android" &&
40
+ react_native_1.UIManager.setLayoutAnimationEnabledExperimental) {
41
+ react_native_1.UIManager.setLayoutAnimationEnabledExperimental(true);
42
+ }
43
+ const AccordionItem = ({ trigger, content, isOpen, onToggle, }) => {
44
+ const handleToggle = () => {
45
+ react_native_1.LayoutAnimation.configureNext(react_native_1.LayoutAnimation.Presets.easeInEaseOut);
46
+ onToggle();
47
+ };
48
+ return (react_1.default.createElement(react_native_1.View, { style: styles.item },
49
+ react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.trigger, onPress: handleToggle, activeOpacity: 0.7 },
50
+ react_1.default.createElement(react_native_1.Text, { style: styles.triggerText }, trigger),
51
+ react_1.default.createElement(react_native_1.Text, { style: [styles.chevron, isOpen && styles.chevronOpen] }, "\u203A")),
52
+ isOpen && (react_1.default.createElement(react_native_1.View, { style: styles.content },
53
+ react_1.default.createElement(react_native_1.Text, { style: styles.contentText }, content)))));
54
+ };
55
+ const Accordion = ({ type = "single", collapsible = false, defaultValue, items, }) => {
56
+ const [openItems, setOpenItems] = (0, react_1.useState)(() => {
57
+ if (defaultValue) {
58
+ return Array.isArray(defaultValue) ? defaultValue : [defaultValue];
59
+ }
60
+ return [];
61
+ });
62
+ const handleToggle = (value) => {
63
+ if (type === "single") {
64
+ if (openItems.includes(value)) {
65
+ setOpenItems(collapsible ? [] : openItems);
66
+ }
67
+ else {
68
+ setOpenItems([value]);
69
+ }
70
+ }
71
+ else {
72
+ setOpenItems((prev) => prev.includes(value)
73
+ ? prev.filter((item) => item !== value)
74
+ : [...prev, value]);
75
+ }
76
+ };
77
+ return (react_1.default.createElement(react_native_1.View, { style: styles.container }, items.map((item) => (react_1.default.createElement(AccordionItem, { key: item.value, value: item.value, trigger: item.trigger, content: item.content, isOpen: openItems.includes(item.value), onToggle: () => handleToggle(item.value) })))));
78
+ };
79
+ exports.Accordion = Accordion;
80
+ const styles = react_native_1.StyleSheet.create({
81
+ container: {
82
+ width: "100%",
83
+ },
84
+ item: {
85
+ borderBottomWidth: 1,
86
+ borderBottomColor: "#e5e7eb",
87
+ },
88
+ trigger: {
89
+ flexDirection: "row",
90
+ justifyContent: "space-between",
91
+ alignItems: "center",
92
+ paddingVertical: 16,
93
+ paddingHorizontal: 0,
94
+ },
95
+ triggerText: {
96
+ fontSize: 15,
97
+ fontWeight: "500",
98
+ color: "#111827",
99
+ flex: 1,
100
+ },
101
+ chevron: {
102
+ fontSize: 20,
103
+ color: "#6b7280",
104
+ transform: [{ rotate: "90deg" }],
105
+ marginLeft: 8,
106
+ },
107
+ chevronOpen: {
108
+ transform: [{ rotate: "270deg" }],
109
+ },
110
+ content: {
111
+ paddingBottom: 16,
112
+ },
113
+ contentText: {
114
+ fontSize: 14,
115
+ color: "#6b7280",
116
+ lineHeight: 20,
117
+ },
118
+ });
@@ -0,0 +1,152 @@
1
+ import React, { useState } from "react";
2
+ import {
3
+ View,
4
+ Text,
5
+ TouchableOpacity,
6
+ StyleSheet,
7
+ LayoutAnimation,
8
+ Platform,
9
+ UIManager,
10
+ } from "react-native";
11
+
12
+ if (
13
+ Platform.OS === "android" &&
14
+ UIManager.setLayoutAnimationEnabledExperimental
15
+ ) {
16
+ UIManager.setLayoutAnimationEnabledExperimental(true);
17
+ }
18
+
19
+ interface AccordionItemProps {
20
+ value: string;
21
+ trigger: string;
22
+ content: string;
23
+ isOpen: boolean;
24
+ onToggle: () => void;
25
+ }
26
+
27
+ const AccordionItem: React.FC<AccordionItemProps> = ({
28
+ trigger,
29
+ content,
30
+ isOpen,
31
+ onToggle,
32
+ }) => {
33
+ const handleToggle = () => {
34
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
35
+ onToggle();
36
+ };
37
+
38
+ return (
39
+ <View style={styles.item}>
40
+ <TouchableOpacity
41
+ style={styles.trigger}
42
+ onPress={handleToggle}
43
+ activeOpacity={0.7}
44
+ >
45
+ <Text style={styles.triggerText}>{trigger}</Text>
46
+ <Text style={[styles.chevron, isOpen && styles.chevronOpen]}>›</Text>
47
+ </TouchableOpacity>
48
+ {isOpen && (
49
+ <View style={styles.content}>
50
+ <Text style={styles.contentText}>{content}</Text>
51
+ </View>
52
+ )}
53
+ </View>
54
+ );
55
+ };
56
+
57
+ interface AccordionProps {
58
+ type?: "single" | "multiple";
59
+ collapsible?: boolean;
60
+ defaultValue?: string | string[];
61
+ children?: React.ReactNode;
62
+ items: Array<{
63
+ value: string;
64
+ trigger: string;
65
+ content: string;
66
+ }>;
67
+ }
68
+
69
+ export const Accordion: React.FC<AccordionProps> = ({
70
+ type = "single",
71
+ collapsible = false,
72
+ defaultValue,
73
+ items,
74
+ }) => {
75
+ const [openItems, setOpenItems] = useState<string[]>(() => {
76
+ if (defaultValue) {
77
+ return Array.isArray(defaultValue) ? defaultValue : [defaultValue];
78
+ }
79
+ return [];
80
+ });
81
+
82
+ const handleToggle = (value: string) => {
83
+ if (type === "single") {
84
+ if (openItems.includes(value)) {
85
+ setOpenItems(collapsible ? [] : openItems);
86
+ } else {
87
+ setOpenItems([value]);
88
+ }
89
+ } else {
90
+ setOpenItems((prev) =>
91
+ prev.includes(value)
92
+ ? prev.filter((item) => item !== value)
93
+ : [...prev, value]
94
+ );
95
+ }
96
+ };
97
+
98
+ return (
99
+ <View style={styles.container}>
100
+ {items.map((item) => (
101
+ <AccordionItem
102
+ key={item.value}
103
+ value={item.value}
104
+ trigger={item.trigger}
105
+ content={item.content}
106
+ isOpen={openItems.includes(item.value)}
107
+ onToggle={() => handleToggle(item.value)}
108
+ />
109
+ ))}
110
+ </View>
111
+ );
112
+ };
113
+
114
+ const styles = StyleSheet.create({
115
+ container: {
116
+ width: "100%",
117
+ },
118
+ item: {
119
+ borderBottomWidth: 1,
120
+ borderBottomColor: "#e5e7eb",
121
+ },
122
+ trigger: {
123
+ flexDirection: "row",
124
+ justifyContent: "space-between",
125
+ alignItems: "center",
126
+ paddingVertical: 16,
127
+ paddingHorizontal: 0,
128
+ },
129
+ triggerText: {
130
+ fontSize: 15,
131
+ fontWeight: "500",
132
+ color: "#111827",
133
+ flex: 1,
134
+ },
135
+ chevron: {
136
+ fontSize: 20,
137
+ color: "#6b7280",
138
+ transform: [{ rotate: "90deg" }],
139
+ marginLeft: 8,
140
+ },
141
+ chevronOpen: {
142
+ transform: [{ rotate: "270deg" }],
143
+ },
144
+ content: {
145
+ paddingBottom: 16,
146
+ },
147
+ contentText: {
148
+ fontSize: 14,
149
+ color: "#6b7280",
150
+ lineHeight: 20,
151
+ },
152
+ });
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ export interface AlertDialogProps {
3
+ open?: boolean;
4
+ onOpenChange?: (open: boolean) => void;
5
+ trigger?: React.ReactNode;
6
+ title: string;
7
+ description: string;
8
+ cancelText?: string;
9
+ actionText?: string;
10
+ onAction?: () => void;
11
+ }
12
+ export declare const AlertDialog: ({ open: controlledOpen, onOpenChange, trigger, title, description, cancelText, actionText, onAction, }: AlertDialogProps) => React.JSX.Element;
@@ -0,0 +1,126 @@
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.AlertDialog = void 0;
37
+ const react_1 = __importStar(require("react"));
38
+ const react_native_1 = require("react-native");
39
+ const AlertDialog = ({ open: controlledOpen, onOpenChange, trigger, title, description, cancelText = "Cancel", actionText = "Continue", onAction, }) => {
40
+ const [internalOpen, setInternalOpen] = (0, react_1.useState)(false);
41
+ const isOpen = controlledOpen !== undefined ? controlledOpen : internalOpen;
42
+ const setOpen = (val) => {
43
+ if (onOpenChange)
44
+ onOpenChange(val);
45
+ setInternalOpen(val);
46
+ };
47
+ return (react_1.default.createElement(react_1.default.Fragment, null,
48
+ trigger && (react_1.default.createElement(react_native_1.Pressable, { onPress: () => setOpen(true) }, trigger)),
49
+ react_1.default.createElement(react_native_1.Modal, { transparent: true, visible: isOpen, animationType: "fade", onRequestClose: () => setOpen(false) },
50
+ react_1.default.createElement(react_native_1.Pressable, { style: styles.overlay, onPress: () => setOpen(false) },
51
+ react_1.default.createElement(react_native_1.Pressable, { style: styles.content, onPress: (e) => e.stopPropagation() },
52
+ react_1.default.createElement(react_native_1.Text, { style: styles.title }, title),
53
+ react_1.default.createElement(react_native_1.Text, { style: styles.description }, description),
54
+ react_1.default.createElement(react_native_1.View, { style: styles.footer },
55
+ react_1.default.createElement(react_native_1.Pressable, { style: [styles.button, styles.cancelButton], onPress: () => setOpen(false) },
56
+ react_1.default.createElement(react_native_1.Text, { style: styles.cancelButtonText }, cancelText)),
57
+ react_1.default.createElement(react_native_1.Pressable, { style: [styles.button, styles.actionButton], onPress: () => {
58
+ onAction?.();
59
+ setOpen(false);
60
+ } },
61
+ react_1.default.createElement(react_native_1.Text, { style: styles.actionButtonText }, actionText))))))));
62
+ };
63
+ exports.AlertDialog = AlertDialog;
64
+ const styles = react_native_1.StyleSheet.create({
65
+ overlay: {
66
+ flex: 1,
67
+ backgroundColor: "rgba(0,0,0,0.5)",
68
+ justifyContent: "center",
69
+ alignItems: "center",
70
+ padding: 20,
71
+ },
72
+ content: {
73
+ backgroundColor: "white",
74
+ borderRadius: 12,
75
+ padding: 24,
76
+ width: "100%",
77
+ maxWidth: 400,
78
+ shadowColor: "#000",
79
+ shadowOffset: { width: 0, height: 2 },
80
+ shadowOpacity: 0.25,
81
+ shadowRadius: 4,
82
+ elevation: 5,
83
+ },
84
+ title: {
85
+ fontSize: 18,
86
+ fontWeight: "600",
87
+ color: "#18181b",
88
+ marginBottom: 8,
89
+ },
90
+ description: {
91
+ fontSize: 14,
92
+ color: "#71717a",
93
+ lineHeight: 20,
94
+ marginBottom: 24,
95
+ },
96
+ footer: {
97
+ flexDirection: "row",
98
+ justifyContent: "flex-end",
99
+ gap: 12,
100
+ },
101
+ button: {
102
+ paddingHorizontal: 16,
103
+ paddingVertical: 10,
104
+ borderRadius: 6,
105
+ minWidth: 80,
106
+ alignItems: "center",
107
+ },
108
+ cancelButton: {
109
+ backgroundColor: "white",
110
+ borderWidth: 1,
111
+ borderColor: "#e4e4e7",
112
+ },
113
+ actionButton: {
114
+ backgroundColor: "#18181b",
115
+ },
116
+ cancelButtonText: {
117
+ fontSize: 14,
118
+ fontWeight: "500",
119
+ color: "#18181b",
120
+ },
121
+ actionButtonText: {
122
+ fontSize: 14,
123
+ fontWeight: "500",
124
+ color: "white",
125
+ },
126
+ });