pejay-ui 1.3.5 → 1.4.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.
package/README.md CHANGED
@@ -43,6 +43,9 @@ This allows you to easily import multiple components:
43
43
  import { Input, Checkbox, AmountInput } from "@/pejay-ui/components";
44
44
  ```
45
45
 
46
+ **Component-Specific Documentation (READMEs):**
47
+ Certain components (like `toast`) ship with localized `README.md` guides. When you install them, the CLI automatically copies their detailed usage documentation directly into the component's folder (e.g., `src/pejay-ui/components/toast/README.md`) so you have helper docs right next to the code.
48
+
46
49
  ### 3. Remove Component
47
50
  ```bash
48
51
  npx pejay-ui remove <component-name>
@@ -84,6 +87,7 @@ npx pejay-ui add form/switch
84
87
  npx pejay-ui add form/textarea
85
88
  npx pejay-ui add form/url-input
86
89
  ```
90
+ *(Supports category-wide commands: `npx pejay-ui add form --all` or `npx pejay-ui add form --select`)*
87
91
 
88
92
  ### Date & Time Pickers
89
93
  ```bash
@@ -92,17 +96,32 @@ npx pejay-ui add form/date-range-picker
92
96
  npx pejay-ui add form/time-picker
93
97
  npx pejay-ui add form/time-range-picker
94
98
  ```
99
+ *(Supports category-wide commands: `npx pejay-ui add form --all` or `npx pejay-ui add form --select`)*
95
100
 
96
101
  ### Dropdowns & Selects
97
102
  ```bash
98
103
  npx pejay-ui add dropdown/select-input
99
104
  npx pejay-ui add dropdown/multiselect-input
100
105
  ```
106
+ *(Supports category-wide commands: `npx pejay-ui add dropdown --all` or `npx pejay-ui add dropdown --select`)*
101
107
 
102
108
  ### Layouts
103
109
  ```bash
104
110
  npx pejay-ui add layouts/lv1
105
111
  ```
112
+ *(Supports category-wide commands: `npx pejay-ui add layouts --all` or `npx pejay-ui add layouts --select`)*
113
+
114
+ ### Toast
115
+ ```bash
116
+ npx pejay-ui add toast
117
+ ```
118
+ *(Includes a localized `README.md` guide copied directly into your components folder).*
119
+
120
+ ### Overlays
121
+ ```bash
122
+ npx pejay-ui add overlays/portal
123
+ ```
124
+ *(Supports category-wide commands: `npx pejay-ui add overlays --all` or `npx pejay-ui add overlays --select`)*
106
125
 
107
126
  ### Scaffolds & Templates
108
127
  ```bash
@@ -113,3 +132,4 @@ npx pejay-ui add axios-client
113
132
  npx pejay-ui add redux-store-client
114
133
  npx pejay-ui add rtk-query-client
115
134
  ```
135
+ *(Supports category-wide commands: `npx pejay-ui add scaffold --all` or `npx pejay-ui add scaffold --select`)*
package/bin/cli.js CHANGED
@@ -38,14 +38,37 @@ const getFilesRecursively = async (dir) => {
38
38
  return results;
39
39
  };
40
40
 
41
+ const loadRegistry = async () => {
42
+ const registryDir = path.join(packageRoot, "registry");
43
+ if (!await fs.pathExists(registryDir)) {
44
+ console.error(`Error: Registry directory not found at ${registryDir}`);
45
+ process.exit(1);
46
+ }
47
+ const files = await fs.readdir(registryDir);
48
+ const registry = {};
49
+ for (const file of files) {
50
+ if (file.endsWith(".json")) {
51
+ const filePath = path.join(registryDir, file);
52
+ try {
53
+ const data = await fs.readJSON(filePath);
54
+ Object.assign(registry, data);
55
+ } catch (e) {
56
+ console.error(`Error reading registry file: ${file}`, e);
57
+ }
58
+ }
59
+ }
60
+ return registry;
61
+ };
62
+ const pkg = await fs.readJSON(path.join(packageRoot, "package.json"));
63
+
41
64
  program
42
65
  .name("pejay-ui")
43
66
  .description("CLI to initialize, add, and remove React UI components")
44
- .version("1.0.0");
67
+ .version(pkg.version);
45
68
 
46
69
  /* =============================
47
70
  INIT COMMAND
48
- ============================= */
71
+ ============================= */
49
72
  program
50
73
  .command("init")
51
74
  .description("Initialize pejay-ui configuration in your project")
@@ -70,7 +93,7 @@ program
70
93
 
71
94
  /* =============================
72
95
  ADD COMMAND
73
- ============================= */
96
+ ============================= */
74
97
  program
75
98
  .command("add <component>")
76
99
  .description("Add a component to your project")
@@ -87,25 +110,45 @@ program
87
110
  }
88
111
 
89
112
  const config = await fs.readJSON(configPath);
90
- const registryPath = path.join(packageRoot, "registry.json");
91
-
92
- if (!await fs.pathExists(registryPath)) {
93
- console.error("Error: Registry configuration not found in the package.");
94
- process.exit(1);
95
- }
96
-
97
- const registry = await fs.readJSON(registryPath);
113
+ const registry = await loadRegistry();
98
114
  const isTsProject = await fs.pathExists(path.join(cwd, "tsconfig.json"));
99
115
 
100
116
  // Determine which components to install
101
117
  let selectedComponents = [];
102
118
 
103
- // Check if it's a category
104
- const categoryComponents = Object.keys(registry).filter(
105
- (key) => registry[key].category === component
106
- );
119
+ // Determine if the input refers to a category that supports category-wide operations
120
+ const categorySupportMap = {};
121
+ for (const [key, compData] of Object.entries(registry)) {
122
+ if (compData.category && compData.supportsCategory) {
123
+ categorySupportMap[compData.category.toLowerCase()] = compData.category;
124
+ }
125
+ }
126
+
127
+ const getCategoryFromInput = (input, supportMap) => {
128
+ const normInput = input.toLowerCase().trim().replace(/s$/, "");
129
+ for (const [normCat, originalCat] of Object.entries(supportMap)) {
130
+ const normCatSingular = normCat.replace(/s$/, "");
131
+ if (normInput === normCatSingular || (normInput === "dropdown" && normCatSingular === "select-dropdown")) {
132
+ return originalCat;
133
+ }
134
+ }
135
+ return null;
136
+ };
137
+
138
+ const targetCategory = getCategoryFromInput(component, categorySupportMap);
139
+ const isExactComponent = !!registry[component];
140
+
141
+ if (targetCategory && (!isExactComponent || options.all || options.select)) {
142
+ // Treat as category installation
143
+ const categoryComponents = Object.keys(registry).filter(
144
+ (key) => registry[key].category === targetCategory
145
+ );
146
+
147
+ if (categoryComponents.length === 0) {
148
+ console.error(`Error: Category '${component}' has no components in the registry.`);
149
+ process.exit(1);
150
+ }
107
151
 
108
- if (categoryComponents.length > 0) {
109
152
  if (options.all) {
110
153
  selectedComponents = categoryComponents;
111
154
  } else {
@@ -114,7 +157,7 @@ program
114
157
  {
115
158
  type: "checkbox",
116
159
  name: "components",
117
- message: `Select components from category "${component}" to add:`,
160
+ message: `Select components from category "${targetCategory}" to add:`,
118
161
  choices: categoryComponents.map((key) => ({
119
162
  name: `${registry[key].name} (${key})`,
120
163
  value: key,
@@ -128,8 +171,8 @@ program
128
171
  }
129
172
  }
130
173
  } else {
131
- // Not a category, treat as a single component key
132
- if (!registry[component]) {
174
+ // Not a category (or exact component targeted without category flags), treat as a single component key
175
+ if (!isExactComponent) {
133
176
  console.error(`Error: Component or Category '${component}' not found in registry.`);
134
177
  console.log(`Available categories/components: ${Array.from(new Set(Object.values(registry).map(c => c.category).filter(Boolean))).join(", ")} or ${Object.keys(registry).join(", ")}`);
135
178
  process.exit(1);
@@ -419,14 +462,7 @@ program
419
462
  }
420
463
 
421
464
  const config = await fs.readJSON(configPath);
422
- const registryPath = path.join(packageRoot, "registry.json");
423
-
424
- if (!await fs.pathExists(registryPath)) {
425
- console.error("Error: Registry configuration not found.");
426
- process.exit(1);
427
- }
428
-
429
- const registry = await fs.readJSON(registryPath);
465
+ const registry = await loadRegistry();
430
466
  const componentData = registry[component];
431
467
 
432
468
  if (!componentData) {
@@ -636,13 +672,7 @@ program
636
672
  }
637
673
  }
638
674
 
639
- const registryPath = path.join(packageRoot, "registry.json");
640
- if (!await fs.pathExists(registryPath)) {
641
- console.error("Error: Registry configuration not found.");
642
- process.exit(1);
643
- }
644
-
645
- const registry = await fs.readJSON(registryPath);
675
+ const registry = await loadRegistry();
646
676
 
647
677
  // Terminal styling colors
648
678
  const GREEN = "\x1b[32m";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pejay-ui",
3
- "version": "1.3.5",
3
+ "version": "1.4.0",
4
4
  "type": "module",
5
5
  "description": "react ui components",
6
6
  "bin": {
@@ -10,7 +10,7 @@
10
10
  "bin/",
11
11
  "templates/",
12
12
  "utils/",
13
- "registry.json"
13
+ "registry/"
14
14
  ],
15
15
  "keywords": [
16
16
  "react",
@@ -0,0 +1,9 @@
1
+ {
2
+ "button": {
3
+ "name": "Button",
4
+ "category": "button",
5
+ "files": ["templates/button/Button.tsx", "templates/button/tooltip.tsx"],
6
+ "utils": ["cn.ts"],
7
+ "peerDependencies": ["clsx", "tailwind-merge"]
8
+ }
9
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "dropdown/select-input": {
3
+ "name": "SelectInput",
4
+ "category": "select-dropdown",
5
+ "files": ["templates/select-dropdown/select-input.tsx"],
6
+ "utils": ["cn.ts"],
7
+ "peerDependencies": [
8
+ "clsx",
9
+ "tailwind-merge",
10
+ "lucide-react",
11
+ "@floating-ui/react"
12
+ ],
13
+ "supportsCategory": true
14
+ },
15
+ "dropdown/multiselect-input": {
16
+ "name": "MultiselectInput",
17
+ "category": "select-dropdown",
18
+ "files": ["templates/select-dropdown/multiselect-input.tsx"],
19
+ "utils": ["cn.ts"],
20
+ "peerDependencies": [
21
+ "clsx",
22
+ "tailwind-merge",
23
+ "lucide-react",
24
+ "@floating-ui/react"
25
+ ],
26
+ "supportsCategory": true
27
+ }
28
+ }
@@ -0,0 +1,322 @@
1
+ {
2
+ "form/input": {
3
+ "name": "Input",
4
+ "category": "form",
5
+ "files": [
6
+ "templates/form/input.tsx"
7
+ ],
8
+ "utils": [
9
+ "cn.ts"
10
+ ],
11
+ "peerDependencies": [
12
+ "clsx",
13
+ "tailwind-merge",
14
+ "lucide-react"
15
+ ],
16
+ "supportsCategory": true
17
+ },
18
+ "form/amount-input": {
19
+ "name": "AmountInput",
20
+ "category": "form",
21
+ "files": [
22
+ "templates/form/amount-input.tsx"
23
+ ],
24
+ "utils": [
25
+ "cn.ts"
26
+ ],
27
+ "peerDependencies": [
28
+ "clsx",
29
+ "tailwind-merge",
30
+ "lucide-react"
31
+ ],
32
+ "supportsCategory": true
33
+ },
34
+ "form/checkbox": {
35
+ "name": "Checkbox",
36
+ "category": "form",
37
+ "files": [
38
+ "templates/form/checkbox.tsx"
39
+ ],
40
+ "utils": [
41
+ "cn.ts"
42
+ ],
43
+ "peerDependencies": [
44
+ "clsx",
45
+ "tailwind-merge",
46
+ "lucide-react"
47
+ ],
48
+ "supportsCategory": true
49
+ },
50
+ "form/checkbox-group": {
51
+ "name": "CheckboxGroup",
52
+ "category": "form",
53
+ "files": [
54
+ "templates/form/checkbox-group.tsx"
55
+ ],
56
+ "utils": [
57
+ "cn.ts"
58
+ ],
59
+ "peerDependencies": [
60
+ "clsx",
61
+ "tailwind-merge"
62
+ ],
63
+ "dependencies": [
64
+ "form/checkbox"
65
+ ],
66
+ "supportsCategory": true
67
+ },
68
+ "form/date-picker": {
69
+ "name": "DatePicker",
70
+ "category": "form",
71
+ "files": [
72
+ "templates/form/date-picker.tsx"
73
+ ],
74
+ "utils": [
75
+ "cn.ts"
76
+ ],
77
+ "peerDependencies": [
78
+ "clsx",
79
+ "tailwind-merge",
80
+ "lucide-react",
81
+ "@floating-ui/react"
82
+ ],
83
+ "dependencies": [
84
+ "dropdown/select-input"
85
+ ],
86
+ "supportsCategory": true
87
+ },
88
+ "form/date-range-picker": {
89
+ "name": "DateRangePicker",
90
+ "category": "form",
91
+ "files": [
92
+ "templates/form/date-range-picker.tsx"
93
+ ],
94
+ "utils": [
95
+ "cn.ts"
96
+ ],
97
+ "peerDependencies": [
98
+ "clsx",
99
+ "tailwind-merge",
100
+ "lucide-react",
101
+ "@floating-ui/react"
102
+ ],
103
+ "dependencies": [
104
+ "dropdown/select-input"
105
+ ],
106
+ "supportsCategory": true
107
+ },
108
+ "form/email-input": {
109
+ "name": "EmailInput",
110
+ "category": "form",
111
+ "files": [
112
+ "templates/form/email-input.tsx"
113
+ ],
114
+ "utils": [
115
+ "cn.ts"
116
+ ],
117
+ "peerDependencies": [
118
+ "clsx",
119
+ "tailwind-merge",
120
+ "lucide-react"
121
+ ],
122
+ "supportsCategory": true
123
+ },
124
+ "form/file-input": {
125
+ "name": "FileInput",
126
+ "category": "form",
127
+ "files": [
128
+ "templates/form/file-input.tsx"
129
+ ],
130
+ "utils": [
131
+ "cn.ts"
132
+ ],
133
+ "peerDependencies": [
134
+ "clsx",
135
+ "tailwind-merge",
136
+ "lucide-react"
137
+ ],
138
+ "supportsCategory": true
139
+ },
140
+ "form/number-input": {
141
+ "name": "NumberInput",
142
+ "category": "form",
143
+ "files": [
144
+ "templates/form/number-input.tsx"
145
+ ],
146
+ "utils": [
147
+ "cn.ts"
148
+ ],
149
+ "peerDependencies": [
150
+ "clsx",
151
+ "tailwind-merge",
152
+ "lucide-react"
153
+ ],
154
+ "supportsCategory": true
155
+ },
156
+ "form/password-input": {
157
+ "name": "PasswordInput",
158
+ "category": "form",
159
+ "files": [
160
+ "templates/form/password-input.tsx"
161
+ ],
162
+ "utils": [
163
+ "cn.ts"
164
+ ],
165
+ "peerDependencies": [
166
+ "clsx",
167
+ "tailwind-merge",
168
+ "lucide-react"
169
+ ],
170
+ "supportsCategory": true
171
+ },
172
+ "form/phone-input": {
173
+ "name": "PhoneInput",
174
+ "category": "form",
175
+ "files": [
176
+ "templates/form/phone-input.tsx"
177
+ ],
178
+ "utils": [
179
+ "cn.ts"
180
+ ],
181
+ "peerDependencies": [
182
+ "clsx",
183
+ "tailwind-merge",
184
+ "lucide-react"
185
+ ],
186
+ "supportsCategory": true
187
+ },
188
+ "form/radio": {
189
+ "name": "Radio",
190
+ "category": "form",
191
+ "files": [
192
+ "templates/form/radio.tsx"
193
+ ],
194
+ "utils": [
195
+ "cn.ts"
196
+ ],
197
+ "peerDependencies": [
198
+ "clsx",
199
+ "tailwind-merge"
200
+ ],
201
+ "supportsCategory": true
202
+ },
203
+ "form/radio-group": {
204
+ "name": "RadioGroup",
205
+ "category": "form",
206
+ "files": [
207
+ "templates/form/radio-group.tsx"
208
+ ],
209
+ "utils": [
210
+ "cn.ts"
211
+ ],
212
+ "peerDependencies": [
213
+ "clsx",
214
+ "tailwind-merge"
215
+ ],
216
+ "dependencies": [
217
+ "form/radio"
218
+ ],
219
+ "supportsCategory": true
220
+ },
221
+ "form/range-slider": {
222
+ "name": "RangeSlider",
223
+ "category": "form",
224
+ "files": [
225
+ "templates/form/range-slider.tsx"
226
+ ],
227
+ "utils": [
228
+ "cn.ts"
229
+ ],
230
+ "peerDependencies": [
231
+ "clsx",
232
+ "tailwind-merge"
233
+ ],
234
+ "supportsCategory": true
235
+ },
236
+ "form/switch": {
237
+ "name": "Switch",
238
+ "category": "form",
239
+ "files": [
240
+ "templates/form/switch.tsx"
241
+ ],
242
+ "utils": [
243
+ "cn.ts"
244
+ ],
245
+ "peerDependencies": [
246
+ "clsx",
247
+ "tailwind-merge"
248
+ ],
249
+ "supportsCategory": true
250
+ },
251
+ "form/textarea": {
252
+ "name": "Textarea",
253
+ "category": "form",
254
+ "files": [
255
+ "templates/form/textarea.tsx"
256
+ ],
257
+ "utils": [
258
+ "cn.ts"
259
+ ],
260
+ "peerDependencies": [
261
+ "clsx",
262
+ "tailwind-merge"
263
+ ],
264
+ "supportsCategory": true
265
+ },
266
+ "form/time-picker": {
267
+ "name": "TimePicker",
268
+ "category": "form",
269
+ "files": [
270
+ "templates/form/time-picker.tsx"
271
+ ],
272
+ "utils": [
273
+ "cn.ts"
274
+ ],
275
+ "peerDependencies": [
276
+ "clsx",
277
+ "tailwind-merge",
278
+ "lucide-react",
279
+ "@floating-ui/react"
280
+ ],
281
+ "dependencies": [
282
+ "dropdown/select-input"
283
+ ],
284
+ "supportsCategory": true
285
+ },
286
+ "form/time-range-picker": {
287
+ "name": "TimeRangePicker",
288
+ "category": "form",
289
+ "files": [
290
+ "templates/form/time-range-picker.tsx"
291
+ ],
292
+ "utils": [
293
+ "cn.ts"
294
+ ],
295
+ "peerDependencies": [
296
+ "clsx",
297
+ "tailwind-merge",
298
+ "lucide-react",
299
+ "@floating-ui/react"
300
+ ],
301
+ "dependencies": [
302
+ "dropdown/select-input"
303
+ ],
304
+ "supportsCategory": true
305
+ },
306
+ "form/url-input": {
307
+ "name": "UrlInput",
308
+ "category": "form",
309
+ "files": [
310
+ "templates/form/url-input.tsx"
311
+ ],
312
+ "utils": [
313
+ "cn.ts"
314
+ ],
315
+ "peerDependencies": [
316
+ "clsx",
317
+ "tailwind-merge",
318
+ "lucide-react"
319
+ ],
320
+ "supportsCategory": true
321
+ }
322
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "layouts/lv1": {
3
+ "name": "AppLayout",
4
+ "category": "layouts",
5
+ "files": [
6
+ "templates/layouts/lv1/app-layout.tsx",
7
+ "templates/layouts/lv1/sidebar-menu.tsx",
8
+ "templates/layouts/lv1/index.ts"
9
+ ],
10
+ "utils": ["cn.ts"],
11
+ "peerDependencies": [
12
+ "clsx",
13
+ "tailwind-merge",
14
+ "lucide-react"
15
+ ],
16
+ "supportsCategory": true
17
+ }
18
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "overlays/portal": {
3
+ "name": "Portal",
4
+ "category": "overlays",
5
+ "files": ["templates/overlays/portal.tsx"],
6
+ "supportsCategory": true
7
+ }
8
+ }
@@ -0,0 +1,83 @@
1
+ {
2
+ "tanstack-query-client": {
3
+ "name": "TanstackQueryClient",
4
+ "category": "scaffold",
5
+ "subcategory": "tanstack-query",
6
+ "targetDirName": "tanstack-query",
7
+ "files": [
8
+ "templates/scaffolds/tanstack-query"
9
+ ],
10
+ "peerDependencies": [
11
+ "@tanstack/react-query"
12
+ ],
13
+ "supportsCategory": true
14
+ },
15
+ "react-router-client": {
16
+ "name": "ReactRouterClient",
17
+ "category": "scaffold",
18
+ "subcategory": "react-router",
19
+ "targetDirName": "react-router",
20
+ "files": [
21
+ "templates/scaffolds/react-router"
22
+ ],
23
+ "peerDependencies": [
24
+ "react-router-dom"
25
+ ],
26
+ "supportsCategory": true
27
+ },
28
+ "tanstack-router-client": {
29
+ "name": "TanstackRouterClient",
30
+ "category": "scaffold",
31
+ "subcategory": "tanstack-router",
32
+ "targetDirName": "tanstack-router",
33
+ "files": [
34
+ "templates/scaffolds/tanstack-router"
35
+ ],
36
+ "peerDependencies": [
37
+ "@tanstack/react-router"
38
+ ],
39
+ "supportsCategory": true
40
+ },
41
+ "axios-client": {
42
+ "name": "AxiosClient",
43
+ "category": "scaffold",
44
+ "subcategory": "axios",
45
+ "targetDirName": "axios",
46
+ "files": [
47
+ "templates/scaffolds/axios"
48
+ ],
49
+ "peerDependencies": [
50
+ "axios"
51
+ ],
52
+ "supportsCategory": true
53
+ },
54
+ "redux-store-client": {
55
+ "name": "ReduxStoreClient",
56
+ "category": "scaffold",
57
+ "subcategory": "redux-store",
58
+ "targetDirName": "redux-store",
59
+ "files": [
60
+ "templates/scaffolds/redux-store"
61
+ ],
62
+ "peerDependencies": [
63
+ "@reduxjs/toolkit",
64
+ "react-redux",
65
+ "redux-persist"
66
+ ],
67
+ "supportsCategory": true
68
+ },
69
+ "rtk-query-client": {
70
+ "name": "RtkQueryClient",
71
+ "category": "scaffold",
72
+ "subcategory": "rtk-query",
73
+ "targetDirName": "rtk-query",
74
+ "files": [
75
+ "templates/scaffolds/rtk-query"
76
+ ],
77
+ "peerDependencies": [
78
+ "@reduxjs/toolkit",
79
+ "react-redux"
80
+ ],
81
+ "supportsCategory": true
82
+ }
83
+ }