svger-cli 2.0.5 → 2.0.6
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/.svgerconfig.example.json +119 -119
- package/CHANGELOG.md +155 -63
- package/DEVELOPMENT.md +352 -352
- package/LICENSE +20 -20
- package/README.md +2654 -2132
- package/SECURITY.md +68 -68
- package/bin/svg-tool.js +2 -2
- package/dist/core/framework-templates.js +285 -285
- package/dist/core/style-compiler.js +201 -201
- package/dist/core/template-manager.js +348 -348
- package/dist/services/svg-service.js +12 -12
- package/dist/templates/ComponentTemplate.js +17 -17
- package/docs/ADR-SVG-INTRGRATION-METHODS-001.adr.md +157 -157
- package/docs/ADR-SVG-INTRGRATION-METHODS-002.adr.md +549 -549
- package/docs/FRAMEWORK-GUIDE.md +768 -768
- package/docs/IMPLEMENTATION-SUMMARY.md +376 -376
- package/package.json +177 -179
|
@@ -77,22 +77,22 @@ export class SVGStyleCompiler {
|
|
|
77
77
|
? `import React from 'react';\nimport type { SVGProps } from "react";`
|
|
78
78
|
: `import type { SVGProps } from "react";`;
|
|
79
79
|
const styledCSS = hasCustomStyles
|
|
80
|
-
? `
|
|
81
|
-
const ${componentName}Styles = \`
|
|
82
|
-
${compiled.cssRules.join('\n')}
|
|
83
|
-
${compiled.mediaQueries.join('\n')}
|
|
84
|
-
\`;
|
|
85
|
-
|
|
86
|
-
// Inject styles
|
|
87
|
-
if (typeof document !== 'undefined') {
|
|
88
|
-
const styleId = '${componentName.toLowerCase()}-styles';
|
|
89
|
-
if (!document.getElementById(styleId)) {
|
|
90
|
-
const style = document.createElement('style');
|
|
91
|
-
style.id = styleId;
|
|
92
|
-
style.textContent = ${componentName}Styles;
|
|
93
|
-
document.head.appendChild(style);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
80
|
+
? `
|
|
81
|
+
const ${componentName}Styles = \`
|
|
82
|
+
${compiled.cssRules.join('\n')}
|
|
83
|
+
${compiled.mediaQueries.join('\n')}
|
|
84
|
+
\`;
|
|
85
|
+
|
|
86
|
+
// Inject styles
|
|
87
|
+
if (typeof document !== 'undefined') {
|
|
88
|
+
const styleId = '${componentName.toLowerCase()}-styles';
|
|
89
|
+
if (!document.getElementById(styleId)) {
|
|
90
|
+
const style = document.createElement('style');
|
|
91
|
+
style.id = styleId;
|
|
92
|
+
style.textContent = ${componentName}Styles;
|
|
93
|
+
document.head.appendChild(style);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
96
|
`
|
|
97
97
|
: '';
|
|
98
98
|
const inlineStyleObject = Object.keys(compiled.inline).length > 0
|
|
@@ -101,187 +101,187 @@ if (typeof document !== 'undefined') {
|
|
|
101
101
|
const classNames = compiled.classes.length > 0
|
|
102
102
|
? `'${compiled.classes.join(' ')} ' + (props.className || '')`
|
|
103
103
|
: 'props.className';
|
|
104
|
-
return `${imports}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* ${componentName} SVG Component with Enhanced Styling
|
|
108
|
-
* Generated by svger-cli with comprehensive styling support
|
|
109
|
-
*/
|
|
110
|
-
|
|
111
|
-
${styledCSS}${inlineStyleObject}
|
|
112
|
-
|
|
113
|
-
export interface ${componentName}Props extends SVGProps<SVGSVGElement> {
|
|
114
|
-
// Enhanced styling props
|
|
115
|
-
size?: number | string;
|
|
116
|
-
variant?: 'primary' | 'secondary' | 'accent' | 'muted';
|
|
117
|
-
responsive?: boolean;
|
|
118
|
-
|
|
119
|
-
// Animation props
|
|
120
|
-
animate?: boolean;
|
|
121
|
-
animationType?: 'spin' | 'pulse' | 'bounce' | 'fade';
|
|
122
|
-
|
|
123
|
-
// Theme props
|
|
124
|
-
theme?: 'light' | 'dark' | 'auto';
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const ${componentName} = React.forwardRef<SVGSVGElement, ${componentName}Props>(
|
|
128
|
-
({
|
|
129
|
-
size,
|
|
130
|
-
variant = 'primary',
|
|
131
|
-
responsive = false,
|
|
132
|
-
animate = false,
|
|
133
|
-
animationType = 'spin',
|
|
134
|
-
theme = 'auto',
|
|
135
|
-
style,
|
|
136
|
-
className,
|
|
137
|
-
...props
|
|
138
|
-
}, ref) => {
|
|
139
|
-
|
|
140
|
-
// Calculate dimensions
|
|
141
|
-
const dimensions = React.useMemo(() => {
|
|
142
|
-
if (size) {
|
|
143
|
-
return { width: size, height: size };
|
|
144
|
-
}
|
|
145
|
-
return {
|
|
146
|
-
width: props.width || ${options.width || 24},
|
|
147
|
-
height: props.height || ${options.height || 24}
|
|
148
|
-
};
|
|
149
|
-
}, [size, props.width, props.height]);
|
|
150
|
-
|
|
151
|
-
// Combine styles
|
|
152
|
-
const combinedStyles = React.useMemo(() => {
|
|
153
|
-
const baseStyles = ${inlineStyleObject ? 'defaultStyles' : '{}'};
|
|
154
|
-
const variantStyles = getVariantStyles(variant);
|
|
155
|
-
const animationStyles = animate ? getAnimationStyles(animationType) : {};
|
|
156
|
-
const themeStyles = getThemeStyles(theme);
|
|
157
|
-
|
|
158
|
-
return {
|
|
159
|
-
...baseStyles,
|
|
160
|
-
...variantStyles,
|
|
161
|
-
...animationStyles,
|
|
162
|
-
...themeStyles,
|
|
163
|
-
...style
|
|
164
|
-
};
|
|
165
|
-
}, [variant, animate, animationType, theme, style]);
|
|
166
|
-
|
|
167
|
-
// Combine class names
|
|
168
|
-
const combinedClassName = React.useMemo(() => {
|
|
169
|
-
const classes = [];
|
|
170
|
-
${compiled.classes.length > 0 ? `classes.push('${compiled.classes.join(' ')}');` : ''}
|
|
171
|
-
if (responsive) classes.push(\`\${componentName.toLowerCase()}-responsive\`);
|
|
172
|
-
if (animate) classes.push(\`\${componentName.toLowerCase()}-animate-\${animationType}\`);
|
|
173
|
-
classes.push(\`\${componentName.toLowerCase()}-variant-\${variant}\`);
|
|
174
|
-
classes.push(\`\${componentName.toLowerCase()}-theme-\${theme}\`);
|
|
175
|
-
if (className) classes.push(className);
|
|
176
|
-
return classes.join(' ');
|
|
177
|
-
}, [responsive, animate, animationType, variant, theme, className]);
|
|
178
|
-
|
|
179
|
-
return (
|
|
180
|
-
<svg
|
|
181
|
-
ref={ref}
|
|
182
|
-
viewBox="0 0 24 24"
|
|
183
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
184
|
-
width={dimensions.width}
|
|
185
|
-
height={dimensions.height}
|
|
186
|
-
fill={props.fill || "${options.fill || 'currentColor'}"}
|
|
187
|
-
className={combinedClassName}
|
|
188
|
-
style={combinedStyles}
|
|
189
|
-
aria-hidden={props['aria-hidden']}
|
|
190
|
-
aria-label={props['aria-label']}
|
|
191
|
-
role={props.role || 'img'}
|
|
192
|
-
{...props}
|
|
193
|
-
>
|
|
194
|
-
${options.title ? `<title>${options.title}</title>` : ''}
|
|
195
|
-
${svgContent}
|
|
196
|
-
</svg>
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
// Utility functions for styling
|
|
202
|
-
function getVariantStyles(variant: string): React.CSSProperties {
|
|
203
|
-
const variants = {
|
|
204
|
-
primary: { color: 'var(--color-primary, #007bff)' },
|
|
205
|
-
secondary: { color: 'var(--color-secondary, #6c757d)' },
|
|
206
|
-
accent: { color: 'var(--color-accent, #28a745)' },
|
|
207
|
-
muted: { color: 'var(--color-muted, #6c757d)', opacity: 0.7 }
|
|
208
|
-
};
|
|
209
|
-
return variants[variant as keyof typeof variants] || variants.primary;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function getAnimationStyles(animationType: string): React.CSSProperties {
|
|
213
|
-
const animations = {
|
|
214
|
-
spin: { animation: 'svger-spin 1s linear infinite' },
|
|
215
|
-
pulse: { animation: 'svger-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite' },
|
|
216
|
-
bounce: { animation: 'svger-bounce 1s infinite' },
|
|
217
|
-
fade: { animation: 'svger-fade 2s ease-in-out infinite alternate' }
|
|
218
|
-
};
|
|
219
|
-
return animations[animationType as keyof typeof animations] || {};
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function getThemeStyles(theme: string): React.CSSProperties {
|
|
223
|
-
if (theme === 'dark') {
|
|
224
|
-
return { filter: 'invert(1) hue-rotate(180deg)' };
|
|
225
|
-
}
|
|
226
|
-
if (theme === 'auto') {
|
|
227
|
-
return { filter: 'var(--svger-theme-filter, none)' };
|
|
228
|
-
}
|
|
229
|
-
return {};
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
${componentName}.displayName = "${componentName}";
|
|
233
|
-
|
|
234
|
-
export default ${componentName};
|
|
235
|
-
|
|
236
|
-
// CSS Animations (injected globally)
|
|
237
|
-
if (typeof document !== 'undefined') {
|
|
238
|
-
const animationCSS = \`
|
|
239
|
-
@keyframes svger-spin {
|
|
240
|
-
from { transform: rotate(0deg); }
|
|
241
|
-
to { transform: rotate(360deg); }
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
@keyframes svger-pulse {
|
|
245
|
-
0%, 100% { opacity: 1; }
|
|
246
|
-
50% { opacity: 0.5; }
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
@keyframes svger-bounce {
|
|
250
|
-
0%, 20%, 53%, 80%, 100% { transform: translate3d(0,0,0); }
|
|
251
|
-
40%, 43% { transform: translate3d(0,-30px,0); }
|
|
252
|
-
70% { transform: translate3d(0,-15px,0); }
|
|
253
|
-
90% { transform: translate3d(0,-4px,0); }
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
@keyframes svger-fade {
|
|
257
|
-
from { opacity: 0.4; }
|
|
258
|
-
to { opacity: 1; }
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/* CSS Custom Properties for theming */
|
|
262
|
-
:root {
|
|
263
|
-
--color-primary: #007bff;
|
|
264
|
-
--color-secondary: #6c757d;
|
|
265
|
-
--color-accent: #28a745;
|
|
266
|
-
--color-muted: #6c757d;
|
|
267
|
-
--svger-theme-filter: none;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
@media (prefers-color-scheme: dark) {
|
|
271
|
-
:root {
|
|
272
|
-
--svger-theme-filter: invert(1) hue-rotate(180deg);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
\`;
|
|
276
|
-
|
|
277
|
-
const globalStyleId = 'svger-global-animations';
|
|
278
|
-
if (!document.getElementById(globalStyleId)) {
|
|
279
|
-
const style = document.createElement('style');
|
|
280
|
-
style.id = globalStyleId;
|
|
281
|
-
style.textContent = animationCSS;
|
|
282
|
-
document.head.appendChild(style);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
104
|
+
return `${imports}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* ${componentName} SVG Component with Enhanced Styling
|
|
108
|
+
* Generated by svger-cli with comprehensive styling support
|
|
109
|
+
*/
|
|
110
|
+
|
|
111
|
+
${styledCSS}${inlineStyleObject}
|
|
112
|
+
|
|
113
|
+
export interface ${componentName}Props extends SVGProps<SVGSVGElement> {
|
|
114
|
+
// Enhanced styling props
|
|
115
|
+
size?: number | string;
|
|
116
|
+
variant?: 'primary' | 'secondary' | 'accent' | 'muted';
|
|
117
|
+
responsive?: boolean;
|
|
118
|
+
|
|
119
|
+
// Animation props
|
|
120
|
+
animate?: boolean;
|
|
121
|
+
animationType?: 'spin' | 'pulse' | 'bounce' | 'fade';
|
|
122
|
+
|
|
123
|
+
// Theme props
|
|
124
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const ${componentName} = React.forwardRef<SVGSVGElement, ${componentName}Props>(
|
|
128
|
+
({
|
|
129
|
+
size,
|
|
130
|
+
variant = 'primary',
|
|
131
|
+
responsive = false,
|
|
132
|
+
animate = false,
|
|
133
|
+
animationType = 'spin',
|
|
134
|
+
theme = 'auto',
|
|
135
|
+
style,
|
|
136
|
+
className,
|
|
137
|
+
...props
|
|
138
|
+
}, ref) => {
|
|
139
|
+
|
|
140
|
+
// Calculate dimensions
|
|
141
|
+
const dimensions = React.useMemo(() => {
|
|
142
|
+
if (size) {
|
|
143
|
+
return { width: size, height: size };
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
width: props.width || ${options.width || 24},
|
|
147
|
+
height: props.height || ${options.height || 24}
|
|
148
|
+
};
|
|
149
|
+
}, [size, props.width, props.height]);
|
|
150
|
+
|
|
151
|
+
// Combine styles
|
|
152
|
+
const combinedStyles = React.useMemo(() => {
|
|
153
|
+
const baseStyles = ${inlineStyleObject ? 'defaultStyles' : '{}'};
|
|
154
|
+
const variantStyles = getVariantStyles(variant);
|
|
155
|
+
const animationStyles = animate ? getAnimationStyles(animationType) : {};
|
|
156
|
+
const themeStyles = getThemeStyles(theme);
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
...baseStyles,
|
|
160
|
+
...variantStyles,
|
|
161
|
+
...animationStyles,
|
|
162
|
+
...themeStyles,
|
|
163
|
+
...style
|
|
164
|
+
};
|
|
165
|
+
}, [variant, animate, animationType, theme, style]);
|
|
166
|
+
|
|
167
|
+
// Combine class names
|
|
168
|
+
const combinedClassName = React.useMemo(() => {
|
|
169
|
+
const classes = [];
|
|
170
|
+
${compiled.classes.length > 0 ? `classes.push('${compiled.classes.join(' ')}');` : ''}
|
|
171
|
+
if (responsive) classes.push(\`\${componentName.toLowerCase()}-responsive\`);
|
|
172
|
+
if (animate) classes.push(\`\${componentName.toLowerCase()}-animate-\${animationType}\`);
|
|
173
|
+
classes.push(\`\${componentName.toLowerCase()}-variant-\${variant}\`);
|
|
174
|
+
classes.push(\`\${componentName.toLowerCase()}-theme-\${theme}\`);
|
|
175
|
+
if (className) classes.push(className);
|
|
176
|
+
return classes.join(' ');
|
|
177
|
+
}, [responsive, animate, animationType, variant, theme, className]);
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<svg
|
|
181
|
+
ref={ref}
|
|
182
|
+
viewBox="0 0 24 24"
|
|
183
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
184
|
+
width={dimensions.width}
|
|
185
|
+
height={dimensions.height}
|
|
186
|
+
fill={props.fill || "${options.fill || 'currentColor'}"}
|
|
187
|
+
className={combinedClassName}
|
|
188
|
+
style={combinedStyles}
|
|
189
|
+
aria-hidden={props['aria-hidden']}
|
|
190
|
+
aria-label={props['aria-label']}
|
|
191
|
+
role={props.role || 'img'}
|
|
192
|
+
{...props}
|
|
193
|
+
>
|
|
194
|
+
${options.title ? `<title>${options.title}</title>` : ''}
|
|
195
|
+
${svgContent}
|
|
196
|
+
</svg>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
// Utility functions for styling
|
|
202
|
+
function getVariantStyles(variant: string): React.CSSProperties {
|
|
203
|
+
const variants = {
|
|
204
|
+
primary: { color: 'var(--color-primary, #007bff)' },
|
|
205
|
+
secondary: { color: 'var(--color-secondary, #6c757d)' },
|
|
206
|
+
accent: { color: 'var(--color-accent, #28a745)' },
|
|
207
|
+
muted: { color: 'var(--color-muted, #6c757d)', opacity: 0.7 }
|
|
208
|
+
};
|
|
209
|
+
return variants[variant as keyof typeof variants] || variants.primary;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function getAnimationStyles(animationType: string): React.CSSProperties {
|
|
213
|
+
const animations = {
|
|
214
|
+
spin: { animation: 'svger-spin 1s linear infinite' },
|
|
215
|
+
pulse: { animation: 'svger-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite' },
|
|
216
|
+
bounce: { animation: 'svger-bounce 1s infinite' },
|
|
217
|
+
fade: { animation: 'svger-fade 2s ease-in-out infinite alternate' }
|
|
218
|
+
};
|
|
219
|
+
return animations[animationType as keyof typeof animations] || {};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function getThemeStyles(theme: string): React.CSSProperties {
|
|
223
|
+
if (theme === 'dark') {
|
|
224
|
+
return { filter: 'invert(1) hue-rotate(180deg)' };
|
|
225
|
+
}
|
|
226
|
+
if (theme === 'auto') {
|
|
227
|
+
return { filter: 'var(--svger-theme-filter, none)' };
|
|
228
|
+
}
|
|
229
|
+
return {};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
${componentName}.displayName = "${componentName}";
|
|
233
|
+
|
|
234
|
+
export default ${componentName};
|
|
235
|
+
|
|
236
|
+
// CSS Animations (injected globally)
|
|
237
|
+
if (typeof document !== 'undefined') {
|
|
238
|
+
const animationCSS = \`
|
|
239
|
+
@keyframes svger-spin {
|
|
240
|
+
from { transform: rotate(0deg); }
|
|
241
|
+
to { transform: rotate(360deg); }
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
@keyframes svger-pulse {
|
|
245
|
+
0%, 100% { opacity: 1; }
|
|
246
|
+
50% { opacity: 0.5; }
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
@keyframes svger-bounce {
|
|
250
|
+
0%, 20%, 53%, 80%, 100% { transform: translate3d(0,0,0); }
|
|
251
|
+
40%, 43% { transform: translate3d(0,-30px,0); }
|
|
252
|
+
70% { transform: translate3d(0,-15px,0); }
|
|
253
|
+
90% { transform: translate3d(0,-4px,0); }
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
@keyframes svger-fade {
|
|
257
|
+
from { opacity: 0.4; }
|
|
258
|
+
to { opacity: 1; }
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/* CSS Custom Properties for theming */
|
|
262
|
+
:root {
|
|
263
|
+
--color-primary: #007bff;
|
|
264
|
+
--color-secondary: #6c757d;
|
|
265
|
+
--color-accent: #28a745;
|
|
266
|
+
--color-muted: #6c757d;
|
|
267
|
+
--svger-theme-filter: none;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
@media (prefers-color-scheme: dark) {
|
|
271
|
+
:root {
|
|
272
|
+
--svger-theme-filter: invert(1) hue-rotate(180deg);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
\`;
|
|
276
|
+
|
|
277
|
+
const globalStyleId = 'svger-global-animations';
|
|
278
|
+
if (!document.getElementById(globalStyleId)) {
|
|
279
|
+
const style = document.createElement('style');
|
|
280
|
+
style.id = globalStyleId;
|
|
281
|
+
style.textContent = animationCSS;
|
|
282
|
+
document.head.appendChild(style);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
285
|
`;
|
|
286
286
|
}
|
|
287
287
|
// Private helper methods
|
|
@@ -428,10 +428,10 @@ if (typeof document !== 'undefined') {
|
|
|
428
428
|
continue;
|
|
429
429
|
const mediaQuery = breakpoints[breakpoint];
|
|
430
430
|
if (mediaQuery && breakpointValue !== undefined) {
|
|
431
|
-
const rule = `@media (min-width: ${mediaQuery}) {
|
|
432
|
-
.${componentName.toLowerCase()}-responsive {
|
|
433
|
-
${this.camelToKebab(prop)}: ${breakpointValue};
|
|
434
|
-
}
|
|
431
|
+
const rule = `@media (min-width: ${mediaQuery}) {
|
|
432
|
+
.${componentName.toLowerCase()}-responsive {
|
|
433
|
+
${this.camelToKebab(prop)}: ${breakpointValue};
|
|
434
|
+
}
|
|
435
435
|
}`;
|
|
436
436
|
compiled.mediaQueries.push(rule);
|
|
437
437
|
}
|