svger-cli 1.0.6 → 1.0.8
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/CODE_OF_CONDUCT.md +79 -0
- package/CONTRIBUTING.md +146 -0
- package/LICENSE +21 -0
- package/README.md +1862 -73
- package/TESTING.md +143 -0
- package/cli-framework.test.js +16 -0
- package/cli-test-angular/Arrowbenddownleft.component.ts +27 -0
- package/cli-test-angular/Vite.component.ts +27 -0
- package/cli-test-angular/index.ts +25 -0
- package/{my-icons/ArrowBendDownLeft.tsx → cli-test-output/Arrowbenddownleft.vue} +28 -12
- package/{my-icons/Vite.tsx → cli-test-output/Vite.vue} +28 -12
- package/cli-test-output/index.ts +25 -0
- package/cli-test-react/Arrowbenddownleft.tsx +39 -0
- package/cli-test-react/Vite.tsx +39 -0
- package/cli-test-react/index.ts +25 -0
- package/cli-test-svelte/Arrowbenddownleft.svelte +22 -0
- package/cli-test-svelte/Vite.svelte +22 -0
- package/cli-test-svelte/index.ts +25 -0
- package/dist/builder.js +12 -13
- package/dist/clean.js +3 -3
- package/dist/cli.js +139 -61
- package/dist/config.d.ts +1 -1
- package/dist/config.js +5 -7
- package/dist/lock.js +1 -1
- package/dist/templates/ComponentTemplate.d.ts +15 -0
- package/dist/templates/ComponentTemplate.js +15 -0
- package/dist/watch.d.ts +2 -1
- package/dist/watch.js +30 -33
- package/docs/ADR-SVG-INTRGRATION-METHODS-002.adr.md +550 -0
- package/docs/FRAMEWORK-GUIDE.md +768 -0
- package/docs/IMPLEMENTATION-SUMMARY.md +376 -0
- package/frameworks.test.js +170 -0
- package/package.json +8 -10
- package/src/builder.ts +12 -13
- package/src/clean.ts +3 -3
- package/src/cli.ts +148 -59
- package/src/config.ts +5 -6
- package/src/core/error-handler.ts +303 -0
- package/src/core/framework-templates.ts +428 -0
- package/src/core/logger.ts +104 -0
- package/src/core/performance-engine.ts +327 -0
- package/src/core/plugin-manager.ts +228 -0
- package/src/core/style-compiler.ts +605 -0
- package/src/core/template-manager.ts +619 -0
- package/src/index.ts +235 -0
- package/src/lock.ts +1 -1
- package/src/processors/svg-processor.ts +288 -0
- package/src/services/config.ts +241 -0
- package/src/services/file-watcher.ts +218 -0
- package/src/services/svg-service.ts +468 -0
- package/src/types/index.ts +169 -0
- package/src/utils/native.ts +352 -0
- package/src/watch.ts +36 -36
- package/test-output-mulit/TestIcon-angular-module.component.ts +26 -0
- package/test-output-mulit/TestIcon-angular-standalone.component.ts +27 -0
- package/test-output-mulit/TestIcon-lit.ts +35 -0
- package/test-output-mulit/TestIcon-preact.tsx +38 -0
- package/test-output-mulit/TestIcon-react.tsx +35 -0
- package/test-output-mulit/TestIcon-solid.tsx +27 -0
- package/test-output-mulit/TestIcon-svelte.svelte +22 -0
- package/test-output-mulit/TestIcon-vanilla.ts +37 -0
- package/test-output-mulit/TestIcon-vue-composition.vue +33 -0
- package/test-output-mulit/TestIcon-vue-options.vue +31 -0
- package/tsconfig.json +11 -9
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
import { Template, ComponentGenerationOptions, TemplateConfig } from '../types/index.js';
|
|
2
|
+
import { logger } from '../core/logger.js';
|
|
3
|
+
import { FileSystem } from '../utils/native.js';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Template management system for customizable component generation
|
|
8
|
+
*/
|
|
9
|
+
export class TemplateManager {
|
|
10
|
+
private static instance: TemplateManager;
|
|
11
|
+
private templates: Map<string, Template> = new Map();
|
|
12
|
+
private defaultTemplate = 'react-functional';
|
|
13
|
+
|
|
14
|
+
private constructor() {
|
|
15
|
+
this.loadBuiltinTemplates();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public static getInstance(): TemplateManager {
|
|
19
|
+
if (!TemplateManager.instance) {
|
|
20
|
+
TemplateManager.instance = new TemplateManager();
|
|
21
|
+
}
|
|
22
|
+
return TemplateManager.instance;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Load built-in templates
|
|
27
|
+
*/
|
|
28
|
+
private loadBuiltinTemplates(): void {
|
|
29
|
+
// Standard React Functional Component
|
|
30
|
+
this.registerTemplate({
|
|
31
|
+
name: 'react-functional',
|
|
32
|
+
generate: (options: ComponentGenerationOptions) => this.generateReactFunctional(options),
|
|
33
|
+
validate: (options: ComponentGenerationOptions) => !!options.componentName && !!options.svgContent
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// React Functional Component with forwardRef
|
|
37
|
+
this.registerTemplate({
|
|
38
|
+
name: 'react-forwardref',
|
|
39
|
+
generate: (options: ComponentGenerationOptions) => this.generateReactForwardRef(options),
|
|
40
|
+
validate: (options: ComponentGenerationOptions) => !!options.componentName && !!options.svgContent
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// React Class Component (legacy support)
|
|
44
|
+
this.registerTemplate({
|
|
45
|
+
name: 'react-class',
|
|
46
|
+
generate: (options: ComponentGenerationOptions) => this.generateReactClass(options),
|
|
47
|
+
validate: (options: ComponentGenerationOptions) => !!options.componentName && !!options.svgContent
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Styled Components Template
|
|
51
|
+
this.registerTemplate({
|
|
52
|
+
name: 'styled-components',
|
|
53
|
+
generate: (options: ComponentGenerationOptions) => this.generateStyledComponents(options),
|
|
54
|
+
validate: (options: ComponentGenerationOptions) => !!options.componentName && !!options.svgContent
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// TypeScript Native (no React)
|
|
58
|
+
this.registerTemplate({
|
|
59
|
+
name: 'typescript-native',
|
|
60
|
+
generate: (options: ComponentGenerationOptions) => this.generateTypeScriptNative(options),
|
|
61
|
+
validate: (options: ComponentGenerationOptions) => !!options.componentName && !!options.svgContent
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Enhanced Styled Template
|
|
65
|
+
this.registerTemplate({
|
|
66
|
+
name: 'enhanced-styled',
|
|
67
|
+
generate: (options: ComponentGenerationOptions) => this.generateEnhancedStyled(options),
|
|
68
|
+
validate: (options: ComponentGenerationOptions) => !!options.componentName && !!options.svgContent
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
logger.debug('Built-in templates loaded');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Register a new template
|
|
76
|
+
*/
|
|
77
|
+
public registerTemplate(template: Template): void {
|
|
78
|
+
this.templates.set(template.name, template);
|
|
79
|
+
logger.debug(`Template registered: ${template.name}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Generate component using specified template
|
|
84
|
+
*/
|
|
85
|
+
public generateComponent(
|
|
86
|
+
templateName: string,
|
|
87
|
+
options: ComponentGenerationOptions
|
|
88
|
+
): string {
|
|
89
|
+
const template = this.templates.get(templateName);
|
|
90
|
+
|
|
91
|
+
if (!template) {
|
|
92
|
+
logger.warn(`Template not found: ${templateName}, using default`);
|
|
93
|
+
return this.generateComponent(this.defaultTemplate, options);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (template.validate && !template.validate(options)) {
|
|
97
|
+
throw new Error(`Invalid options for template: ${templateName}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return template.generate(options);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Load custom template from file
|
|
105
|
+
*/
|
|
106
|
+
public async loadCustomTemplate(templatePath: string): Promise<void> {
|
|
107
|
+
try {
|
|
108
|
+
const resolvedPath = path.resolve(templatePath);
|
|
109
|
+
|
|
110
|
+
if (!(await FileSystem.exists(resolvedPath))) {
|
|
111
|
+
throw new Error(`Template file not found: ${resolvedPath}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const templateContent = await FileSystem.readFile(resolvedPath, 'utf-8');
|
|
115
|
+
|
|
116
|
+
// Parse template (assuming it's a JavaScript module)
|
|
117
|
+
const templateName = path.basename(templatePath, path.extname(templatePath));
|
|
118
|
+
|
|
119
|
+
// For now, we'll treat it as a simple string template
|
|
120
|
+
this.registerTemplate({
|
|
121
|
+
name: templateName,
|
|
122
|
+
generate: (options: ComponentGenerationOptions) => {
|
|
123
|
+
return this.processStringTemplate(templateContent, options);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
logger.info(`Custom template loaded: ${templateName}`);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
logger.error(`Failed to load template from ${templatePath}:`, error);
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get list of available templates
|
|
136
|
+
*/
|
|
137
|
+
public getAvailableTemplates(): string[] {
|
|
138
|
+
return Array.from(this.templates.keys());
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Set default template
|
|
143
|
+
*/
|
|
144
|
+
public setDefaultTemplate(templateName: string): void {
|
|
145
|
+
if (!this.templates.has(templateName)) {
|
|
146
|
+
throw new Error(`Template not found: ${templateName}`);
|
|
147
|
+
}
|
|
148
|
+
this.defaultTemplate = templateName;
|
|
149
|
+
logger.info(`Default template set to: ${templateName}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ---- Built-in Template Generators ----
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Standard React Functional Component
|
|
156
|
+
*/
|
|
157
|
+
private generateReactFunctional(options: ComponentGenerationOptions): string {
|
|
158
|
+
const {
|
|
159
|
+
componentName,
|
|
160
|
+
svgContent,
|
|
161
|
+
defaultWidth = 24,
|
|
162
|
+
defaultHeight = 24,
|
|
163
|
+
defaultFill = 'currentColor'
|
|
164
|
+
} = options;
|
|
165
|
+
|
|
166
|
+
return `import React from "react";
|
|
167
|
+
import type { SVGProps } from "react";
|
|
168
|
+
|
|
169
|
+
export interface ${componentName}Props extends SVGProps<SVGSVGElement> {
|
|
170
|
+
size?: number | string;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* ${componentName} SVG Component
|
|
175
|
+
* Generated by svger-cli
|
|
176
|
+
*/
|
|
177
|
+
const ${componentName} = React.forwardRef<SVGSVGElement, ${componentName}Props>(
|
|
178
|
+
({ size, className, style, ...props }, ref) => {
|
|
179
|
+
const dimensions = size ? { width: size, height: size } : {
|
|
180
|
+
width: props.width || ${defaultWidth},
|
|
181
|
+
height: props.height || ${defaultHeight}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<svg
|
|
186
|
+
ref={ref}
|
|
187
|
+
viewBox="0 0 ${defaultWidth} ${defaultHeight}"
|
|
188
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
189
|
+
width={dimensions.width}
|
|
190
|
+
height={dimensions.height}
|
|
191
|
+
fill={props.fill || "${defaultFill}"}
|
|
192
|
+
className={className}
|
|
193
|
+
style={style}
|
|
194
|
+
{...props}
|
|
195
|
+
>
|
|
196
|
+
${svgContent}
|
|
197
|
+
</svg>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
${componentName}.displayName = "${componentName}";
|
|
203
|
+
|
|
204
|
+
export default ${componentName};
|
|
205
|
+
`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* React Functional Component with forwardRef
|
|
210
|
+
*/
|
|
211
|
+
private generateReactForwardRef(options: ComponentGenerationOptions): string {
|
|
212
|
+
const {
|
|
213
|
+
componentName,
|
|
214
|
+
svgContent,
|
|
215
|
+
defaultWidth = 24,
|
|
216
|
+
defaultHeight = 24,
|
|
217
|
+
defaultFill = 'currentColor'
|
|
218
|
+
} = options;
|
|
219
|
+
|
|
220
|
+
return `import { forwardRef } from "react";
|
|
221
|
+
import type { SVGProps } from "react";
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* ${componentName} SVG Component with forwardRef
|
|
225
|
+
* Generated by svger-cli
|
|
226
|
+
*/
|
|
227
|
+
const ${componentName} = forwardRef<SVGSVGElement, SVGProps<SVGSVGElement>>(
|
|
228
|
+
(props, ref) => (
|
|
229
|
+
<svg
|
|
230
|
+
ref={ref}
|
|
231
|
+
viewBox="0 0 ${defaultWidth} ${defaultHeight}"
|
|
232
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
233
|
+
width={props.width || ${defaultWidth}}
|
|
234
|
+
height={props.height || ${defaultHeight}}
|
|
235
|
+
fill={props.fill || "${defaultFill}"}
|
|
236
|
+
className={props.className}
|
|
237
|
+
style={props.style}
|
|
238
|
+
{...props}
|
|
239
|
+
>
|
|
240
|
+
${svgContent}
|
|
241
|
+
</svg>
|
|
242
|
+
)
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
${componentName}.displayName = "${componentName}";
|
|
246
|
+
|
|
247
|
+
export default ${componentName};
|
|
248
|
+
`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* React Class Component (legacy)
|
|
253
|
+
*/
|
|
254
|
+
private generateReactClass(options: ComponentGenerationOptions): string {
|
|
255
|
+
const {
|
|
256
|
+
componentName,
|
|
257
|
+
svgContent,
|
|
258
|
+
defaultWidth = 24,
|
|
259
|
+
defaultHeight = 24,
|
|
260
|
+
defaultFill = 'currentColor'
|
|
261
|
+
} = options;
|
|
262
|
+
|
|
263
|
+
return `import { Component } from "react";
|
|
264
|
+
import type { SVGProps } from "react";
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* ${componentName} SVG Component (Class-based)
|
|
268
|
+
* Generated by svger-cli
|
|
269
|
+
*/
|
|
270
|
+
class ${componentName} extends Component<SVGProps<SVGSVGElement>> {
|
|
271
|
+
render() {
|
|
272
|
+
const { width = ${defaultWidth}, height = ${defaultHeight}, fill = "${defaultFill}", ...props } = this.props;
|
|
273
|
+
|
|
274
|
+
return (
|
|
275
|
+
<svg
|
|
276
|
+
viewBox="0 0 ${defaultWidth} ${defaultHeight}"
|
|
277
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
278
|
+
width={width}
|
|
279
|
+
height={height}
|
|
280
|
+
fill={fill}
|
|
281
|
+
{...props}
|
|
282
|
+
>
|
|
283
|
+
${svgContent}
|
|
284
|
+
</svg>
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export default ${componentName};
|
|
290
|
+
`;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Styled Components Template
|
|
295
|
+
*/
|
|
296
|
+
private generateStyledComponents(options: ComponentGenerationOptions): string {
|
|
297
|
+
const {
|
|
298
|
+
componentName,
|
|
299
|
+
svgContent,
|
|
300
|
+
defaultWidth = 24,
|
|
301
|
+
defaultHeight = 24,
|
|
302
|
+
defaultFill = 'currentColor'
|
|
303
|
+
} = options;
|
|
304
|
+
|
|
305
|
+
return `import styled from "styled-components";
|
|
306
|
+
import type { SVGProps } from "react";
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* ${componentName} SVG Component with Styled Components
|
|
310
|
+
* Generated by svger-cli
|
|
311
|
+
*/
|
|
312
|
+
const StyledSVG = styled.svg<SVGProps<SVGSVGElement>>\`
|
|
313
|
+
width: \${props => props.width || '${defaultWidth}px'};
|
|
314
|
+
height: \${props => props.height || '${defaultHeight}px'};
|
|
315
|
+
fill: \${props => props.fill || '${defaultFill}'};
|
|
316
|
+
\`;
|
|
317
|
+
|
|
318
|
+
const ${componentName} = (props: SVGProps<SVGSVGElement>) => (
|
|
319
|
+
<StyledSVG
|
|
320
|
+
viewBox="0 0 ${defaultWidth} ${defaultHeight}"
|
|
321
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
322
|
+
{...props}
|
|
323
|
+
>
|
|
324
|
+
${svgContent}
|
|
325
|
+
</StyledSVG>
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
${componentName}.displayName = "${componentName}";
|
|
329
|
+
|
|
330
|
+
export default ${componentName};
|
|
331
|
+
`;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* TypeScript Native (no React dependencies)
|
|
336
|
+
*/
|
|
337
|
+
private generateTypeScriptNative(options: ComponentGenerationOptions): string {
|
|
338
|
+
const {
|
|
339
|
+
componentName,
|
|
340
|
+
svgContent,
|
|
341
|
+
defaultWidth = 24,
|
|
342
|
+
defaultHeight = 24,
|
|
343
|
+
defaultFill = 'currentColor'
|
|
344
|
+
} = options;
|
|
345
|
+
|
|
346
|
+
return `/**
|
|
347
|
+
* ${componentName} SVG Icon (Native TypeScript)
|
|
348
|
+
* Generated by svger-cli
|
|
349
|
+
*/
|
|
350
|
+
|
|
351
|
+
export interface ${componentName}Options {
|
|
352
|
+
width?: number | string;
|
|
353
|
+
height?: number | string;
|
|
354
|
+
fill?: string;
|
|
355
|
+
className?: string;
|
|
356
|
+
style?: Record<string, any>;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export function ${componentName}(options: ${componentName}Options = {}): string {
|
|
360
|
+
const {
|
|
361
|
+
width = ${defaultWidth},
|
|
362
|
+
height = ${defaultHeight},
|
|
363
|
+
fill = "${defaultFill}",
|
|
364
|
+
className = "",
|
|
365
|
+
style = {}
|
|
366
|
+
} = options;
|
|
367
|
+
|
|
368
|
+
const styleString = Object.entries(style)
|
|
369
|
+
.map(([key, value]) => \`\${key}: \${value}\`)
|
|
370
|
+
.join("; ");
|
|
371
|
+
|
|
372
|
+
return \`
|
|
373
|
+
<svg
|
|
374
|
+
viewBox="0 0 ${defaultWidth} ${defaultHeight}"
|
|
375
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
376
|
+
width="\${width}"
|
|
377
|
+
height="\${height}"
|
|
378
|
+
fill="\${fill}"
|
|
379
|
+
class="\${className}"
|
|
380
|
+
style="\${styleString}"
|
|
381
|
+
>
|
|
382
|
+
${svgContent}
|
|
383
|
+
</svg>
|
|
384
|
+
\`.trim();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export default ${componentName};
|
|
388
|
+
`;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Process string template with variable substitution
|
|
393
|
+
*/
|
|
394
|
+
private processStringTemplate(
|
|
395
|
+
template: string,
|
|
396
|
+
options: ComponentGenerationOptions
|
|
397
|
+
): string {
|
|
398
|
+
const variables = {
|
|
399
|
+
...options,
|
|
400
|
+
defaultWidth: options.defaultWidth || 24,
|
|
401
|
+
defaultHeight: options.defaultHeight || 24,
|
|
402
|
+
defaultFill: options.defaultFill || 'currentColor'
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
let processed = template;
|
|
406
|
+
|
|
407
|
+
// Replace variables in the format {{variableName}}
|
|
408
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
409
|
+
const regex = new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, 'g');
|
|
410
|
+
processed = processed.replace(regex, String(value));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return processed;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Enhanced Styled Template with comprehensive styling support
|
|
418
|
+
*/
|
|
419
|
+
private generateEnhancedStyled(options: ComponentGenerationOptions): string {
|
|
420
|
+
const {
|
|
421
|
+
componentName,
|
|
422
|
+
svgContent,
|
|
423
|
+
defaultWidth = 24,
|
|
424
|
+
defaultHeight = 24,
|
|
425
|
+
defaultFill = 'currentColor'
|
|
426
|
+
} = options;
|
|
427
|
+
|
|
428
|
+
return `import React from "react";
|
|
429
|
+
import type { SVGProps } from "react";
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* ${componentName} SVG Component with Enhanced Styling
|
|
433
|
+
* Generated by svger-cli with comprehensive styling support
|
|
434
|
+
*/
|
|
435
|
+
|
|
436
|
+
export interface ${componentName}Props extends SVGProps<SVGSVGElement> {
|
|
437
|
+
// Size variants
|
|
438
|
+
size?: number | string | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
439
|
+
|
|
440
|
+
// Color variants
|
|
441
|
+
variant?: 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info';
|
|
442
|
+
|
|
443
|
+
// Animation options
|
|
444
|
+
animate?: boolean | 'spin' | 'pulse' | 'bounce' | 'fade';
|
|
445
|
+
|
|
446
|
+
// Theme support
|
|
447
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
448
|
+
|
|
449
|
+
// Responsive behavior
|
|
450
|
+
responsive?: boolean;
|
|
451
|
+
|
|
452
|
+
// Interaction states
|
|
453
|
+
loading?: boolean;
|
|
454
|
+
disabled?: boolean;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const sizeMap = {
|
|
458
|
+
xs: 12,
|
|
459
|
+
sm: 16,
|
|
460
|
+
md: 24,
|
|
461
|
+
lg: 32,
|
|
462
|
+
xl: 48,
|
|
463
|
+
} as const;
|
|
464
|
+
|
|
465
|
+
const colorMap = {
|
|
466
|
+
primary: '#007bff',
|
|
467
|
+
secondary: '#6c757d',
|
|
468
|
+
success: '#28a745',
|
|
469
|
+
warning: '#ffc107',
|
|
470
|
+
danger: '#dc3545',
|
|
471
|
+
info: '#17a2b8',
|
|
472
|
+
} as const;
|
|
473
|
+
|
|
474
|
+
const ${componentName} = React.forwardRef<SVGSVGElement, ${componentName}Props>(
|
|
475
|
+
({
|
|
476
|
+
size = 'md',
|
|
477
|
+
variant,
|
|
478
|
+
animate = false,
|
|
479
|
+
theme = 'auto',
|
|
480
|
+
responsive = false,
|
|
481
|
+
loading = false,
|
|
482
|
+
disabled = false,
|
|
483
|
+
style,
|
|
484
|
+
className,
|
|
485
|
+
...props
|
|
486
|
+
}, ref) => {
|
|
487
|
+
|
|
488
|
+
// Calculate size
|
|
489
|
+
const dimensions = React.useMemo(() => {
|
|
490
|
+
if (typeof size === 'number') return { width: size, height: size };
|
|
491
|
+
if (typeof size === 'string' && !isNaN(Number(size))) return { width: Number(size), height: Number(size) };
|
|
492
|
+
const mappedSize = sizeMap[size as keyof typeof sizeMap] || sizeMap.md;
|
|
493
|
+
return { width: mappedSize, height: mappedSize };
|
|
494
|
+
}, [size]);
|
|
495
|
+
|
|
496
|
+
// Generate styles
|
|
497
|
+
const computedStyles = React.useMemo(() => {
|
|
498
|
+
const baseStyles: React.CSSProperties = {
|
|
499
|
+
display: 'inline-block',
|
|
500
|
+
verticalAlign: 'middle',
|
|
501
|
+
transition: 'all 0.2s ease-in-out',
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
// Apply color variant
|
|
505
|
+
if (variant) {
|
|
506
|
+
baseStyles.color = colorMap[variant];
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Apply theme
|
|
510
|
+
if (theme === 'dark') {
|
|
511
|
+
baseStyles.filter = 'invert(1)';
|
|
512
|
+
} else if (theme === 'auto') {
|
|
513
|
+
baseStyles.filter = 'var(--svger-theme-filter, none)';
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Apply animation
|
|
517
|
+
if (animate) {
|
|
518
|
+
if (animate === true || animate === 'spin') {
|
|
519
|
+
baseStyles.animation = 'svger-spin 2s linear infinite';
|
|
520
|
+
} else if (animate === 'pulse') {
|
|
521
|
+
baseStyles.animation = 'svger-pulse 2s ease-in-out infinite';
|
|
522
|
+
} else if (animate === 'bounce') {
|
|
523
|
+
baseStyles.animation = 'svger-bounce 1s infinite';
|
|
524
|
+
} else if (animate === 'fade') {
|
|
525
|
+
baseStyles.animation = 'svger-fade 2s ease-in-out infinite alternate';
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Apply loading state
|
|
530
|
+
if (loading) {
|
|
531
|
+
baseStyles.animation = 'svger-spin 1s linear infinite';
|
|
532
|
+
baseStyles.opacity = 0.7;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Apply disabled state
|
|
536
|
+
if (disabled) {
|
|
537
|
+
baseStyles.opacity = 0.5;
|
|
538
|
+
baseStyles.pointerEvents = 'none';
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Apply responsive behavior
|
|
542
|
+
if (responsive) {
|
|
543
|
+
baseStyles.width = '100%';
|
|
544
|
+
baseStyles.height = 'auto';
|
|
545
|
+
baseStyles.maxWidth = \`\${dimensions.width}px\`;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return { ...baseStyles, ...style };
|
|
549
|
+
}, [variant, theme, animate, loading, disabled, responsive, dimensions, style]);
|
|
550
|
+
|
|
551
|
+
// Generate class names
|
|
552
|
+
const classNames = React.useMemo(() => {
|
|
553
|
+
const classes = ['svger-icon'];
|
|
554
|
+
|
|
555
|
+
if (typeof size === 'string' && sizeMap[size as keyof typeof sizeMap]) {
|
|
556
|
+
classes.push(\`svger-size-\${size}\`);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (variant) {
|
|
560
|
+
classes.push(\`svger-variant-\${variant}\`);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (theme) {
|
|
564
|
+
classes.push(\`svger-theme-\${theme}\`);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (animate) {
|
|
568
|
+
const animationType = animate === true ? 'spin' : animate;
|
|
569
|
+
classes.push(\`svger-animate-\${animationType}\`);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (responsive) {
|
|
573
|
+
classes.push('svger-responsive');
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (loading) {
|
|
577
|
+
classes.push('svger-loading');
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (disabled) {
|
|
581
|
+
classes.push('svger-disabled');
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
if (className) {
|
|
585
|
+
classes.push(className);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
return classes.join(' ');
|
|
589
|
+
}, [size, variant, theme, animate, responsive, loading, disabled, className]);
|
|
590
|
+
|
|
591
|
+
return (
|
|
592
|
+
<svg
|
|
593
|
+
ref={ref}
|
|
594
|
+
viewBox="0 0 ${defaultWidth} ${defaultHeight}"
|
|
595
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
596
|
+
width={responsive ? '100%' : dimensions.width}
|
|
597
|
+
height={responsive ? 'auto' : dimensions.height}
|
|
598
|
+
fill={props.fill || "${defaultFill}"}
|
|
599
|
+
className={classNames}
|
|
600
|
+
style={computedStyles}
|
|
601
|
+
aria-hidden={props['aria-hidden'] || (disabled ? 'true' : undefined)}
|
|
602
|
+
aria-busy={loading ? 'true' : undefined}
|
|
603
|
+
{...props}
|
|
604
|
+
>
|
|
605
|
+
${svgContent}
|
|
606
|
+
</svg>
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
${componentName}.displayName = "${componentName}";
|
|
612
|
+
|
|
613
|
+
export default ${componentName};
|
|
614
|
+
`;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Export singleton instance
|
|
619
|
+
export const templateManager = TemplateManager.getInstance();
|