pejay-ui 1.4.3 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +26 -0
  2. package/bin/cli.js +45 -15
  3. package/package.json +2 -1
  4. package/registry/buttons.json +3 -2
  5. package/registry/dropdowns.json +3 -1
  6. package/registry/forms.json +51 -23
  7. package/registry/hotkeys.json +12 -0
  8. package/registry/overlays.json +18 -2
  9. package/registry/panels.json +21 -0
  10. package/registry/skeleton.json +20 -0
  11. package/registry/spinner.json +13 -0
  12. package/templates/button/Button.tsx +8 -7
  13. package/templates/button/README.md +81 -0
  14. package/templates/button/index.ts +1 -2
  15. package/templates/form/{checkbox-group.tsx → choices/checkbox-group.tsx} +1 -1
  16. package/templates/form/{checkbox.tsx → choices/checkbox.tsx} +1 -1
  17. package/templates/form/{radio-group.tsx → choices/radio-group.tsx} +1 -1
  18. package/templates/form/{radio.tsx → choices/radio.tsx} +1 -1
  19. package/templates/form/choices/readme.checkbox-group.md +27 -0
  20. package/templates/form/choices/readme.checkbox.md +26 -0
  21. package/templates/form/choices/readme.radio-group.md +26 -0
  22. package/templates/form/choices/readme.radio.md +24 -0
  23. package/templates/form/choices/readme.switch.md +26 -0
  24. package/templates/form/{switch.tsx → choices/switch.tsx} +1 -1
  25. package/templates/form/{file-input.tsx → file/file-input.tsx} +2 -2
  26. package/templates/form/file/readme.file-input.md +26 -0
  27. package/templates/form/index.ts +19 -22
  28. package/templates/form/{amount-input.tsx → numeric/amount-input.tsx} +2 -2
  29. package/templates/form/{number-input.tsx → numeric/number-input.tsx} +2 -2
  30. package/templates/form/{range-slider.tsx → numeric/range-slider.tsx} +1 -1
  31. package/templates/form/numeric/readme.amount-input.md +27 -0
  32. package/templates/form/numeric/readme.number-input.md +26 -0
  33. package/templates/form/numeric/readme.range-slider.md +27 -0
  34. package/templates/form/{date-picker.tsx → pickers/date-picker.tsx} +2 -2
  35. package/templates/form/{date-range-picker.tsx → pickers/date-range-picker.tsx} +2 -2
  36. package/templates/form/pickers/readme.date-picker.md +26 -0
  37. package/templates/form/pickers/readme.date-range-picker.md +25 -0
  38. package/templates/form/pickers/readme.time-picker.md +25 -0
  39. package/templates/form/pickers/readme.time-range-picker.md +25 -0
  40. package/templates/form/{time-picker.tsx → pickers/time-picker.tsx} +1 -1
  41. package/templates/form/{time-range-picker.tsx → pickers/time-range-picker.tsx} +1 -1
  42. package/templates/form/{input.tsx → text-inputs/input.tsx} +1 -1
  43. package/templates/form/{password-input.tsx → text-inputs/password-input.tsx} +1 -1
  44. package/templates/form/text-inputs/readme.email-input.md +24 -0
  45. package/templates/form/text-inputs/readme.input.md +28 -0
  46. package/templates/form/text-inputs/readme.password-input.md +24 -0
  47. package/templates/form/text-inputs/readme.phone-input.md +24 -0
  48. package/templates/form/text-inputs/readme.textarea.md +24 -0
  49. package/templates/form/text-inputs/readme.url-input.md +23 -0
  50. package/templates/form/{textarea.tsx → text-inputs/textarea.tsx} +1 -1
  51. package/templates/hotkeys/README.md +134 -0
  52. package/templates/hotkeys/components/HotkeyProvider.tsx +78 -0
  53. package/templates/hotkeys/components/HotkeysHelpModal.tsx +102 -0
  54. package/templates/hotkeys/core/key-matcher.ts +106 -0
  55. package/templates/hotkeys/core/registry.ts +39 -0
  56. package/templates/hotkeys/core/types.ts +15 -0
  57. package/templates/hotkeys/hooks/useHotkey.ts +43 -0
  58. package/templates/hotkeys/index.ts +6 -0
  59. package/templates/layouts/lv1/app-layout.tsx +1 -1
  60. package/templates/layouts/lv1/sidebar-menu.tsx +1 -1
  61. package/templates/notes/app-provider/app-provider-side-panel-modals-roadmap.md +606 -0
  62. package/templates/notes/app-provider/manual-open-close-side-panel-and-modal.md +913 -0
  63. package/templates/notes/app-provider/side-panel-card-hooks-and-complexity.md +578 -0
  64. package/templates/notes/under-dev/AppProvider.tsx +92 -0
  65. package/templates/notes/under-dev/app-context.ts +14 -0
  66. package/templates/notes/under-dev/card/base-card.tsx +35 -0
  67. package/templates/notes/under-dev/card/index.ts +4 -0
  68. package/templates/notes/under-dev/card/modal-card.tsx +88 -0
  69. package/templates/notes/under-dev/card/side-panel-card.tsx +127 -0
  70. package/templates/notes/under-dev/form-overlay-registry.ts +42 -0
  71. package/templates/notes/under-dev/keyboard-shortcuts-help.tsx +79 -0
  72. package/templates/notes/under-dev/keyboard-utils.ts +22 -0
  73. package/templates/notes/under-dev/overlay/backdrop.tsx +95 -0
  74. package/templates/notes/under-dev/overlay/index.ts +4 -0
  75. package/templates/notes/under-dev/overlay/modal.tsx +43 -0
  76. package/templates/notes/under-dev/overlay/side-panel.tsx +126 -0
  77. package/templates/notes/under-dev/overlay-close.ts +50 -0
  78. package/templates/notes/under-dev/page-shortcut-registry.ts +9 -0
  79. package/templates/notes/under-dev/unsaved-changes-notify.ts +11 -0
  80. package/templates/notes/under-dev/use-keyboard-shortcuts.tsx +110 -0
  81. package/templates/notes/under-dev/useFormDirty.ts +6 -0
  82. package/templates/notes/under-dev/useFormOverlayRegistration.ts +47 -0
  83. package/templates/notes/under-dev/useFormPanel.tsx +18 -0
  84. package/templates/notes/under-dev/useFormTabHandler.ts +22 -0
  85. package/templates/notes/under-dev/useHorizontalWheelScroll.ts +27 -0
  86. package/templates/notes/under-dev/useOverlay.ts +41 -0
  87. package/templates/overlays/index.ts +2 -1
  88. package/templates/overlays/portal/portal.tsx +26 -0
  89. package/templates/overlays/tooltip/readme.tooltip.md +26 -0
  90. package/templates/{button → overlays/tooltip}/tooltip.tsx +1 -1
  91. package/templates/panels/COMPONENTS.md +103 -0
  92. package/templates/panels/README.md +702 -0
  93. package/templates/panels/components/base-card.tsx +33 -0
  94. package/templates/panels/components/index.ts +8 -0
  95. package/templates/panels/components/modal/backdrop.tsx +88 -0
  96. package/templates/panels/components/modal/modal-card.tsx +139 -0
  97. package/templates/panels/components/modal/modal-raw.tsx +36 -0
  98. package/templates/panels/components/modal/modal.tsx +49 -0
  99. package/templates/panels/components/side-panel/side-panel-card.tsx +123 -0
  100. package/templates/panels/components/side-panel/side-panel-raw.tsx +25 -0
  101. package/templates/panels/components/side-panel/side-panel.tsx +135 -0
  102. package/templates/panels/core/PanelProvider.tsx +145 -0
  103. package/templates/panels/core/constants.ts +9 -0
  104. package/templates/panels/core/form-overlay-registry.ts +35 -0
  105. package/templates/panels/core/index.ts +6 -0
  106. package/templates/panels/core/overlay-close.ts +11 -0
  107. package/templates/panels/core/panel-context.ts +41 -0
  108. package/templates/panels/core/types.ts +41 -0
  109. package/templates/panels/hooks/index.ts +7 -0
  110. package/templates/panels/hooks/useFormDirty.ts +6 -0
  111. package/templates/panels/hooks/useFormOverlayRegistration.ts +92 -0
  112. package/templates/panels/hooks/useFormPanel.tsx +18 -0
  113. package/templates/panels/hooks/useFormTabHandler.ts +25 -0
  114. package/templates/panels/hooks/useHorizontalWheelScroll.ts +31 -0
  115. package/templates/panels/hooks/useModalForm.tsx +22 -0
  116. package/templates/panels/hooks/useOverlay.ts +65 -0
  117. package/templates/panels/index.ts +3 -0
  118. package/templates/panels/vendor-example/by-using-modal/VendorModalPage.tsx +47 -0
  119. package/templates/panels/vendor-example/by-using-modal/useVendorModalForm.tsx +19 -0
  120. package/templates/panels/vendor-example/by-using-modal/vendor-modal-form.tsx +112 -0
  121. package/templates/panels/vendor-example/by-using-modal/vendor-types.ts +29 -0
  122. package/templates/panels/vendor-example/by-using-sidepanel/VendorsPage.tsx +47 -0
  123. package/templates/panels/vendor-example/by-using-sidepanel/useVendorFormPanel.tsx +15 -0
  124. package/templates/panels/vendor-example/by-using-sidepanel/vendor-form.tsx +108 -0
  125. package/templates/panels/vendor-example/by-using-sidepanel/vendor-types.ts +29 -0
  126. package/templates/select-dropdown/README.md +62 -0
  127. package/templates/select-dropdown/multiselect-input.tsx +2 -2
  128. package/templates/select-dropdown/select-input.tsx +2 -2
  129. package/templates/skeleton/README.md +53 -0
  130. package/templates/skeleton/index.ts +2 -0
  131. package/templates/skeleton/skeleton.css +36 -0
  132. package/templates/skeleton/skeleton.tsx +40 -0
  133. package/templates/skeleton/types.ts +12 -0
  134. package/templates/spinner/README.md +51 -0
  135. package/templates/spinner/index.ts +1 -0
  136. package/templates/spinner/spinner.css +58 -0
  137. package/templates/spinner/spinner.tsx +263 -0
  138. package/templates/toast/container.tsx +2 -2
  139. package/templates/utilities/formater.dateTime.md +74 -0
  140. package/templates/utilities/formater.dateTime.ts +310 -0
  141. package/templates/utilities/formater.phoneNumber.md +32 -0
  142. package/templates/utilities/formater.phoneNumber.ts +143 -0
  143. package/templates/utilities/sanitize.md +23 -0
  144. package/templates/utilities/sanitize.ts +148 -0
  145. /package/templates/form/{email-input.tsx → text-inputs/email-input.tsx} +0 -0
  146. /package/templates/form/{phone-input.tsx → text-inputs/phone-input.tsx} +0 -0
  147. /package/templates/form/{url-input.tsx → text-inputs/url-input.tsx} +0 -0
  148. /package/templates/{overlays → notes/under-dev/overlay}/portal.tsx +0 -0
package/README.md CHANGED
@@ -129,15 +129,41 @@ npx pejay-ui add toast
129
129
  ```bash
130
130
  npx pejay-ui add overlays/portal
131
131
  ```
132
+ ```bash
133
+ npx pejay-ui add overlays/tooltip
134
+ ```
132
135
  *(Supports category-wide commands: `npx pejay-ui add overlays --all` or `npx pejay-ui add overlays --select`)*
133
136
 
137
+ ### Spinners
138
+ ```bash
139
+ npx pejay-ui add spinner
140
+ ```
141
+ *(Includes a localized `README.md` showing all 9 spinner styles).*
142
+
143
+ ### Skeletons
144
+ ```bash
145
+ npx pejay-ui add skeleton
146
+ ```
147
+ *(Includes a localized `README.md` showing all skeleton presets).*
148
+
134
149
  ### Scaffolds & Templates
135
150
  ```bash
136
151
  npx pejay-ui add tanstack-query-client
152
+ ```
153
+ ```bash
137
154
  npx pejay-ui add react-router-client
155
+ ```
156
+ ```bash
138
157
  npx pejay-ui add tanstack-router-client
158
+ ```
159
+ ```bash
139
160
  npx pejay-ui add axios-client
161
+ ```
162
+ ```bash
140
163
  npx pejay-ui add redux-store-client
164
+ ```
165
+ ```bash
141
166
  npx pejay-ui add rtk-query-client
142
167
  ```
143
168
  *(Supports category-wide commands: `npx pejay-ui add scaffold --all` or `npx pejay-ui add scaffold --select`)*
169
+
package/bin/cli.js CHANGED
@@ -38,6 +38,24 @@ const getFilesRecursively = async (dir) => {
38
38
  return results;
39
39
  };
40
40
 
41
+ /*
42
+ * resolveTargetDir – single source of truth for where a component's files land.
43
+ *
44
+ * Priority:
45
+ * 1. category === "scaffold" → src/<targetDirName|key>/
46
+ * 2. outputDirectory present → <baseDir>/<outputDirectory>/
47
+ * 3. default → <baseDir>/components/<category>/
48
+ */
49
+ const resolveTargetDir = (cwd, baseDir, compKey, compData) => {
50
+ if (compData.category === "scaffold") {
51
+ return path.join(cwd, "src", compData.targetDirName || compKey);
52
+ }
53
+ if (compData.outputDirectory) {
54
+ return path.join(cwd, baseDir, compData.outputDirectory);
55
+ }
56
+ return path.join(cwd, baseDir, "components", compData.category);
57
+ };
58
+
41
59
  const loadRegistry = async () => {
42
60
  const registryDir = path.join(packageRoot, "registry");
43
61
  if (!await fs.pathExists(registryDir)) {
@@ -224,12 +242,7 @@ program
224
242
  let isAlreadyPresent = !!config.installed?.[compToInstall];
225
243
 
226
244
  if (!isAlreadyPresent) {
227
- let targetDir;
228
- if (componentData.category === "scaffold") {
229
- targetDir = path.join(cwd, "src", componentData.targetDirName || compToInstall);
230
- } else {
231
- targetDir = path.join(cwd, config.baseDir, "components", componentData.category);
232
- }
245
+ let targetDir = resolveTargetDir(cwd, config.baseDir, compToInstall, componentData);
233
246
 
234
247
  const sourceFiles = componentData.files || (componentData.path ? [componentData.path] : []);
235
248
  for (const srcFilePath of sourceFiles) {
@@ -355,12 +368,7 @@ program
355
368
  }
356
369
 
357
370
  // 3. Process & Copy Component Files
358
- let targetDir;
359
- if (componentData.category === "scaffold") {
360
- targetDir = path.join(cwd, "src", componentData.targetDirName || compToInstall);
361
- } else {
362
- targetDir = path.join(cwd, config.baseDir, "components", componentData.category);
363
- }
371
+ let targetDir = resolveTargetDir(cwd, config.baseDir, compToInstall, componentData);
364
372
 
365
373
  // Determine list of files to copy
366
374
  const sourceFiles = componentData.files || (componentData.path ? [componentData.path] : []);
@@ -391,6 +399,17 @@ program
391
399
  ? `${relativeToUtils}/cn`
392
400
  : `${relativeToUtils}/cn.js`;
393
401
  componentCode = componentCode.replace(/@\/utils\/cn/g, cnImportPath);
402
+ componentCode = componentCode.replace(/@\/utils/g, cnImportPath);
403
+
404
+ const relativeToBaseDir = path.relative(fileDir, path.join(cwd, config.baseDir)).replace(/\\/g, "/");
405
+ let pejayUiImportPath = relativeToBaseDir;
406
+ if (!pejayUiImportPath.startsWith(".")) {
407
+ pejayUiImportPath = `./${pejayUiImportPath}`;
408
+ }
409
+ if (pejayUiImportPath.endsWith("/")) {
410
+ pejayUiImportPath = pejayUiImportPath.slice(0, -1);
411
+ }
412
+ componentCode = componentCode.replace(/@\/pejay-ui/g, pejayUiImportPath);
394
413
 
395
414
  if (!isTsProject) {
396
415
  const transformed = babel.transformSync(componentCode, {
@@ -421,6 +440,17 @@ program
421
440
  ? `${relativeToUtils}/cn`
422
441
  : `${relativeToUtils}/cn.js`;
423
442
  componentCode = componentCode.replace(/@\/utils\/cn/g, cnImportPath);
443
+ componentCode = componentCode.replace(/@\/utils/g, cnImportPath);
444
+
445
+ const relativeToBaseDir = path.relative(fileDir, path.join(cwd, config.baseDir)).replace(/\\/g, "/");
446
+ let pejayUiImportPath = relativeToBaseDir;
447
+ if (!pejayUiImportPath.startsWith(".")) {
448
+ pejayUiImportPath = `./${pejayUiImportPath}`;
449
+ }
450
+ if (pejayUiImportPath.endsWith("/")) {
451
+ pejayUiImportPath = pejayUiImportPath.slice(0, -1);
452
+ }
453
+ componentCode = componentCode.replace(/@\/pejay-ui/g, pejayUiImportPath);
424
454
 
425
455
  if (!isTsProject) {
426
456
  const transformed = babel.transformSync(componentCode, {
@@ -440,7 +470,7 @@ program
440
470
  }
441
471
 
442
472
  // 3.5 Automatically generate/update category-level and global index.ts/index.js files
443
- if (componentData.category && !["scaffold", "scaffolds", "script", "scripts"].includes(componentData.category.toLowerCase())) {
473
+ if (componentData.category && !componentData.skipIndex && !["scaffold", "scaffolds", "script", "scripts"].includes(componentData.category.toLowerCase())) {
444
474
  const indexExt = isTsProject ? "ts" : "js";
445
475
  const filesInDir = await fs.readdir(targetDir);
446
476
  const exportableFiles = [];
@@ -548,9 +578,9 @@ program
548
578
  }
549
579
 
550
580
  // 1.5 Update index files
551
- if (componentData.category && !["scaffold", "scaffolds", "script", "scripts"].includes(componentData.category.toLowerCase())) {
581
+ if (componentData.category && !componentData.skipIndex && !["scaffold", "scaffolds", "script", "scripts"].includes(componentData.category.toLowerCase())) {
552
582
  const isTsProject = await fs.pathExists(path.join(cwd, "tsconfig.json"));
553
- const targetDir = path.join(cwd, config.baseDir, "components", componentData.category);
583
+ const targetDir = resolveTargetDir(cwd, config.baseDir, component, componentData);
554
584
  const indexExt = isTsProject ? "ts" : "js";
555
585
 
556
586
  if (await fs.pathExists(targetDir)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pejay-ui",
3
- "version": "1.4.3",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "description": "A powerful React UI component CLI tool to initialize, add, and scaffold premium Tailwind CSS components, layouts, overlays, and client templates.",
6
6
  "bin": {
@@ -66,6 +66,7 @@
66
66
  "clsx": "^2.1.1",
67
67
  "dayjs": "^1.11.21",
68
68
  "lucide-react": "^1.17.0",
69
+ "motion": "^12.42.0",
69
70
  "react": "^19.2.6",
70
71
  "react-dom": "^19.2.6",
71
72
  "react-router-dom": "^7.16.0",
@@ -2,8 +2,9 @@
2
2
  "button": {
3
3
  "name": "Button",
4
4
  "category": "button",
5
- "files": ["templates/button/Button.tsx", "templates/button/tooltip.tsx"],
5
+ "files": ["templates/button/Button.tsx", "templates/button/README.md"],
6
6
  "utils": ["cn.ts"],
7
- "peerDependencies": ["clsx", "tailwind-merge"]
7
+ "peerDependencies": ["clsx", "tailwind-merge"],
8
+ "dependencies": ["overlays/tooltip", "spinner"]
8
9
  }
9
10
  }
@@ -2,7 +2,7 @@
2
2
  "dropdown/select-input": {
3
3
  "name": "SelectInput",
4
4
  "category": "select-dropdown",
5
- "files": ["templates/select-dropdown/select-input.tsx"],
5
+ "files": ["templates/select-dropdown/select-input.tsx", "templates/select-dropdown/README.md"],
6
6
  "utils": ["cn.ts"],
7
7
  "peerDependencies": [
8
8
  "clsx",
@@ -10,6 +10,7 @@
10
10
  "lucide-react",
11
11
  "@floating-ui/react"
12
12
  ],
13
+ "dependencies": ["overlays/tooltip"],
13
14
  "supportsCategory": true
14
15
  },
15
16
  "dropdown/multiselect-input": {
@@ -23,6 +24,7 @@
23
24
  "lucide-react",
24
25
  "@floating-ui/react"
25
26
  ],
27
+ "dependencies": ["overlays/tooltip"],
26
28
  "supportsCategory": true
27
29
  }
28
30
  }
@@ -3,7 +3,8 @@
3
3
  "name": "Input",
4
4
  "category": "form",
5
5
  "files": [
6
- "templates/form/input.tsx"
6
+ "templates/form/text-inputs/input.tsx",
7
+ "templates/form/text-inputs/readme.input.md"
7
8
  ],
8
9
  "utils": [
9
10
  "cn.ts"
@@ -19,7 +20,8 @@
19
20
  "name": "AmountInput",
20
21
  "category": "form",
21
22
  "files": [
22
- "templates/form/amount-input.tsx"
23
+ "templates/form/numeric/amount-input.tsx",
24
+ "templates/form/numeric/readme.amount-input.md"
23
25
  ],
24
26
  "utils": [
25
27
  "cn.ts"
@@ -29,13 +31,17 @@
29
31
  "tailwind-merge",
30
32
  "lucide-react"
31
33
  ],
32
- "supportsCategory": true
34
+ "supportsCategory": true,
35
+ "dependencies": [
36
+ "form/input"
37
+ ]
33
38
  },
34
39
  "form/checkbox": {
35
40
  "name": "Checkbox",
36
41
  "category": "form",
37
42
  "files": [
38
- "templates/form/checkbox.tsx"
43
+ "templates/form/choices/checkbox.tsx",
44
+ "templates/form/choices/readme.checkbox.md"
39
45
  ],
40
46
  "utils": [
41
47
  "cn.ts"
@@ -51,7 +57,8 @@
51
57
  "name": "CheckboxGroup",
52
58
  "category": "form",
53
59
  "files": [
54
- "templates/form/checkbox-group.tsx"
60
+ "templates/form/choices/checkbox-group.tsx",
61
+ "templates/form/choices/readme.checkbox-group.md"
55
62
  ],
56
63
  "utils": [
57
64
  "cn.ts"
@@ -69,7 +76,8 @@
69
76
  "name": "DatePicker",
70
77
  "category": "form",
71
78
  "files": [
72
- "templates/form/date-picker.tsx"
79
+ "templates/form/pickers/date-picker.tsx",
80
+ "templates/form/pickers/readme.date-picker.md"
73
81
  ],
74
82
  "utils": [
75
83
  "cn.ts"
@@ -89,7 +97,8 @@
89
97
  "name": "DateRangePicker",
90
98
  "category": "form",
91
99
  "files": [
92
- "templates/form/date-range-picker.tsx"
100
+ "templates/form/pickers/date-range-picker.tsx",
101
+ "templates/form/pickers/readme.date-range-picker.md"
93
102
  ],
94
103
  "utils": [
95
104
  "cn.ts"
@@ -109,7 +118,8 @@
109
118
  "name": "EmailInput",
110
119
  "category": "form",
111
120
  "files": [
112
- "templates/form/email-input.tsx"
121
+ "templates/form/text-inputs/email-input.tsx",
122
+ "templates/form/text-inputs/readme.email-input.md"
113
123
  ],
114
124
  "utils": [
115
125
  "cn.ts"
@@ -125,7 +135,8 @@
125
135
  "name": "FileInput",
126
136
  "category": "form",
127
137
  "files": [
128
- "templates/form/file-input.tsx"
138
+ "templates/form/file/file-input.tsx",
139
+ "templates/form/file/readme.file-input.md"
129
140
  ],
130
141
  "utils": [
131
142
  "cn.ts"
@@ -135,13 +146,17 @@
135
146
  "tailwind-merge",
136
147
  "lucide-react"
137
148
  ],
138
- "supportsCategory": true
149
+ "supportsCategory": true,
150
+ "dependencies": [
151
+ "form/input"
152
+ ]
139
153
  },
140
154
  "form/number-input": {
141
155
  "name": "NumberInput",
142
156
  "category": "form",
143
157
  "files": [
144
- "templates/form/number-input.tsx"
158
+ "templates/form/numeric/number-input.tsx",
159
+ "templates/form/numeric/readme.number-input.md"
145
160
  ],
146
161
  "utils": [
147
162
  "cn.ts"
@@ -151,13 +166,17 @@
151
166
  "tailwind-merge",
152
167
  "lucide-react"
153
168
  ],
154
- "supportsCategory": true
169
+ "supportsCategory": true,
170
+ "dependencies": [
171
+ "form/input"
172
+ ]
155
173
  },
156
174
  "form/password-input": {
157
175
  "name": "PasswordInput",
158
176
  "category": "form",
159
177
  "files": [
160
- "templates/form/password-input.tsx"
178
+ "templates/form/text-inputs/password-input.tsx",
179
+ "templates/form/text-inputs/readme.password-input.md"
161
180
  ],
162
181
  "utils": [
163
182
  "cn.ts"
@@ -173,7 +192,8 @@
173
192
  "name": "PhoneInput",
174
193
  "category": "form",
175
194
  "files": [
176
- "templates/form/phone-input.tsx"
195
+ "templates/form/text-inputs/phone-input.tsx",
196
+ "templates/form/text-inputs/readme.phone-input.md"
177
197
  ],
178
198
  "utils": [
179
199
  "cn.ts"
@@ -189,7 +209,8 @@
189
209
  "name": "Radio",
190
210
  "category": "form",
191
211
  "files": [
192
- "templates/form/radio.tsx"
212
+ "templates/form/choices/radio.tsx",
213
+ "templates/form/choices/readme.radio.md"
193
214
  ],
194
215
  "utils": [
195
216
  "cn.ts"
@@ -204,7 +225,8 @@
204
225
  "name": "RadioGroup",
205
226
  "category": "form",
206
227
  "files": [
207
- "templates/form/radio-group.tsx"
228
+ "templates/form/choices/radio-group.tsx",
229
+ "templates/form/choices/readme.radio-group.md"
208
230
  ],
209
231
  "utils": [
210
232
  "cn.ts"
@@ -222,7 +244,8 @@
222
244
  "name": "RangeSlider",
223
245
  "category": "form",
224
246
  "files": [
225
- "templates/form/range-slider.tsx"
247
+ "templates/form/numeric/range-slider.tsx",
248
+ "templates/form/numeric/readme.range-slider.md"
226
249
  ],
227
250
  "utils": [
228
251
  "cn.ts"
@@ -237,7 +260,8 @@
237
260
  "name": "Switch",
238
261
  "category": "form",
239
262
  "files": [
240
- "templates/form/switch.tsx"
263
+ "templates/form/choices/switch.tsx",
264
+ "templates/form/choices/readme.switch.md"
241
265
  ],
242
266
  "utils": [
243
267
  "cn.ts"
@@ -252,7 +276,8 @@
252
276
  "name": "Textarea",
253
277
  "category": "form",
254
278
  "files": [
255
- "templates/form/textarea.tsx"
279
+ "templates/form/text-inputs/textarea.tsx",
280
+ "templates/form/text-inputs/readme.textarea.md"
256
281
  ],
257
282
  "utils": [
258
283
  "cn.ts"
@@ -267,7 +292,8 @@
267
292
  "name": "TimePicker",
268
293
  "category": "form",
269
294
  "files": [
270
- "templates/form/time-picker.tsx"
295
+ "templates/form/pickers/time-picker.tsx",
296
+ "templates/form/pickers/readme.time-picker.md"
271
297
  ],
272
298
  "utils": [
273
299
  "cn.ts"
@@ -287,7 +313,8 @@
287
313
  "name": "TimeRangePicker",
288
314
  "category": "form",
289
315
  "files": [
290
- "templates/form/time-range-picker.tsx"
316
+ "templates/form/pickers/time-range-picker.tsx",
317
+ "templates/form/pickers/readme.time-range-picker.md"
291
318
  ],
292
319
  "utils": [
293
320
  "cn.ts"
@@ -307,7 +334,8 @@
307
334
  "name": "UrlInput",
308
335
  "category": "form",
309
336
  "files": [
310
- "templates/form/url-input.tsx"
337
+ "templates/form/text-inputs/url-input.tsx",
338
+ "templates/form/text-inputs/readme.url-input.md"
311
339
  ],
312
340
  "utils": [
313
341
  "cn.ts"
@@ -319,4 +347,4 @@
319
347
  ],
320
348
  "supportsCategory": true
321
349
  }
322
- }
350
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "hotkeys": {
3
+ "name": "Hotkeys",
4
+ "category": "hotkeys",
5
+ "outputDirectory": "hotkeys",
6
+ "skipIndex": true,
7
+ "files": ["templates/hotkeys"],
8
+ "peerDependencies": [
9
+ "lucide-react"
10
+ ]
11
+ }
12
+ }
@@ -2,7 +2,23 @@
2
2
  "overlays/portal": {
3
3
  "name": "Portal",
4
4
  "category": "overlays",
5
- "files": ["templates/overlays/portal.tsx"],
5
+ "files": [
6
+ "templates/overlays/portal/portal.tsx"
7
+ ],
6
8
  "supportsCategory": true
9
+ },
10
+ "overlays/tooltip": {
11
+ "name": "Tooltip",
12
+ "category": "overlays",
13
+ "files": [
14
+ "templates/overlays/tooltip/tooltip.tsx",
15
+ "templates/overlays/tooltip/readme.tooltip.md"
16
+ ],
17
+ "supportsCategory": true,
18
+ "peerDependencies": [
19
+ "clsx",
20
+ "tailwind-merge",
21
+ "@floating-ui/react"
22
+ ]
7
23
  }
8
- }
24
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "panels": {
3
+ "name": "Panels",
4
+ "category": "panels",
5
+ "outputDirectory": "panels",
6
+ "skipIndex": true,
7
+ "files": ["templates/panels"],
8
+ "utils": ["cn.ts"],
9
+ "dependencies": [
10
+ "overlays/portal",
11
+ "toast",
12
+ "hotkeys"
13
+ ],
14
+ "peerDependencies": [
15
+ "clsx",
16
+ "tailwind-merge",
17
+ "lucide-react",
18
+ "motion"
19
+ ]
20
+ }
21
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "skeleton": {
3
+ "name": "Skeleton",
4
+ "category": "skeleton",
5
+ "files": [
6
+ "templates/skeleton/skeleton.tsx",
7
+ "templates/skeleton/types.ts",
8
+ "templates/skeleton/skeleton.css",
9
+ "templates/skeleton/README.md"
10
+ ],
11
+ "utils": [
12
+ "cn.ts"
13
+ ],
14
+ "peerDependencies": [
15
+ "clsx",
16
+ "tailwind-merge"
17
+ ],
18
+ "supportsCategory": true
19
+ }
20
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "spinner": {
3
+ "name": "Spinner",
4
+ "category": "spinner",
5
+ "outputDirectory": "spinner",
6
+ "skipIndex": true,
7
+ "files": [
8
+ "templates/spinner/spinner.tsx",
9
+ "templates/spinner/spinner.css",
10
+ "templates/spinner/README.md"
11
+ ]
12
+ }
13
+ }
@@ -1,7 +1,8 @@
1
1
  import React from "react";
2
- import { cn } from "@/utils/cn";
2
+ import { cn } from "@/pejay-ui/utils/cn";
3
3
 
4
- import { Tooltip } from "./tooltip";
4
+ import { Spinner } from "@/pejay-ui/spinner";
5
+ import { Tooltip } from "@/pejay-ui/components/overlays";
5
6
 
6
7
  /* ─────────────────────────────────────────────
7
8
  Types
@@ -28,7 +29,8 @@ export type ButtonVariant =
28
29
  | "success-ghost"
29
30
  | "warning-ghost"
30
31
  | "black-ghost"
31
- | "white-ghost";
32
+ | "white-ghost"
33
+ | "soft";
32
34
  export type RoundedStyle = "full" | "lg" | "md" | "sm" | "none";
33
35
 
34
36
  export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
@@ -67,6 +69,7 @@ const variantMap: Record<ButtonVariant, string> = {
67
69
  warning: "bg-amber-500 hover:bg-amber-600 active:bg-amber-700 text-white",
68
70
  black: "bg-black hover:bg-black/80 active:bg-black/70 text-white",
69
71
  white: "bg-white hover:bg-white/80 active:bg-white/70 text-black",
72
+ soft: "bg-sky-500/10 hover:bg-sky-500/20 active:bg-sky-500/30 text-sky-500",
70
73
 
71
74
  /* ── Soft (coloured text, always-visible tint) ───────── */
72
75
  "primary-soft": "text-sky-500 bg-current/10 hover:bg-current/15 active:bg-current/20",
@@ -119,10 +122,8 @@ export const Button = ({
119
122
  ? stripInteractive(variantMap[variant])
120
123
  : variantMap[variant];
121
124
 
122
- /* Render content: loader node / fallback text / normal children */
123
- const content = isLoading
124
- ? (loader ?? <span className="text-sm font-medium">Loading…</span>)
125
- : children;
125
+ /* Render content: loader node / spinner / normal children */
126
+ const content = isLoading ? (loader ?? <Spinner size="sm" />) : children;
126
127
 
127
128
  return (
128
129
  <Tooltip content={tooltipContent}>
@@ -0,0 +1,81 @@
1
+ # Button & Tooltip Components
2
+
3
+ A highly flexible button component with built-in loading states, visual style presets (variants), size options, prefix/suffix icon slots, and hover tooltip support.
4
+
5
+ ---
6
+
7
+ ## 1. Button Usage & API
8
+
9
+ ```tsx
10
+ import { Button } from "@/pejay-ui/components/button";
11
+
12
+ // Basic Button
13
+ <Button onClick={handleClick}>Click Me</Button>
14
+
15
+ // Custom Styling & Icons
16
+ <Button variant="danger" size="lg" icon={TrashIcon} loading={isDeleting}>
17
+ Delete Record
18
+ </Button>
19
+ ```
20
+
21
+ ### Component Props
22
+
23
+ | Prop | Type | Default | Description |
24
+ | :--- | :--- | :--- | :--- |
25
+ | `children` | `ReactNode` | — | Text or elements inside the button. |
26
+ | `variant` | `ButtonVariant` | `"primary"` | Visual preset theme style. (See options below) |
27
+ | `rounded` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"lg"` | Controls the button border radius. |
28
+ | `disableHoverEffect` | `boolean` | `false` | Removes hover/active style transforms (keeps it static). |
29
+ | `isLoading` | `boolean` | `false` | Displays a loading spinner and disables clicks. |
30
+ | `loader` | `ReactNode` | — | Custom loading component override. |
31
+ | `fullWidth` | `boolean` | `false` | Sets button width to `w-full` (100% parent container width). |
32
+ | `tooltipContent` | `ReactNode \| string` | — | Display a hover tooltip panel with this content. |
33
+
34
+ ---
35
+
36
+ ## 2. Visual Variants & Color Palette Reference
37
+
38
+ We categorize button variants into 3 distinct styling structures. Colors are predefined and tailored for dark/light contrast:
39
+
40
+ ### A. Solid Variants (Filled background, white/black text)
41
+ * **`primary`** : Sky Blue (`bg-sky-500` $\rightarrow$ hover `bg-sky-600` $\rightarrow$ active `bg-sky-700`)
42
+ * **`danger`** : Red (`bg-red-600` $\rightarrow$ hover `bg-red-700` $\rightarrow$ active `bg-red-800`)
43
+ * **`success`** : Emerald (`bg-emerald-600` $\rightarrow$ hover `bg-emerald-700` $\rightarrow$ active `bg-emerald-800`)
44
+ * **`warning`** : Amber (`bg-amber-500` $\rightarrow$ hover `bg-amber-600` $\rightarrow$ active `bg-amber-700`)
45
+ * **`black`** : Black (`bg-black` $\rightarrow$ hover `bg-black/80` $\rightarrow$ active `bg-black/70`)
46
+ * **`white`** : White (`bg-white`, text black $\rightarrow$ hover `bg-white/80` $\rightarrow$ active `bg-white/70`)
47
+
48
+ ---
49
+
50
+ ### B. Soft Variants (Always-visible colored tint background + matching text color)
51
+ *Soft variants use `bg-current` opacity mappings, meaning the background tint color automatically inherits and matches the text color:*
52
+ * **`primary-soft`** / **`soft`** : Sky Blue text + 10% opacity Sky Blue background.
53
+ * **`danger-soft`** : Red text + 10% opacity Red background.
54
+ * **`success-soft`** : Emerald text + 10% opacity Emerald background.
55
+ * **`warning-soft`** : Amber text + 10% opacity Amber background.
56
+ * **`black-soft`** : Black text + 10% opacity Black background.
57
+ * **`white-soft`** : White text + 10% opacity White background.
58
+
59
+ ---
60
+
61
+ ### C. Ghost Variants (Transparent background $\rightarrow$ fades in soft tint on hover)
62
+ * **`primary-ghost`** : Sky Blue text.
63
+ * **`danger-ghost`** : Red text.
64
+ * **`success-ghost`** : Emerald text.
65
+ * **`warning-ghost`** : Amber text.
66
+ * **`black-ghost`** : Black text.
67
+ * **`white-ghost`** : White text.
68
+
69
+ ---
70
+
71
+ ## 3. Tooltip Component
72
+
73
+ A standalone utility to show extra info bubbles on hover for any parent component.
74
+
75
+ ```tsx
76
+ import { Tooltip } from "@/pejay-ui/components/overlays/tooltip";
77
+
78
+ <Tooltip content="Upload CSV file" placement="bottom">
79
+ <div className="p-2 border rounded">Hover Over Me</div>
80
+ </Tooltip>
81
+ ```
@@ -1,2 +1 @@
1
- export * from "./Button";
2
- export * from "./tooltip";
1
+ export * from "./Button";
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { cn } from "@/utils/cn";
2
+ import { cn } from "@/pejay-ui/utils/cn";
3
3
  import { Checkbox, type CheckboxProps } from "./checkbox";
4
4
 
5
5
  /*
@@ -1,6 +1,6 @@
1
1
  import React, { useState } from "react";
2
2
  import { Check } from "lucide-react";
3
- import { cn } from "@/utils/cn";
3
+ import { cn } from "@/pejay-ui/utils/cn";
4
4
 
5
5
  /*
6
6
  * ============================================================================
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { cn } from "@/utils/cn";
2
+ import { cn } from "@/pejay-ui/utils/cn";
3
3
  import { Radio, type RadioProps } from "./radio";
4
4
 
5
5
  /*