react-native-varia 0.2.2 → 0.2.4
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/bin/cli.js +316 -139
- package/lib/components/Accordion.tsx +113 -0
- package/lib/components/Button.tsx +19 -8
- package/lib/components/CircleProgress.tsx +46 -28
- package/lib/components/Divider.tsx +18 -15
- package/lib/components/Drawer.tsx +496 -0
- package/lib/components/Field.tsx +24 -39
- package/lib/components/GradientBackground.tsx +25 -7
- package/lib/components/GradientText.tsx +38 -11
- package/lib/components/IconWrapper.tsx +20 -14
- package/lib/components/Input.tsx +106 -25
- package/lib/components/NumberInput.tsx +88 -19
- package/lib/components/OldSlider.tsx +327 -0
- package/lib/components/RadioGroup.tsx +55 -17
- package/lib/components/ReText.tsx +1 -1
- package/lib/components/Select.tsx +58 -22
- package/lib/components/Slider.tsx +176 -115
- package/lib/components/Slideshow.tsx +68 -69
- package/lib/components/SlidingDrawer.tsx +72 -29
- package/lib/components/Spinner.tsx +6 -2
- package/lib/components/Toast.tsx +89 -0
- package/lib/components/context/Field.tsx +27 -0
- package/lib/icons/Minus.tsx +24 -0
- package/lib/icons/Plus.tsx +23 -0
- package/lib/theme/Button.recipe.tsx +11 -1
- package/lib/theme/CircleProgress.recipe.tsx +3 -3
- package/lib/theme/Field.recipe.tsx +17 -2
- package/lib/theme/Input.recipe.tsx +12 -3
- package/lib/theme/NumberInput.recipe.tsx +9 -4
- package/lib/theme/RadioGroup.recipe.tsx +7 -1
- package/lib/theme/Select.recipe.tsx +7 -7
- package/lib/theme/Slider.recipe.tsx +366 -22
- package/lib/theme/Slideshow.recipe.tsx +1 -1
- package/lib/theme/SlidingDrawer.recipe.tsx +58 -4
- package/lib/theme/Toast.recipe.tsx +71 -0
- package/package.json +3 -2
- package/lib/components/NewSelect.tsx +0 -202
- package/lib/components/index.tsx +0 -83
- package/lib/components/layoutTest.tsx +0 -74
- package/lib/theme/Button.recipe-old.tsx +0 -67
package/bin/cli.js
CHANGED
|
@@ -1,32 +1,71 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const { program } = require("commander");
|
|
4
|
+
const inquirer = require("inquirer");
|
|
4
5
|
const path = require("path");
|
|
5
6
|
const fs = require("fs-extra");
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
const COMPONENT_DEPENDENCIES = {
|
|
9
|
+
Button: ["Spinner", "IconWrapper"],
|
|
10
|
+
Field: ["Text"],
|
|
11
|
+
Link: ["Text"],
|
|
12
|
+
Modal: ["Text"],
|
|
13
|
+
ReText: ["Text"],
|
|
14
|
+
Select: ["Text"],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const ICON_DEPENDENCIES = {
|
|
18
|
+
NumberInput: ["Plus", "Minus"],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const CONTEXT_DEPENDENCIES = {
|
|
22
|
+
Field: true
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function copyContextComponentIfNeeded(component, destComponents, overwrite = true) {
|
|
26
|
+
if (!CONTEXT_DEPENDENCIES[component]) return;
|
|
27
|
+
|
|
28
|
+
const contextSrc = path.join(COMPONENTS_DIR, "context", `${component}.tsx`);
|
|
29
|
+
const contextDest = path.join(process.cwd(), destComponents, "context", `${component}.tsx`);
|
|
30
|
+
copyFile(contextSrc, contextDest, `Context de "${component}"`, overwrite);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function resolveDependencies(component, seen = new Set()) {
|
|
34
|
+
if (seen.has(component)) return [];
|
|
35
|
+
seen.add(component);
|
|
36
|
+
|
|
37
|
+
const deps = COMPONENT_DEPENDENCIES[component] || [];
|
|
38
|
+
let allDeps = [...deps];
|
|
39
|
+
|
|
40
|
+
for (const dep of deps) {
|
|
41
|
+
allDeps = [...allDeps, ...resolveDependencies(dep, seen)];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return [...new Set(allDeps)];
|
|
45
|
+
}
|
|
46
|
+
|
|
8
47
|
const COMPONENTS_DIR = path.join(__dirname, "../lib/components");
|
|
9
48
|
const THEME_DIR = path.join(__dirname, "../lib/theme");
|
|
10
49
|
|
|
11
|
-
/**
|
|
12
|
-
* Obtiene la lista de todos los componentes disponibles en la librería.
|
|
13
|
-
*/
|
|
14
50
|
function getAvailableComponents() {
|
|
15
51
|
if (!fs.existsSync(COMPONENTS_DIR)) return [];
|
|
16
52
|
return fs.readdirSync(COMPONENTS_DIR).map((name) => path.parse(name).name);
|
|
17
53
|
}
|
|
18
54
|
|
|
19
|
-
|
|
20
|
-
* Copia un archivo desde origen a destino.
|
|
21
|
-
*/
|
|
22
|
-
function copyFile(srcPath, destPath, label) {
|
|
55
|
+
function copyFile(srcPath, destPath, label, overwrite = true) {
|
|
23
56
|
if (!fs.existsSync(srcPath)) {
|
|
24
57
|
console.warn(`⚠️ ${label} no encontrado: ${srcPath}`);
|
|
25
58
|
return false;
|
|
26
59
|
}
|
|
27
60
|
try {
|
|
28
|
-
fs.ensureDirSync(path.dirname(destPath));
|
|
29
|
-
|
|
61
|
+
fs.ensureDirSync(path.dirname(destPath));
|
|
62
|
+
|
|
63
|
+
if (!overwrite && fs.existsSync(destPath)) {
|
|
64
|
+
console.log(`⏭️ Saltando ${label}, ya existe en ${destPath}`);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
fs.copySync(srcPath, destPath, { overwrite: true });
|
|
30
69
|
console.log(`✅ ${label} copiado a: ${destPath}`);
|
|
31
70
|
return true;
|
|
32
71
|
} catch (err) {
|
|
@@ -35,17 +74,28 @@ function copyFile(srcPath, destPath, label) {
|
|
|
35
74
|
}
|
|
36
75
|
}
|
|
37
76
|
|
|
38
|
-
|
|
39
|
-
* Copia un componente y su recipe.
|
|
40
|
-
*/
|
|
41
|
-
function copyComponentAndRecipe(component, destComponents, destTheme) {
|
|
77
|
+
function copyComponentAndRecipe(component, destComponents, destTheme, overwrite = true) {
|
|
42
78
|
const componentSrc = path.join(COMPONENTS_DIR, `${component}.tsx`);
|
|
43
79
|
const componentDest = path.join(process.cwd(), destComponents, `${component}.tsx`);
|
|
44
|
-
copyFile(componentSrc, componentDest, `Componente "${component}"
|
|
80
|
+
copyFile(componentSrc, componentDest, `Componente "${component}"`, overwrite);
|
|
45
81
|
|
|
46
82
|
const recipeSrc = path.join(THEME_DIR, `${component}.recipe.tsx`);
|
|
47
83
|
const recipeDest = path.join(process.cwd(), destTheme, `${component}.recipe.tsx`);
|
|
48
|
-
copyFile(recipeSrc, recipeDest, `Recipe de "${component}"
|
|
84
|
+
copyFile(recipeSrc, recipeDest, `Recipe de "${component}"`, overwrite);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function confirmOverwrite(existingComponents) {
|
|
88
|
+
const { overwrite } = await inquirer.prompt([
|
|
89
|
+
{
|
|
90
|
+
type: "confirm",
|
|
91
|
+
name: "overwrite",
|
|
92
|
+
message: `Los siguientes componentes ya existen: ${existingComponents.join(
|
|
93
|
+
", "
|
|
94
|
+
)}\n¿Quieres sobrescribirlos?`,
|
|
95
|
+
default: false,
|
|
96
|
+
},
|
|
97
|
+
]);
|
|
98
|
+
return overwrite;
|
|
49
99
|
}
|
|
50
100
|
|
|
51
101
|
program
|
|
@@ -53,74 +103,141 @@ program
|
|
|
53
103
|
.description("CLI para instalar componentes de react-native-varia")
|
|
54
104
|
.version("1.0.0");
|
|
55
105
|
|
|
56
|
-
// program
|
|
57
|
-
// .command('setup')
|
|
58
|
-
// .description('Copia mixins y utils a src/styles')
|
|
59
|
-
// .action(() => {
|
|
60
|
-
// const sourceDir = path.join(__dirname, '../lib');
|
|
61
|
-
// const destDir = path.join(process.cwd(), 'src/style');
|
|
62
|
-
|
|
63
|
-
// const filesToCopy = ['mixins', 'utils', 'types'];
|
|
64
|
-
|
|
65
|
-
// filesToCopy.forEach((file) => {
|
|
66
|
-
// const srcPath = path.join(sourceDir, `${file}.ts`);
|
|
67
|
-
// const destPath = path.join(destDir, `${file}.ts`);
|
|
68
|
-
|
|
69
|
-
// try {
|
|
70
|
-
// fs.ensureDirSync(destDir); // Asegura que el directorio de destino exista
|
|
71
|
-
// fs.copySync(srcPath, destPath, { overwrite: true });
|
|
72
|
-
// console.log(`✅ ${file}.tsx copiado a ${destPath}`);
|
|
73
|
-
// } catch (err) {
|
|
74
|
-
// console.error(`❌ Error al copiar ${file}.tsx: ${err.message}`);
|
|
75
|
-
// }
|
|
76
|
-
// });
|
|
77
|
-
// });
|
|
78
|
-
|
|
79
106
|
program
|
|
80
|
-
.command(
|
|
81
|
-
.description(
|
|
107
|
+
.command("setup")
|
|
108
|
+
.description("Copia la carpeta varia y patterns al proyecto (src/style/varia y src/patterns)")
|
|
82
109
|
.action(() => {
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
|
|
110
|
+
const variaSrc = path.join(__dirname, "../lib/varia");
|
|
111
|
+
const variaDest = path.join(process.cwd(), "src/style/varia");
|
|
112
|
+
|
|
113
|
+
const patternsSrc = path.join(__dirname, "../lib/patterns");
|
|
114
|
+
const patternsDest = path.join(process.cwd(), "src/patterns");
|
|
86
115
|
|
|
87
116
|
try {
|
|
88
|
-
fs.ensureDirSync(
|
|
89
|
-
fs.copySync(
|
|
90
|
-
console.log(`✅ Carpeta "varia" copiada a ${
|
|
117
|
+
fs.ensureDirSync(path.dirname(variaDest));
|
|
118
|
+
fs.copySync(variaSrc, variaDest, { overwrite: true });
|
|
119
|
+
console.log(`✅ Carpeta "varia" copiada a ${variaDest}`);
|
|
120
|
+
|
|
121
|
+
fs.ensureDirSync(path.dirname(patternsDest));
|
|
122
|
+
fs.copySync(patternsSrc, patternsDest, { overwrite: true });
|
|
123
|
+
console.log(`✅ Carpeta "patterns" copiada a ${patternsDest}`);
|
|
91
124
|
} catch (err) {
|
|
92
|
-
console.error(`❌ Error
|
|
125
|
+
console.error(`❌ Error durante el setup: ${err.message}`);
|
|
93
126
|
}
|
|
94
127
|
});
|
|
95
128
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
.
|
|
129
|
+
|
|
130
|
+
program
|
|
131
|
+
.command("add-patterns")
|
|
132
|
+
.description("Copia la carpeta lib/patterns a src/patterns")
|
|
99
133
|
.action(() => {
|
|
100
|
-
const sourceDir = path.join(__dirname,
|
|
101
|
-
const destDir = path.join(process.cwd(),
|
|
134
|
+
const sourceDir = path.join(__dirname, "../lib/patterns");
|
|
135
|
+
const destDir = path.join(process.cwd(), "src/patterns");
|
|
102
136
|
|
|
103
137
|
try {
|
|
104
|
-
fs.ensureDirSync(destDir);
|
|
138
|
+
fs.ensureDirSync(destDir);
|
|
105
139
|
fs.copySync(sourceDir, destDir, { overwrite: true });
|
|
106
|
-
console.log(
|
|
140
|
+
console.log("✅ Carpeta patterns copiada a src/patterns");
|
|
107
141
|
} catch (err) {
|
|
108
142
|
console.error(`❌ Error al copiar patterns: ${err.message}`);
|
|
109
143
|
}
|
|
110
144
|
});
|
|
111
145
|
|
|
146
|
+
// program
|
|
147
|
+
// .command("add <components...>")
|
|
148
|
+
// .description("Copia uno o más componentes y sus recipes desde la librería a tu proyecto")
|
|
149
|
+
// .option("-d, --dest <path>", "Ruta de destino de componentes", "src/components")
|
|
150
|
+
// .option("-t, --theme <path>", "Ruta de destino de recipes", "src/theme")
|
|
151
|
+
// .action(async (components, options) => {
|
|
152
|
+
// const available = getAvailableComponents();
|
|
153
|
+
// const componentsCapitalized = components.map(
|
|
154
|
+
// (c) => c.charAt(0).toUpperCase() + c.slice(1)
|
|
155
|
+
// );
|
|
156
|
+
|
|
157
|
+
// // 🔹 Obtener dependencias totales
|
|
158
|
+
// const allComponents = new Set();
|
|
159
|
+
// const componentToDeps = {}; // para saber qué depende de qué
|
|
160
|
+
// for (const c of componentsCapitalized) {
|
|
161
|
+
// const deps = resolveDependencies(c);
|
|
162
|
+
// componentToDeps[c] = deps;
|
|
163
|
+
// allComponents.add(c);
|
|
164
|
+
// deps.forEach((d) => allComponents.add(d));
|
|
165
|
+
// }
|
|
166
|
+
|
|
167
|
+
// const allComponentsArray = Array.from(allComponents);
|
|
168
|
+
|
|
169
|
+
// // 🔹 Validar existencia
|
|
170
|
+
// const notFound = allComponentsArray.filter((c) => !available.includes(c));
|
|
171
|
+
// if (notFound.length > 0) {
|
|
172
|
+
// console.error(`❌ Los siguientes componentes no existen: ${notFound.join(", ")}`);
|
|
173
|
+
// console.log("\n📦 Componentes disponibles:");
|
|
174
|
+
// available.forEach((name) => console.log(` - ${name}`));
|
|
175
|
+
// process.exit(1);
|
|
176
|
+
// }
|
|
177
|
+
|
|
178
|
+
// // 🔹 Preparar instalación
|
|
179
|
+
// for (const mainComponent of componentsCapitalized) {
|
|
180
|
+
// const componentPath = path.join(process.cwd(), options.dest, `${mainComponent}.tsx`);
|
|
181
|
+
// let overwriteMain = true;
|
|
182
|
+
|
|
183
|
+
// // Preguntar solo si el componente principal existe
|
|
184
|
+
// if (fs.existsSync(componentPath)) {
|
|
185
|
+
// const { overwrite } = await inquirer.prompt([
|
|
186
|
+
// {
|
|
187
|
+
// type: "confirm",
|
|
188
|
+
// name: "overwrite",
|
|
189
|
+
// message: `El componente "${mainComponent}" ya existe. ¿Deseas sobrescribirlo?`,
|
|
190
|
+
// default: false,
|
|
191
|
+
// },
|
|
192
|
+
// ]);
|
|
193
|
+
|
|
194
|
+
// overwriteMain = overwrite;
|
|
195
|
+
// if (!overwriteMain) {
|
|
196
|
+
// console.log(`⏭️ Componente "${mainComponent}" no sobrescrito.`);
|
|
197
|
+
// }
|
|
198
|
+
// }
|
|
199
|
+
|
|
200
|
+
// // 🔹 Copiar componente principal
|
|
201
|
+
// copyComponentAndRecipe(mainComponent, options.dest, options.theme, overwriteMain);
|
|
202
|
+
|
|
203
|
+
// // 🔹 Copiar dependencias (sin sobrescribir si existen)
|
|
204
|
+
// const deps = componentToDeps[mainComponent] || [];
|
|
205
|
+
// for (const dep of deps) {
|
|
206
|
+
// const depPath = path.join(process.cwd(), options.dest, `${dep}.tsx`);
|
|
207
|
+
// const exists = fs.existsSync(depPath);
|
|
208
|
+
// if (exists) {
|
|
209
|
+
// console.log(`⏭️ Dependencia "${dep}" ya existe. No se sobrescribe.`);
|
|
210
|
+
// continue;
|
|
211
|
+
// }
|
|
212
|
+
// copyComponentAndRecipe(dep, options.dest, options.theme, true);
|
|
213
|
+
// }
|
|
214
|
+
// }
|
|
215
|
+
// });
|
|
216
|
+
|
|
112
217
|
program
|
|
113
218
|
.command("add <components...>")
|
|
114
219
|
.description("Copia uno o más componentes y sus recipes desde la librería a tu proyecto")
|
|
115
220
|
.option("-d, --dest <path>", "Ruta de destino de componentes", "src/components")
|
|
116
221
|
.option("-t, --theme <path>", "Ruta de destino de recipes", "src/theme")
|
|
117
|
-
.
|
|
222
|
+
.option("-i, --icons <path>", "Ruta de destino de íconos", "src/icons")
|
|
223
|
+
.action(async (components, options) => {
|
|
118
224
|
const available = getAvailableComponents();
|
|
119
225
|
const componentsCapitalized = components.map(
|
|
120
226
|
(c) => c.charAt(0).toUpperCase() + c.slice(1)
|
|
121
227
|
);
|
|
122
228
|
|
|
123
|
-
const
|
|
229
|
+
const allComponents = new Set();
|
|
230
|
+
const componentToDeps = {};
|
|
231
|
+
for (const c of componentsCapitalized) {
|
|
232
|
+
const deps = resolveDependencies(c);
|
|
233
|
+
componentToDeps[c] = deps;
|
|
234
|
+
allComponents.add(c);
|
|
235
|
+
deps.forEach((d) => allComponents.add(d));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const allComponentsArray = Array.from(allComponents);
|
|
239
|
+
|
|
240
|
+
const notFound = allComponentsArray.filter((c) => !available.includes(c));
|
|
124
241
|
if (notFound.length > 0) {
|
|
125
242
|
console.error(`❌ Los siguientes componentes no existen: ${notFound.join(", ")}`);
|
|
126
243
|
console.log("\n📦 Componentes disponibles:");
|
|
@@ -128,90 +245,150 @@ program
|
|
|
128
245
|
process.exit(1);
|
|
129
246
|
}
|
|
130
247
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
});
|
|
134
|
-
});
|
|
248
|
+
function copyContextComponentIfNeeded(component, destComponents, overwrite = true) {
|
|
249
|
+
if (!CONTEXT_DEPENDENCIES[component]) return;
|
|
135
250
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const destPath = path.join(process.cwd(), dest, `${iconName}.tsx`); // nombre final
|
|
140
|
-
|
|
141
|
-
if (!fs.existsSync(srcPath)) {
|
|
142
|
-
console.error(`❌ Plantilla de icono no encontrada en: ${srcPath}`);
|
|
143
|
-
return false;
|
|
251
|
+
const contextSrc = path.join(COMPONENTS_DIR, "context", `${component}.tsx`);
|
|
252
|
+
const contextDest = path.join(process.cwd(), destComponents, "context", `${component}.tsx`);
|
|
253
|
+
copyFile(contextSrc, contextDest, `Context de "${component}"`, overwrite);
|
|
144
254
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
let
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
255
|
+
|
|
256
|
+
for (const mainComponent of componentsCapitalized) {
|
|
257
|
+
const componentPath = path.join(process.cwd(), options.dest, `${mainComponent}.tsx`);
|
|
258
|
+
let overwriteMain = true;
|
|
259
|
+
|
|
260
|
+
if (fs.existsSync(componentPath)) {
|
|
261
|
+
const { overwrite } = await inquirer.prompt([
|
|
262
|
+
{
|
|
263
|
+
type: "confirm",
|
|
264
|
+
name: "overwrite",
|
|
265
|
+
message: `El componente "${mainComponent}" ya existe. ¿Deseas sobrescribirlo?`,
|
|
266
|
+
default: false,
|
|
267
|
+
},
|
|
268
|
+
]);
|
|
269
|
+
|
|
270
|
+
overwriteMain = overwrite;
|
|
271
|
+
if (!overwriteMain) {
|
|
272
|
+
console.log(`⏭️ Componente "${mainComponent}" no sobrescrito.`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
copyComponentAndRecipe(mainComponent, options.dest, options.theme, overwriteMain);
|
|
277
|
+
|
|
278
|
+
copyContextComponentIfNeeded(mainComponent, options.dest, overwriteMain);
|
|
279
|
+
|
|
280
|
+
const deps = componentToDeps[mainComponent] || [];
|
|
281
|
+
for (const dep of deps) {
|
|
282
|
+
const depPath = path.join(process.cwd(), options.dest, `${dep}.tsx`);
|
|
283
|
+
const exists = fs.existsSync(depPath);
|
|
284
|
+
if (exists) {
|
|
285
|
+
console.log(`⏭️ Dependencia "${dep}" ya existe. No se sobrescribe.`);
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
copyComponentAndRecipe(dep, options.dest, options.theme, true);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const iconDeps = ICON_DEPENDENCIES[mainComponent] || [];
|
|
292
|
+
if (iconDeps.length > 0) {
|
|
293
|
+
console.log(`🎨 Añadiendo íconos requeridos por "${mainComponent}": ${iconDeps.join(", ")}`);
|
|
294
|
+
|
|
295
|
+
for (const iconName of iconDeps) {
|
|
296
|
+
const destIconPath = path.join(process.cwd(), options.icons, `${iconName}.tsx`);
|
|
297
|
+
if (fs.existsSync(destIconPath)) {
|
|
298
|
+
console.log(`⏭️ Icono "${iconName}" ya existe. No se sobrescribe.`);
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
copyIconTemplate(iconName, options.icons);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
ensureIconWrapper(options.dest, options.theme);
|
|
305
|
+
}
|
|
161
306
|
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
function copyIconTemplate(iconName, dest) {
|
|
311
|
+
const srcPath = path.join(COMPONENTS_DIR, "Icon.tsx");
|
|
312
|
+
const destPath = path.join(process.cwd(), dest, `${iconName}.tsx`);
|
|
313
|
+
|
|
314
|
+
if (!fs.existsSync(srcPath)) {
|
|
315
|
+
console.error(`❌ Plantilla de icono no encontrada en: ${srcPath}`);
|
|
316
|
+
return false;
|
|
162
317
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
let content = fs.readFileSync(srcPath, "utf-8");
|
|
321
|
+
content = content.replace(/\bIconName\b/g, iconName);
|
|
322
|
+
|
|
323
|
+
fs.ensureDirSync(path.dirname(destPath));
|
|
324
|
+
fs.writeFileSync(destPath, content);
|
|
325
|
+
|
|
326
|
+
console.log(`✅ Icono "${iconName}" copiado a: ${destPath}`);
|
|
327
|
+
return true;
|
|
328
|
+
} catch (err) {
|
|
329
|
+
console.error(`❌ No se pudo copiar el icono "${iconName}": ${err.message}`);
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function ensureIconWrapper(destComponents, destTheme) {
|
|
335
|
+
const wrapperComponentDest = path.join(process.cwd(), destComponents, "IconWrapper.tsx");
|
|
336
|
+
const wrapperRecipeDest = path.join(process.cwd(), destTheme, "IconWrapper.recipe.tsx");
|
|
337
|
+
|
|
338
|
+
if (fs.existsSync(wrapperComponentDest)) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const wrapperComponentSrc = path.join(COMPONENTS_DIR, "IconWrapper.tsx");
|
|
343
|
+
const wrapperRecipeSrc = path.join(THEME_DIR, "IconWrapper.recipe.tsx");
|
|
344
|
+
|
|
345
|
+
if (fs.existsSync(wrapperComponentSrc)) {
|
|
346
|
+
fs.ensureDirSync(path.dirname(wrapperComponentDest));
|
|
347
|
+
fs.copySync(wrapperComponentSrc, wrapperComponentDest, { overwrite: true });
|
|
348
|
+
console.log(`✅ IconWrapper copiado a: ${wrapperComponentDest}`);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (fs.existsSync(wrapperRecipeSrc)) {
|
|
352
|
+
fs.ensureDirSync(path.dirname(wrapperRecipeDest));
|
|
353
|
+
fs.copySync(wrapperRecipeSrc, wrapperRecipeDest, { overwrite: true });
|
|
354
|
+
console.log(`✅ IconWrapper recipe copiado a: ${wrapperRecipeDest}`);
|
|
190
355
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
program
|
|
359
|
+
.command("add-icon [iconName]")
|
|
360
|
+
.description("Copia un icono basado en la plantilla Icon.tsx")
|
|
361
|
+
.option("-n, --name <iconName>", "Nombre del icono")
|
|
362
|
+
.option("-d, --dest <path>", "Ruta de destino de iconos", "src/icons")
|
|
363
|
+
.option("-c, --components <path>", "Ruta de destino de componentes", "src/components")
|
|
364
|
+
.option("-t, --theme <path>", "Ruta de destino de recipes", "src/theme")
|
|
365
|
+
.action(async (iconNameArg, options) => {
|
|
366
|
+
const rawName = iconNameArg || options.name;
|
|
367
|
+
if (!rawName) {
|
|
368
|
+
console.error("❌ Debes indicar el nombre del icono como argumento o con --name");
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const finalName = rawName.charAt(0).toUpperCase() + rawName.slice(1);
|
|
373
|
+
const destPath = path.join(process.cwd(), options.dest, `${finalName}.tsx`);
|
|
374
|
+
|
|
375
|
+
if (fs.existsSync(destPath)) {
|
|
376
|
+
const { overwrite } = await inquirer.prompt([
|
|
377
|
+
{
|
|
378
|
+
type: "confirm",
|
|
379
|
+
name: "overwrite",
|
|
380
|
+
message: `El icono "${finalName}" ya existe. ¿Quieres sobrescribirlo?`,
|
|
381
|
+
default: false,
|
|
382
|
+
},
|
|
383
|
+
]);
|
|
384
|
+
if (!overwrite) {
|
|
385
|
+
console.log("⏭️ Icono no sobrescrito.");
|
|
386
|
+
return;
|
|
205
387
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
ensureIconWrapper(options.components, options.theme);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
program.parse(process.argv);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
copyIconTemplate(finalName, options.dest);
|
|
391
|
+
ensureIconWrapper(options.components, options.theme);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import React, {ReactNode, useState} from 'react'
|
|
2
|
+
import {View, Pressable} from 'react-native'
|
|
3
|
+
import Animated from 'react-native-reanimated'
|
|
4
|
+
import {LinearTransition} from 'react-native-reanimated'
|
|
5
|
+
import {StyleSheet} from 'react-native-unistyles'
|
|
6
|
+
import Text from './Text'
|
|
7
|
+
|
|
8
|
+
type AccordionItemProps = {
|
|
9
|
+
title: string
|
|
10
|
+
itemKey: string
|
|
11
|
+
children: ReactNode
|
|
12
|
+
isOpen?: boolean
|
|
13
|
+
onToggle?: () => void
|
|
14
|
+
scrollViewRef?: any
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type AccordionGroupRootProps = {
|
|
18
|
+
children: ReactNode
|
|
19
|
+
defaultOpenKeys?: string[]
|
|
20
|
+
allowMultiple?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const AccordionGroup = {
|
|
24
|
+
Root: AccordionGroupRoot,
|
|
25
|
+
Item: AccordionGroupItem,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function AccordionGroupRoot({
|
|
29
|
+
children,
|
|
30
|
+
defaultOpenKeys = [],
|
|
31
|
+
allowMultiple = false,
|
|
32
|
+
}: AccordionGroupRootProps) {
|
|
33
|
+
const [openKeys, setOpenKeys] = useState<Set<string>>(
|
|
34
|
+
() => new Set(defaultOpenKeys),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
const toggleItem = (key: string) => {
|
|
38
|
+
setOpenKeys(prev => {
|
|
39
|
+
const newSet = new Set(prev)
|
|
40
|
+
if (newSet.has(key)) {
|
|
41
|
+
newSet.delete(key)
|
|
42
|
+
} else {
|
|
43
|
+
if (allowMultiple) {
|
|
44
|
+
newSet.add(key)
|
|
45
|
+
} else {
|
|
46
|
+
return new Set([key])
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return newSet
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const items = React.Children.map(children, child => {
|
|
54
|
+
if (!React.isValidElement(child)) return child
|
|
55
|
+
const cp = child.props as AccordionItemProps
|
|
56
|
+
const key = cp.itemKey
|
|
57
|
+
const isOpen = openKeys.has(key)
|
|
58
|
+
return React.cloneElement(child, {
|
|
59
|
+
isOpen,
|
|
60
|
+
onToggle: () => toggleItem(key),
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
return <View style={styles.groupContainer}>{items}</View>
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function AccordionGroupItem({
|
|
68
|
+
title,
|
|
69
|
+
children,
|
|
70
|
+
isOpen = false,
|
|
71
|
+
onToggle,
|
|
72
|
+
}: AccordionItemProps) {
|
|
73
|
+
return (
|
|
74
|
+
<Animated.View
|
|
75
|
+
// Aquí aplicas la transición de layout
|
|
76
|
+
layout={LinearTransition.duration(150)}
|
|
77
|
+
style={styles.itemContainer}>
|
|
78
|
+
<Pressable onPress={onToggle} style={styles.header}>
|
|
79
|
+
<Text style={styles.headerText}>{title}</Text>
|
|
80
|
+
</Pressable>
|
|
81
|
+
|
|
82
|
+
{isOpen && (
|
|
83
|
+
<View style={styles.contentContainer}>
|
|
84
|
+
<View style={styles.innerContent}>{children}</View>
|
|
85
|
+
</View>
|
|
86
|
+
)}
|
|
87
|
+
</Animated.View>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const styles = StyleSheet.create({
|
|
92
|
+
groupContainer: {},
|
|
93
|
+
itemContainer: {
|
|
94
|
+
borderWidth: 1,
|
|
95
|
+
borderColor: '#ddd',
|
|
96
|
+
borderRadius: 6,
|
|
97
|
+
marginVertical: 8,
|
|
98
|
+
overflow: 'hidden', // importante para que no se vea contenido fuera
|
|
99
|
+
},
|
|
100
|
+
header: {
|
|
101
|
+
padding: 12,
|
|
102
|
+
backgroundColor: '#f0f0f0',
|
|
103
|
+
},
|
|
104
|
+
headerText: {
|
|
105
|
+
fontSize: 16,
|
|
106
|
+
},
|
|
107
|
+
contentContainer: {
|
|
108
|
+
// no necesitamos animar altura manual, lo hace el layout
|
|
109
|
+
},
|
|
110
|
+
innerContent: {
|
|
111
|
+
padding: 12,
|
|
112
|
+
},
|
|
113
|
+
})
|