create-rn-folder-structure 1.0.2 → 1.0.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/generators/components-advanced.js +221 -0
- package/generators/components-basic.js +243 -0
- package/generators/components-intermediate.js +290 -0
- package/generators/components-upload.js +181 -0
- package/generators/components.js +659 -0
- package/generators/constants.js +54 -0
- package/generators/features.js +51 -0
- package/generators/helpers.js +195 -0
- package/generators/hooks.js +239 -0
- package/generators/navigation.js +138 -0
- package/generators/readme.js +51 -0
- package/generators/shared.js +56 -0
- package/generators/storage.js +175 -0
- package/generators/store.js +181 -0
- package/generators/utils.js +202 -0
- package/package.json +24 -5
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
function write(root, filePath, content) {
|
|
5
|
+
const fullPath = path.join(root, filePath);
|
|
6
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
7
|
+
fs.writeFileSync(fullPath, content);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
module.exports = function generateComponents(projectRoot) {
|
|
11
|
+
console.log("🎨 Creating Shared UI Components...");
|
|
12
|
+
|
|
13
|
+
const base = "src/shared/components";
|
|
14
|
+
|
|
15
|
+
// -----------------------------------------------------
|
|
16
|
+
// BUTTON
|
|
17
|
+
// -----------------------------------------------------
|
|
18
|
+
write(
|
|
19
|
+
projectRoot,
|
|
20
|
+
`${base}/Button.tsx`,
|
|
21
|
+
`
|
|
22
|
+
import React from "react";
|
|
23
|
+
import { TouchableOpacity, Text, StyleSheet } from "react-native";
|
|
24
|
+
|
|
25
|
+
export default function Button({ title, onPress, style = {}, textStyle = {} }) {
|
|
26
|
+
return (
|
|
27
|
+
<TouchableOpacity style={[styles.button, style]} onPress={onPress}>
|
|
28
|
+
<Text style={[styles.text, textStyle]}>{title}</Text>
|
|
29
|
+
</TouchableOpacity>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const styles = StyleSheet.create({
|
|
34
|
+
button: {
|
|
35
|
+
backgroundColor: "#1e90ff",
|
|
36
|
+
padding: 14,
|
|
37
|
+
borderRadius: 8,
|
|
38
|
+
alignItems: "center",
|
|
39
|
+
},
|
|
40
|
+
text: {
|
|
41
|
+
color: "#fff",
|
|
42
|
+
fontWeight: "600",
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
`
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// -----------------------------------------------------
|
|
49
|
+
// CARD
|
|
50
|
+
// -----------------------------------------------------
|
|
51
|
+
write(
|
|
52
|
+
projectRoot,
|
|
53
|
+
`${base}/Card.tsx`,
|
|
54
|
+
`
|
|
55
|
+
import React from "react";
|
|
56
|
+
import { View, StyleSheet } from "react-native";
|
|
57
|
+
|
|
58
|
+
export default function Card({ children, style = {} }) {
|
|
59
|
+
return <View style={[styles.card, style]}>{children}</View>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const styles = StyleSheet.create({
|
|
63
|
+
card: {
|
|
64
|
+
padding: 16,
|
|
65
|
+
borderRadius: 10,
|
|
66
|
+
backgroundColor: "#fff",
|
|
67
|
+
elevation: 4,
|
|
68
|
+
shadowColor: "#000",
|
|
69
|
+
shadowOpacity: 0.1,
|
|
70
|
+
shadowRadius: 6,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
`
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// -----------------------------------------------------
|
|
77
|
+
// RADIO BUTTON
|
|
78
|
+
// -----------------------------------------------------
|
|
79
|
+
write(
|
|
80
|
+
projectRoot,
|
|
81
|
+
`${base}/RadioButton.tsx`,
|
|
82
|
+
`
|
|
83
|
+
import React from "react";
|
|
84
|
+
import { TouchableOpacity, View, StyleSheet } from "react-native";
|
|
85
|
+
|
|
86
|
+
export default function RadioButton({ selected, onPress }) {
|
|
87
|
+
return (
|
|
88
|
+
<TouchableOpacity onPress={onPress} style={styles.outer}>
|
|
89
|
+
{selected && <View style={styles.inner} />}
|
|
90
|
+
</TouchableOpacity>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const styles = StyleSheet.create({
|
|
95
|
+
outer: {
|
|
96
|
+
width: 22,
|
|
97
|
+
height: 22,
|
|
98
|
+
borderRadius: 11,
|
|
99
|
+
borderWidth: 2,
|
|
100
|
+
borderColor: "#1e90ff",
|
|
101
|
+
justifyContent: "center",
|
|
102
|
+
alignItems: "center",
|
|
103
|
+
},
|
|
104
|
+
inner: {
|
|
105
|
+
width: 12,
|
|
106
|
+
height: 12,
|
|
107
|
+
borderRadius: 6,
|
|
108
|
+
backgroundColor: "#1e90ff",
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
`
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// -----------------------------------------------------
|
|
115
|
+
// SWITCH (Simple)
|
|
116
|
+
// -----------------------------------------------------
|
|
117
|
+
write(
|
|
118
|
+
projectRoot,
|
|
119
|
+
`${base}/Switch.tsx`,
|
|
120
|
+
`
|
|
121
|
+
import React from "react";
|
|
122
|
+
import { Pressable, View, StyleSheet } from "react-native";
|
|
123
|
+
|
|
124
|
+
export default function Switch({ value, onToggle }) {
|
|
125
|
+
return (
|
|
126
|
+
<Pressable onPress={onToggle} style={[styles.container, value && styles.active]}>
|
|
127
|
+
<View style={[styles.circle, value && styles.circleActive]} />
|
|
128
|
+
</Pressable>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const styles = StyleSheet.create({
|
|
133
|
+
container: {
|
|
134
|
+
width: 50,
|
|
135
|
+
height: 28,
|
|
136
|
+
borderRadius: 14,
|
|
137
|
+
backgroundColor: "#ccc",
|
|
138
|
+
justifyContent: "center",
|
|
139
|
+
},
|
|
140
|
+
active: {
|
|
141
|
+
backgroundColor: "#1e90ff",
|
|
142
|
+
},
|
|
143
|
+
circle: {
|
|
144
|
+
width: 20,
|
|
145
|
+
height: 20,
|
|
146
|
+
backgroundColor: "#fff",
|
|
147
|
+
borderRadius: 10,
|
|
148
|
+
marginLeft: 4,
|
|
149
|
+
},
|
|
150
|
+
circleActive: {
|
|
151
|
+
marginLeft: 26,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
`
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// -----------------------------------------------------
|
|
158
|
+
// MODAL
|
|
159
|
+
// -----------------------------------------------------
|
|
160
|
+
write(
|
|
161
|
+
projectRoot,
|
|
162
|
+
`${base}/Modal.tsx`,
|
|
163
|
+
`
|
|
164
|
+
import React from "react";
|
|
165
|
+
import { Modal, View, StyleSheet } from "react-native";
|
|
166
|
+
|
|
167
|
+
export default function CustomModal({ visible, children }) {
|
|
168
|
+
return (
|
|
169
|
+
<Modal visible={visible} transparent animationType="fade">
|
|
170
|
+
<View style={styles.center}>
|
|
171
|
+
<View style={styles.box}>{children}</View>
|
|
172
|
+
</View>
|
|
173
|
+
</Modal>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const styles = StyleSheet.create({
|
|
178
|
+
center: {
|
|
179
|
+
flex: 1,
|
|
180
|
+
justifyContent: "center",
|
|
181
|
+
alignItems: "center",
|
|
182
|
+
backgroundColor: "rgba(0,0,0,0.4)",
|
|
183
|
+
},
|
|
184
|
+
box: {
|
|
185
|
+
width: "80%",
|
|
186
|
+
padding: 20,
|
|
187
|
+
backgroundColor: "#fff",
|
|
188
|
+
borderRadius: 10,
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
`
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
// -----------------------------------------------------
|
|
195
|
+
// LINE SEPARATOR
|
|
196
|
+
// -----------------------------------------------------
|
|
197
|
+
write(
|
|
198
|
+
projectRoot,
|
|
199
|
+
`${base}/LineSeparator.tsx`,
|
|
200
|
+
`
|
|
201
|
+
import React from "react";
|
|
202
|
+
import { View } from "react-native";
|
|
203
|
+
|
|
204
|
+
export default function LineSeparator() {
|
|
205
|
+
return <View style={{ height: 1, backgroundColor: "#ddd", marginVertical: 10 }} />;
|
|
206
|
+
}
|
|
207
|
+
`
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
// -----------------------------------------------------
|
|
211
|
+
// TEXT INPUT
|
|
212
|
+
// -----------------------------------------------------
|
|
213
|
+
write(
|
|
214
|
+
projectRoot,
|
|
215
|
+
`${base}/TextInput.tsx`,
|
|
216
|
+
`
|
|
217
|
+
import React from "react";
|
|
218
|
+
import { TextInput as RNInput, StyleSheet } from "react-native";
|
|
219
|
+
|
|
220
|
+
export default function TextInput({ style = {}, ...rest }) {
|
|
221
|
+
return <RNInput style={[styles.input, style]} {...rest} />;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const styles = StyleSheet.create({
|
|
225
|
+
input: {
|
|
226
|
+
borderWidth: 1,
|
|
227
|
+
borderColor: "#bbb",
|
|
228
|
+
padding: 12,
|
|
229
|
+
borderRadius: 8,
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
`
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
// -----------------------------------------------------
|
|
236
|
+
// CHECKBOX
|
|
237
|
+
// -----------------------------------------------------
|
|
238
|
+
write(
|
|
239
|
+
projectRoot,
|
|
240
|
+
`${base}/Checkbox.tsx`,
|
|
241
|
+
`
|
|
242
|
+
import React from "react";
|
|
243
|
+
import { TouchableOpacity, View, StyleSheet } from "react-native";
|
|
244
|
+
|
|
245
|
+
export default function Checkbox({ checked, onPress }) {
|
|
246
|
+
return (
|
|
247
|
+
<TouchableOpacity onPress={onPress} style={styles.box}>
|
|
248
|
+
{checked && <View style={styles.inner} />}
|
|
249
|
+
</TouchableOpacity>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const styles = StyleSheet.create({
|
|
254
|
+
box: {
|
|
255
|
+
width: 22,
|
|
256
|
+
height: 22,
|
|
257
|
+
borderWidth: 2,
|
|
258
|
+
borderColor: "#1e90ff",
|
|
259
|
+
borderRadius: 4,
|
|
260
|
+
justifyContent: "center",
|
|
261
|
+
alignItems: "center",
|
|
262
|
+
},
|
|
263
|
+
inner: {
|
|
264
|
+
width: 12,
|
|
265
|
+
height: 12,
|
|
266
|
+
backgroundColor: "#1e90ff",
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
`
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
// -----------------------------------------------------
|
|
273
|
+
// DROPDOWN (Basic)
|
|
274
|
+
// -----------------------------------------------------
|
|
275
|
+
write(
|
|
276
|
+
projectRoot,
|
|
277
|
+
`${base}/Dropdown.tsx`,
|
|
278
|
+
`
|
|
279
|
+
import React, { useState } from "react";
|
|
280
|
+
import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
|
|
281
|
+
|
|
282
|
+
export default function Dropdown({ options = [], selected, onSelect }) {
|
|
283
|
+
const [open, setOpen] = useState(false);
|
|
284
|
+
|
|
285
|
+
return (
|
|
286
|
+
<View style={styles.container}>
|
|
287
|
+
<TouchableOpacity style={styles.box} onPress={() => setOpen(!open)}>
|
|
288
|
+
<Text>{selected || "Select option"}</Text>
|
|
289
|
+
</TouchableOpacity>
|
|
290
|
+
|
|
291
|
+
{open &&
|
|
292
|
+
options.map((opt) => (
|
|
293
|
+
<TouchableOpacity
|
|
294
|
+
key={opt}
|
|
295
|
+
style={styles.option}
|
|
296
|
+
onPress={() => {
|
|
297
|
+
onSelect(opt);
|
|
298
|
+
setOpen(false);
|
|
299
|
+
}}
|
|
300
|
+
>
|
|
301
|
+
<Text>{opt}</Text>
|
|
302
|
+
</TouchableOpacity>
|
|
303
|
+
))}
|
|
304
|
+
</View>
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const styles = StyleSheet.create({
|
|
309
|
+
container: {},
|
|
310
|
+
box: {
|
|
311
|
+
padding: 12,
|
|
312
|
+
borderWidth: 1,
|
|
313
|
+
borderColor: "#bbb",
|
|
314
|
+
borderRadius: 8,
|
|
315
|
+
},
|
|
316
|
+
option: {
|
|
317
|
+
padding: 12,
|
|
318
|
+
backgroundColor: "#eee",
|
|
319
|
+
borderBottomWidth: 1,
|
|
320
|
+
borderColor: "#ccc",
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
`
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
// -----------------------------------------------------
|
|
327
|
+
// SEARCH BAR
|
|
328
|
+
// -----------------------------------------------------
|
|
329
|
+
write(
|
|
330
|
+
projectRoot,
|
|
331
|
+
`${base}/SearchBar.tsx`,
|
|
332
|
+
`
|
|
333
|
+
import React from "react";
|
|
334
|
+
import { TextInput, StyleSheet } from "react-native";
|
|
335
|
+
|
|
336
|
+
export default function SearchBar({ ...props }) {
|
|
337
|
+
return <TextInput style={styles.input} placeholder="Search..." {...props} />;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const styles = StyleSheet.create({
|
|
341
|
+
input: {
|
|
342
|
+
backgroundColor: "#f0f0f0",
|
|
343
|
+
padding: 12,
|
|
344
|
+
borderRadius: 8,
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
`
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
// -----------------------------------------------------
|
|
351
|
+
// FLATLIST CARD
|
|
352
|
+
// -----------------------------------------------------
|
|
353
|
+
write(
|
|
354
|
+
projectRoot,
|
|
355
|
+
`${base}/FlatListCard.tsx`,
|
|
356
|
+
`
|
|
357
|
+
import React from "react";
|
|
358
|
+
import { View, Text, StyleSheet } from "react-native";
|
|
359
|
+
|
|
360
|
+
export default function FlatListCard({ title, subtitle }) {
|
|
361
|
+
return (
|
|
362
|
+
<View style={styles.card}>
|
|
363
|
+
<Text style={styles.title}>{title}</Text>
|
|
364
|
+
{subtitle && <Text style={styles.subtitle}>{subtitle}</Text>}
|
|
365
|
+
</View>
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const styles = StyleSheet.create({
|
|
370
|
+
card: {
|
|
371
|
+
padding: 16,
|
|
372
|
+
borderBottomWidth: 1,
|
|
373
|
+
borderColor: "#ddd",
|
|
374
|
+
},
|
|
375
|
+
title: { fontSize: 16, fontWeight: "600" },
|
|
376
|
+
subtitle: { color: "#666", marginTop: 4 },
|
|
377
|
+
});
|
|
378
|
+
`
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
// -----------------------------------------------------
|
|
382
|
+
// ALERT MESSAGE
|
|
383
|
+
// -----------------------------------------------------
|
|
384
|
+
write(
|
|
385
|
+
projectRoot,
|
|
386
|
+
`${base}/AlertMessage.tsx`,
|
|
387
|
+
`
|
|
388
|
+
import React from "react";
|
|
389
|
+
import { View, Text, StyleSheet } from "react-native";
|
|
390
|
+
|
|
391
|
+
export default function AlertMessage({ message, type = "error" }) {
|
|
392
|
+
return (
|
|
393
|
+
<View style={[styles.alert, type === "success" && styles.success]}>
|
|
394
|
+
<Text style={styles.text}>{message}</Text>
|
|
395
|
+
</View>
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const styles = StyleSheet.create({
|
|
400
|
+
alert: {
|
|
401
|
+
padding: 12,
|
|
402
|
+
backgroundColor: "#ffcccc",
|
|
403
|
+
borderLeftWidth: 4,
|
|
404
|
+
borderColor: "red",
|
|
405
|
+
marginVertical: 8,
|
|
406
|
+
},
|
|
407
|
+
success: {
|
|
408
|
+
backgroundColor: "#ccffcc",
|
|
409
|
+
borderColor: "green",
|
|
410
|
+
},
|
|
411
|
+
text: { color: "#333" },
|
|
412
|
+
});
|
|
413
|
+
`
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
// -----------------------------------------------------
|
|
417
|
+
// TOAST MESSAGE
|
|
418
|
+
// -----------------------------------------------------
|
|
419
|
+
write(
|
|
420
|
+
projectRoot,
|
|
421
|
+
`${base}/Toast.tsx`,
|
|
422
|
+
`
|
|
423
|
+
import React, { useEffect, useState } from "react";
|
|
424
|
+
import { View, Text, StyleSheet, Animated } from "react-native";
|
|
425
|
+
|
|
426
|
+
export default function Toast({ message, visible }) {
|
|
427
|
+
const [fade] = useState(new Animated.Value(0));
|
|
428
|
+
|
|
429
|
+
useEffect(() => {
|
|
430
|
+
if (visible) {
|
|
431
|
+
Animated.timing(fade, { toValue: 1, duration: 200, useNativeDriver: true }).start();
|
|
432
|
+
setTimeout(() => {
|
|
433
|
+
Animated.timing(fade, { toValue: 0, duration: 200, useNativeDriver: true }).start();
|
|
434
|
+
}, 2000);
|
|
435
|
+
}
|
|
436
|
+
}, [visible]);
|
|
437
|
+
|
|
438
|
+
if (!visible) return null;
|
|
439
|
+
|
|
440
|
+
return (
|
|
441
|
+
<Animated.View style={[styles.toast, { opacity: fade }]}>
|
|
442
|
+
<Text style={styles.text}>{message}</Text>
|
|
443
|
+
</Animated.View>
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const styles = StyleSheet.create({
|
|
448
|
+
toast: {
|
|
449
|
+
position: "absolute",
|
|
450
|
+
bottom: 40,
|
|
451
|
+
alignSelf: "center",
|
|
452
|
+
backgroundColor: "#333",
|
|
453
|
+
padding: 12,
|
|
454
|
+
borderRadius: 8,
|
|
455
|
+
},
|
|
456
|
+
text: { color: "#fff" },
|
|
457
|
+
});
|
|
458
|
+
`
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
// -----------------------------------------------------
|
|
462
|
+
// SIMPLE LOADER
|
|
463
|
+
// -----------------------------------------------------
|
|
464
|
+
write(
|
|
465
|
+
projectRoot,
|
|
466
|
+
`${base}/Loader.tsx`,
|
|
467
|
+
`
|
|
468
|
+
import React from "react";
|
|
469
|
+
import { ActivityIndicator, View } from "react-native";
|
|
470
|
+
|
|
471
|
+
export default function Loader() {
|
|
472
|
+
return (
|
|
473
|
+
<View style={{ padding: 20 }}>
|
|
474
|
+
<ActivityIndicator size="large" />
|
|
475
|
+
</View>
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
`
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
// -----------------------------------------------------
|
|
482
|
+
// SHIMMER LOADER
|
|
483
|
+
// -----------------------------------------------------
|
|
484
|
+
write(
|
|
485
|
+
projectRoot,
|
|
486
|
+
`${base}/Shimmer.tsx`,
|
|
487
|
+
`
|
|
488
|
+
import React, { useRef, useEffect } from "react";
|
|
489
|
+
import { View, Animated, StyleSheet } from "react-native";
|
|
490
|
+
|
|
491
|
+
export default function Shimmer({ width = "100%", height = 20 }) {
|
|
492
|
+
const shimmer = useRef(new Animated.Value(-1)).current;
|
|
493
|
+
|
|
494
|
+
useEffect(() => {
|
|
495
|
+
Animated.loop(
|
|
496
|
+
Animated.timing(shimmer, {
|
|
497
|
+
toValue: 1,
|
|
498
|
+
duration: 1000,
|
|
499
|
+
useNativeDriver: true,
|
|
500
|
+
})
|
|
501
|
+
).start();
|
|
502
|
+
}, []);
|
|
503
|
+
|
|
504
|
+
return (
|
|
505
|
+
<View style={[styles.container, { width, height }]}>
|
|
506
|
+
<Animated.View
|
|
507
|
+
style={[
|
|
508
|
+
styles.shimmer,
|
|
509
|
+
{
|
|
510
|
+
transform: [
|
|
511
|
+
{
|
|
512
|
+
translateX: shimmer.interpolate({
|
|
513
|
+
inputRange: [-1, 1],
|
|
514
|
+
outputRange: [-200, 200],
|
|
515
|
+
}),
|
|
516
|
+
},
|
|
517
|
+
],
|
|
518
|
+
},
|
|
519
|
+
]}
|
|
520
|
+
/>
|
|
521
|
+
</View>
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const styles = StyleSheet.create({
|
|
526
|
+
container: {
|
|
527
|
+
backgroundColor: "#e0e0e0",
|
|
528
|
+
overflow: "hidden",
|
|
529
|
+
borderRadius: 6,
|
|
530
|
+
},
|
|
531
|
+
shimmer: {
|
|
532
|
+
width: "40%",
|
|
533
|
+
height: "100%",
|
|
534
|
+
backgroundColor: "#ccc",
|
|
535
|
+
opacity: 0.6,
|
|
536
|
+
},
|
|
537
|
+
});
|
|
538
|
+
`
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
// -----------------------------------------------------
|
|
542
|
+
// PROGRESS BAR
|
|
543
|
+
// -----------------------------------------------------
|
|
544
|
+
write(
|
|
545
|
+
projectRoot,
|
|
546
|
+
`${base}/ProgressBar.tsx`,
|
|
547
|
+
`
|
|
548
|
+
import React from "react";
|
|
549
|
+
import { View, StyleSheet } from "react-native";
|
|
550
|
+
|
|
551
|
+
export default function ProgressBar({ progress = 0 }) {
|
|
552
|
+
return (
|
|
553
|
+
<View style={styles.container}>
|
|
554
|
+
<View style={[styles.bar, { width: \`\${progress}%\` }]} />
|
|
555
|
+
</View>
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const styles = StyleSheet.create({
|
|
560
|
+
container: {
|
|
561
|
+
height: 10,
|
|
562
|
+
borderRadius: 5,
|
|
563
|
+
overflow: "hidden",
|
|
564
|
+
backgroundColor: "#ddd",
|
|
565
|
+
},
|
|
566
|
+
bar: {
|
|
567
|
+
height: "100%",
|
|
568
|
+
backgroundColor: "#1e90ff",
|
|
569
|
+
},
|
|
570
|
+
});
|
|
571
|
+
`
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
// -----------------------------------------------------
|
|
575
|
+
// FILE UPLOAD (DocumentPicker)
|
|
576
|
+
// -----------------------------------------------------
|
|
577
|
+
write(
|
|
578
|
+
projectRoot,
|
|
579
|
+
`${base}/FileUpload.tsx`,
|
|
580
|
+
`
|
|
581
|
+
import React from "react";
|
|
582
|
+
import { TouchableOpacity, Text } from "react-native";
|
|
583
|
+
import DocumentPicker from "react-native-document-picker";
|
|
584
|
+
|
|
585
|
+
export default function FileUpload({ onSelect }) {
|
|
586
|
+
const pick = async () => {
|
|
587
|
+
try {
|
|
588
|
+
const res = await DocumentPicker.pickSingle();
|
|
589
|
+
onSelect(res);
|
|
590
|
+
} catch {}
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
return (
|
|
594
|
+
<TouchableOpacity onPress={pick} style={{ padding: 12, backgroundColor: "#eee" }}>
|
|
595
|
+
<Text>Select File</Text>
|
|
596
|
+
</TouchableOpacity>
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
`
|
|
600
|
+
);
|
|
601
|
+
|
|
602
|
+
// -----------------------------------------------------
|
|
603
|
+
// IMAGE UPLOAD
|
|
604
|
+
// -----------------------------------------------------
|
|
605
|
+
write(
|
|
606
|
+
projectRoot,
|
|
607
|
+
`${base}/ImageUpload.tsx`,
|
|
608
|
+
`
|
|
609
|
+
import React from "react";
|
|
610
|
+
import { TouchableOpacity, Text } from "react-native";
|
|
611
|
+
import { launchImageLibrary } from "react-native-image-picker";
|
|
612
|
+
|
|
613
|
+
export default function ImageUpload({ onSelect }) {
|
|
614
|
+
const pick = async () => {
|
|
615
|
+
const result = await launchImageLibrary({ mediaType: "photo" });
|
|
616
|
+
if (result.assets && result.assets.length > 0) {
|
|
617
|
+
onSelect(result.assets[0]);
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
return (
|
|
622
|
+
<TouchableOpacity onPress={pick} style={{ padding: 12, backgroundColor: "#eee" }}>
|
|
623
|
+
<Text>Select Image</Text>
|
|
624
|
+
</TouchableOpacity>
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
`
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
// -----------------------------------------------------
|
|
631
|
+
// VIDEO UPLOAD
|
|
632
|
+
// -----------------------------------------------------
|
|
633
|
+
write(
|
|
634
|
+
projectRoot,
|
|
635
|
+
`${base}/VideoUpload.tsx`,
|
|
636
|
+
`
|
|
637
|
+
import React from "react";
|
|
638
|
+
import { TouchableOpacity, Text } from "react-native";
|
|
639
|
+
import { launchImageLibrary } from "react-native-image-picker";
|
|
640
|
+
|
|
641
|
+
export default function VideoUpload({ onSelect }) {
|
|
642
|
+
const pick = async () => {
|
|
643
|
+
const result = await launchImageLibrary({ mediaType: "video" });
|
|
644
|
+
if (result.assets && result.assets.length > 0) {
|
|
645
|
+
onSelect(result.assets[0]);
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
return (
|
|
650
|
+
<TouchableOpacity onPress={pick} style={{ padding: 12, backgroundColor: "#eee" }}>
|
|
651
|
+
<Text>Select Video</Text>
|
|
652
|
+
</TouchableOpacity>
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
`
|
|
656
|
+
);
|
|
657
|
+
|
|
658
|
+
console.log("✅ Shared UI Components Created Successfully!");
|
|
659
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
function write(root, filePath, content) {
|
|
5
|
+
const fullPath = path.join(root, filePath);
|
|
6
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
7
|
+
fs.writeFileSync(fullPath, content);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
module.exports = function generateConstants(projectRoot) {
|
|
11
|
+
console.log("🔧 Creating constants...");
|
|
12
|
+
|
|
13
|
+
const base = "src/shared/constants";
|
|
14
|
+
|
|
15
|
+
write(
|
|
16
|
+
projectRoot,
|
|
17
|
+
`${base}/colors.ts`,
|
|
18
|
+
`
|
|
19
|
+
export const Colors = {
|
|
20
|
+
primary: "#007AFF",
|
|
21
|
+
secondary: "#FF9500",
|
|
22
|
+
danger: "#FF3B30",
|
|
23
|
+
success: "#34C759",
|
|
24
|
+
background: "#F2F2F7",
|
|
25
|
+
};
|
|
26
|
+
`
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
write(
|
|
30
|
+
projectRoot,
|
|
31
|
+
`${base}/fonts.ts`,
|
|
32
|
+
`
|
|
33
|
+
export const Fonts = {
|
|
34
|
+
regular: "System",
|
|
35
|
+
medium: "System-Medium",
|
|
36
|
+
bold: "System-Bold"
|
|
37
|
+
}
|
|
38
|
+
`
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
write(
|
|
42
|
+
projectRoot,
|
|
43
|
+
`${base}/sizes.ts`,
|
|
44
|
+
`
|
|
45
|
+
export const Sizes = {
|
|
46
|
+
padding: 16,
|
|
47
|
+
radius: 8,
|
|
48
|
+
margin: 12,
|
|
49
|
+
};
|
|
50
|
+
`
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
console.log("✅ Constants created successfully!");
|
|
54
|
+
};
|