@kubit-ui-web/react-components 2.0.0-beta.10 → 2.0.0-beta.12

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 (134) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/.storybook/bundle-sizes.json +13 -0
  3. package/dist/cjs/assets/banner_kubit_readme.png +0 -0
  4. package/dist/cjs/components/avatar/fragments/drawContent.js +1 -1
  5. package/dist/cjs/components/badge/badgeStandAlone.js +1 -1
  6. package/dist/cjs/components/button/button.js +1 -1
  7. package/dist/cjs/components/button/buttonStandAlone.js +1 -1
  8. package/dist/cjs/components/cardImage/cardImageStandAlone.js +1 -1
  9. package/dist/cjs/components/carousel/hooks/useCarousel.js +1 -1
  10. package/dist/cjs/components/carousel/hooks/useCarouselKeyNavigation.js +1 -1
  11. package/dist/cjs/components/checkboxBase/checkboxBaseStandAlone.js +1 -1
  12. package/dist/cjs/components/chip/chipStandAlone.js +1 -1
  13. package/dist/cjs/components/container/containerStandAlone.js +1 -1
  14. package/dist/cjs/components/dataTable/hooks/useDataTableHasScroll.js +1 -1
  15. package/dist/cjs/components/dataTable/hooks/useDataTableShadow.js +1 -1
  16. package/dist/cjs/components/dataTable/hooks/useDataTableStickyDividers.js +1 -1
  17. package/dist/cjs/components/dataTable/hooks/useDataTableStickyLeftColumns.js +1 -1
  18. package/dist/cjs/components/dataTable/hooks/useDataTableStickyRightColumns.js +1 -1
  19. package/dist/cjs/components/dropdownSelected/dropdownSelectedControlled.js +1 -1
  20. package/dist/cjs/components/dropdownSelected/dropdownSelectedStandAlone.js +1 -1
  21. package/dist/cjs/components/input/input.js +1 -1
  22. package/dist/cjs/components/inputSignature/inputSignatureStandAlone.js +1 -1
  23. package/dist/cjs/components/listOptions/listOptionsStandAlone.js +1 -1
  24. package/dist/cjs/components/message/messageStandAlone.js +1 -1
  25. package/dist/cjs/components/modal/fragments/modalHeader.js +1 -1
  26. package/dist/cjs/components/modal/modalControlled.js +1 -1
  27. package/dist/cjs/components/modal/modalStandAlone.js +1 -1
  28. package/dist/cjs/components/modal/modalUnControlled.js +1 -1
  29. package/dist/cjs/components/option/optionStandAlone.js +1 -1
  30. package/dist/cjs/components/popover/hooks/usePopoverInteractions.js +1 -1
  31. package/dist/cjs/components/popover/hooks/usePopoverLifecycle.js +1 -1
  32. package/dist/cjs/components/radioButton/radioButtonStandAlone.js +1 -1
  33. package/dist/cjs/components/selectorBoxFile/components/selectorBoxFileContainerBox.js +1 -1
  34. package/dist/cjs/components/selectorBoxFile/components/selectorBoxFileErrorMessage.js +1 -1
  35. package/dist/cjs/components/selectorBoxFile/components/selectorBoxFileHeader.js +1 -1
  36. package/dist/cjs/components/slider/sliderStandAlone.js +1 -1
  37. package/dist/cjs/components/table/hooks/useTableHasScroll.js +1 -1
  38. package/dist/cjs/components/table/hooks/useTableShadow.js +1 -1
  39. package/dist/cjs/components/table/hooks/useTableStickyLeftColumns.js +1 -1
  40. package/dist/cjs/components/table/hooks/useTableStickyRightColumns.js +1 -1
  41. package/dist/cjs/components/tabs/tabsStandAlone.js +1 -1
  42. package/dist/cjs/components/tag/tagStandAlone.js +1 -1
  43. package/dist/cjs/components/textArea/components/errorStandAlone.js +1 -1
  44. package/dist/cjs/components/textArea/components/helpMessageStandAlone.js +1 -1
  45. package/dist/cjs/components/textArea/components/titleStandAlone.js +1 -1
  46. package/dist/cjs/components/textArea/textAreaStandAlone.js +1 -1
  47. package/dist/cjs/components/toggle/toggleStandAlone.js +1 -1
  48. package/dist/cjs/components/tooltip/tooltipControlled.js +1 -1
  49. package/dist/cjs/components/tooltip/tooltipStandAlone.js +1 -1
  50. package/dist/cjs/components/tooltip/tooltipUnControlled.js +1 -1
  51. package/dist/cjs/lib/components/errorMessage/errorMessageStandAlone.js +1 -1
  52. package/dist/cjs/lib/hooks/useClickOutside/useClickOutside.js +1 -1
  53. package/dist/cjs/lib/hooks/useContentVisibility/useContentVisibility.js +1 -1
  54. package/dist/cjs/lib/hooks/useManageState/useManageState.js +1 -1
  55. package/dist/cjs/lib/hooks/useScrollDetection/useScrollDetection.js +1 -0
  56. package/dist/cjs/lib/hooks/useScrollEffect/useScrollEffect.js +1 -1
  57. package/dist/cjs/lib/hooks/useSwipeDown/useSwipeDown.js +1 -1
  58. package/dist/cjs/lib/utils/keyboard/keyboard.js +1 -1
  59. package/dist/cjs/lib/utils/process/processCommonProp.js +1 -0
  60. package/dist/cjs/scripts/generate-bundle-sizes.mjs +236 -0
  61. package/dist/cjs/scripts/new-component-web-ui.js +2092 -0
  62. package/dist/esm/.storybook/bundle-sizes.json +13 -0
  63. package/dist/esm/assets/banner_kubit_readme.png +0 -0
  64. package/dist/esm/components/avatar/fragments/drawContent.js +1 -1
  65. package/dist/esm/components/badge/badgeStandAlone.js +6 -6
  66. package/dist/esm/components/button/button.js +1 -1
  67. package/dist/esm/components/button/buttonStandAlone.js +3 -3
  68. package/dist/esm/components/cardImage/cardImageStandAlone.js +7 -7
  69. package/dist/esm/components/carousel/hooks/useCarousel.js +1 -1
  70. package/dist/esm/components/carousel/hooks/useCarouselKeyNavigation.js +1 -1
  71. package/dist/esm/components/checkboxBase/checkboxBaseStandAlone.js +4 -4
  72. package/dist/esm/components/chip/chipStandAlone.js +7 -7
  73. package/dist/esm/components/container/containerStandAlone.js +4 -4
  74. package/dist/esm/components/dataTable/hooks/useDataTableHasScroll.js +1 -1
  75. package/dist/esm/components/dataTable/hooks/useDataTableShadow.js +1 -1
  76. package/dist/esm/components/dataTable/hooks/useDataTableStickyDividers.js +1 -1
  77. package/dist/esm/components/dataTable/hooks/useDataTableStickyLeftColumns.js +1 -1
  78. package/dist/esm/components/dataTable/hooks/useDataTableStickyRightColumns.js +1 -1
  79. package/dist/esm/components/dropdownSelected/dropdownSelectedControlled.js +1 -1
  80. package/dist/esm/components/dropdownSelected/dropdownSelectedStandAlone.js +5 -5
  81. package/dist/esm/components/input/input.js +2 -2
  82. package/dist/esm/components/inputSignature/inputSignatureStandAlone.js +3 -3
  83. package/dist/esm/components/listOptions/listOptionsStandAlone.js +2 -2
  84. package/dist/esm/components/message/messageStandAlone.js +5 -5
  85. package/dist/esm/components/modal/fragments/modalHeader.js +1 -1
  86. package/dist/esm/components/modal/modalControlled.js +1 -1
  87. package/dist/esm/components/modal/modalStandAlone.js +3 -3
  88. package/dist/esm/components/modal/modalUnControlled.js +2 -2
  89. package/dist/esm/components/option/optionStandAlone.js +9 -9
  90. package/dist/esm/components/popover/hooks/usePopoverInteractions.js +1 -1
  91. package/dist/esm/components/popover/hooks/usePopoverLifecycle.js +1 -1
  92. package/dist/esm/components/radioButton/radioButtonStandAlone.js +4 -4
  93. package/dist/esm/components/selectorBoxFile/components/selectorBoxFileContainerBox.js +4 -4
  94. package/dist/esm/components/selectorBoxFile/components/selectorBoxFileErrorMessage.js +2 -2
  95. package/dist/esm/components/selectorBoxFile/components/selectorBoxFileHeader.js +5 -5
  96. package/dist/esm/components/slider/sliderStandAlone.js +14 -14
  97. package/dist/esm/components/table/hooks/useTableHasScroll.js +1 -1
  98. package/dist/esm/components/table/hooks/useTableShadow.js +1 -1
  99. package/dist/esm/components/table/hooks/useTableStickyLeftColumns.js +1 -1
  100. package/dist/esm/components/table/hooks/useTableStickyRightColumns.js +1 -1
  101. package/dist/esm/components/tabs/tabsStandAlone.js +4 -4
  102. package/dist/esm/components/tag/tagStandAlone.js +4 -4
  103. package/dist/esm/components/textArea/components/errorStandAlone.js +3 -3
  104. package/dist/esm/components/textArea/components/helpMessageStandAlone.js +1 -1
  105. package/dist/esm/components/textArea/components/titleStandAlone.js +1 -1
  106. package/dist/esm/components/textArea/textAreaStandAlone.js +11 -11
  107. package/dist/esm/components/toggle/toggleStandAlone.js +4 -4
  108. package/dist/esm/components/tooltip/tooltipControlled.js +1 -1
  109. package/dist/esm/components/tooltip/tooltipStandAlone.js +7 -7
  110. package/dist/esm/components/tooltip/tooltipUnControlled.js +1 -1
  111. package/dist/esm/lib/components/errorMessage/errorMessageStandAlone.js +4 -4
  112. package/dist/esm/lib/hooks/useClickOutside/useClickOutside.js +1 -1
  113. package/dist/esm/lib/hooks/useContentVisibility/useContentVisibility.js +1 -1
  114. package/dist/esm/lib/hooks/useManageState/useManageState.js +1 -1
  115. package/dist/esm/lib/hooks/useScrollDetection/useScrollDetection.js +1 -0
  116. package/dist/esm/lib/hooks/useScrollEffect/useScrollEffect.js +1 -1
  117. package/dist/esm/lib/hooks/useSwipeDown/useSwipeDown.js +1 -1
  118. package/dist/esm/lib/utils/keyboard/keyboard.js +1 -1
  119. package/dist/esm/lib/utils/process/processCommonProp.js +1 -0
  120. package/dist/esm/scripts/generate-bundle-sizes.mjs +236 -0
  121. package/dist/esm/scripts/new-component-web-ui.js +2092 -0
  122. package/package.json +11 -11
  123. package/dist/cjs/lib/hooks/useInputFocus/useInputFocus.js +0 -1
  124. package/dist/cjs/lib/hooks/useScrollDetectionWithAutoFocus/useScrollDetectionWithAutoFocus.js +0 -1
  125. package/dist/cjs/lib/utils/process/processIcon/processIcon.js +0 -1
  126. package/dist/cjs/lib/utils/process/processText/processText.js +0 -1
  127. package/dist/cjs/lib/utils/resizeObserver/resizeObserver.js +0 -1
  128. package/dist/esm/lib/hooks/useInputFocus/useInputFocus.js +0 -1
  129. package/dist/esm/lib/hooks/useScrollDetectionWithAutoFocus/useScrollDetectionWithAutoFocus.js +0 -1
  130. package/dist/esm/lib/utils/process/processIcon/processIcon.js +0 -1
  131. package/dist/esm/lib/utils/process/processText/processText.js +0 -1
  132. package/dist/esm/lib/utils/resizeObserver/resizeObserver.js +0 -1
  133. /package/dist/cjs/lib/{hooks/syncRefs → utils/refs}/syncRefs.js +0 -0
  134. /package/dist/esm/lib/{hooks/syncRefs → utils/refs}/syncRefs.js +0 -0
@@ -0,0 +1,2092 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const readline = require('readline');
6
+
7
+ // Create readline interface for interactive prompts
8
+ const rl = readline.createInterface({
9
+ input: process.stdin,
10
+ output: process.stdout,
11
+ });
12
+
13
+ // ================================================================
14
+ // CLI ARGUMENT PARSING
15
+ // ================================================================
16
+
17
+ /**
18
+ * Parse command line arguments manually (no external dependencies)
19
+ * Supports both long (--name) and short (-n) flags
20
+ */
21
+ const parseArguments = (args) => {
22
+ const parsed = {
23
+ name: null,
24
+ states: false,
25
+ controlled: false,
26
+ responsive: false,
27
+ help: false,
28
+ version: false,
29
+ mode: 'interactive', // 'interactive' or 'automatic'
30
+ unknownArgs: [],
31
+ };
32
+
33
+ for (let i = 0; i < args.length; i++) {
34
+ const arg = args[i];
35
+ const nextArg = args[i + 1];
36
+
37
+ switch (arg) {
38
+ case '--name':
39
+ case '-n':
40
+ if (nextArg && !nextArg.startsWith('-')) {
41
+ parsed.name = nextArg;
42
+ parsed.mode = 'automatic';
43
+ i++; // Skip next argument as it's the value
44
+ } else {
45
+ parsed.unknownArgs.push(`${arg} requires a value`);
46
+ }
47
+ break;
48
+ case '--states':
49
+ case '-s':
50
+ parsed.states = true;
51
+ break;
52
+ case '--controlled':
53
+ case '-c':
54
+ parsed.controlled = true;
55
+ break;
56
+ case '--responsive':
57
+ case '-r':
58
+ parsed.responsive = true;
59
+ break;
60
+ case '--help':
61
+ case '-h':
62
+ parsed.help = true;
63
+ break;
64
+ case '--version':
65
+ case '-v':
66
+ parsed.version = true;
67
+ break;
68
+ default:
69
+ // Handle combined short flags like -scr
70
+ if (arg.startsWith('-') && !arg.startsWith('--') && arg.length > 2) {
71
+ for (let j = 1; j < arg.length; j++) {
72
+ switch (arg[j]) {
73
+ case 's':
74
+ parsed.states = true;
75
+ break;
76
+ case 'c':
77
+ parsed.controlled = true;
78
+ break;
79
+ case 'r':
80
+ parsed.responsive = true;
81
+ break;
82
+ case 'h':
83
+ parsed.help = true;
84
+ break;
85
+ case 'v':
86
+ parsed.version = true;
87
+ break;
88
+ case 'n':
89
+ // For combined flags, -n needs to be the last one if it requires a value
90
+ if (
91
+ j === arg.length - 1 &&
92
+ nextArg &&
93
+ !nextArg.startsWith('-')
94
+ ) {
95
+ parsed.name = nextArg;
96
+ parsed.mode = 'automatic';
97
+ i++; // Skip next argument as it's the value
98
+ } else {
99
+ parsed.unknownArgs.push(
100
+ `-n flag in combined flags must be last and followed by a value`,
101
+ );
102
+ }
103
+ break;
104
+ default:
105
+ parsed.unknownArgs.push(`Unknown flag: -${arg[j]}`);
106
+ break;
107
+ }
108
+ }
109
+ } else if (arg.startsWith('-')) {
110
+ parsed.unknownArgs.push(`Unknown argument: ${arg}`);
111
+ } else {
112
+ parsed.unknownArgs.push(`Unexpected argument: ${arg}`);
113
+ }
114
+ break;
115
+ }
116
+ }
117
+
118
+ return parsed;
119
+ };
120
+
121
+ /**
122
+ * Display comprehensive help information
123
+ */
124
+ const showHelp = () => {
125
+ console.log(`
126
+ 🎨 WEB UI Components - New Component Generator
127
+
128
+ USAGE:
129
+ node new-component-web-ui.js [OPTIONS]
130
+
131
+ MODES:
132
+ Interactive Mode (default):
133
+ node new-component-web-ui.js
134
+
135
+ Automatic Mode:
136
+ node new-component-web-ui.js --name <ComponentName> [FLAGS]
137
+
138
+ OPTIONS:
139
+ -n, --name <name> Component name (PascalCase) - enables automatic mode
140
+ -s, --states Include component states (DEFAULT, HOVER, etc.)
141
+ -c, --controlled Generate both Controlled and Uncontrolled versions
142
+ -r, --responsive Include responsive logic and breakpoint handling
143
+ -h, --help Show this help information
144
+ -v, --version Show version information
145
+
146
+ FLAGS:
147
+ • Boolean flags don't require values - their presence means true
148
+ • Flags can be combined: -scr (same as --states --controlled --responsive)
149
+ • Without --name, the script runs in interactive mode
150
+
151
+ EXAMPLES:
152
+ # Interactive mode (asks questions step by step)
153
+ node new-component-web-ui.js
154
+
155
+ # Basic automatic mode (all options default to false)
156
+ node new-component-web-ui.js --name ToggleV2
157
+
158
+ # Automatic mode with specific features
159
+ node new-component-web-ui.js --name ToggleV2 --states --controlled
160
+
161
+ # Using short flags
162
+ node new-component-web-ui.js -n ToggleV2 -s -c -r
163
+
164
+ # Combined short flags
165
+ node new-component-web-ui.js -n ToggleV2 -scr
166
+
167
+ COMPONENT FEATURES:
168
+ • States: DEFAULT, HOVER, PRESSED, FOCUSED, etc.
169
+ • Controlled: Manage component state externally
170
+ • Uncontrolled: Internal state management with default values
171
+ • Responsive: Different behavior for mobile, tablet, desktop
172
+
173
+ VENUS ARCHITECTURE:
174
+ • Hybrid styling: CSS for structure + Design System for theming
175
+ • Accessibility-first development with comprehensive testing
176
+ • TypeScript with strict typing and comprehensive interfaces
177
+ • Storybook documentation with automated argTypes
178
+
179
+ For more information, visit: https://github.com/santander-group-ods/SOFTWEB-web-ui-components
180
+ `);
181
+ };
182
+
183
+ /**
184
+ * Display version information
185
+ */
186
+ const showVersion = () => {
187
+ console.log('🎨 WEB UI Components Generator v2.0.0');
188
+ console.log('Venus Architecture - Modern React Component Library');
189
+ };
190
+
191
+ /**
192
+ * Validate component name
193
+ */
194
+ const validateComponentName = (name) => {
195
+ if (!name) {
196
+ return { valid: false, error: 'Component name is required' };
197
+ }
198
+
199
+ // Check for valid characters (letters, numbers, but must start with letter)
200
+ if (!/^[A-Za-z][A-Za-z0-9]*$/.test(name)) {
201
+ return {
202
+ valid: false,
203
+ error:
204
+ 'Component name must start with a letter and contain only letters and numbers',
205
+ };
206
+ }
207
+
208
+ // Check if it starts with uppercase (PascalCase)
209
+ if (!/^[A-Z]/.test(name)) {
210
+ return {
211
+ valid: false,
212
+ error:
213
+ 'Component name must be in PascalCase (start with uppercase letter)',
214
+ };
215
+ }
216
+
217
+ return { valid: true };
218
+ };
219
+
220
+ /**
221
+ * Display usage information for invalid arguments
222
+ */
223
+ const showUsageError = (error) => {
224
+ console.log(`❌ ${error}\n`);
225
+ console.log('💡 Usage examples:');
226
+ console.log(' Interactive: node new-component-web-ui.js');
227
+ console.log(
228
+ ' Automatic: node new-component-web-ui.js --name ComponentName',
229
+ );
230
+ console.log(' Help: node new-component-web-ui.js --help');
231
+ console.log('');
232
+ };
233
+
234
+ // ================================================================
235
+ // Utility functions
236
+ const toSnakeCase = (str) => {
237
+ return str
238
+ .replace(/([A-Z])/g, '_$1')
239
+ .toLowerCase()
240
+ .replace(/^_/, '');
241
+ };
242
+
243
+ const toComponentName = (str) => {
244
+ // Convert component name to the format used in useClassname component parameter
245
+ // For multi-word components, use snake_case (e.g., aaNew -> aa_new)
246
+ // For V2 components, keep the V2 suffix in snake_case format (e.g., toggleV2 -> toggle_v2)
247
+ return toSnakeCase(str);
248
+ };
249
+
250
+ const toPascalCase = (str) => {
251
+ return str.charAt(0).toUpperCase() + str.slice(1);
252
+ };
253
+
254
+ const toCamelCase = (str) => {
255
+ return str.charAt(0).toLowerCase() + str.slice(1);
256
+ };
257
+
258
+ const toKebabCase = (str) => {
259
+ return str
260
+ .replace(/([A-Z])/g, '-$1')
261
+ .toLowerCase()
262
+ .replace(/^-/, '');
263
+ };
264
+
265
+ const toUpperSnakeCase = (str) => {
266
+ return str
267
+ .replace(/([A-Z])/g, '_$1')
268
+ .toUpperCase()
269
+ .replace(/^_/, '');
270
+ };
271
+
272
+ // Helper functions for V2 components
273
+ const isV2Component = (componentName) => {
274
+ return componentName.toLowerCase().endsWith('v2');
275
+ };
276
+
277
+ const getInternalComponentName = (componentName) => {
278
+ // For V2 components, remove V2 suffix for internal file names
279
+ if (isV2Component(componentName)) {
280
+ return componentName.slice(0, -2); // Remove 'V2' suffix
281
+ }
282
+ return componentName;
283
+ };
284
+
285
+ const getInternalPascalName = (pascalName) => {
286
+ // For V2 components, remove V2 suffix for internal usage
287
+ if (pascalName.endsWith('V2')) {
288
+ return pascalName.slice(0, -2); // Remove 'V2' suffix
289
+ }
290
+ return pascalName;
291
+ };
292
+
293
+ // Template generators
294
+ const generateStandAloneTemplate = (
295
+ componentName,
296
+ pascalName,
297
+ needsResponsive,
298
+ ) => {
299
+ const internalComponentName = getInternalComponentName(componentName);
300
+ const internalPascalName = getInternalPascalName(pascalName);
301
+
302
+ const responsiveImport = needsResponsive
303
+ ? `
304
+ import { useActiveBreakpoints } from '@/lib/hooks/useMediaDevice/useActiveBreakpoints';`
305
+ : '';
306
+
307
+ const responsiveLogic = needsResponsive
308
+ ? `
309
+
310
+ // ==========================================================================
311
+ // RESPONSIVE LOGIC - Get current device breakpoints
312
+ // ==========================================================================
313
+
314
+ // Get active breakpoints for responsive behavior
315
+ const { isMobile, isTablet, isDesktop, isMobileOrTablet } = useActiveBreakpoints();`
316
+ : '';
317
+
318
+ return `import './${internalComponentName}.css';
319
+
320
+ import { type ForwardedRef, forwardRef } from 'react';${responsiveImport}
321
+
322
+ import { classNames } from '@/lib/utils/classNames/classNames';
323
+ import { pickCustomAttributes } from '@/lib/utils/pickCustomAttributes/pickCustomAttributes';
324
+
325
+ import type { ${internalPascalName}StandAloneProps } from './types/${internalComponentName}';
326
+
327
+ /**
328
+ * ${internalPascalName} standalone component for rendering the styled element.
329
+ *
330
+ * This component is responsible for rendering the actual element with
331
+ * the correct classes and layout. It is used internally by higher-level
332
+ * components and is not intended to be used directly in most cases.
333
+ *
334
+ * @example
335
+ * \`\`\`tsx
336
+ * <${internalPascalName}StandAlone cssVariantClasses={{}} cssSizeClasses={{}}>
337
+ * Content
338
+ * </${internalPascalName}StandAlone>
339
+ * \`\`\`
340
+ *
341
+ * @returns The rendered element.
342
+ */
343
+ export const ${internalPascalName}StandAlone = forwardRef(
344
+ (
345
+ {
346
+ cssVariantClasses,
347
+ cssSizeClasses,
348
+ children,
349
+ ...props
350
+ }: ${internalPascalName}StandAloneProps,
351
+ ref: ForwardedRef<HTMLDivElement> | undefined | null,
352
+ ): JSX.Element => {${responsiveLogic}
353
+
354
+ const customProps = pickCustomAttributes(props);
355
+
356
+ return (
357
+ <div
358
+ {...customProps}
359
+ ref={ref}
360
+ className={classNames(
361
+ 'kbt-${toKebabCase(internalComponentName)}',
362
+ cssVariantClasses?.${toComponentName(componentName)},
363
+ cssSizeClasses?.${toComponentName(componentName)},
364
+ )}
365
+ data-testid="${toKebabCase(internalComponentName)}"
366
+ >
367
+ {children}
368
+ {/* Add component content here */}${
369
+ needsResponsive
370
+ ? `
371
+
372
+ {/* Example: Conditional rendering based on breakpoints */}
373
+ {isMobileOrTablet && (
374
+ <div className="kbt-${toKebabCase(internalComponentName)}__mobile-content">
375
+ Mobile/Tablet content
376
+ </div>
377
+ )}
378
+
379
+ {isDesktop && (
380
+ <div className="kbt-${toKebabCase(internalComponentName)}__desktop-content">
381
+ Desktop content
382
+ </div>
383
+ )}`
384
+ : ''
385
+ }
386
+ </div>
387
+ );
388
+ },
389
+ );
390
+ `;
391
+ };
392
+
393
+ const generateControlledTemplate = (componentName, pascalName, hasStates) => {
394
+ const internalComponentName = getInternalComponentName(componentName);
395
+ const internalPascalName = getInternalPascalName(pascalName);
396
+
397
+ return `import { type ForwardedRef, forwardRef } from 'react';
398
+
399
+ import { useClassname } from '@/lib/hooks/useClassName/useClassName';
400
+
401
+ import { ${internalPascalName}StandAlone } from './${internalComponentName}StandAlone';
402
+ import type { ${internalPascalName}ControlledProps } from './types/${internalComponentName}';
403
+ import type {
404
+ ${internalPascalName}SizeCssClasses,
405
+ ${internalPascalName}VariantCssClasses,
406
+ } from './types/${internalComponentName}Theme';
407
+
408
+ /**
409
+ * ${internalPascalName} controlled component
410
+ *
411
+ * @param props - \`${internalPascalName}ControlledProps\` include:
412
+ *
413
+ * - Add controlled component prop descriptions here
414
+ */
415
+ export const ${internalPascalName}Controlled = forwardRef(
416
+ (
417
+ {
418
+ additionalSizeClasses,
419
+ additionalVariantClasses,
420
+ size,
421
+ variant,
422
+ ...props
423
+ }: ${internalPascalName}ControlledProps,
424
+ ref: ForwardedRef<HTMLDivElement> | undefined | null,
425
+ ): JSX.Element => {
426
+ const cssVariantClasses = useClassname<${internalPascalName}VariantCssClasses>({
427
+ additionalClasses: additionalVariantClasses,
428
+ component: '${toComponentName(componentName)}',
429
+ variant,
430
+ });
431
+ const cssSizeClasses = useClassname<${internalPascalName}SizeCssClasses>({
432
+ additionalClasses: additionalSizeClasses,
433
+ component: '${toComponentName(componentName)}',
434
+ variant: size,
435
+ });
436
+
437
+ return (
438
+ <${internalPascalName}StandAlone
439
+ {...props}
440
+ ref={ref}
441
+ cssSizeClasses={cssSizeClasses}
442
+ cssVariantClasses={cssVariantClasses}
443
+ />
444
+ );
445
+ },
446
+ );
447
+ `;
448
+ };
449
+
450
+ const generateUncontrolledTemplate = (componentName, pascalName) => {
451
+ const internalComponentName = getInternalComponentName(componentName);
452
+ const internalPascalName = getInternalPascalName(pascalName);
453
+
454
+ return `import { type ForwardedRef, forwardRef } from 'react';
455
+
456
+ import { ${internalPascalName}Controlled } from './${internalComponentName}Controlled';
457
+ import type { ${internalPascalName}UncontrolledProps } from './types/${internalComponentName}';
458
+
459
+ /**
460
+ * ${internalPascalName} uncontrolled component
461
+ *
462
+ * @param props - \`${internalPascalName}UncontrolledProps\` include:
463
+ *
464
+ * - Add uncontrolled component prop descriptions here
465
+ */
466
+ export const ${internalPascalName}Uncontrolled = forwardRef(
467
+ (
468
+ {
469
+ variant,
470
+ size,
471
+ // Add default values for uncontrolled props
472
+ ...props
473
+ }: ${internalPascalName}UncontrolledProps,
474
+ ref: ForwardedRef<HTMLDivElement> | undefined | null,
475
+ ): JSX.Element => {
476
+ // TODO: Add uncontrolled state management
477
+ // const [internalState, setInternalState] = useState(defaultValue);
478
+
479
+ return (
480
+ <${internalPascalName}Controlled
481
+ {...props}
482
+ ref={ref}
483
+ variant={variant}
484
+ size={size}
485
+ />
486
+ );
487
+ },
488
+ );
489
+ `;
490
+ };
491
+
492
+ const generateMainComponentTemplate = (
493
+ componentName,
494
+ pascalName,
495
+ hasStates,
496
+ ) => {
497
+ const internalComponentName = getInternalComponentName(componentName);
498
+ const internalPascalName = getInternalPascalName(pascalName);
499
+
500
+ return `import { type ForwardedRef, forwardRef } from 'react';
501
+
502
+ import { useClassname } from '@/lib/hooks/useClassName/useClassName';
503
+
504
+ import { ${internalPascalName}StandAlone } from './${internalComponentName}StandAlone';
505
+ import type { ${internalPascalName}Props } from './types/${internalComponentName}';
506
+ import type {
507
+ ${internalPascalName}SizeCssClasses,
508
+ ${internalPascalName}VariantCssClasses,
509
+ } from './types/${internalComponentName}Theme';
510
+
511
+ /**
512
+ * ${internalPascalName} component
513
+ *
514
+ * @param props - \`${internalPascalName}Props\` include:
515
+ *
516
+ * - Add prop descriptions here
517
+ */
518
+ export const ${internalPascalName} = forwardRef(
519
+ <Variant extends string | undefined, Size extends string | undefined>(
520
+ {
521
+ additionalSizeClasses,
522
+ additionalVariantClasses,
523
+ size,
524
+ variant,
525
+ ...props
526
+ }: ${internalPascalName}Props<Variant, Size>,
527
+ ref: ForwardedRef<HTMLDivElement> | undefined | null,
528
+ ): JSX.Element => {
529
+ const cssVariantClasses = useClassname<${internalPascalName}VariantCssClasses>({
530
+ additionalClasses: additionalVariantClasses,
531
+ component: '${toComponentName(componentName)}',
532
+ variant,
533
+ });
534
+ const cssSizeClasses = useClassname<${internalPascalName}SizeCssClasses>({
535
+ additionalClasses: additionalSizeClasses,
536
+ component: '${toComponentName(componentName)}',
537
+ variant: size,
538
+ });
539
+
540
+ return (
541
+ <${internalPascalName}StandAlone
542
+ {...props}
543
+ ref={ref}
544
+ cssSizeClasses={cssSizeClasses}
545
+ cssVariantClasses={cssVariantClasses}
546
+ />
547
+ );
548
+ },
549
+ );
550
+ `;
551
+ };
552
+
553
+ const generateIndexTemplate = (
554
+ componentName,
555
+ pascalName,
556
+ needsBothVersions,
557
+ ) => {
558
+ const internalComponentName = getInternalComponentName(componentName);
559
+ const internalPascalName = getInternalPascalName(pascalName);
560
+
561
+ // Check if this is a V2 component
562
+ const isV2 = isV2Component(pascalName);
563
+
564
+ if (needsBothVersions) {
565
+ if (isV2) {
566
+ // For V2 components with both versions, export with V2 suffix
567
+ return `export { ${internalPascalName}Controlled as ${pascalName}Controlled } from './${internalComponentName}Controlled';
568
+ export { ${internalPascalName}Uncontrolled as ${pascalName}Uncontrolled } from './${internalComponentName}Uncontrolled';
569
+ export { ${internalPascalName}StandAlone as ${pascalName}StandAlone } from './${internalComponentName}StandAlone';
570
+
571
+ // Export types
572
+ export * from './types';
573
+ `;
574
+ } else {
575
+ return `export { ${internalPascalName}Controlled } from './${internalComponentName}Controlled';
576
+ export { ${internalPascalName}Uncontrolled } from './${internalComponentName}Uncontrolled';
577
+ export { ${internalPascalName}StandAlone } from './${internalComponentName}StandAlone';
578
+
579
+ // Export types
580
+ export type * from './types/${internalComponentName}';
581
+ `;
582
+ }
583
+ }
584
+
585
+ if (isV2) {
586
+ // For single V2 components, export main component with V2 suffix
587
+ return `export { ${internalPascalName} as ${pascalName} } from './${internalComponentName}';
588
+
589
+ // Export types
590
+ export * from './types';
591
+ `;
592
+ } else {
593
+ return `export { ${internalPascalName} } from './${internalComponentName}';
594
+
595
+ // Export types
596
+ export type * from './types/${internalComponentName}';
597
+ `;
598
+ }
599
+ };
600
+
601
+ const generateMainTypeTemplate = (
602
+ componentName,
603
+ pascalName,
604
+ needsBothVersions,
605
+ hasStates,
606
+ ) => {
607
+ const internalComponentName = getInternalComponentName(componentName);
608
+ const internalPascalName = getInternalPascalName(pascalName);
609
+
610
+ // Use folder name for import paths but internal names for types
611
+ const folderName = componentName; // Keep V2 in folder name if present
612
+
613
+ const interfaces = needsBothVersions
614
+ ? `
615
+
616
+ export interface ${internalPascalName}ControlledProps extends ${internalPascalName}StandAloneProps {
617
+ // Add controlled component specific props here
618
+ variant?: string;
619
+ size?: string;
620
+ additionalVariantClasses?: ${internalPascalName}VariantCssClasses;
621
+ additionalSizeClasses?: ${internalPascalName}SizeCssClasses;
622
+ }
623
+
624
+ export interface ${internalPascalName}UncontrolledProps extends ${internalPascalName}ControlledProps {
625
+ // Add uncontrolled component specific props here
626
+ // defaultValue?: any;
627
+ }`
628
+ : `export interface ${internalPascalName}Props<
629
+ Variant = undefined extends string | unknown
630
+ ? string | undefined
631
+ : string | unknown,
632
+ Size = undefined extends string | unknown
633
+ ? string | undefined
634
+ : string | unknown,
635
+ > extends ${internalPascalName}StandAloneProps {
636
+ // Add additional props for the main component here
637
+ variant?: Variant;
638
+ size?: Size;
639
+ additionalVariantClasses?: ${internalPascalName}VariantCssClasses;
640
+ additionalSizeClasses?: ${internalPascalName}SizeCssClasses;
641
+ }`;
642
+
643
+ return `import type { PropsWithChildren } from 'react';
644
+
645
+ import type { DataAttributes } from '@/lib/types/dataAttributes/dataAttributes';
646
+
647
+ import type {
648
+ ${internalPascalName}SizeCssClasses,
649
+ ${internalPascalName}VariantCssClasses,
650
+ } from './${internalComponentName}Theme';
651
+
652
+ /**
653
+ * Interface for the standalone ${internalPascalName} component.
654
+ */
655
+ export interface ${internalPascalName}StandAloneProps
656
+ extends PropsWithChildren<{}>,
657
+ DataAttributes {
658
+ cssVariantClasses?: ${internalPascalName}VariantCssClasses;
659
+ cssSizeClasses?: ${internalPascalName}SizeCssClasses;
660
+ // Add StandAlone component props here
661
+ }
662
+ ${interfaces}
663
+ `;
664
+ };
665
+
666
+ const generateStateTemplate = (componentName, pascalName) => {
667
+ const internalPascalName = getInternalPascalName(pascalName);
668
+
669
+ return `export enum ${internalPascalName}StateType {
670
+ DEFAULT = 'DEFAULT',
671
+ }
672
+ `;
673
+ };
674
+
675
+ const generateThemeTypeTemplate = (componentName, pascalName, hasStates) => {
676
+ const internalComponentName = getInternalComponentName(componentName);
677
+ const internalPascalName = getInternalPascalName(pascalName);
678
+
679
+ return `// External types
680
+ import type { CssGeneratorType } from '@kubit/kubit-css-style-generator';
681
+
682
+ import type { CssGenerator } from '@/lib/types/cssGenerator/cssGenerator';
683
+
684
+ /**
685
+ * @description
686
+ * ${internalPascalName} size props type
687
+ */
688
+ export interface ${internalPascalName}SizeStyleProps {
689
+ ${toComponentName(componentName)}: CssGeneratorType;
690
+ }
691
+
692
+ export interface ${internalPascalName}VariantStyleProps {
693
+ ${toComponentName(componentName)}: CssGeneratorType;
694
+ }
695
+
696
+ export type ${internalPascalName}VariantStyles<Variant extends string> = {
697
+ [key in Variant]?: ${internalPascalName}VariantStyleProps;
698
+ };
699
+
700
+ /**
701
+ * @description
702
+ * ${internalPascalName} size type
703
+ */
704
+ export type ${internalPascalName}SizeStyles<Size extends string> = {
705
+ [key in Size]?: ${internalPascalName}SizeStyleProps;
706
+ };
707
+
708
+ /**
709
+ * @description
710
+ * ${internalPascalName} styles type
711
+ */
712
+ export type ${internalPascalName}Styles<
713
+ Variant extends string,
714
+ Size extends string,
715
+ > = ${internalPascalName}VariantStyles<Variant> & ${internalPascalName}SizeStyles<Size>;
716
+
717
+ // Create the classNames interfaces
718
+ export type ${internalPascalName}VariantCssClasses = CssGenerator<
719
+ ${internalPascalName}VariantStyleProps,
720
+ '${toComponentName(componentName)}'
721
+ >;
722
+ export type ${internalPascalName}SizeCssClasses = CssGenerator<${internalPascalName}SizeStyleProps, '${toComponentName(componentName)}'>;
723
+ `;
724
+ };
725
+
726
+ const generateTypesIndexTemplate = (componentName, hasStates) => {
727
+ const internalComponentName = getInternalComponentName(componentName);
728
+ const pascalName = toPascalCase(componentName);
729
+ const internalPascalName = getInternalPascalName(pascalName);
730
+ const isV2 = isV2Component(pascalName);
731
+
732
+ if (isV2) {
733
+ // For V2 components, export types with V2 suffix using aliases
734
+ return `// Export types with V2 suffix to avoid conflicts with V1
735
+ export type {
736
+ ${internalPascalName}StandAloneProps as ${pascalName}StandAloneProps,
737
+ ${internalPascalName}ControlledProps as ${pascalName}ControlledProps,
738
+ ${internalPascalName}UncontrolledProps as ${pascalName}UncontrolledProps,
739
+ } from './${internalComponentName}';
740
+
741
+ export type {
742
+ ${internalPascalName}VariantCssClasses as ${pascalName}VariantCssClasses,
743
+ ${internalPascalName}SizeCssClasses as ${pascalName}SizeCssClasses,
744
+ ${internalPascalName}VariantStyleProps as ${pascalName}VariantStyleProps,
745
+ ${internalPascalName}SizeStyleProps as ${pascalName}SizeStyleProps,
746
+ ${internalPascalName}VariantStyles as ${pascalName}VariantStyles,
747
+ ${internalPascalName}SizeStyles as ${pascalName}SizeStyles,
748
+ } from './${internalComponentName}Theme';
749
+ `;
750
+ } else {
751
+ // For V1 components, use direct exports
752
+ return `export * from './${internalComponentName}';
753
+ export * from './${internalComponentName}Theme';
754
+ `;
755
+ }
756
+ };
757
+
758
+ const generateTestTemplate = (componentName, pascalName, needsBothVersions) => {
759
+ const internalComponentName = getInternalComponentName(componentName);
760
+ const internalPascalName = getInternalPascalName(pascalName);
761
+
762
+ if (needsBothVersions) {
763
+ return `import { screen } from '@testing-library/react';
764
+ import { axe } from 'vitest-axe';
765
+
766
+ import { render } from '@/lib/tests/render/render';
767
+
768
+ import { ${internalPascalName}Controlled } from '../${internalComponentName}Controlled';
769
+ import { ${internalPascalName}Uncontrolled } from '../${internalComponentName}Uncontrolled';
770
+ import type { ${internalPascalName}ControlledProps, ${internalPascalName}UncontrolledProps } from '../types/${internalComponentName}';
771
+
772
+ describe('${pascalName}', () => {
773
+ describe('Controlled', () => {
774
+ const controlledProps: ${internalPascalName}ControlledProps = {
775
+ variant: 'REGULAR',
776
+ // Add controlled-specific props for testing
777
+ };
778
+
779
+ it('should render controlled version correctly', () => {
780
+ const { container } = render(<${internalPascalName}Controlled {...controlledProps} />);
781
+
782
+ const component = container.firstChild;
783
+ expect(component).toBeInTheDocument();
784
+ });
785
+
786
+ it('should have no accessibility violations in controlled mode', async () => {
787
+ const { container } = render(<${internalPascalName}Controlled {...controlledProps} />);
788
+
789
+ const results = await axe(container);
790
+ expect(results.violations).toHaveLength(0);
791
+ });
792
+ });
793
+
794
+ describe('Uncontrolled', () => {
795
+ const uncontrolledProps: ${internalPascalName}UncontrolledProps = {
796
+ // Add uncontrolled-specific props for testing
797
+ // defaultValue: 'some default value',
798
+ };
799
+
800
+ it('should render uncontrolled version correctly', () => {
801
+ const { container } = render(<${internalPascalName}Uncontrolled {...uncontrolledProps} />);
802
+
803
+ const component = container.firstChild;
804
+ expect(component).toBeInTheDocument();
805
+ });
806
+
807
+ it('should have no accessibility violations in uncontrolled mode', async () => {
808
+ const { container } = render(<${internalPascalName}Uncontrolled {...uncontrolledProps} />);
809
+
810
+ const results = await axe(container);
811
+ expect(results.violations).toHaveLength(0);
812
+ });
813
+ });
814
+ });
815
+ `;
816
+ }
817
+
818
+ return `import { screen } from '@testing-library/react';
819
+ import { axe } from 'vitest-axe';
820
+
821
+ import { render } from '@/lib/tests/render/render';
822
+
823
+ import { ${internalPascalName} } from '../${internalComponentName}';
824
+ import type { ${internalPascalName}Props } from '../types/${internalComponentName}';
825
+
826
+ describe('${pascalName}', () => {
827
+ const defaultProps: ${internalPascalName}Props = {
828
+ // Add default props for testing
829
+ };
830
+
831
+ it('should render component correctly', () => {
832
+ const { container } = render(<${internalPascalName} {...defaultProps} />);
833
+
834
+ const component = container.firstChild;
835
+ expect(component).toBeInTheDocument();
836
+ });
837
+
838
+ it('should have no accessibility violations', async () => {
839
+ const { container } = render(<${internalPascalName} {...defaultProps} />);
840
+
841
+ const results = await axe(container);
842
+ expect(results.violations).toHaveLength(0);
843
+ });
844
+ });
845
+ `;
846
+ };
847
+
848
+ const generateStoriesTemplate = (componentName, pascalName) => {
849
+ const internalComponentName = getInternalComponentName(componentName);
850
+ const internalPascalName = getInternalPascalName(pascalName);
851
+
852
+ return `import type { Meta, StoryObj } from '@storybook/react-vite';
853
+
854
+ import { ${internalPascalName} as ${internalPascalName}Story } from '../${internalComponentName}';
855
+ import { argtypes } from './argtypes';
856
+
857
+ const meta = {
858
+ argTypes: argtypes(),
859
+ component: ${internalPascalName}Story,
860
+ parameters: {
861
+ figmaUrl: '<URL FOR THE COMPONENT DESIGN>',
862
+ githubUrl: '<URL FOR THE COMPONENT SOURCE>',
863
+ layout: 'centered',
864
+ },
865
+ tags: ['autodocs', 'actions'],
866
+ title: 'Components/<COMPONENT_GROUP>/${pascalName}',
867
+ } satisfies Meta<typeof ${internalPascalName}Story>;
868
+
869
+ export default meta;
870
+
871
+ type Story = StoryObj<typeof meta>;
872
+
873
+ export const Default: Story = {
874
+ args: {
875
+ children: '${pascalName} Content',
876
+ variant: 'REGULAR',
877
+ size: 'MEDIUM',
878
+ },
879
+ };
880
+ `;
881
+ };
882
+
883
+ const generateControlledStoriesTemplate = (componentName, pascalName) => {
884
+ const internalComponentName = getInternalComponentName(componentName);
885
+ const internalPascalName = getInternalPascalName(pascalName);
886
+
887
+ return `import type { Meta, StoryObj } from '@storybook/react-vite';
888
+
889
+ import { ${internalPascalName}Controlled as ${internalPascalName}ControlledStory } from '../${internalComponentName}Controlled';
890
+ import { argtypes } from './argtypesControlled';
891
+
892
+ const meta = {
893
+ argTypes: argtypes(),
894
+ component: ${internalPascalName}ControlledStory,
895
+ parameters: {
896
+ figmaUrl: '<URL FOR THE COMPONENT DESIGN>',
897
+ githubUrl: '<URL FOR THE COMPONENT SOURCE>',
898
+ layout: 'centered',
899
+ },
900
+ tags: ['autodocs', 'actions'],
901
+ title: 'Components/<COMPONENT_GROUP>/${pascalName}/Controlled',
902
+ } satisfies Meta<typeof ${internalPascalName}ControlledStory>;
903
+
904
+ export default meta;
905
+
906
+ type Story = StoryObj<typeof meta>;
907
+
908
+ export const Default: Story = {
909
+ args: {
910
+ variant: 'REGULAR',
911
+ size: 'MEDIUM',
912
+ children: '${pascalName} Controlled',
913
+ },
914
+ };
915
+ `;
916
+ };
917
+
918
+ const generateUncontrolledStoriesTemplate = (componentName, pascalName) => {
919
+ const internalComponentName = getInternalComponentName(componentName);
920
+ const internalPascalName = getInternalPascalName(pascalName);
921
+
922
+ return `import type { Meta, StoryObj } from '@storybook/react-vite';
923
+
924
+ import { ${internalPascalName}Uncontrolled as ${internalPascalName}UncontrolledStory } from '../${internalComponentName}Uncontrolled';
925
+ import { argtypes } from './argtypesUncontrolled';
926
+
927
+ const meta = {
928
+ argTypes: argtypes(),
929
+ component: ${internalPascalName}UncontrolledStory,
930
+ parameters: {
931
+ figmaUrl: '<URL FOR THE COMPONENT DESIGN>',
932
+ githubUrl: '<URL FOR THE COMPONENT SOURCE>',
933
+ layout: 'centered',
934
+ },
935
+ tags: ['autodocs', 'actions'],
936
+ title: 'Components/<COMPONENT_GROUP>/${pascalName}/Uncontrolled',
937
+ } satisfies Meta<typeof ${internalPascalName}UncontrolledStory>;
938
+
939
+ export default meta;
940
+
941
+ type Story = StoryObj<typeof meta>;
942
+
943
+ export const Default: Story = {
944
+ args: {
945
+ variant: 'REGULAR',
946
+ size: 'MEDIUM',
947
+ children: '${pascalName} Uncontrolled',
948
+ // defaultValue: 'Initial value',
949
+ },
950
+ };
951
+ `;
952
+ };
953
+
954
+ const generateArgtypesTemplate = (componentName, pascalName) => {
955
+ const internalComponentName = getInternalComponentName(componentName);
956
+ const internalPascalName = getInternalPascalName(pascalName);
957
+
958
+ return `import type { ArgTypes } from 'storybook/internal/types';
959
+
960
+ import { configArgTypes } from '@/lib/storybook/argtypes/argtypes';
961
+ import { getDisabledArgTypes } from '@/lib/storybook/argtypes/disabledArgTypes';
962
+ import { getSelectorArgTypes } from '@/lib/storybook/argtypes/selectorArgTypes';
963
+ import { getStringtArgTypes } from '@/lib/storybook/argtypes/stringArgTypes';
964
+ import { getVariantArgTypes } from '@/lib/storybook/argtypes/variantArgtypes';
965
+ import { CATEGORY_CONTROL } from '@/lib/storybook/constants/categoryControl';
966
+
967
+ export const argtypes = (): ArgTypes => {
968
+ return {
969
+ ...configArgTypes,
970
+ ...getDisabledArgTypes([
971
+ // Add properties to disable in Storybook controls
972
+ 'cssVariantClasses',
973
+ 'cssSizeClasses',
974
+ ]),
975
+ children: getStringtArgTypes({
976
+ category: CATEGORY_CONTROL.CONTENT,
977
+ keyName: 'children',
978
+ name: '${internalComponentName}',
979
+ }),
980
+ size: getSelectorArgTypes({
981
+ name: '${internalComponentName}',
982
+ options: { LARGE: 'LARGE', MEDIUM: 'MEDIUM', SMALL: 'SMALL' }, // Update with actual size options
983
+ category: CATEGORY_CONTROL.MODIFIERS,
984
+ keyName: 'size',
985
+ }),
986
+ variant: getVariantArgTypes({
987
+ name: '${internalComponentName}',
988
+ keyVariant: 'variant',
989
+ variants: { REGULAR: 'REGULAR' }, // Update with actual variant options
990
+ category: CATEGORY_CONTROL.MODIFIERS,
991
+ }),
992
+ // Add more component-specific argTypes here
993
+ };
994
+ };
995
+ `;
996
+ };
997
+
998
+ const generateArgtypesControlledTemplate = (componentName, pascalName) => {
999
+ const internalComponentName = getInternalComponentName(componentName);
1000
+ const internalPascalName = getInternalPascalName(pascalName);
1001
+
1002
+ return `import type { ArgTypes } from 'storybook/internal/types';
1003
+
1004
+ import { configArgTypes } from '@/lib/storybook/argtypes/argtypes';
1005
+ import { getDisabledArgTypes } from '@/lib/storybook/argtypes/disabledArgTypes';
1006
+ import { getSelectorArgTypes } from '@/lib/storybook/argtypes/selectorArgTypes';
1007
+ import { getStringtArgTypes } from '@/lib/storybook/argtypes/stringArgTypes';
1008
+ import { getVariantArgTypes } from '@/lib/storybook/argtypes/variantArgtypes';
1009
+ import { CATEGORY_CONTROL } from '@/lib/storybook/constants/categoryControl';
1010
+
1011
+ export const argtypes = (): ArgTypes => {
1012
+ return {
1013
+ ...configArgTypes,
1014
+ ...getDisabledArgTypes([
1015
+ // Add properties to disable in Storybook controls
1016
+ 'cssVariantClasses',
1017
+ 'cssSizeClasses',
1018
+ ]),
1019
+ children: getStringtArgTypes({
1020
+ category: CATEGORY_CONTROL.CONTENT,
1021
+ keyName: 'children',
1022
+ name: '${internalComponentName}',
1023
+ }),
1024
+ size: getSelectorArgTypes({
1025
+ name: '${internalComponentName}',
1026
+ options: { LARGE: 'LARGE', MEDIUM: 'MEDIUM', SMALL: 'SMALL' }, // Update with actual size options
1027
+ category: CATEGORY_CONTROL.MODIFIERS,
1028
+ keyName: 'size',
1029
+ }),
1030
+ variant: getVariantArgTypes({
1031
+ name: '${internalComponentName}',
1032
+ keyVariant: 'variant',
1033
+ variants: { REGULAR: 'REGULAR' }, // Update with actual variant options
1034
+ category: CATEGORY_CONTROL.MODIFIERS,
1035
+ }),
1036
+ // Add more controlled-specific argTypes here
1037
+ };
1038
+ };
1039
+ `;
1040
+ };
1041
+
1042
+ const generateArgtypesUncontrolledTemplate = (componentName, pascalName) => {
1043
+ const internalComponentName = getInternalComponentName(componentName);
1044
+ const internalPascalName = getInternalPascalName(pascalName);
1045
+
1046
+ return `import type { ArgTypes } from 'storybook/internal/types';
1047
+
1048
+ import { configArgTypes } from '@/lib/storybook/argtypes/argtypes';
1049
+ import { getDisabledArgTypes } from '@/lib/storybook/argtypes/disabledArgTypes';
1050
+ import { getSelectorArgTypes } from '@/lib/storybook/argtypes/selectorArgTypes';
1051
+ import { getStringtArgTypes } from '@/lib/storybook/argtypes/stringArgTypes';
1052
+ import { getVariantArgTypes } from '@/lib/storybook/argtypes/variantArgtypes';
1053
+ import { CATEGORY_CONTROL } from '@/lib/storybook/constants/categoryControl';
1054
+
1055
+ export const argtypes = (): ArgTypes => {
1056
+ return {
1057
+ ...configArgTypes,
1058
+ ...getDisabledArgTypes([
1059
+ // Add properties to disable in Storybook controls
1060
+ 'cssVariantClasses',
1061
+ 'cssSizeClasses',
1062
+ ]),
1063
+ children: getStringtArgTypes({
1064
+ category: CATEGORY_CONTROL.CONTENT,
1065
+ keyName: 'children',
1066
+ name: '${internalComponentName}',
1067
+ }),
1068
+ size: getSelectorArgTypes({
1069
+ name: '${internalComponentName}',
1070
+ options: { LARGE: 'LARGE', MEDIUM: 'MEDIUM', SMALL: 'SMALL' }, // Update with actual size options
1071
+ category: CATEGORY_CONTROL.MODIFIERS,
1072
+ keyName: 'size',
1073
+ }),
1074
+ variant: getVariantArgTypes({
1075
+ name: '${internalComponentName}',
1076
+ keyVariant: 'variant',
1077
+ variants: { REGULAR: 'REGULAR' }, // Update with actual variant options
1078
+ category: CATEGORY_CONTROL.MODIFIERS,
1079
+ }),
1080
+ // Add more uncontrolled-specific argTypes here
1081
+ };
1082
+ };
1083
+ `;
1084
+ };
1085
+
1086
+ const generateCssTemplate = (componentName, pascalName, hasStates) => {
1087
+ const internalComponentName = getInternalComponentName(componentName);
1088
+ const kebabCaseName = toKebabCase(internalComponentName);
1089
+
1090
+ return `/*
1091
+ * ${pascalName} Component CSS - Venus Architecture
1092
+ *
1093
+ * This file contains STRUCTURAL BASE STYLES for the ${pascalName} component.
1094
+ * These styles are theme-independent and handle layout, positioning, and behaviors
1095
+ * that don't change based on design system tokens.
1096
+ *
1097
+ * VENUS HYBRID STYLING APPROACH:
1098
+ * - CSS Files (.css): Structural layout, positioning, behavioral styles
1099
+ * - Design System (styles.ts): Theme-based styles (colors, spacings, typography)
1100
+ *
1101
+ * DESIGN SYSTEM INTEGRATION:
1102
+ * - Theme styles: /lib/designSystem/kubit/components/${componentName}/styles.ts
1103
+ * - Variants: /lib/designSystem/kubit/components/${componentName}/variants.ts
1104
+ *
1105
+ * RESPONSIVE STRATEGY:
1106
+ * - CSS: Static structural responsive behavior (if needed)
1107
+ * - Design System: $mediaQueries for theme-based responsive styles
1108
+ * - Component Logic: useActiveBreakpoints hook for behavior-based responsive logic
1109
+ */
1110
+
1111
+ .kbt-${kebabCaseName} {
1112
+ /* === STRUCTURAL BASE STYLES === */
1113
+ /* Layout and positioning (theme-independent) */
1114
+ display: flex;
1115
+ flex-direction: column;
1116
+ align-items: center;
1117
+ justify-content: center;
1118
+ box-sizing: border-box;
1119
+ position: relative;
1120
+
1121
+ /* Add additional structural properties here */
1122
+ /* Examples: gap, min-height, overflow, etc. */
1123
+ }
1124
+
1125
+ /* === INTERACTION STATES === */
1126
+ /* These handle behavior, not appearance (colors come from design system) */
1127
+
1128
+ /* Disabled state - structural behavior */
1129
+ .kbt-${kebabCaseName}:disabled {
1130
+ cursor: not-allowed;
1131
+ pointer-events: none;
1132
+ /* Opacity and colors are handled by design system */
1133
+ }
1134
+
1135
+ /* Focus state - accessibility requirement */
1136
+ .kbt-${kebabCaseName}:focus-visible {
1137
+ outline: 2px solid var(--focus-color, #005fcc);
1138
+ outline-offset: 2px;
1139
+ /* Focus colors should come from design system when available */
1140
+ }
1141
+
1142
+ /* === CSS CUSTOM PROPERTIES === */
1143
+ /* For dynamic control of structural properties */
1144
+ .kbt-${kebabCaseName} {
1145
+ --${kebabCaseName}-display: flex;
1146
+ --${kebabCaseName}-visibility: visible;
1147
+ /* Add more CSS variables as needed for dynamic behavior */
1148
+ }
1149
+
1150
+ /* === UTILITY CLASSES === */
1151
+ /* For conditional structural behavior */
1152
+
1153
+ .kbt-${kebabCaseName}--hidden {
1154
+ display: none;
1155
+ }
1156
+
1157
+ .kbt-${kebabCaseName}--expanded {
1158
+ /* Add expanded state structural changes */
1159
+ }
1160
+
1161
+ /* === IMPORTANT NOTES === */
1162
+ /*
1163
+ * DO NOT ADD HERE:
1164
+ * - Colors (use design system COLORS tokens)
1165
+ * - Spacings/margins/padding (use design system SPACINGS tokens)
1166
+ * - Typography (use design system typography tokens)
1167
+ * - Border colors (use design system BORDERS tokens)
1168
+ * - Theme-based responsive styles (use $mediaQueries in styles.ts)
1169
+ *
1170
+ * ADD HERE:
1171
+ * - Layout properties (display, position, flexbox, grid)
1172
+ * - Structural behavior (cursor, z-index, overflow)
1173
+ * - Animations and transitions (that don't involve colors)
1174
+ * - Complex selectors and pseudo-elements
1175
+ * - CSS variables for dynamic properties
1176
+ */
1177
+ `;
1178
+ };
1179
+
1180
+ // Design System template generators
1181
+ const generateDesignSystemIndexTemplate = (componentName, pascalName) => {
1182
+ return `export { get${pascalName}Styles } from './styles';
1183
+ export * from './variants';
1184
+ `;
1185
+ };
1186
+
1187
+ const generateDesignSystemStylesTemplate = (componentName, pascalName) => {
1188
+ const internalComponentName = getInternalComponentName(componentName);
1189
+ const internalPascalName = getInternalPascalName(pascalName);
1190
+
1191
+ return `import { BORDERS, RADIUS } from '../../foundations/borders';
1192
+ import { COLORS } from '../../foundations/colors';
1193
+ import { SPACINGS } from '../../foundations/spacings';
1194
+ import { ${pascalName}SizeType, ${pascalName}VariantType } from './variants';
1195
+
1196
+ /**
1197
+ * ${internalPascalName} Design System Styles - Venus Architecture
1198
+ *
1199
+ * This file contains THEME-BASED STYLES for the ${pascalName} component.
1200
+ * These styles use design system tokens and handle theming, variants, and responsive behavior.
1201
+ *
1202
+ * INTEGRATION WITH CSS:
1203
+ * - CSS file (${internalComponentName}.css): Handles structural layout and positioning
1204
+ * - This file: Handles colors, spacings, typography, and theme-based responsive behavior
1205
+ *
1206
+ * RESPONSIVE STRATEGY:
1207
+ * - Use $mediaQueries for theme-based responsive styles
1208
+ * - Use component-level useActiveBreakpoints for logic-based responsive behavior
1209
+ */
1210
+
1211
+ // Common styles shared across all variants and sizes
1212
+ const ${internalComponentName}CommonProps = {
1213
+ // Base container styling using design tokens
1214
+ border_radius: RADIUS.radius_50,
1215
+ display: 'flex',
1216
+ align_items: 'center',
1217
+ justify_content: 'center',
1218
+
1219
+ // TODO: Replace with actual component-specific styles
1220
+ // Examples:
1221
+ // border: \`\${BORDERS.border_100} solid transparent\`,
1222
+ // transition: 'all 0.2s ease-in-out',
1223
+ };
1224
+
1225
+ export const ${toUpperSnakeCase(pascalName)} = {
1226
+ // =================================================================
1227
+ // RESPONSIVE BEHAVIOR - Theme-based responsive styles
1228
+ // =================================================================
1229
+ // Use this for styles that change based on design tokens at different breakpoints
1230
+ $mediaQueries: {
1231
+ desktop: {
1232
+ ['default']: {
1233
+ ${internalComponentName}: {
1234
+ // TODO: Add desktop-specific theme styles
1235
+ padding: SPACINGS.spacing_300,
1236
+ gap: SPACINGS.spacing_200,
1237
+ // Add more desktop-specific styling here
1238
+ },
1239
+ },
1240
+ },
1241
+ mobile: {
1242
+ ['default']: {
1243
+ ${internalComponentName}: {
1244
+ // TODO: Add mobile-specific theme styles
1245
+ padding: SPACINGS.spacing_150,
1246
+ gap: SPACINGS.spacing_100,
1247
+ // Add more mobile-specific styling here
1248
+ },
1249
+ },
1250
+ },
1251
+ tablet: {
1252
+ ['default']: {
1253
+ ${internalComponentName}: {
1254
+ // TODO: Add tablet-specific theme styles
1255
+ padding: SPACINGS.spacing_200,
1256
+ gap: SPACINGS.spacing_150,
1257
+ // Add more tablet-specific styling here
1258
+ },
1259
+ },
1260
+ },
1261
+ },
1262
+
1263
+ // =================================================================
1264
+ // SIZE VARIANTS - Applied via size prop
1265
+ // =================================================================
1266
+ // TODO: Replace placeholder sizes with actual component sizes
1267
+ [${pascalName}SizeType.LARGE]: {
1268
+ ...${internalComponentName}CommonProps,
1269
+ padding: SPACINGS.spacing_300,
1270
+ // Add more large size styling
1271
+ },
1272
+ [${pascalName}SizeType.MEDIUM]: {
1273
+ ...${internalComponentName}CommonProps,
1274
+ padding: SPACINGS.spacing_200,
1275
+ // Add more medium size styling
1276
+ },
1277
+ [${pascalName}SizeType.SMALL]: {
1278
+ ...${internalComponentName}CommonProps,
1279
+ padding: SPACINGS.spacing_150,
1280
+ // Add more small size styling
1281
+ },
1282
+
1283
+ // =================================================================
1284
+ // STYLE VARIANTS - Applied via variant prop
1285
+ // =================================================================
1286
+ // TODO: Replace placeholder variant with actual component variants
1287
+ [${pascalName}VariantType.REGULAR]: {
1288
+ ...${internalComponentName}CommonProps,
1289
+ background_color: COLORS.NEUTRAL.color_neutral_bg_250,
1290
+ border: \`\${BORDERS.border_100} solid \${COLORS.NEUTRAL.color_neutral_border_200}\`,
1291
+ color: COLORS.NEUTRAL.color_neutral_font_50,
1292
+
1293
+ // TODO: Add hover, focus, and other interaction states
1294
+ // '&:hover': {
1295
+ // background_color: COLORS.NEUTRAL.color_neutral_bg_200,
1296
+ // },
1297
+ // '&:focus': {
1298
+ // border_color: COLORS.BRAND.color_brand_bg_100,
1299
+ // },
1300
+ },
1301
+
1302
+ // TODO: Add more variants as needed (PRIMARY, SECONDARY, DANGER, etc.)
1303
+ // [${pascalName}VariantType.PRIMARY]: {
1304
+ // ...${internalComponentName}CommonProps,
1305
+ // background_color: COLORS.BRAND.color_brand_bg_100,
1306
+ // color: COLORS.NEUTRAL.color_neutral_font_500,
1307
+ // border: 'none',
1308
+ // },
1309
+ };
1310
+
1311
+ // =================================================================
1312
+ // NOTES FOR IMPLEMENTATION:
1313
+ // =================================================================
1314
+ // 1. Replace all TODO comments with actual component styling
1315
+ // 2. Use design system tokens (COLORS, SPACINGS, BORDERS, etc.) instead of hardcoded values
1316
+ // 3. Add appropriate variants for your component (PRIMARY, SECONDARY, etc.)
1317
+ // 4. Add appropriate sizes if needed (XS, S, M, L, XL, etc.)
1318
+ // 5. Include interaction states (hover, focus, active, disabled)
1319
+ // 6. Use $mediaQueries for responsive behavior based on design tokens
1320
+ // 7. Ensure consistency with existing Venus components
1321
+ `;
1322
+ };
1323
+
1324
+ const generateDesignSystemVariantsTemplate = (componentName, pascalName) => {
1325
+ return `export const ${pascalName}VariantType = {
1326
+ REGULAR: 'REGULAR',
1327
+ // Add more variants as needed
1328
+ } as const;
1329
+
1330
+ export const ${pascalName}SizeType = {
1331
+ LARGE: 'LARGE',
1332
+ MEDIUM: 'MEDIUM',
1333
+ SMALL: 'SMALL',
1334
+ } as const;
1335
+ `;
1336
+ };
1337
+
1338
+ const generateDesignSystemVariantStylesTemplate = (
1339
+ componentName,
1340
+ pascalName,
1341
+ hasStates,
1342
+ ) => {
1343
+ const internalComponentName = getInternalComponentName(componentName);
1344
+ const internalPascalName = getInternalPascalName(pascalName);
1345
+ const folderName = componentName; // Keep V2 in folder name if present
1346
+
1347
+ const statesImport = hasStates
1348
+ ? `
1349
+ import { ${internalPascalName}StateType } from '@/components/${folderName}/types/state';`
1350
+ : '';
1351
+
1352
+ const statesStructure = hasStates
1353
+ ? `
1354
+ return {
1355
+ [${pascalName}VariantType.REGULAR]: {
1356
+ [${internalPascalName}StateType.DEFAULT]: get${internalPascalName}CommonStyles({
1357
+ // Add DEFAULT state specific styles here
1358
+ }),
1359
+ // Add more states here like:
1360
+ // [${internalPascalName}StateType.HOVER]: get${internalPascalName}CommonStyles({
1361
+ // // Add HOVER state specific styles here
1362
+ // }),
1363
+ },
1364
+ };`
1365
+ : `
1366
+ return {
1367
+ [${pascalName}VariantType.REGULAR]: get${internalPascalName}CommonStyles({
1368
+ // Add REGULAR variant specific styles here
1369
+ }),
1370
+ };`;
1371
+
1372
+ return `import { ${internalPascalName}StylesType } from '@/components/${folderName}/types/${internalComponentName}Theme';${statesImport}
1373
+
1374
+ import { get${internalPascalName}CommonStyles } from '../commonStyles';
1375
+ import { ${pascalName}VariantType } from '../variants';
1376
+
1377
+ export const get${internalPascalName}RegularStyles = (): ${internalPascalName}StylesType<${pascalName}VariantType> => {${statesStructure}
1378
+ };
1379
+ `;
1380
+ };
1381
+
1382
+ // File creation functions
1383
+ const createDirectory = (dirPath) => {
1384
+ if (!fs.existsSync(dirPath)) {
1385
+ fs.mkdirSync(dirPath, { recursive: true });
1386
+ console.log(`✅ Created directory: ${dirPath}`);
1387
+ }
1388
+ };
1389
+
1390
+ const createFile = (filePath, content) => {
1391
+ fs.writeFileSync(filePath, content, 'utf8');
1392
+ console.log(`✅ Created file: ${filePath}`);
1393
+ };
1394
+
1395
+ // File update functions
1396
+ const updateComponentsIndex = (componentName) => {
1397
+ const indexFilePath = path.join(
1398
+ __dirname,
1399
+ '..',
1400
+ 'src',
1401
+ 'components',
1402
+ 'index.ts',
1403
+ );
1404
+
1405
+ try {
1406
+ let content = fs.readFileSync(indexFilePath, 'utf8');
1407
+ const exportLine = `export * from './${componentName}';`;
1408
+
1409
+ if (!content.includes(exportLine)) {
1410
+ content += `\n${exportLine}`;
1411
+ fs.writeFileSync(indexFilePath, content, 'utf8');
1412
+ console.log(`✅ Updated: src/components/index.ts`);
1413
+ }
1414
+ } catch (error) {
1415
+ console.log(`⚠️ Could not update components/index.ts: ${error.message}`);
1416
+ }
1417
+ };
1418
+
1419
+ const updateKubitComponentsStyles = (componentName, pascalName, folderName) => {
1420
+ const stylesFilePath = path.join(
1421
+ __dirname,
1422
+ '..',
1423
+ 'src',
1424
+ 'lib',
1425
+ 'designSystem',
1426
+ 'kubit',
1427
+ 'components',
1428
+ 'styles.ts',
1429
+ );
1430
+
1431
+ try {
1432
+ let content = fs.readFileSync(stylesFilePath, 'utf8');
1433
+ const exportLine = `export * from './${folderName}/styles';`;
1434
+
1435
+ if (!content.includes(exportLine)) {
1436
+ content += `${exportLine}\n`;
1437
+ fs.writeFileSync(stylesFilePath, content, 'utf8');
1438
+ console.log(`✅ Updated: lib/designSystem/kubit/components/styles.ts`);
1439
+ }
1440
+ } catch (error) {
1441
+ console.log(`⚠️ Could not update styles.ts: ${error.message}`);
1442
+ }
1443
+ };
1444
+
1445
+ const updateKubitComponentsVariants = (
1446
+ componentName,
1447
+ pascalName,
1448
+ folderName,
1449
+ ) => {
1450
+ const variantsFilePath = path.join(
1451
+ __dirname,
1452
+ '..',
1453
+ 'src',
1454
+ 'lib',
1455
+ 'designSystem',
1456
+ 'kubit',
1457
+ 'components',
1458
+ 'variants.ts',
1459
+ );
1460
+
1461
+ try {
1462
+ let content = fs.readFileSync(variantsFilePath, 'utf8');
1463
+ const exportLine = `export * from './${folderName}/variants';`;
1464
+
1465
+ if (!content.includes(exportLine)) {
1466
+ content += `${exportLine}\n`;
1467
+ fs.writeFileSync(variantsFilePath, content, 'utf8');
1468
+ console.log(`✅ Updated: lib/designSystem/kubit/components/variants.ts`);
1469
+ }
1470
+ } catch (error) {
1471
+ console.log(`⚠️ Could not update variants.ts: ${error.message}`);
1472
+ }
1473
+ };
1474
+
1475
+ const updateKubitTheme = (componentName, pascalName, folderName) => {
1476
+ const themeFilePath = path.join(
1477
+ __dirname,
1478
+ '..',
1479
+ 'src',
1480
+ 'lib',
1481
+ 'designSystem',
1482
+ 'kubit',
1483
+ 'components',
1484
+ 'theme.ts',
1485
+ );
1486
+
1487
+ try {
1488
+ let content = fs.readFileSync(themeFilePath, 'utf8');
1489
+ const upperSnakeName = toUpperSnakeCase(pascalName);
1490
+ const importLine = `import { ${upperSnakeName} } from './${folderName}/styles';`;
1491
+
1492
+ // Add import at the beginning (in alphabetical order)
1493
+ if (!content.includes(importLine)) {
1494
+ const lines = content.split('\n');
1495
+ const importIndex = lines.findIndex(
1496
+ (line) => line.startsWith('import') && line > importLine,
1497
+ );
1498
+ if (importIndex > 0) {
1499
+ lines.splice(importIndex, 0, importLine);
1500
+ } else {
1501
+ // Add at the end of imports
1502
+ const lastImportIndex = lines.findLastIndex((line) =>
1503
+ line.startsWith('import'),
1504
+ );
1505
+ lines.splice(lastImportIndex + 1, 0, importLine);
1506
+ }
1507
+
1508
+ // Add to KUBIT_STYLES export
1509
+ const exportStartIndex = lines.findIndex((line) =>
1510
+ line.includes('export const KUBIT_STYLES = {'),
1511
+ );
1512
+ if (exportStartIndex > 0) {
1513
+ const exportEndIndex = lines.findIndex(
1514
+ (line, index) => index > exportStartIndex && line.includes('};'),
1515
+ );
1516
+
1517
+ // Find correct alphabetical position
1518
+ let insertIndex = exportStartIndex + 1;
1519
+ for (let i = exportStartIndex + 1; i < exportEndIndex; i++) {
1520
+ const line = lines[i].trim();
1521
+ if (line && !line.startsWith('//') && line < ` ${upperSnakeName},`) {
1522
+ insertIndex = i + 1;
1523
+ } else {
1524
+ break;
1525
+ }
1526
+ }
1527
+
1528
+ lines.splice(insertIndex, 0, ` ${upperSnakeName},`);
1529
+ }
1530
+
1531
+ content = lines.join('\n');
1532
+ fs.writeFileSync(themeFilePath, content, 'utf8');
1533
+ console.log(`✅ Updated: lib/designSystem/kubit/components/theme.ts`);
1534
+ }
1535
+ } catch (error) {
1536
+ console.log(`⚠️ Could not update theme.ts: ${error.message}`);
1537
+ }
1538
+ };
1539
+
1540
+ // Main component generation function
1541
+ const generateComponent = async (componentName, options) => {
1542
+ const { hasStates, needsBothVersions, needsResponsive } = options;
1543
+ const pascalName = toPascalCase(componentName);
1544
+
1545
+ // For V2 components, use the folder name WITH V2 but internal names WITHOUT V2
1546
+ const folderName = componentName; // Keep V2 in folder name if present
1547
+ const internalComponentName = getInternalComponentName(componentName);
1548
+ const internalPascalName = getInternalPascalName(pascalName);
1549
+
1550
+ console.log(`\n🚀 Generating component: ${pascalName}\n`);
1551
+
1552
+ // Create main component directory (with V2 in name if applicable)
1553
+ const componentDir = path.join(
1554
+ __dirname,
1555
+ '..',
1556
+ 'src',
1557
+ 'components',
1558
+ folderName,
1559
+ );
1560
+ createDirectory(componentDir);
1561
+
1562
+ // Create subdirectories
1563
+ const typesDir = path.join(componentDir, 'types');
1564
+ const storiesDir = path.join(componentDir, 'stories');
1565
+ const testsDir = path.join(componentDir, '__tests__');
1566
+
1567
+ createDirectory(typesDir);
1568
+ createDirectory(storiesDir);
1569
+ createDirectory(testsDir);
1570
+
1571
+ // Generate main component files (use internal names for file content)
1572
+ const standAloneContent = generateStandAloneTemplate(
1573
+ componentName,
1574
+ pascalName,
1575
+ needsResponsive,
1576
+ );
1577
+ const cssContent = generateCssTemplate(
1578
+ internalComponentName,
1579
+ internalPascalName,
1580
+ hasStates,
1581
+ );
1582
+ const indexContent = generateIndexTemplate(
1583
+ componentName,
1584
+ pascalName,
1585
+ needsBothVersions,
1586
+ ); // Use componentName to detect V2 properly
1587
+
1588
+ createFile(
1589
+ path.join(componentDir, `${internalComponentName}StandAlone.tsx`),
1590
+ standAloneContent,
1591
+ );
1592
+ createFile(
1593
+ path.join(componentDir, `${internalComponentName}.css`),
1594
+ cssContent,
1595
+ );
1596
+ createFile(path.join(componentDir, 'index.ts'), indexContent);
1597
+
1598
+ // Generate component files based on control type (use original componentName for styles)
1599
+ if (needsBothVersions) {
1600
+ const controlledContent = generateControlledTemplate(
1601
+ componentName,
1602
+ pascalName,
1603
+ hasStates,
1604
+ );
1605
+ const uncontrolledContent = generateUncontrolledTemplate(
1606
+ componentName,
1607
+ pascalName,
1608
+ );
1609
+
1610
+ createFile(
1611
+ path.join(componentDir, `${internalComponentName}Controlled.tsx`),
1612
+ controlledContent,
1613
+ );
1614
+ createFile(
1615
+ path.join(componentDir, `${internalComponentName}Uncontrolled.tsx`),
1616
+ uncontrolledContent,
1617
+ );
1618
+ } else {
1619
+ const mainComponentContent = generateMainComponentTemplate(
1620
+ componentName,
1621
+ pascalName,
1622
+ hasStates,
1623
+ );
1624
+ createFile(
1625
+ path.join(componentDir, `${internalComponentName}.tsx`),
1626
+ mainComponentContent,
1627
+ );
1628
+ }
1629
+
1630
+ // Generate types (use internal names for files, but export types need original names for V2 compatibility)
1631
+ const typesIndexContent = generateTypesIndexTemplate(
1632
+ componentName,
1633
+ hasStates,
1634
+ ); // Use componentName to keep V2
1635
+ const mainTypeContent = generateMainTypeTemplate(
1636
+ componentName,
1637
+ pascalName,
1638
+ needsBothVersions,
1639
+ hasStates,
1640
+ ); // Use componentName to keep V2
1641
+ const themeTypeContent = generateThemeTypeTemplate(
1642
+ componentName,
1643
+ pascalName,
1644
+ hasStates,
1645
+ ); // Use componentName to keep V2
1646
+
1647
+ createFile(path.join(typesDir, 'index.ts'), typesIndexContent);
1648
+ createFile(
1649
+ path.join(typesDir, `${internalComponentName}.ts`),
1650
+ mainTypeContent,
1651
+ );
1652
+ createFile(
1653
+ path.join(typesDir, `${internalComponentName}Theme.ts`),
1654
+ themeTypeContent,
1655
+ );
1656
+
1657
+ // Generate state file if needed (use internal name for file, but state names should reflect original)
1658
+ if (hasStates) {
1659
+ const stateContent = generateStateTemplate(
1660
+ internalComponentName,
1661
+ pascalName,
1662
+ );
1663
+ createFile(path.join(typesDir, 'state.ts'), stateContent);
1664
+ }
1665
+
1666
+ // Generate test (use internal name for file names, but test names should reflect original)
1667
+ const testContent = generateTestTemplate(
1668
+ internalComponentName,
1669
+ pascalName,
1670
+ needsBothVersions,
1671
+ );
1672
+ createFile(
1673
+ path.join(testsDir, `${internalComponentName}.test.tsx`),
1674
+ testContent,
1675
+ );
1676
+
1677
+ // Generate stories (use internal names for file names, but component names should be original)
1678
+ if (needsBothVersions) {
1679
+ const controlledStoriesContent = generateControlledStoriesTemplate(
1680
+ internalComponentName,
1681
+ pascalName,
1682
+ );
1683
+ const uncontrolledStoriesContent = generateUncontrolledStoriesTemplate(
1684
+ internalComponentName,
1685
+ pascalName,
1686
+ );
1687
+ const controlledArgtypesContent = generateArgtypesControlledTemplate(
1688
+ internalComponentName,
1689
+ pascalName,
1690
+ );
1691
+ const uncontrolledArgtypesContent = generateArgtypesUncontrolledTemplate(
1692
+ internalComponentName,
1693
+ pascalName,
1694
+ );
1695
+
1696
+ createFile(
1697
+ path.join(storiesDir, `${internalComponentName}Controlled.stories.tsx`),
1698
+ controlledStoriesContent,
1699
+ );
1700
+ createFile(
1701
+ path.join(storiesDir, `${internalComponentName}Uncontrolled.stories.tsx`),
1702
+ uncontrolledStoriesContent,
1703
+ );
1704
+ createFile(
1705
+ path.join(storiesDir, 'argtypesControlled.ts'),
1706
+ controlledArgtypesContent,
1707
+ );
1708
+ createFile(
1709
+ path.join(storiesDir, 'argtypesUncontrolled.ts'),
1710
+ uncontrolledArgtypesContent,
1711
+ );
1712
+ } else {
1713
+ const storiesContent = generateStoriesTemplate(
1714
+ internalComponentName,
1715
+ pascalName,
1716
+ );
1717
+ const argtypesContent = generateArgtypesTemplate(
1718
+ internalComponentName,
1719
+ pascalName,
1720
+ );
1721
+
1722
+ createFile(
1723
+ path.join(storiesDir, `${internalComponentName}.stories.tsx`),
1724
+ storiesContent,
1725
+ );
1726
+ createFile(path.join(storiesDir, 'argtypes.ts'), argtypesContent);
1727
+ }
1728
+
1729
+ // Generate Kubit Design System files
1730
+ console.log('\n📐 Generating Kubit Design System files...\n');
1731
+
1732
+ const kubitDesignSystemDir = path.join(
1733
+ __dirname,
1734
+ '..',
1735
+ 'src',
1736
+ 'lib',
1737
+ 'designSystem',
1738
+ 'kubit',
1739
+ 'components',
1740
+ folderName,
1741
+ );
1742
+
1743
+ createDirectory(kubitDesignSystemDir);
1744
+
1745
+ const kubitDesignSystemStylesContent = generateDesignSystemStylesTemplate(
1746
+ componentName,
1747
+ pascalName,
1748
+ );
1749
+ const kubitDesignSystemVariantsContent = generateDesignSystemVariantsTemplate(
1750
+ componentName,
1751
+ pascalName,
1752
+ );
1753
+
1754
+ createFile(
1755
+ path.join(kubitDesignSystemDir, 'styles.ts'),
1756
+ kubitDesignSystemStylesContent,
1757
+ );
1758
+ createFile(
1759
+ path.join(kubitDesignSystemDir, 'variants.ts'),
1760
+ kubitDesignSystemVariantsContent,
1761
+ );
1762
+
1763
+ // Update index files and constants
1764
+ console.log(`\n🔄 Updating existing system files...`);
1765
+
1766
+ updateComponentsIndex(folderName);
1767
+ updateKubitComponentsStyles(folderName, pascalName, folderName);
1768
+ updateKubitComponentsVariants(folderName, pascalName, folderName);
1769
+ updateKubitTheme(folderName, pascalName, folderName);
1770
+
1771
+ console.log(`\n✨ Component ${pascalName} generated successfully!`);
1772
+ console.log(`📁 Location: src/components/${folderName}/`);
1773
+
1774
+ console.log(`\n� Files generated:`);
1775
+ console.log(` • ${internalComponentName}StandAlone.tsx`);
1776
+ if (needsBothVersions) {
1777
+ console.log(` • ${internalComponentName}Controlled.tsx`);
1778
+ console.log(` • ${internalComponentName}Uncontrolled.tsx`);
1779
+ } else {
1780
+ console.log(` • ${internalComponentName}.tsx`);
1781
+ }
1782
+ console.log(` • ${internalComponentName}.css`);
1783
+ console.log(` • index.ts`);
1784
+ console.log(` • types/index.ts`);
1785
+ console.log(` • types/${internalComponentName}.ts`);
1786
+ console.log(` • types/${internalComponentName}Theme.ts`);
1787
+ if (hasStates) {
1788
+ console.log(` • types/state.ts`);
1789
+ }
1790
+ console.log(` • stories/${internalComponentName}.stories.tsx`);
1791
+ if (needsBothVersions) {
1792
+ console.log(` • stories/${internalComponentName}Controlled.stories.tsx`);
1793
+ console.log(
1794
+ ` • stories/${internalComponentName}Uncontrolled.stories.tsx`,
1795
+ );
1796
+ console.log(` • stories/argtypesControlled.ts`);
1797
+ console.log(` • stories/argtypesUncontrolled.ts`);
1798
+ }
1799
+ console.log(` • stories/argtypes.ts`);
1800
+ console.log(` • __tests__/${internalComponentName}.test.tsx`);
1801
+ console.log(`\n📁 Design System files (Venus Hybrid Architecture):`);
1802
+ console.log(` • lib/designSystem/kubit/components/${folderName}/styles.ts`);
1803
+ console.log(
1804
+ ` └─ Theme-based styles: colors, spacings, typography, responsive variants`,
1805
+ );
1806
+ console.log(
1807
+ ` • lib/designSystem/kubit/components/${folderName}/variants.ts`,
1808
+ );
1809
+ console.log(
1810
+ ` └─ Component variant constants (PRIMARY, SECONDARY, sizes, etc.)`,
1811
+ );
1812
+
1813
+ console.log(`\n📝 Next steps:`);
1814
+ console.log(`\n🎯 Component Development:`);
1815
+ console.log(
1816
+ `1. Implement the component logic in ${internalComponentName}StandAlone.tsx`,
1817
+ );
1818
+ console.log(` • Add proper component content and behavior`);
1819
+ console.log(` • Use semantic HTML elements`);
1820
+ console.log(` • Implement accessibility attributes (ARIA, roles, etc.)`);
1821
+ console.log(`2. Add structural CSS in ${internalComponentName}.css`);
1822
+ console.log(` • Layout, positioning, and behavior-based styles`);
1823
+ console.log(` • Focus states, disabled states, transitions`);
1824
+ console.log(` • CSS variables for dynamic properties`);
1825
+ console.log(
1826
+ `3. Update TypeScript types in types/${internalComponentName}.ts as needed`,
1827
+ );
1828
+ if (hasStates) {
1829
+ console.log(`4. Define component states in types/state.ts`);
1830
+ console.log(` • Add HOVER, PRESSED, FOCUSED, etc. as needed`);
1831
+ console.log(`5. Add state logic in component files`);
1832
+ console.log(` • Use useManageState hook for complex state management`);
1833
+ }
1834
+
1835
+ console.log(`\n🎨 Design System Integration:`);
1836
+ console.log(
1837
+ `${hasStates ? '6' : '4'}. Configure design system styles in lib/designSystem/kubit/components/${folderName}/styles.ts`,
1838
+ );
1839
+ console.log(
1840
+ ` • Update variant styles with actual design tokens (COLORS, SPACINGS, etc.)`,
1841
+ );
1842
+ console.log(` • Add responsive behavior via $mediaQueries`);
1843
+ console.log(` • Remove placeholder styles and add real component variants`);
1844
+ console.log(
1845
+ `${hasStates ? '7' : '5'}. Update variants in lib/designSystem/kubit/components/${folderName}/variants.ts`,
1846
+ );
1847
+ console.log(
1848
+ ` • Replace placeholder variants with actual component variants`,
1849
+ );
1850
+ console.log(` • Follow naming convention: PRIMARY, SECONDARY, etc.`);
1851
+
1852
+ if (needsResponsive) {
1853
+ console.log(`${hasStates ? '8' : '6'}. Add responsive logic:`);
1854
+ console.log(
1855
+ ` • CSS: Use $mediaQueries in styles.ts for theme-based responsive styles`,
1856
+ );
1857
+ console.log(
1858
+ ` • Logic: Use useActiveBreakpoints hook for behavior-based responsive logic`,
1859
+ );
1860
+ }
1861
+
1862
+ console.log(`\n📖 Documentation & Testing:`);
1863
+ console.log(
1864
+ `${hasStates ? (needsResponsive ? '9' : '8') : needsResponsive ? '7' : '6'}. Update Storybook stories in stories/${internalComponentName}.stories.tsx`,
1865
+ );
1866
+ console.log(` • Replace placeholder content with real examples`);
1867
+ console.log(` • Add multiple story variants showing different use cases`);
1868
+ console.log(` • Update argtypes.ts with actual props and variants`);
1869
+ console.log(
1870
+ `${hasStates ? (needsResponsive ? '10' : '9') : needsResponsive ? '8' : '7'}. Write comprehensive tests in __tests__/${internalComponentName}.test.tsx`,
1871
+ );
1872
+ console.log(` • Test all component variants and sizes`);
1873
+ console.log(` • Test user interactions (click, keyboard navigation, etc.)`);
1874
+ console.log(` • Test accessibility with axe`);
1875
+ console.log(` • Test responsive behavior if applicable`);
1876
+
1877
+ console.log(`\n💡 Venus Architecture Notes:`);
1878
+ console.log(
1879
+ ` • This component uses Venus hybrid styling: CSS for structure + Design System for theming`,
1880
+ );
1881
+ console.log(
1882
+ ` • CSS files handle layout, positioning, and theme-independent behaviors`,
1883
+ );
1884
+ console.log(
1885
+ ` • Design System handles colors, spacings, typography, and responsive variants`,
1886
+ );
1887
+ console.log(
1888
+ ` • Use hooks from /lib/hooks/ (useClassname, useManageState, etc.)`,
1889
+ );
1890
+ console.log(
1891
+ ` • Follow Venus naming conventions: kbt- prefix, snake_case for component names`,
1892
+ );
1893
+ console.log(
1894
+ ` • Import design tokens from foundations (COLORS, SPACINGS, BORDERS, etc.)`,
1895
+ );
1896
+ };
1897
+
1898
+ // Interactive prompts
1899
+ const askQuestion = (question) => {
1900
+ return new Promise((resolve) => {
1901
+ rl.question(question, (answer) => {
1902
+ resolve(answer.trim());
1903
+ });
1904
+ });
1905
+ };
1906
+
1907
+ const askForStates = async () => {
1908
+ const hasStates = await askQuestion(
1909
+ '🎯 Will this component have states (DEFAULT, HOVER, PRESSED, etc.)? (y/n): ',
1910
+ );
1911
+ return hasStates.toLowerCase() === 'y' || hasStates.toLowerCase() === 'yes';
1912
+ };
1913
+
1914
+ const askForControlType = async () => {
1915
+ const needsBothVersions = await askQuestion(
1916
+ '🎛️ Does this component need both Controlled and Uncontrolled versions? (y/n): ',
1917
+ );
1918
+ return (
1919
+ needsBothVersions.toLowerCase() === 'y' ||
1920
+ needsBothVersions.toLowerCase() === 'yes'
1921
+ );
1922
+ };
1923
+
1924
+ const askForResponsive = async () => {
1925
+ const needsResponsive = await askQuestion(
1926
+ '📱 Will this component need responsive logic (different behavior on mobile, tablet, desktop)? (y/n): ',
1927
+ );
1928
+ return (
1929
+ needsResponsive.toLowerCase() === 'y' ||
1930
+ needsResponsive.toLowerCase() === 'yes'
1931
+ );
1932
+ };
1933
+
1934
+ const main = async () => {
1935
+ // Parse command line arguments
1936
+ const args = parseArguments(process.argv.slice(2));
1937
+
1938
+ // Handle unknown arguments
1939
+ if (args.unknownArgs.length > 0) {
1940
+ console.log('❌ Error: Invalid arguments detected:');
1941
+ args.unknownArgs.forEach((error) => console.log(` • ${error}`));
1942
+ console.log('');
1943
+ showHelp();
1944
+ process.exit(1);
1945
+ }
1946
+
1947
+ // Handle help and version flags
1948
+ if (args.help) {
1949
+ showHelp();
1950
+ process.exit(0);
1951
+ }
1952
+
1953
+ if (args.version) {
1954
+ showVersion();
1955
+ process.exit(0);
1956
+ }
1957
+
1958
+ // Determine mode and run accordingly
1959
+ if (args.mode === 'automatic') {
1960
+ await runAutomaticMode(args);
1961
+ } else {
1962
+ await runInteractiveMode();
1963
+ }
1964
+ };
1965
+
1966
+ /**
1967
+ * Run the script in automatic mode using CLI arguments
1968
+ */
1969
+ const runAutomaticMode = async (args) => {
1970
+ console.log('🎨 WEB UI Components - New Component Generator');
1971
+ console.log('=====================================================');
1972
+ console.log('🤖 Running in automatic mode\n');
1973
+
1974
+ try {
1975
+ // Validate component name
1976
+ const validation = validateComponentName(args.name);
1977
+ if (!validation.valid) {
1978
+ showUsageError(validation.error);
1979
+ process.exit(1);
1980
+ }
1981
+
1982
+ // Convert to camelCase for file names
1983
+ const componentName = toCamelCase(args.name);
1984
+
1985
+ // Use CLI arguments for options
1986
+ const hasStates = args.states;
1987
+ const needsBothVersions = args.controlled;
1988
+ const needsResponsive = args.responsive;
1989
+
1990
+ // Show configuration summary
1991
+ console.log('� Component Configuration:');
1992
+ console.log(` Component name: ${toPascalCase(componentName)}`);
1993
+ console.log(` Variants: REGULAR (default - can be extended later)`);
1994
+ console.log(
1995
+ ` States: ${hasStates ? 'DEFAULT (can be extended later)' : 'None'}`,
1996
+ );
1997
+ console.log(
1998
+ ` Control versions: ${needsBothVersions ? 'Both Controlled and Uncontrolled' : 'Single component'}`,
1999
+ );
2000
+ console.log(` Responsive: ${needsResponsive ? 'Yes' : 'No'}`);
2001
+
2002
+ // Generate component directly
2003
+ await generateComponent(componentName, {
2004
+ hasStates,
2005
+ needsBothVersions,
2006
+ needsResponsive,
2007
+ });
2008
+ } catch (error) {
2009
+ console.error('❌ Error generating component:', error.message);
2010
+ process.exit(1);
2011
+ } finally {
2012
+ rl.close();
2013
+ }
2014
+ };
2015
+
2016
+ /**
2017
+ * Run the script in interactive mode (original behavior)
2018
+ */
2019
+ const runInteractiveMode = async () => {
2020
+ console.log('🎨 WEB UI Components - New Component Generator');
2021
+ console.log('=====================================================');
2022
+ console.log('💬 Running in interactive mode\n');
2023
+
2024
+ try {
2025
+ // Ask for component name
2026
+ let componentName = await askQuestion(
2027
+ '📝 Enter the component name (PascalCase): ',
2028
+ );
2029
+
2030
+ // Validate component name
2031
+ const validation = validateComponentName(componentName);
2032
+ if (!validation.valid) {
2033
+ console.log(`❌ ${validation.error}`);
2034
+ process.exit(1);
2035
+ }
2036
+
2037
+ // Convert to camelCase for file names
2038
+ componentName = toCamelCase(componentName);
2039
+
2040
+ // Ask for states
2041
+ const hasStates = await askForStates();
2042
+
2043
+ // Ask for control type
2044
+ const needsBothVersions = await askForControlType();
2045
+
2046
+ // Ask for responsive support
2047
+ const needsResponsive = await askForResponsive();
2048
+
2049
+ console.log('\n📋 Summary:');
2050
+ console.log(` Component name: ${toPascalCase(componentName)}`);
2051
+ console.log(` Variants: REGULAR (default - can be extended later)`);
2052
+ console.log(
2053
+ ` States: ${hasStates ? 'DEFAULT (can be extended later)' : 'None'}`,
2054
+ );
2055
+ console.log(
2056
+ ` Control versions: ${needsBothVersions ? 'Both Controlled and Uncontrolled' : 'Single component'}`,
2057
+ );
2058
+ console.log(` Responsive: ${needsResponsive ? 'Yes' : 'No'}`);
2059
+
2060
+ const confirm = await askQuestion('\n✅ Generate component? (y/n): ');
2061
+
2062
+ if (confirm.toLowerCase() === 'y' || confirm.toLowerCase() === 'yes') {
2063
+ await generateComponent(componentName, {
2064
+ hasStates,
2065
+ needsBothVersions,
2066
+ needsResponsive,
2067
+ });
2068
+ } else {
2069
+ console.log('❌ Component generation cancelled');
2070
+ }
2071
+ } catch (error) {
2072
+ console.error('❌ Error generating component:', error.message);
2073
+ process.exit(1);
2074
+ } finally {
2075
+ rl.close();
2076
+ }
2077
+ };
2078
+
2079
+ // Run the script
2080
+ if (require.main === module) {
2081
+ main();
2082
+ }
2083
+
2084
+ module.exports = {
2085
+ generateComponent,
2086
+ toPascalCase,
2087
+ toCamelCase,
2088
+ toKebabCase,
2089
+ toSnakeCase,
2090
+ toUpperSnakeCase,
2091
+ toComponentName,
2092
+ };