create-bdpa-react-scaffold 0.1.1

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 (3) hide show
  1. package/README.md +85 -0
  2. package/create-ui-lib.js +714 -0
  3. package/package.json +38 -0
package/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # create-bdpa-react-scaffold
2
+
3
+ Scaffold a ready-to-run React + Tailwind UI library demo built with Vite. This CLI creates a project with common UI components and a demo app.
4
+
5
+ ## Quick Start
6
+
7
+ Run with npx (no global install needed):
8
+
9
+ ```bash
10
+ npx create-bdpa-react-scaffold my-app
11
+ ```
12
+
13
+ Then start the dev server:
14
+
15
+ ```bash
16
+ cd my-app
17
+ npm run dev
18
+ ```
19
+
20
+ ## Options
21
+
22
+ - `--pm <npm|yarn|pnpm>`: Choose package manager (default: `npm`).
23
+ - `--no-install`: Skip dependency installation.
24
+ - `--force`: Allow writing into a non-empty directory.
25
+
26
+ Examples:
27
+
28
+ ```bash
29
+ npx create-bdpa-react-scaffold my-app --pm pnpm
30
+ npx create-bdpa-react-scaffold . --force --no-install
31
+ ```
32
+
33
+ ## What You Get
34
+
35
+ - Vite + React 18 project
36
+ - Tailwind CSS configured (`postcss.config.cjs`, `tailwind.config.cjs`)
37
+ - Example UI components (Button, Card, Input, FormField, Table, Navbar, Sidebar, Modal, Tabs, Toast)
38
+ - Simple Auth pages (Login, Register)
39
+ - Demo app and wiring
40
+
41
+ ## Local Development (for this CLI)
42
+
43
+ Link the CLI locally to test changes without publishing:
44
+
45
+ ```bash
46
+ npm install
47
+ npm link
48
+
49
+ # Now you can run it from anywhere
50
+ create-bdpa-react-scaffold my-test-app --no-install
51
+ ```
52
+
53
+ To unlink later:
54
+
55
+ ```bash
56
+ npm unlink -g create-bdpa-react-scaffold
57
+ ```
58
+
59
+ ## Publishing to npm
60
+
61
+ 1) Pick a unique package name. This repo uses `create-bdpa-react-scaffold`.
62
+
63
+ 2) Login to npm:
64
+
65
+ ```bash
66
+ npm login
67
+ ```
68
+
69
+ 3) Publish (public):
70
+
71
+ ```bash
72
+ npm publish --access public
73
+ ```
74
+
75
+ If your package name is scoped (e.g. `@yourname/create-bdpa-react-scaffold`), include `--access public` on first publish.
76
+
77
+ After publishing, users can run:
78
+
79
+ ```bash
80
+ npx create-bdpa-react-scaffold my-app
81
+ ```
82
+
83
+ ## License
84
+
85
+ UNLICENSED (update as desired).
@@ -0,0 +1,714 @@
1
+ #!/usr/bin/env node
2
+ // create-ui-lib.js
3
+ // Full upgraded scaffolding script for a complete React + Tailwind UI library
4
+
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+ const { spawnSync } = require("child_process");
8
+
9
+ // -------------------------------
10
+ // CLI args parsing
11
+ // -------------------------------
12
+ const argv = process.argv.slice(2);
13
+ let targetArg = ".";
14
+ let packageManager = "npm";
15
+ let doInstall = true;
16
+ let forceWrite = false;
17
+
18
+ for (let i = 0; i < argv.length; i++) {
19
+ const a = argv[i];
20
+ if (!a) continue;
21
+ if (a === "--pm" && i + 1 < argv.length) {
22
+ packageManager = argv[i + 1];
23
+ i++;
24
+ continue;
25
+ }
26
+ if (a === "--no-install") {
27
+ doInstall = false;
28
+ continue;
29
+ }
30
+ if (a === "--force") {
31
+ forceWrite = true;
32
+ continue;
33
+ }
34
+ if (!a.startsWith("-")) {
35
+ targetArg = a;
36
+ continue;
37
+ }
38
+ }
39
+
40
+ const BASE_DIR = path.resolve(process.cwd(), targetArg);
41
+
42
+ function ensureTargetDir(dir, { force } = { force: false }) {
43
+ if (!fs.existsSync(dir)) {
44
+ fs.mkdirSync(dir, { recursive: true });
45
+ return;
46
+ }
47
+ const entries = fs.readdirSync(dir).filter((e) => e !== ".git");
48
+ if (entries.length > 0 && !force) {
49
+ console.error(`\nDirectory ${dir} is not empty. Use --force to continue.`);
50
+ process.exit(1);
51
+ }
52
+ }
53
+
54
+ function write(filePath, content) {
55
+ const fullPath = path.join(BASE_DIR, filePath);
56
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
57
+ fs.writeFileSync(fullPath, content.trimStart(), "utf8");
58
+ console.log("✔ Created:", filePath);
59
+ }
60
+
61
+ function installDependencies(pm, cwd) {
62
+ const pmCmd = pm === "yarn" ? "yarn" : pm === "pnpm" ? "pnpm" : "npm";
63
+ console.log(`\n📦 Installing dependencies with ${pmCmd}...`);
64
+ const args = pmCmd === "npm" ? ["install"] : ["install"];
65
+ const result = spawnSync(pmCmd, args, { stdio: "inherit", cwd });
66
+ if (result.status !== 0) {
67
+ console.error(`\n❌ ${pmCmd} install failed.`);
68
+ process.exit(result.status || 1);
69
+ }
70
+ console.log("\n✅ Dependencies installed.");
71
+ }
72
+
73
+ // Prepare target directory
74
+ ensureTargetDir(BASE_DIR, { force: forceWrite });
75
+
76
+ // -------------------------------
77
+ // Root files
78
+ // -------------------------------
79
+
80
+ write("package.json", `
81
+ {
82
+ "name": "my-ui-lib",
83
+ "version": "2.0.0",
84
+ "private": true,
85
+ "scripts": {
86
+ "dev": "vite",
87
+ "build": "vite build",
88
+ "preview": "vite preview"
89
+ },
90
+ "dependencies": {
91
+ "react": "^18.2.0",
92
+ "react-dom": "^18.2.0",
93
+ "lucide-react": "^0.344.0"
94
+ },
95
+ "devDependencies": {
96
+ "@vitejs/plugin-react-swc": "^3.5.0",
97
+ "autoprefixer": "^10.4.20",
98
+ "postcss": "^8.4.47",
99
+ "tailwindcss": "^3.4.0",
100
+ "vite": "^5.0.0"
101
+ }
102
+ }
103
+ `);
104
+
105
+ write("postcss.config.cjs", `
106
+ module.exports = {
107
+ plugins: {
108
+ tailwindcss: {},
109
+ autoprefixer: {}
110
+ }
111
+ };
112
+ `);
113
+
114
+ write("tailwind.config.cjs", `
115
+ module.exports = {
116
+ content: [
117
+ "./index.html",
118
+ "./src/**/*.{js,jsx,ts,tsx}"
119
+ ],
120
+ theme: {
121
+ extend: {
122
+ colors: {
123
+ milwaukeeBlue: "#2563eb",
124
+ milwaukeeGold: "#fbbf24"
125
+ }
126
+ }
127
+ },
128
+ plugins: []
129
+ };
130
+ `);
131
+
132
+ write("vite.config.mts", `
133
+ import { defineConfig } from "vite";
134
+ import react from "@vitejs/plugin-react-swc";
135
+
136
+ export default defineConfig({
137
+ plugins: [react()]
138
+ });
139
+ `);
140
+
141
+ write("index.html", `
142
+ <!doctype html>
143
+ <html lang="en">
144
+ <head>
145
+ <meta charset="UTF-8" />
146
+ <title>My UI Library Demo</title>
147
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
148
+ </head>
149
+ <body class="bg-gray-100">
150
+ <div id="root"></div>
151
+ <script type="module" src="/src/main.jsx"></script>
152
+ </body>
153
+ </html>
154
+ `);
155
+
156
+ // -------------------------------
157
+ // src root
158
+ // -------------------------------
159
+
160
+ write("src/index.css", `
161
+ @tailwind base;
162
+ @tailwind components;
163
+ @tailwind utilities;
164
+
165
+ body {
166
+ @apply bg-gray-100 text-gray-900;
167
+ }
168
+
169
+ h1, h2, h3, h4 {
170
+ @apply font-semibold;
171
+ }
172
+ `);
173
+
174
+ write("src/main.jsx", `
175
+ import React from "react";
176
+ import ReactDOM from "react-dom/client";
177
+ import App from "./App.jsx";
178
+ import "./index.css";
179
+ import { ToastProvider } from "./components/ui/ToastProvider.jsx";
180
+
181
+ ReactDOM.createRoot(document.getElementById("root")).render(
182
+ <React.StrictMode>
183
+ <ToastProvider>
184
+ <App />
185
+ </ToastProvider>
186
+ </React.StrictMode>
187
+ );
188
+ `);
189
+
190
+ write("src/index.js", `
191
+ export { default as Button } from "./components/ui/Button.jsx";
192
+ export { default as Card } from "./components/ui/Card.jsx";
193
+ export { default as Input } from "./components/ui/Input.jsx";
194
+ export { default as FormField } from "./components/ui/FormField.jsx";
195
+ export { default as Table } from "./components/ui/Table.jsx";
196
+ export { default as Navbar } from "./components/ui/Navbar.jsx";
197
+ export { default as Sidebar } from "./components/ui/Sidebar.jsx";
198
+ export { default as Modal } from "./components/ui/Modal.jsx";
199
+ export { default as Tabs } from "./components/ui/Tabs.jsx";
200
+ export { ToastProvider, useToast } from "./components/ui/ToastProvider.jsx";
201
+
202
+ export { default as Login } from "./components/auth/Login.jsx";
203
+ export { default as Register } from "./components/auth/Register.jsx";
204
+
205
+ export { default as Container } from "./components/layout/Container.jsx";
206
+ export { default as Section } from "./components/layout/Section.jsx";
207
+ `);
208
+
209
+ // -------------------------------
210
+ // App.jsx (FULL DEMO)
211
+ // -------------------------------
212
+
213
+ write("src/App.jsx", `
214
+ import { useState } from "react";
215
+ import {
216
+ Button,
217
+ Card,
218
+ Input,
219
+ FormField,
220
+ Table,
221
+ Navbar,
222
+ Sidebar,
223
+ Modal,
224
+ Tabs,
225
+ useToast
226
+ } from "./index.js";
227
+
228
+ const columns = [
229
+ { key: "name", label: "Student" },
230
+ { key: "course", label: "Course" },
231
+ { key: "status", label: "Status" }
232
+ ];
233
+
234
+ const data = [
235
+ { name: "Alex", course: "Web Design Fundamentals", status: "Enrolled" },
236
+ { name: "Jordan", course: "Advanced Web App Design", status: "Waitlisted" },
237
+ { name: "Taylor", course: "eSports Strategy", status: "Enrolled" }
238
+ ];
239
+
240
+ export default function App() {
241
+ const [sidebarOpen, setSidebarOpen] = useState(false);
242
+ const [modalOpen, setModalOpen] = useState(false);
243
+ const toast = useToast();
244
+
245
+ const tabs = [
246
+ { label: "Overview", content: <p>Welcome to the UI Library demo.</p> },
247
+ { label: "Components", content: <p>Buttons, Cards, Inputs, Tables, and more.</p> },
248
+ { label: "Auth", content: <p>Login + Registration pages included.</p> }
249
+ ];
250
+
251
+ return (
252
+ <div className="flex h-screen overflow-hidden">
253
+
254
+ {/* Sidebar */}
255
+ <Sidebar
256
+ open={sidebarOpen}
257
+ onToggle={() => setSidebarOpen(!sidebarOpen)}
258
+ links={[
259
+ { label: "Home", href: "#" },
260
+ { label: "Login", href: "/login" },
261
+ { label: "Register", href: "/register" }
262
+ ]}
263
+ />
264
+
265
+ {/* Main content */}
266
+ <div className="flex-1 flex flex-col">
267
+
268
+ {/* Navbar */}
269
+ <Navbar onMenuClick={() => setSidebarOpen(!sidebarOpen)} />
270
+
271
+ {/* Page content */}
272
+ <div className="p-6 space-y-6 overflow-auto">
273
+
274
+ <Tabs tabs={tabs} />
275
+
276
+ <div className="grid md:grid-cols-2 gap-6">
277
+
278
+ {/* Form/Card example */}
279
+ <Card>
280
+ <h2 className="text-lg font-semibold mb-4">Sample Form</h2>
281
+
282
+ <div className="space-y-4">
283
+ <FormField label="Student Name">
284
+ <Input placeholder="e.g. Alex Johnson" />
285
+ </FormField>
286
+
287
+ <FormField label="Email">
288
+ <Input type="email" placeholder="student@example.com" />
289
+ </FormField>
290
+
291
+ <FormField label="Course">
292
+ <Input placeholder="Web Design Fundamentals" />
293
+ </FormField>
294
+
295
+ <div className="flex gap-2 pt-2">
296
+ <Button variant="primary">Save</Button>
297
+ <Button variant="secondary">Cancel</Button>
298
+ </div>
299
+ </div>
300
+ </Card>
301
+
302
+ {/* Table example */}
303
+ <Card>
304
+ <h2 className="text-lg font-semibold mb-4">Enrollment Overview</h2>
305
+ <Table columns={columns} data={data} />
306
+ </Card>
307
+ </div>
308
+
309
+ {/* Buttons */}
310
+ <Card>
311
+ <h2 className="text-lg font-semibold mb-4">Button Variants</h2>
312
+ <div className="flex flex-wrap gap-3">
313
+ <Button variant="primary">Primary</Button>
314
+ <Button variant="secondary">Secondary</Button>
315
+ <Button variant="danger">Danger</Button>
316
+ <Button variant="outline">Outline</Button>
317
+ </div>
318
+ </Card>
319
+
320
+ {/* Modal + Toast */}
321
+ <div className="flex gap-4">
322
+ <Button onClick={() => setModalOpen(true)}>Open Modal</Button>
323
+ <Button onClick={() => toast.show("This is a toast!", "success")}>
324
+ Show Toast
325
+ </Button>
326
+ </div>
327
+
328
+ <Modal open={modalOpen} onClose={() => setModalOpen(false)}>
329
+ <h2 className="text-lg font-semibold mb-4">Modal Title</h2>
330
+ <p>This is a modal example.</p>
331
+ <Button className="mt-4" onClick={() => setModalOpen(false)}>
332
+ Close
333
+ </Button>
334
+ </Modal>
335
+
336
+ </div>
337
+ </div>
338
+ </div>
339
+ );
340
+ }
341
+ `);
342
+
343
+ // -------------------------------
344
+ // UI Components
345
+ // -------------------------------
346
+
347
+ write("src/components/ui/Button.jsx", `
348
+ export default function Button({
349
+ variant = "primary",
350
+ children,
351
+ className = "",
352
+ ...props
353
+ }) {
354
+ const base =
355
+ "inline-flex items-center justify-center px-4 py-2 rounded-md font-semibold text-sm transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2";
356
+
357
+ const variants = {
358
+ primary:
359
+ "bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",
360
+ secondary:
361
+ "bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-400",
362
+ danger:
363
+ "bg-red-600 text-white hover:bg-red-700 focus:ring-red-500",
364
+ outline:
365
+ "border border-gray-300 text-gray-800 hover:bg-gray-100 focus:ring-gray-400"
366
+ };
367
+
368
+ return (
369
+ <button
370
+ className={\`\${base} \${variants[variant]} \${className}\`}
371
+ {...props}
372
+ >
373
+ {children}
374
+ </button>
375
+ );
376
+ }
377
+ `);
378
+
379
+ write("src/components/ui/Card.jsx", `
380
+ export default function Card({ children, className = "" }) {
381
+ return (
382
+ <div
383
+ className={\`bg-white shadow-sm rounded-lg p-4 md:p-6 border border-gray-200 \${className}\`}
384
+ >
385
+ {children}
386
+ </div>
387
+ );
388
+ }
389
+ `);
390
+
391
+ write("src/components/ui/Input.jsx", `
392
+ export default function Input({ label, className = "", ...props }) {
393
+ return (
394
+ <label className="flex flex-col gap-1 text-sm">
395
+ {label && (
396
+ <span className="font-medium text-gray-700">
397
+ {label}
398
+ </span>
399
+ )}
400
+ <input
401
+ className={\`border border-gray-300 rounded-md px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none text-sm \${className}\`}
402
+ {...props}
403
+ />
404
+ </label>
405
+ );
406
+ }
407
+ `);
408
+
409
+ write("src/components/ui/FormField.jsx", `
410
+ export default function FormField({ label, error, children, helperText }) {
411
+ return (
412
+ <div className="flex flex-col gap-1 text-sm">
413
+ {label && (
414
+ <label className="font-medium text-gray-700">
415
+ {label}
416
+ </label>
417
+ )}
418
+
419
+ {children}
420
+
421
+ {helperText && !error && (
422
+ <p className="text-xs text-gray-500">{helperText}</p>
423
+ )}
424
+
425
+ {error && (
426
+ <p className="text-xs text-red-600">
427
+ {error}
428
+ </p>
429
+ )}
430
+ </div>
431
+ );
432
+ }
433
+ `);
434
+
435
+ write("src/components/ui/Table.jsx", `
436
+ export default function Table({ columns, data }) {
437
+ return (
438
+ <div className="overflow-x-auto">
439
+ <table className="min-w-full border border-gray-200 bg-white rounded-lg overflow-hidden">
440
+ <thead className="bg-gray-100">
441
+ <tr>
442
+ {columns.map((col) => (
443
+ <th
444
+ key={col.key}
445
+ className="px-4 py-2 text-left text-xs font-semibold text-gray-700 border-b border-gray-200"
446
+ >
447
+ {col.label}
448
+ </th>
449
+ ))}
450
+ </tr>
451
+ </thead>
452
+
453
+ <tbody>
454
+ {data.map((row, i) => (
455
+ <tr
456
+ key={i}
457
+ className={i % 2 === 0 ? "bg-white" : "bg-gray-50"}
458
+ >
459
+ {columns.map((col) => (
460
+ <td
461
+ key={col.key}
462
+ className="px-4 py-2 text-sm text-gray-800 border-b border-gray-100"
463
+ >
464
+ {row[col.key]}
465
+ </td>
466
+ ))}
467
+ </tr>
468
+ ))}
469
+ </tbody>
470
+ </table>
471
+ </div>
472
+ );
473
+ }
474
+ `);
475
+
476
+ write("src/components/ui/Navbar.jsx", `
477
+ import { Menu } from "lucide-react";
478
+
479
+ export default function Navbar({ onMenuClick }) {
480
+ return (
481
+ <nav className="bg-white border-b border-gray-200 px-4 py-3 flex items-center justify-between">
482
+ <button className="md:hidden" onClick={onMenuClick}>
483
+ <Menu />
484
+ </button>
485
+ <h1 className="text-xl font-bold">My UI Library</h1>
486
+ </nav>
487
+ );
488
+ }
489
+ `);
490
+
491
+ write("src/components/ui/Sidebar.jsx", `
492
+ export default function Sidebar({ open, onToggle, links }) {
493
+ return (
494
+ <div
495
+ className={\`
496
+ fixed md:static inset-y-0 left-0 z-40
497
+ bg-white border-r border-gray-200
498
+ h-full w-64 transform
499
+ transition-transform duration-200
500
+ \${open ? "translate-x-0" : "-translate-x-full md:translate-x-0"}
501
+ \`}
502
+ >
503
+ <div className="p-4 border-b border-gray-200 flex justify-between items-center">
504
+ <h2 className="font-semibold">Menu</h2>
505
+ <button className="md:hidden" onClick={onToggle}>✕</button>
506
+ </div>
507
+
508
+ <ul className="p-4 space-y-2">
509
+ {links.map((l) => (
510
+ <li key={l.label}>
511
+ <a href={l.href} className="block px-2 py-2 rounded hover:bg-gray-100">
512
+ {l.label}
513
+ </a>
514
+ </li>
515
+ ))}
516
+ </ul>
517
+ </div>
518
+ );
519
+ }
520
+ `);
521
+
522
+ write("src/components/ui/Modal.jsx", `
523
+ export default function Modal({ open, onClose, children }) {
524
+ if (!open) return null;
525
+
526
+ return (
527
+ <div className="fixed inset-0 bg-black/40 flex items-center justify-center z-50">
528
+ <div className="bg-white rounded-lg shadow-lg p-6 w-full max-w-md relative">
529
+ <button
530
+ className="absolute top-3 right-3 text-gray-500 hover:text-gray-700"
531
+ onClick={onClose}
532
+ >
533
+
534
+ </button>
535
+
536
+ {children}
537
+ </div>
538
+ </div>
539
+ );
540
+ }
541
+ `);
542
+
543
+ write("src/components/ui/Tabs.jsx", `
544
+ import { useState } from "react";
545
+
546
+ export default function Tabs({ tabs }) {
547
+ const [active, setActive] = useState(0);
548
+
549
+ return (
550
+ <div>
551
+ <div className="flex gap-4 border-b border-gray-200">
552
+ {tabs.map((t, i) => (
553
+ <button
554
+ key={i}
555
+ onClick={() => setActive(i)}
556
+ className={\`pb-2 text-sm font-medium \${active === i
557
+ ? "border-b-2 border-blue-600 text-blue-600"
558
+ : "text-gray-600 hover:text-gray-800"
559
+ }\`}
560
+ >
561
+ {t.label}
562
+ </button>
563
+ ))}
564
+ </div>
565
+
566
+ <div className="mt-4">{tabs[active].content}</div>
567
+ </div>
568
+ );
569
+ }
570
+ `);
571
+
572
+ write("src/components/ui/ToastProvider.jsx", `
573
+ import { createContext, useContext, useState } from "react";
574
+
575
+ const ToastContext = createContext();
576
+
577
+ export function useToast() {
578
+ return useContext(ToastContext);
579
+ }
580
+
581
+ export function ToastProvider({ children }) {
582
+ const [toasts, setToasts] = useState([]);
583
+
584
+ const show = (message, type = "info") => {
585
+ const id = Date.now();
586
+ setToasts((t) => [...t, { id, message, type }]);
587
+ setTimeout(() => {
588
+ setToasts((t) => t.filter((toast) => toast.id !== id));
589
+ }, 3000);
590
+ };
591
+
592
+ return (
593
+ <ToastContext.Provider value={{ show }}>
594
+ {children}
595
+
596
+ <div className="fixed bottom-4 right-4 space-y-3 z-50">
597
+ {toasts.map((t) => (
598
+ <div
599
+ key={t.id}
600
+ className={\`px-4 py-2 rounded-md shadow text-white \${t.type === "success"
601
+ ? "bg-green-600"
602
+ : t.type === "error"
603
+ ? "bg-red-600"
604
+ : "bg-gray-800"
605
+ }\`}
606
+ >
607
+ {t.message}
608
+ </div>
609
+ ))}
610
+ </div>
611
+ </ToastContext.Provider>
612
+ );
613
+ }
614
+ `);
615
+
616
+ // -------------------------------
617
+ // Auth Components
618
+ // -------------------------------
619
+
620
+ write("src/components/auth/Login.jsx", `
621
+ import Button from "../ui/Button.jsx";
622
+ import Input from "../ui/Input.jsx";
623
+ import Card from "../ui/Card.jsx";
624
+
625
+ export default function Login({ onSubmit }) {
626
+ return (
627
+ <div className="flex items-center justify-center min-h-screen bg-gray-100">
628
+ <Card className="w-full max-w-sm space-y-4">
629
+ <h2 className="text-xl font-bold">Sign In</h2>
630
+
631
+ <Input label="Email" type="email" placeholder="you@example.com" />
632
+ <Input label="Password" type="password" placeholder="••••••••" />
633
+
634
+ <Button className="w-full" onClick={onSubmit}>
635
+ Sign In
636
+ </Button>
637
+
638
+ <p className="text-sm text-center text-gray-600">
639
+ Don’t have an account?{" "}
640
+ <a href="/register" className="text-blue-600 hover:underline">
641
+ Create one
642
+ </a>
643
+ </p>
644
+ </Card>
645
+ </div>
646
+ );
647
+ }`);
648
+
649
+ write("src/components/auth/Register.jsx", `
650
+ import Button from "../ui/Button.jsx";
651
+ import Input from "../ui/Input.jsx";
652
+ import Card from "../ui/Card.jsx";
653
+
654
+ export default function Register({ onSubmit }) {
655
+ return (
656
+ <div className="flex items-center justify-center min-h-screen bg-gray-100">
657
+ <Card className="w-full max-w-sm space-y-4">
658
+ <h2 className="text-xl font-bold">Create Account</h2>
659
+
660
+ <Input label="Full Name" placeholder="Your Name" />
661
+ <Input label="Email" type="email" placeholder="you@example.com" />
662
+ <Input label="Password" type="password" placeholder="" />
663
+
664
+ <Button className="w-full" onClick={onSubmit}>
665
+ Register
666
+ </Button>
667
+
668
+ <p className="text-sm text-center text-gray-600">
669
+ Already have an account?{" "}
670
+ <a href="/login" className="text-blue-600 hover:underline">
671
+ Sign in
672
+ </a>
673
+ </p>
674
+ </Card>
675
+ </div>
676
+ );
677
+ }
678
+ `);
679
+
680
+ // -------------------------------
681
+ // Layout Components
682
+ // -------------------------------
683
+
684
+ write("src/components/layout/Container.jsx", `
685
+ export default function Container({ children, className = "" }) {
686
+ return (
687
+ <div className={\`max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 \${className}\`}>
688
+ {children}
689
+ </div>
690
+ );
691
+ }
692
+ `);
693
+
694
+ write("src/components/layout/Section.jsx", `
695
+ export default function Section({ children, className = "" }) {
696
+ return (
697
+ <section className={\`py-12 \${className}\`}>
698
+ {children}
699
+ </section>
700
+ );
701
+ }
702
+ `);
703
+
704
+ console.log("\n✅ UI Library scaffolding complete!");
705
+ console.log("\nNext steps:");
706
+ console.log(" 1. npm run dev");
707
+ console.log(" 3. Open http://localhost:5173 in your browser\n");
708
+
709
+ // Install dependencies unless disabled
710
+ if (doInstall) {
711
+ installDependencies(packageManager, BASE_DIR);
712
+ } else {
713
+ console.log("\nℹ️ Skipping install (flag --no-install). Run manually later.");
714
+ }
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "create-bdpa-react-scaffold",
3
+ "version": "0.1.1",
4
+ "private": false,
5
+ "description": "Scaffold a React + Tailwind UI library demo via Vite.",
6
+ "bin": {
7
+ "create-bdpa-react-scaffold": "./create-ui-lib.js"
8
+ },
9
+ "files": [
10
+ "create-ui-lib.js",
11
+ "README.md"
12
+ ],
13
+ "keywords": [
14
+ "create",
15
+ "scaffold",
16
+ "react",
17
+ "tailwind",
18
+ "vite",
19
+ "ui",
20
+ "template"
21
+ ],
22
+ "engines": {
23
+ "node": ">=18"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/lxdavis9lxd/create-bdpa-react-scaffold-.git"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/lxdavis9lxd/create-bdpa-react-scaffold-/issues"
31
+ },
32
+ "homepage": "https://github.com/lxdavis9lxd/create-bdpa-react-scaffold-#readme",
33
+ "license": "UNLICENSED",
34
+ "scripts": {
35
+ "prepublishOnly": "node -e \"require('fs').accessSync('./create-ui-lib.js');\"",
36
+ "publish:public": "npm publish --access public"
37
+ }
38
+ }