@shohojdhara/atomix 0.3.14 → 0.3.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/build-tools/EXAMPLES.md +372 -0
- package/build-tools/README.md +242 -0
- package/build-tools/__tests__/error-handler.test.js +230 -0
- package/build-tools/__tests__/index.test.js +141 -0
- package/build-tools/__tests__/rollup-plugin.test.js +194 -0
- package/build-tools/__tests__/utils.test.js +161 -0
- package/build-tools/__tests__/vite-plugin.test.js +129 -0
- package/build-tools/__tests__/webpack-loader.test.js +190 -0
- package/build-tools/error-handler.js +308 -0
- package/build-tools/index.d.ts +43 -0
- package/build-tools/index.js +88 -0
- package/build-tools/package.json +67 -0
- package/build-tools/rollup-plugin.js +236 -0
- package/build-tools/types.d.ts +163 -0
- package/build-tools/utils.js +203 -0
- package/build-tools/vite-plugin.js +161 -0
- package/build-tools/webpack-loader.js +123 -0
- package/dist/atomix.css +203 -90
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +3 -3
- package/dist/atomix.min.css.map +1 -1
- package/dist/build-tools/EXAMPLES.md +372 -0
- package/dist/build-tools/README.md +242 -0
- package/dist/build-tools/__tests__/error-handler.test.js +230 -0
- package/dist/build-tools/__tests__/index.test.js +141 -0
- package/dist/build-tools/__tests__/rollup-plugin.test.js +194 -0
- package/dist/build-tools/__tests__/utils.test.js +161 -0
- package/dist/build-tools/__tests__/vite-plugin.test.js +129 -0
- package/dist/build-tools/__tests__/webpack-loader.test.js +190 -0
- package/dist/build-tools/error-handler.js +308 -0
- package/dist/build-tools/index.d.ts +43 -0
- package/dist/build-tools/index.js +88 -0
- package/dist/build-tools/package.json +67 -0
- package/dist/build-tools/rollup-plugin.js +236 -0
- package/dist/build-tools/types.d.ts +163 -0
- package/dist/build-tools/utils.js +203 -0
- package/dist/build-tools/vite-plugin.js +161 -0
- package/dist/build-tools/webpack-loader.js +123 -0
- package/dist/charts.d.ts +1 -1
- package/dist/charts.js +86 -57
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +136 -112
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +2 -5
- package/dist/forms.js +140 -128
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +1 -1
- package/dist/heavy.js +136 -112
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +9 -61
- package/dist/index.esm.js +237 -286
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +250 -299
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +23 -8
- package/scripts/atomix-cli.js +170 -73
- package/scripts/cli/__tests__/README.md +81 -0
- package/scripts/cli/__tests__/basic.test.js +115 -0
- package/scripts/cli/__tests__/component-generator.test.js +332 -0
- package/scripts/cli/__tests__/integration.test.js +327 -0
- package/scripts/cli/__tests__/test-setup.js +133 -0
- package/scripts/cli/__tests__/token-manager.test.js +251 -0
- package/scripts/cli/__tests__/utils.test.js +161 -0
- package/scripts/cli/component-generator.js +253 -299
- package/scripts/cli/dependency-checker.js +355 -0
- package/scripts/cli/interactive-init.js +46 -5
- package/scripts/cli/template-manager.js +0 -2
- package/scripts/cli/templates/common-templates.js +636 -0
- package/scripts/cli/templates/composable-templates.js +148 -126
- package/scripts/cli/templates/index.js +23 -16
- package/scripts/cli/templates/project-templates.js +151 -23
- package/scripts/cli/templates/react-templates.js +280 -210
- package/scripts/cli/templates/scss-templates.js +90 -91
- package/scripts/cli/templates/testing-templates.js +206 -27
- package/scripts/cli/templates/testing-utils.js +278 -0
- package/scripts/cli/templates/types-templates.js +70 -56
- package/scripts/cli/theme-bridge.js +8 -2
- package/scripts/cli/token-manager.js +318 -206
- package/scripts/cli/utils.js +0 -1
- package/src/components/Accordion/Accordion.stories.tsx +369 -870
- package/src/components/AtomixGlass/AtomixGlass.tsx +80 -39
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +103 -81
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +8 -7
- package/src/components/AtomixGlass/glass-utils.ts +2 -2
- package/src/components/AtomixGlass/shader-utils.ts +5 -0
- package/src/components/AtomixGlass/stories/Customization.stories.tsx +131 -0
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +2957 -2853
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +1 -1
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +348 -0
- package/src/components/AtomixGlass/stories/Performance.stories.tsx +103 -0
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +50 -35
- package/src/components/AtomixGlass/stories/{ShaderVariants.stories.tsx → Shaders.stories.tsx} +1 -1
- package/src/components/AtomixGlass/stories/shared-components.tsx +90 -190
- package/src/components/Avatar/Avatar.stories.tsx +213 -1
- package/src/components/Badge/Badge.stories.tsx +121 -362
- package/src/components/Block/Block.stories.tsx +21 -12
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +141 -23
- package/src/components/Button/Button.stories.tsx +463 -1126
- package/src/components/Button/Button.test.tsx +107 -0
- package/src/components/Button/Button.tsx +46 -50
- package/src/components/Button/ButtonGroup.stories.tsx +373 -217
- package/src/components/Callout/Callout.stories.tsx +289 -634
- package/src/components/Card/Card.stories.tsx +248 -68
- package/src/components/Chart/Chart.stories.tsx +150 -8
- package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +151 -69
- package/src/components/Countdown/Countdown.stories.tsx +115 -8
- package/src/components/DataTable/DataTable.stories.tsx +346 -146
- package/src/components/DatePicker/DatePicker.stories.tsx +325 -1066
- package/src/components/Dropdown/Dropdown.stories.tsx +153 -33
- package/src/components/EdgePanel/EdgePanel.stories.tsx +230 -21
- package/src/components/Footer/Footer.stories.tsx +392 -328
- package/src/components/Form/Checkbox.stories.tsx +140 -6
- package/src/components/Form/Checkbox.test.tsx +63 -0
- package/src/components/Form/Checkbox.tsx +87 -51
- package/src/components/Form/Form.stories.tsx +119 -20
- package/src/components/Form/FormGroup.stories.tsx +127 -4
- package/src/components/Form/Radio.stories.tsx +140 -5
- package/src/components/Form/Select.stories.tsx +140 -8
- package/src/components/Form/Textarea.stories.tsx +149 -6
- package/src/components/Hero/Hero.stories.tsx +333 -32
- package/src/components/List/List.stories.tsx +141 -3
- package/src/components/Modal/Modal.stories.tsx +181 -42
- package/src/components/Popover/Popover.stories.tsx +448 -98
- package/src/components/Progress/Progress.stories.tsx +167 -5
- package/src/components/River/River.stories.tsx +1 -1
- package/src/components/SectionIntro/SectionIntro.stories.tsx +240 -48
- package/src/components/Spinner/Spinner.stories.tsx +102 -8
- package/src/components/Steps/Steps.stories.tsx +172 -43
- package/src/components/Tabs/Tabs.stories.tsx +136 -10
- package/src/components/Testimonial/Testimonial.stories.tsx +120 -3
- package/src/components/Todo/Todo.stories.tsx +198 -9
- package/src/components/Toggle/Toggle.stories.tsx +126 -39
- package/src/components/Tooltip/Tooltip.stories.tsx +194 -104
- package/src/components/Upload/Upload.stories.tsx +113 -24
- package/src/lib/README.md +2 -2
- package/src/lib/__tests__/theme-tools.test.ts +193 -0
- package/src/lib/composables/index.ts +2 -2
- package/src/lib/composables/useAtomixGlass.ts +28 -56
- package/src/lib/composables/useChartExport.ts +2 -7
- package/src/lib/composables/useDataTable.ts +46 -29
- package/src/lib/constants/components.ts +9 -32
- package/src/lib/theme/devtools/CLI.ts +1 -1
- package/src/lib/types/components.ts +1 -1
- package/src/lib/utils/__tests__/csv.test.ts +45 -0
- package/src/lib/utils/csv.ts +17 -0
- package/src/lib/utils/dataTableExport.ts +1 -10
- package/src/styles/01-settings/_index.scss +2 -1
- package/src/styles/01-settings/_settings.accordion.scss +28 -7
- package/src/styles/01-settings/_settings.colors.scss +11 -11
- package/src/styles/01-settings/_settings.typography.scss +5 -5
- package/src/styles/02-tools/_tools.utility-api.scss +14 -0
- package/src/styles/06-components/_components.accordion.scss +56 -14
- package/src/styles/06-components/_components.checkbox.scss +23 -17
- package/src/styles/99-utilities/_index.scss +2 -0
- package/src/styles/99-utilities/_utilities.scss +3 -1
- package/src/styles/99-utilities/_utilities.text-gradient.scss +45 -0
- package/themes/dark-complementary/README.md +98 -0
- package/themes/dark-complementary/index.scss +158 -0
- package/themes/default-light/README.md +81 -0
- package/themes/default-light/index.scss +154 -0
- package/themes/high-contrast/README.md +105 -0
- package/themes/high-contrast/index.scss +172 -0
- package/themes/test-theme/README.md +38 -0
- package/themes/test-theme/index.scss +47 -0
- package/scripts/cli/templates-original-backup.js +0 -1655
- package/scripts/cli/templates_backup.js +0 -684
- package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +0 -1438
- package/src/lib/composables/useButton.ts +0 -93
- package/src/lib/composables/useCheckbox.ts +0 -70
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
import inquirer from 'inquirer';
|
|
7
7
|
import chalk from 'chalk';
|
|
8
|
-
import { readFile, writeFile, mkdir
|
|
8
|
+
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
9
9
|
import { existsSync } from 'fs';
|
|
10
|
-
import { join
|
|
10
|
+
import { join } from 'path';
|
|
11
11
|
import ora from 'ora';
|
|
12
12
|
import boxen from 'boxen';
|
|
13
13
|
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
validatePath,
|
|
16
16
|
validateComponentName,
|
|
17
17
|
sanitizeInput,
|
|
18
|
-
fileExists,
|
|
19
18
|
AtomixCLIError
|
|
20
19
|
} from './utils.js';
|
|
21
20
|
import { componentTemplates } from './templates.js';
|
|
@@ -112,239 +111,21 @@ export const COMPONENT_FEATURES = {
|
|
|
112
111
|
* Simple component template
|
|
113
112
|
*/
|
|
114
113
|
function getSimpleTemplate(name) {
|
|
115
|
-
return
|
|
116
|
-
import type { ${name}Props } from './${name}.types';
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* ${name} - Simple Presentational Component
|
|
120
|
-
*
|
|
121
|
-
* A basic component for rendering content with minimal overhead.
|
|
122
|
-
*/
|
|
123
|
-
export const ${name} = ({ children, className, ...props }: ${name}Props) => {
|
|
124
|
-
return (
|
|
125
|
-
<div className={\`c-${name.toLowerCase()} \${className || ''}\`} {...props}>
|
|
126
|
-
{children}
|
|
127
|
-
</div>
|
|
128
|
-
);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
${name}.displayName = '${name}';
|
|
132
|
-
`;
|
|
114
|
+
return componentTemplates.react.simple(name);
|
|
133
115
|
}
|
|
134
116
|
|
|
135
117
|
/**
|
|
136
118
|
* Medium component template
|
|
137
119
|
*/
|
|
138
120
|
function getMediumTemplate(name) {
|
|
139
|
-
return
|
|
140
|
-
import { cn } from '../../lib/utils';
|
|
141
|
-
import type { ${name}Props, ${name}State } from './${name}.types';
|
|
142
|
-
import { use${name} } from '../../lib/composables/use${name}';
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* ${name} - Component with State and Interactions
|
|
146
|
-
*
|
|
147
|
-
* A component with internal state management and event handling.
|
|
148
|
-
*/
|
|
149
|
-
export const ${name} = React.forwardRef<HTMLDivElement, ${name}Props>(
|
|
150
|
-
({
|
|
151
|
-
children,
|
|
152
|
-
className,
|
|
153
|
-
defaultOpen = false,
|
|
154
|
-
onOpenChange,
|
|
155
|
-
disabled = false,
|
|
156
|
-
...props
|
|
157
|
-
}, ref) => {
|
|
158
|
-
const {
|
|
159
|
-
isOpen,
|
|
160
|
-
toggle,
|
|
161
|
-
setIsOpen
|
|
162
|
-
} = use${name}({
|
|
163
|
-
defaultOpen,
|
|
164
|
-
onOpenChange
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
|
|
168
|
-
if (disabled) return;
|
|
169
|
-
|
|
170
|
-
switch (e.key) {
|
|
171
|
-
case 'Enter':
|
|
172
|
-
case ' ':
|
|
173
|
-
e.preventDefault();
|
|
174
|
-
toggle();
|
|
175
|
-
break;
|
|
176
|
-
case 'Escape':
|
|
177
|
-
setIsOpen(false);
|
|
178
|
-
break;
|
|
179
|
-
}
|
|
180
|
-
}, [disabled, toggle, setIsOpen]);
|
|
181
|
-
|
|
182
|
-
return (
|
|
183
|
-
<div
|
|
184
|
-
ref={ref}
|
|
185
|
-
className={cn(
|
|
186
|
-
'c-' + '${name.toLowerCase()}',
|
|
187
|
-
isOpen && 'is-open',
|
|
188
|
-
disabled && 'is-disabled',
|
|
189
|
-
className
|
|
190
|
-
)}
|
|
191
|
-
data-state={isOpen ? 'open' : 'closed'}
|
|
192
|
-
data-disabled={disabled}
|
|
193
|
-
onKeyDown={handleKeyDown}
|
|
194
|
-
tabIndex={disabled ? -1 : 0}
|
|
195
|
-
role="button"
|
|
196
|
-
aria-pressed={isOpen}
|
|
197
|
-
aria-disabled={disabled}
|
|
198
|
-
{...props}
|
|
199
|
-
>
|
|
200
|
-
{children}
|
|
201
|
-
</div>
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
${name}.displayName = '${name}';
|
|
207
|
-
`;
|
|
121
|
+
return componentTemplates.react.medium(name);
|
|
208
122
|
}
|
|
209
123
|
|
|
210
124
|
/**
|
|
211
125
|
* Complex component template
|
|
212
126
|
*/
|
|
213
127
|
function getComplexTemplate(name) {
|
|
214
|
-
return
|
|
215
|
-
import { cn } from '../../lib/utils';
|
|
216
|
-
import type { ${name}Props, ${name}ContextValue } from './${name}.types';
|
|
217
|
-
import { use${name} } from '../../lib/composables/use${name}';
|
|
218
|
-
import { ${name}Context } from './${name}.context';
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* ${name} - Advanced Component with Context and Animations
|
|
222
|
-
*
|
|
223
|
-
* A feature-rich component with context integration, animations,
|
|
224
|
-
* accessibility, and validation.
|
|
225
|
-
*/
|
|
226
|
-
|
|
227
|
-
// Root Component
|
|
228
|
-
export const ${name} = React.forwardRef<HTMLDivElement, ${name}Props>(
|
|
229
|
-
({
|
|
230
|
-
children,
|
|
231
|
-
className,
|
|
232
|
-
defaultOpen = false,
|
|
233
|
-
onOpenChange,
|
|
234
|
-
disabled = false,
|
|
235
|
-
required = false,
|
|
236
|
-
validate,
|
|
237
|
-
animation = true,
|
|
238
|
-
...props
|
|
239
|
-
}, ref) => {
|
|
240
|
-
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
241
|
-
const [isValid, setIsValid] = useState(true);
|
|
242
|
-
const [validationMessage, setValidationMessage] = useState('');
|
|
243
|
-
const elementRef = useRef<HTMLDivElement>(null);
|
|
244
|
-
|
|
245
|
-
const toggle = useCallback(() => {
|
|
246
|
-
if (disabled) return;
|
|
247
|
-
|
|
248
|
-
const newState = !isOpen;
|
|
249
|
-
|
|
250
|
-
// Validation before opening
|
|
251
|
-
if (newState && validate) {
|
|
252
|
-
const result = validate();
|
|
253
|
-
setIsValid(result.valid);
|
|
254
|
-
setValidationMessage(result.message || '');
|
|
255
|
-
|
|
256
|
-
if (!result.valid) {
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
setIsOpen(newState);
|
|
262
|
-
onOpenChange?.(newState);
|
|
263
|
-
}, [isOpen, disabled, validate, onOpenChange]);
|
|
264
|
-
|
|
265
|
-
const contextValue: ${name}ContextValue = React.useMemo(() => ({
|
|
266
|
-
isOpen,
|
|
267
|
-
toggle,
|
|
268
|
-
setIsOpen,
|
|
269
|
-
disabled,
|
|
270
|
-
required,
|
|
271
|
-
isValid,
|
|
272
|
-
validationMessage
|
|
273
|
-
}), [isOpen, toggle, disabled, required, isValid, validationMessage]);
|
|
274
|
-
|
|
275
|
-
// Focus management
|
|
276
|
-
useEffect(() => {
|
|
277
|
-
if (isOpen && elementRef.current) {
|
|
278
|
-
elementRef.current.focus();
|
|
279
|
-
}
|
|
280
|
-
}, [isOpen]);
|
|
281
|
-
|
|
282
|
-
// Keyboard navigation
|
|
283
|
-
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
|
|
284
|
-
if (disabled) return;
|
|
285
|
-
|
|
286
|
-
switch (e.key) {
|
|
287
|
-
case 'Enter':
|
|
288
|
-
case ' ':
|
|
289
|
-
e.preventDefault();
|
|
290
|
-
toggle();
|
|
291
|
-
break;
|
|
292
|
-
case 'Escape':
|
|
293
|
-
setIsOpen(false);
|
|
294
|
-
break;
|
|
295
|
-
case 'ArrowDown':
|
|
296
|
-
if (isOpen) e.preventDefault();
|
|
297
|
-
break;
|
|
298
|
-
}
|
|
299
|
-
}, [disabled, isOpen, toggle]);
|
|
300
|
-
|
|
301
|
-
return (
|
|
302
|
-
<${name}Context.Provider value={contextValue}>
|
|
303
|
-
<div
|
|
304
|
-
ref={(node) => {
|
|
305
|
-
elementRef.current = node;
|
|
306
|
-
if (typeof ref === 'function') ref(node);
|
|
307
|
-
else if (ref) ref.current = node;
|
|
308
|
-
}}
|
|
309
|
-
className={cn(
|
|
310
|
-
'c-${name.toLowerCase()}',
|
|
311
|
-
isOpen && 'is-open',
|
|
312
|
-
disabled && 'is-disabled',
|
|
313
|
-
!isValid && 'is-invalid',
|
|
314
|
-
animation && 'has-animation',
|
|
315
|
-
className
|
|
316
|
-
)}
|
|
317
|
-
data-state={isOpen ? 'open' : 'closed'}
|
|
318
|
-
data-disabled={disabled}
|
|
319
|
-
data-valid={isValid}
|
|
320
|
-
onKeyDown={handleKeyDown}
|
|
321
|
-
role="region"
|
|
322
|
-
aria-expanded={isOpen}
|
|
323
|
-
aria-disabled={disabled}
|
|
324
|
-
aria-invalid={!isValid}
|
|
325
|
-
aria-describedby={!isValid ? \`${name.toLowerCase()}-error\` : undefined}
|
|
326
|
-
{...props}
|
|
327
|
-
>
|
|
328
|
-
{children}
|
|
329
|
-
|
|
330
|
-
{!isValid && validationMessage && (
|
|
331
|
-
<div
|
|
332
|
-
id={\`${name.toLowerCase()}-error\`}
|
|
333
|
-
className="c-${name.toLowerCase()}__error"
|
|
334
|
-
role="alert"
|
|
335
|
-
aria-live="polite"
|
|
336
|
-
>
|
|
337
|
-
{validationMessage}
|
|
338
|
-
</div>
|
|
339
|
-
)}
|
|
340
|
-
</div>
|
|
341
|
-
</${name}Context.Provider>
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
);
|
|
345
|
-
|
|
346
|
-
${name}.displayName = '${name}';
|
|
347
|
-
`;
|
|
128
|
+
return componentTemplates.react.complex(name);
|
|
348
129
|
}
|
|
349
130
|
|
|
350
131
|
/**
|
|
@@ -380,81 +161,246 @@ export function generateComponentByComplexity(name, complexity, options = {}) {
|
|
|
380
161
|
* Interactive component generation
|
|
381
162
|
*/
|
|
382
163
|
export async function interactiveComponentGeneration() {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
{
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
{
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
{
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
{
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
164
|
+
try {
|
|
165
|
+
console.log(boxen(
|
|
166
|
+
chalk.bold.cyan('🎨 Interactive Component Generator\n\n') +
|
|
167
|
+
chalk.gray('Let\'s create a new component for your design system'),
|
|
168
|
+
{
|
|
169
|
+
padding: 1,
|
|
170
|
+
margin: 1,
|
|
171
|
+
borderStyle: 'round',
|
|
172
|
+
borderColor: 'cyan'
|
|
173
|
+
}
|
|
174
|
+
));
|
|
175
|
+
|
|
176
|
+
// Step 1: Component name
|
|
177
|
+
const { componentName } = await inquirer.prompt([
|
|
178
|
+
{
|
|
179
|
+
type: 'input',
|
|
180
|
+
name: 'componentName',
|
|
181
|
+
message: 'What is your component name?',
|
|
182
|
+
validate: (input) => {
|
|
183
|
+
const validation = validateComponentName(input);
|
|
184
|
+
return validation.isValid || validation.error;
|
|
185
|
+
},
|
|
186
|
+
filter: (input) => sanitizeInput(input)
|
|
187
|
+
}
|
|
188
|
+
]);
|
|
189
|
+
|
|
190
|
+
// Step 2: Complexity level
|
|
191
|
+
const { complexity } = await inquirer.prompt([
|
|
192
|
+
{
|
|
193
|
+
type: 'list',
|
|
194
|
+
name: 'complexity',
|
|
195
|
+
message: 'What is the complexity level?',
|
|
196
|
+
choices: Object.values(COMPLEXITY_LEVELS).map(level => ({
|
|
197
|
+
name: `${chalk.bold(level.name.charAt(0).toUpperCase() + level.name.slice(1))} - ${level.description}`,
|
|
198
|
+
value: level.name,
|
|
199
|
+
short: level.name
|
|
200
|
+
})),
|
|
201
|
+
default: 'medium'
|
|
202
|
+
}
|
|
203
|
+
]);
|
|
204
|
+
|
|
205
|
+
// Step 3: Features
|
|
206
|
+
const { features } = await inquirer.prompt([
|
|
207
|
+
{
|
|
208
|
+
type: 'checkbox',
|
|
209
|
+
name: 'features',
|
|
210
|
+
message: 'Select features to include:',
|
|
211
|
+
choices: Object.values(COMPONENT_FEATURES).map(feature => ({
|
|
212
|
+
name: `${feature.description}`,
|
|
213
|
+
value: feature.name,
|
|
214
|
+
checked: feature.default
|
|
215
|
+
}))
|
|
216
|
+
}
|
|
217
|
+
]);
|
|
218
|
+
|
|
219
|
+
// Step 4: Output path
|
|
220
|
+
const { outputPath } = await inquirer.prompt([
|
|
221
|
+
{
|
|
222
|
+
type: 'input',
|
|
223
|
+
name: 'outputPath',
|
|
224
|
+
message: 'Output directory:',
|
|
225
|
+
default: './src/components',
|
|
226
|
+
validate: (input) => {
|
|
227
|
+
const validation = validatePath(sanitizeInput(input));
|
|
228
|
+
return validation.isValid || validation.error;
|
|
229
|
+
},
|
|
230
|
+
filter: (input) => sanitizeInput(input)
|
|
231
|
+
}
|
|
232
|
+
]);
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
name: componentName,
|
|
236
|
+
complexity,
|
|
237
|
+
features,
|
|
238
|
+
outputPath
|
|
239
|
+
};
|
|
240
|
+
} catch (error) {
|
|
241
|
+
if (error.message === 'User cancelled') {
|
|
242
|
+
return null;
|
|
449
243
|
}
|
|
450
|
-
|
|
244
|
+
throw error;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
451
247
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
248
|
+
/**
|
|
249
|
+
* Generate all component files
|
|
250
|
+
*/
|
|
251
|
+
export async function generateComponentFiles(name, options = {}) {
|
|
252
|
+
const {
|
|
253
|
+
outputPath = './src/components',
|
|
254
|
+
complexity = 'medium',
|
|
255
|
+
features = [],
|
|
256
|
+
spinner
|
|
257
|
+
} = options;
|
|
258
|
+
|
|
259
|
+
const componentPath = join(outputPath, name);
|
|
260
|
+
|
|
261
|
+
// Create component directory
|
|
262
|
+
await mkdir(componentPath, { recursive: true });
|
|
263
|
+
|
|
264
|
+
// Determine if we need to generate a composable hook
|
|
265
|
+
const hasComposable = features.includes('HOOK') || COMPONENT_FEATURES.HOOK.default;
|
|
266
|
+
|
|
267
|
+
// Determine if we need glass effect
|
|
268
|
+
const hasGlass = features.includes('STYLES') || COMPONENT_FEATURES.STYLES.default;
|
|
269
|
+
|
|
270
|
+
// Generate main component file
|
|
271
|
+
const componentContent = generateComponentByComplexity(name, complexity, {
|
|
272
|
+
hasComposable,
|
|
273
|
+
hasGlass
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
await writeFile(
|
|
277
|
+
join(componentPath, `${name}.tsx`),
|
|
278
|
+
componentContent
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
if (spinner) spinner.text = `Generating ${name}.tsx...`;
|
|
282
|
+
|
|
283
|
+
// Generate index file
|
|
284
|
+
const indexContent = componentTemplates.react.index(name);
|
|
285
|
+
await writeFile(
|
|
286
|
+
join(componentPath, 'index.ts'),
|
|
287
|
+
indexContent
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
if (spinner) spinner.text = `Generating index.ts...`;
|
|
291
|
+
|
|
292
|
+
// Generate storybook file if feature is enabled
|
|
293
|
+
if (features.includes('storybook') || COMPONENT_FEATURES.STORYBOOK.default) {
|
|
294
|
+
const storyContent = componentTemplates.react.story(name);
|
|
295
|
+
await writeFile(
|
|
296
|
+
join(componentPath, `${name}.stories.tsx`),
|
|
297
|
+
storyContent
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
if (spinner) spinner.text = `Generating ${name}.stories.tsx...`;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Generate test file if feature is enabled
|
|
304
|
+
if (features.includes('tests') || COMPONENT_FEATURES.TESTS.default) {
|
|
305
|
+
const testContent = componentTemplates.react.test(name);
|
|
306
|
+
await writeFile(
|
|
307
|
+
join(componentPath, `${name}.test.tsx`),
|
|
308
|
+
testContent
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
if (spinner) spinner.text = `Generating ${name}.test.tsx...`;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Generate composable hook if feature is enabled
|
|
315
|
+
if (hasComposable) {
|
|
316
|
+
const composableDir = join(outputPath, '..', 'lib', 'composables');
|
|
317
|
+
await mkdir(composableDir, { recursive: true });
|
|
318
|
+
|
|
319
|
+
const composableContent = componentTemplates.composable.useHook(name);
|
|
320
|
+
await writeFile(
|
|
321
|
+
join(composableDir, `use${name}.ts`),
|
|
322
|
+
composableContent
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
if (spinner) spinner.text = `Generating use${name}.ts...`;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Generate constants and types if TypeScript feature is enabled
|
|
329
|
+
if (features.includes('typescript') || COMPONENT_FEATURES.TYPESCRIPT.default) {
|
|
330
|
+
// Note: In a real implementation, we'd append to existing files rather than overwrite
|
|
331
|
+
// This is simplified for demonstration purposes
|
|
332
|
+
const constantsContent = componentTemplates.types.constants(name);
|
|
333
|
+
const typesContent = componentTemplates.types.types(name);
|
|
334
|
+
|
|
335
|
+
// In a real scenario, these would be appended to existing files
|
|
336
|
+
// For now, we'll just show what would be added
|
|
337
|
+
console.log(chalk.blue(`\n💡 Remember to add these to the appropriate files:`));
|
|
338
|
+
console.log(chalk.gray(`Constants to src/lib/constants/components.ts:`));
|
|
339
|
+
console.log(chalk.gray(constantsContent.substring(0, 100) + '...'));
|
|
340
|
+
console.log(chalk.gray(`Types to src/lib/types/components.ts:`));
|
|
341
|
+
console.log(chalk.gray(typesContent.substring(0, 100) + '...'));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Generate SCSS files if styles feature is enabled
|
|
345
|
+
if (features.includes('styles') || COMPONENT_FEATURES.STYLES.default) {
|
|
346
|
+
const stylesBasePath = join(outputPath, '..', 'styles');
|
|
347
|
+
|
|
348
|
+
// Create settings file
|
|
349
|
+
const settingsPath = join(stylesBasePath, '01-settings');
|
|
350
|
+
await mkdir(settingsPath, { recursive: true });
|
|
351
|
+
|
|
352
|
+
const settingsContent = componentTemplates.scss.settings(name);
|
|
353
|
+
await writeFile(
|
|
354
|
+
join(settingsPath, `_settings.${name.toLowerCase()}.scss`),
|
|
355
|
+
settingsContent
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
if (spinner) spinner.text = `Generating _settings.${name.toLowerCase()}.scss...`;
|
|
359
|
+
|
|
360
|
+
// Create components file
|
|
361
|
+
const componentsPath = join(stylesBasePath, '06-components');
|
|
362
|
+
await mkdir(componentsPath, { recursive: true });
|
|
363
|
+
|
|
364
|
+
const componentStylesContent = componentTemplates.scss.component(name);
|
|
365
|
+
await writeFile(
|
|
366
|
+
join(componentsPath, `_components.${name.toLowerCase()}.scss`),
|
|
367
|
+
componentStylesContent
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
if (spinner) spinner.text = `Generating _components.${name.toLowerCase()}.scss...`;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return componentPath;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Generate a new component
|
|
378
|
+
*/
|
|
379
|
+
export async function generateComponent(options) {
|
|
380
|
+
const { name, complexity, features, outputPath } = options;
|
|
381
|
+
|
|
382
|
+
const spinner = ora({
|
|
383
|
+
text: chalk.blue('Generating component...'),
|
|
384
|
+
spinner: 'clock'
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
try {
|
|
388
|
+
spinner.start();
|
|
389
|
+
|
|
390
|
+
const componentPath = await generateComponentFiles(name, {
|
|
391
|
+
complexity,
|
|
392
|
+
features,
|
|
393
|
+
outputPath,
|
|
394
|
+
spinner
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
spinner.succeed(chalk.green(`Component ${name} generated successfully!\n${chalk.gray('Location:')} ${componentPath}`));
|
|
398
|
+
|
|
399
|
+
return componentPath;
|
|
400
|
+
} catch (error) {
|
|
401
|
+
spinner.fail(chalk.red(`Failed to generate component: ${error.message}`));
|
|
402
|
+
throw error;
|
|
403
|
+
}
|
|
458
404
|
}
|
|
459
405
|
|
|
460
406
|
/**
|
|
@@ -504,6 +450,15 @@ export async function validateGeneratedComponent(name, componentPath) {
|
|
|
504
450
|
suggestion: 'Add ARIA attributes and roles for better accessibility'
|
|
505
451
|
});
|
|
506
452
|
}
|
|
453
|
+
|
|
454
|
+
// Check for hardcoded styles
|
|
455
|
+
if (content.match(/#[0-9a-fA-F]{3,6}|rgb\(|rgba\(/)) {
|
|
456
|
+
warnings.push({
|
|
457
|
+
file: `${name}.tsx`,
|
|
458
|
+
issue: 'Using hardcoded values instead of design tokens',
|
|
459
|
+
suggestion: 'Use design tokens (e.g., var(--color-primary)) or utility classes'
|
|
460
|
+
});
|
|
461
|
+
}
|
|
507
462
|
} else {
|
|
508
463
|
issues.push({
|
|
509
464
|
file: `${name}.tsx`,
|
|
@@ -513,7 +468,6 @@ export async function validateGeneratedComponent(name, componentPath) {
|
|
|
513
468
|
}
|
|
514
469
|
|
|
515
470
|
// Check for SCSS file
|
|
516
|
-
const scssFile = join(componentPath, `${name}.scss`);
|
|
517
471
|
if (existsSync(componentPath)) {
|
|
518
472
|
// Check in styles directory
|
|
519
473
|
const globalScss = join(process.cwd(), 'src/styles/06-components', `_components.${name.toLowerCase()}.scss`);
|
|
@@ -592,7 +546,7 @@ export function displayValidationReport(result) {
|
|
|
592
546
|
console.log(chalk.bold.yellow(`\n⚠️ Found ${result.warnings.length} warning(s):\n`));
|
|
593
547
|
result.warnings.forEach((warning, index) => {
|
|
594
548
|
console.log(chalk.yellow(` ${index + 1}. ${warning.file}`));
|
|
595
|
-
console.log(chalk.gray(` Warning: ${warning.
|
|
549
|
+
console.log(chalk.gray(` Warning: ${warning.issue}`));
|
|
596
550
|
console.log(chalk.cyan(` Suggestion: ${warning.suggestion}\n`));
|
|
597
551
|
});
|
|
598
552
|
}
|