@svgflex/angular 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +102 -0
- package/fesm2022/svgflex-angular.mjs +181 -0
- package/fesm2022/svgflex-angular.mjs.map +1 -0
- package/index.d.ts +65 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Kosuke Shimizu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# @svgflex/angular
|
|
2
|
+
|
|
3
|
+
SVG icons with easy color and size customization for Angular.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Easy color and size customization using `currentColor`
|
|
8
|
+
- Support for inline SVG (full style control) or external references
|
|
9
|
+
- TypeScript support with full type definitions
|
|
10
|
+
- Flexible sizing: numbers (px), strings with units, or width/height objects
|
|
11
|
+
- SCSS/CSS class support
|
|
12
|
+
- Accessibility features (ARIA labels)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @svgflex/angular
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
### Basic Usage
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { Component } from '@angular/core';
|
|
26
|
+
import { SvgflexComponent } from '@svgflex/angular';
|
|
27
|
+
|
|
28
|
+
@Component({
|
|
29
|
+
selector: 'app-root',
|
|
30
|
+
standalone: true,
|
|
31
|
+
imports: [SvgflexComponent],
|
|
32
|
+
template: `
|
|
33
|
+
<svgflex src="assets/icons/home.svg"></svgflex>
|
|
34
|
+
`
|
|
35
|
+
})
|
|
36
|
+
export class AppComponent {}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### With Color and Size
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<!-- Using currentColor (inherits from parent text color) -->
|
|
43
|
+
<svgflex src="assets/icons/home.svg" color="currentColor" size="24"></svgflex>
|
|
44
|
+
|
|
45
|
+
<!-- Custom color -->
|
|
46
|
+
<svgflex src="assets/icons/star.svg" color="#ff0000" size="32"></svgflex>
|
|
47
|
+
|
|
48
|
+
<!-- Size with units -->
|
|
49
|
+
<svgflex src="assets/icons/user.svg" color="blue" size="2rem"></svgflex>
|
|
50
|
+
|
|
51
|
+
<!-- Different width and height -->
|
|
52
|
+
<svgflex
|
|
53
|
+
src="assets/icons/logo.svg"
|
|
54
|
+
[size]="{width: '100px', height: '50px'}"
|
|
55
|
+
></svgflex>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### With CSS Classes
|
|
59
|
+
|
|
60
|
+
```html
|
|
61
|
+
<svgflex
|
|
62
|
+
src="assets/icons/heart.svg"
|
|
63
|
+
class="icon-large text-red"
|
|
64
|
+
></svgflex>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```scss
|
|
68
|
+
.icon-large {
|
|
69
|
+
width: 48px;
|
|
70
|
+
height: 48px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.text-red {
|
|
74
|
+
color: red;
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Accessibility
|
|
79
|
+
|
|
80
|
+
```html
|
|
81
|
+
<svgflex
|
|
82
|
+
src="assets/icons/close.svg"
|
|
83
|
+
ariaLabel="Close dialog"
|
|
84
|
+
></svgflex>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## API
|
|
88
|
+
|
|
89
|
+
### Inputs
|
|
90
|
+
|
|
91
|
+
| Input | Type | Default | Description |
|
|
92
|
+
|-------|------|---------|-------------|
|
|
93
|
+
| `src` | `string` | *required* | Path or URL to the SVG file |
|
|
94
|
+
| `color` | `string` | `'currentColor'` | CSS color value |
|
|
95
|
+
| `size` | `number \| string \| {width, height}` | `24` | Size in px, or with units, or object |
|
|
96
|
+
| `class` | `string` | `''` | Additional CSS classes |
|
|
97
|
+
| `ariaLabel` | `string` | `undefined` | Accessibility label |
|
|
98
|
+
| `inline` | `boolean` | `true` | Whether to inline SVG content |
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
MIT
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Injectable, inject, HostBinding, Input, Component } from '@angular/core';
|
|
3
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
4
|
+
import { CommonModule } from '@angular/common';
|
|
5
|
+
import { of } from 'rxjs';
|
|
6
|
+
import { map, catchError, shareReplay } from 'rxjs/operators';
|
|
7
|
+
import { sanitizeSvg, processSvgConfig } from '@svgflex/core';
|
|
8
|
+
export * from '@svgflex/core';
|
|
9
|
+
import * as i1 from '@angular/common/http';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Service for loading and caching SVG content
|
|
13
|
+
*/
|
|
14
|
+
class SvgLoaderService {
|
|
15
|
+
http;
|
|
16
|
+
cache = new Map();
|
|
17
|
+
constructor(http) {
|
|
18
|
+
this.http = http;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Loads SVG content from a URL with caching
|
|
22
|
+
*/
|
|
23
|
+
loadSvg(url) {
|
|
24
|
+
console.log('[SvgLoaderService] Loading SVG from:', url);
|
|
25
|
+
if (this.cache.has(url)) {
|
|
26
|
+
console.log('[SvgLoaderService] Returning cached SVG for:', url);
|
|
27
|
+
return this.cache.get(url);
|
|
28
|
+
}
|
|
29
|
+
const svg$ = this.http.get(url, { responseType: 'text' }).pipe(map(svgContent => {
|
|
30
|
+
console.log('[SvgLoaderService] Successfully loaded SVG from:', url, 'Length:', svgContent.length);
|
|
31
|
+
return sanitizeSvg(svgContent);
|
|
32
|
+
}), catchError(error => {
|
|
33
|
+
console.error(`[SvgLoaderService] Failed to load SVG from ${url}:`, error);
|
|
34
|
+
return of('');
|
|
35
|
+
}), shareReplay(1));
|
|
36
|
+
this.cache.set(url, svg$);
|
|
37
|
+
return svg$;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Clears the SVG cache
|
|
41
|
+
*/
|
|
42
|
+
clearCache() {
|
|
43
|
+
this.cache.clear();
|
|
44
|
+
}
|
|
45
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: SvgLoaderService, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
46
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: SvgLoaderService, providedIn: 'root' });
|
|
47
|
+
}
|
|
48
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: SvgLoaderService, decorators: [{
|
|
49
|
+
type: Injectable,
|
|
50
|
+
args: [{
|
|
51
|
+
providedIn: 'root'
|
|
52
|
+
}]
|
|
53
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }] });
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* SVGFlex Component
|
|
57
|
+
*
|
|
58
|
+
* Renders SVG icons with customizable color and size.
|
|
59
|
+
* Supports both inline SVG (for full style control) and external references.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* <svgflex src="assets/icons/home.svg" color="red" size="32"></svgflex>
|
|
63
|
+
* <svgflex src="assets/icons/user.svg" color="currentColor" [size]="{width: '2rem', height: '2rem'}"></svgflex>
|
|
64
|
+
*/
|
|
65
|
+
class SvgflexComponent {
|
|
66
|
+
svgLoader = inject(SvgLoaderService);
|
|
67
|
+
sanitizer = inject(DomSanitizer);
|
|
68
|
+
/** Path or URL to the SVG file */
|
|
69
|
+
src;
|
|
70
|
+
/** Color for the SVG (defaults to 'currentColor') */
|
|
71
|
+
color;
|
|
72
|
+
/** Size for the SVG (number in px, string with units, or {width, height} object) */
|
|
73
|
+
size;
|
|
74
|
+
/** Additional CSS classes */
|
|
75
|
+
class;
|
|
76
|
+
/** Accessibility label */
|
|
77
|
+
ariaLabel;
|
|
78
|
+
/** Whether to inline the SVG (default: true) */
|
|
79
|
+
inline = true;
|
|
80
|
+
// Processed values
|
|
81
|
+
width = '24px';
|
|
82
|
+
height = '24px';
|
|
83
|
+
processedColor = 'currentColor';
|
|
84
|
+
svgContent = null;
|
|
85
|
+
// Apply CSS classes to :host element
|
|
86
|
+
get hostClasses() {
|
|
87
|
+
return this.class || '';
|
|
88
|
+
}
|
|
89
|
+
// Apply inline styles to :host element when size is specified
|
|
90
|
+
get hostWidth() {
|
|
91
|
+
return this.size ? this.width : null;
|
|
92
|
+
}
|
|
93
|
+
get hostHeight() {
|
|
94
|
+
return this.size ? this.height : null;
|
|
95
|
+
}
|
|
96
|
+
ngOnChanges(changes) {
|
|
97
|
+
const config = {
|
|
98
|
+
src: this.src,
|
|
99
|
+
color: this.color,
|
|
100
|
+
size: this.size,
|
|
101
|
+
class: this.class,
|
|
102
|
+
ariaLabel: this.ariaLabel,
|
|
103
|
+
inline: this.inline
|
|
104
|
+
};
|
|
105
|
+
const processed = processSvgConfig(config);
|
|
106
|
+
this.width = processed.width;
|
|
107
|
+
this.height = processed.height;
|
|
108
|
+
this.processedColor = processed.color;
|
|
109
|
+
this.ariaLabel = processed.ariaLabel;
|
|
110
|
+
console.log('[SvgflexComponent] Processed config:', {
|
|
111
|
+
width: this.width,
|
|
112
|
+
height: this.height,
|
|
113
|
+
color: this.processedColor,
|
|
114
|
+
src: this.src
|
|
115
|
+
});
|
|
116
|
+
// Load SVG content if inline mode is enabled
|
|
117
|
+
// Check if src or inline changed, or if it's the first change (firstChange)
|
|
118
|
+
if (this.inline && (changes['src'] || changes['inline'] || Object.keys(changes).length > 0)) {
|
|
119
|
+
this.loadSvgContent();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
loadSvgContent() {
|
|
123
|
+
console.log('[SvgflexComponent] loadSvgContent called, src:', this.src, 'inline:', this.inline);
|
|
124
|
+
if (!this.src) {
|
|
125
|
+
console.log('[SvgflexComponent] No src provided, skipping load');
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
this.svgLoader.loadSvg(this.src).subscribe((content) => {
|
|
129
|
+
console.log('[SvgflexComponent] Received SVG content, length:', content.length);
|
|
130
|
+
if (content) {
|
|
131
|
+
// SVG is already sanitized by SvgLoaderService
|
|
132
|
+
// Directly bypass security and trust the HTML
|
|
133
|
+
this.svgContent = this.sanitizer.bypassSecurityTrustHtml(content);
|
|
134
|
+
console.log('[SvgflexComponent] SVG content set successfully');
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
console.log('[SvgflexComponent] Received empty content');
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: SvgflexComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
142
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: SvgflexComponent, isStandalone: true, selector: "svgflex", inputs: { src: "src", color: "color", size: "size", class: "class", ariaLabel: "ariaLabel", inline: "inline" }, host: { properties: { "class": "this.hostClasses", "style.width": "this.hostWidth", "style.height": "this.hostHeight" } }, usesOnChanges: true, ngImport: i0, template: "<div\n class=\"svgflex-container\"\n [style.color]=\"processedColor\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.role]=\"ariaLabel ? 'img' : null\"\n>\n @if (inline && svgContent) {\n <div [innerHTML]=\"svgContent\" class=\"svgflex-inline\"></div>\n } @else if (!inline) {\n <img\n [src]=\"src\"\n [alt]=\"ariaLabel || ''\"\n [style.width]=\"width\"\n [style.height]=\"height\"\n />\n } @else {\n <div class=\"svgflex-loading\"></div>\n }\n</div>\n", styles: [":host{display:inline-block}:host.block{display:block}:host.flex{display:flex}.svgflex-container{display:flex;align-items:center;justify-content:center;width:100%;height:100%;color:inherit}.svgflex-inline{display:contents}.svgflex-inline ::ng-deep svg{width:100%;height:100%;fill:currentColor}.svgflex-inline ::ng-deep svg path,.svgflex-inline ::ng-deep svg circle,.svgflex-inline ::ng-deep svg rect,.svgflex-inline ::ng-deep svg polygon,.svgflex-inline ::ng-deep svg polyline,.svgflex-inline ::ng-deep svg ellipse,.svgflex-inline ::ng-deep svg line{fill:currentColor}.svgflex-inline ::ng-deep svg[stroke]{stroke:currentColor}.svgflex-inline ::ng-deep svg[stroke] path,.svgflex-inline ::ng-deep svg[stroke] circle,.svgflex-inline ::ng-deep svg[stroke] rect,.svgflex-inline ::ng-deep svg[stroke] polygon,.svgflex-inline ::ng-deep svg[stroke] polyline,.svgflex-inline ::ng-deep svg[stroke] ellipse,.svgflex-inline ::ng-deep svg[stroke] line{stroke:currentColor}.svgflex-loading{width:100%;height:100%;background-color:currentColor;opacity:.1;border-radius:2px}img{display:block;max-width:100%;max-height:100%}:host.icon-sm{width:16px;height:16px}:host.icon-md{width:24px;height:24px}:host.icon-lg{width:32px;height:32px}:host.icon-xl{width:48px;height:48px}:host.icon-2xl{width:64px;height:64px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
143
|
+
}
|
|
144
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: SvgflexComponent, decorators: [{
|
|
145
|
+
type: Component,
|
|
146
|
+
args: [{ selector: 'svgflex', standalone: true, imports: [CommonModule], template: "<div\n class=\"svgflex-container\"\n [style.color]=\"processedColor\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.role]=\"ariaLabel ? 'img' : null\"\n>\n @if (inline && svgContent) {\n <div [innerHTML]=\"svgContent\" class=\"svgflex-inline\"></div>\n } @else if (!inline) {\n <img\n [src]=\"src\"\n [alt]=\"ariaLabel || ''\"\n [style.width]=\"width\"\n [style.height]=\"height\"\n />\n } @else {\n <div class=\"svgflex-loading\"></div>\n }\n</div>\n", styles: [":host{display:inline-block}:host.block{display:block}:host.flex{display:flex}.svgflex-container{display:flex;align-items:center;justify-content:center;width:100%;height:100%;color:inherit}.svgflex-inline{display:contents}.svgflex-inline ::ng-deep svg{width:100%;height:100%;fill:currentColor}.svgflex-inline ::ng-deep svg path,.svgflex-inline ::ng-deep svg circle,.svgflex-inline ::ng-deep svg rect,.svgflex-inline ::ng-deep svg polygon,.svgflex-inline ::ng-deep svg polyline,.svgflex-inline ::ng-deep svg ellipse,.svgflex-inline ::ng-deep svg line{fill:currentColor}.svgflex-inline ::ng-deep svg[stroke]{stroke:currentColor}.svgflex-inline ::ng-deep svg[stroke] path,.svgflex-inline ::ng-deep svg[stroke] circle,.svgflex-inline ::ng-deep svg[stroke] rect,.svgflex-inline ::ng-deep svg[stroke] polygon,.svgflex-inline ::ng-deep svg[stroke] polyline,.svgflex-inline ::ng-deep svg[stroke] ellipse,.svgflex-inline ::ng-deep svg[stroke] line{stroke:currentColor}.svgflex-loading{width:100%;height:100%;background-color:currentColor;opacity:.1;border-radius:2px}img{display:block;max-width:100%;max-height:100%}:host.icon-sm{width:16px;height:16px}:host.icon-md{width:24px;height:24px}:host.icon-lg{width:32px;height:32px}:host.icon-xl{width:48px;height:48px}:host.icon-2xl{width:64px;height:64px}\n"] }]
|
|
147
|
+
}], propDecorators: { src: [{
|
|
148
|
+
type: Input,
|
|
149
|
+
args: [{ required: true }]
|
|
150
|
+
}], color: [{
|
|
151
|
+
type: Input
|
|
152
|
+
}], size: [{
|
|
153
|
+
type: Input
|
|
154
|
+
}], class: [{
|
|
155
|
+
type: Input
|
|
156
|
+
}], ariaLabel: [{
|
|
157
|
+
type: Input
|
|
158
|
+
}], inline: [{
|
|
159
|
+
type: Input
|
|
160
|
+
}], hostClasses: [{
|
|
161
|
+
type: HostBinding,
|
|
162
|
+
args: ['class']
|
|
163
|
+
}], hostWidth: [{
|
|
164
|
+
type: HostBinding,
|
|
165
|
+
args: ['style.width']
|
|
166
|
+
}], hostHeight: [{
|
|
167
|
+
type: HostBinding,
|
|
168
|
+
args: ['style.height']
|
|
169
|
+
}] } });
|
|
170
|
+
|
|
171
|
+
/*
|
|
172
|
+
* Public API Surface of @svgflex/angular
|
|
173
|
+
*/
|
|
174
|
+
// Components
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Generated bundle index. Do not edit.
|
|
178
|
+
*/
|
|
179
|
+
|
|
180
|
+
export { SvgLoaderService, SvgflexComponent };
|
|
181
|
+
//# sourceMappingURL=svgflex-angular.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svgflex-angular.mjs","sources":["../../../packages/angular/src/lib/services/svg-loader.service.ts","../../../packages/angular/src/lib/components/svgflex.component.ts","../../../packages/angular/src/lib/components/svgflex.component.html","../../../packages/angular/src/public-api.ts","../../../packages/angular/src/svgflex-angular.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { Observable, of } from 'rxjs';\nimport { map, catchError, shareReplay } from 'rxjs/operators';\nimport { sanitizeSvg } from '@svgflex/core';\n\n/**\n * Service for loading and caching SVG content\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class SvgLoaderService {\n private cache = new Map<string, Observable<string>>();\n\n constructor(private http: HttpClient) {}\n\n /**\n * Loads SVG content from a URL with caching\n */\n loadSvg(url: string): Observable<string> {\n console.log('[SvgLoaderService] Loading SVG from:', url);\n\n if (this.cache.has(url)) {\n console.log('[SvgLoaderService] Returning cached SVG for:', url);\n return this.cache.get(url)!;\n }\n\n const svg$ = this.http.get(url, { responseType: 'text' }).pipe(\n map(svgContent => {\n console.log('[SvgLoaderService] Successfully loaded SVG from:', url, 'Length:', svgContent.length);\n return sanitizeSvg(svgContent);\n }),\n catchError(error => {\n console.error(`[SvgLoaderService] Failed to load SVG from ${url}:`, error);\n return of('');\n }),\n shareReplay(1)\n );\n\n this.cache.set(url, svg$);\n return svg$;\n }\n\n /**\n * Clears the SVG cache\n */\n clearCache(): void {\n this.cache.clear();\n }\n}\n","import {\n Component,\n Input,\n OnChanges,\n SimpleChanges,\n HostBinding,\n inject\n} from '@angular/core';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport { CommonModule } from '@angular/common';\nimport { SvgLoaderService } from '../services/svg-loader.service';\nimport { processSvgConfig, SvgFlexConfig, SvgSize, SvgColor } from '@svgflex/core';\n\n/**\n * SVGFlex Component\n *\n * Renders SVG icons with customizable color and size.\n * Supports both inline SVG (for full style control) and external references.\n *\n * @example\n * <svgflex src=\"assets/icons/home.svg\" color=\"red\" size=\"32\"></svgflex>\n * <svgflex src=\"assets/icons/user.svg\" color=\"currentColor\" [size]=\"{width: '2rem', height: '2rem'}\"></svgflex>\n */\n@Component({\n selector: 'svgflex',\n standalone: true,\n imports: [CommonModule],\n templateUrl: './svgflex.component.html',\n styleUrl: './svgflex.component.scss'\n})\nexport class SvgflexComponent implements OnChanges {\n private svgLoader = inject(SvgLoaderService);\n private sanitizer = inject(DomSanitizer);\n\n /** Path or URL to the SVG file */\n @Input({ required: true }) src!: string;\n\n /** Color for the SVG (defaults to 'currentColor') */\n @Input() color?: SvgColor;\n\n /** Size for the SVG (number in px, string with units, or {width, height} object) */\n @Input() size?: SvgSize;\n\n /** Additional CSS classes */\n @Input() class?: string;\n\n /** Accessibility label */\n @Input() ariaLabel?: string;\n\n /** Whether to inline the SVG (default: true) */\n @Input() inline: boolean = true;\n\n // Processed values\n protected width: string = '24px';\n protected height: string = '24px';\n protected processedColor: string = 'currentColor';\n protected svgContent: any = null;\n\n // Apply CSS classes to :host element\n @HostBinding('class')\n get hostClasses(): string {\n return this.class || '';\n }\n\n // Apply inline styles to :host element when size is specified\n @HostBinding('style.width')\n get hostWidth(): string | null {\n return this.size ? this.width : null;\n }\n\n @HostBinding('style.height')\n get hostHeight(): string | null {\n return this.size ? this.height : null;\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n const config: SvgFlexConfig = {\n src: this.src,\n color: this.color,\n size: this.size,\n class: this.class,\n ariaLabel: this.ariaLabel,\n inline: this.inline\n };\n\n const processed = processSvgConfig(config);\n\n this.width = processed.width;\n this.height = processed.height;\n this.processedColor = processed.color;\n this.ariaLabel = processed.ariaLabel;\n\n console.log('[SvgflexComponent] Processed config:', {\n width: this.width,\n height: this.height,\n color: this.processedColor,\n src: this.src\n });\n\n // Load SVG content if inline mode is enabled\n // Check if src or inline changed, or if it's the first change (firstChange)\n if (this.inline && (changes['src'] || changes['inline'] || Object.keys(changes).length > 0)) {\n this.loadSvgContent();\n }\n }\n\n private loadSvgContent(): void {\n console.log('[SvgflexComponent] loadSvgContent called, src:', this.src, 'inline:', this.inline);\n\n if (!this.src) {\n console.log('[SvgflexComponent] No src provided, skipping load');\n return;\n }\n\n this.svgLoader.loadSvg(this.src).subscribe((content: string) => {\n console.log('[SvgflexComponent] Received SVG content, length:', content.length);\n if (content) {\n // SVG is already sanitized by SvgLoaderService\n // Directly bypass security and trust the HTML\n this.svgContent = this.sanitizer.bypassSecurityTrustHtml(content);\n console.log('[SvgflexComponent] SVG content set successfully');\n } else {\n console.log('[SvgflexComponent] Received empty content');\n }\n });\n }\n}\n","<div\n class=\"svgflex-container\"\n [style.color]=\"processedColor\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.role]=\"ariaLabel ? 'img' : null\"\n>\n @if (inline && svgContent) {\n <div [innerHTML]=\"svgContent\" class=\"svgflex-inline\"></div>\n } @else if (!inline) {\n <img\n [src]=\"src\"\n [alt]=\"ariaLabel || ''\"\n [style.width]=\"width\"\n [style.height]=\"height\"\n />\n } @else {\n <div class=\"svgflex-loading\"></div>\n }\n</div>\n","/*\n * Public API Surface of @svgflex/angular\n */\n\n// Components\nexport * from './lib/components/svgflex.component';\n\n// Services\nexport * from './lib/services/svg-loader.service';\n\n// Re-export core types and utils for convenience\nexport * from '@svgflex/core';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { OnChanges, SimpleChanges } from '@angular/core';
|
|
3
|
+
import { SvgColor, SvgSize } from '@svgflex/core';
|
|
4
|
+
export * from '@svgflex/core';
|
|
5
|
+
import { HttpClient } from '@angular/common/http';
|
|
6
|
+
import { Observable } from 'rxjs';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* SVGFlex Component
|
|
10
|
+
*
|
|
11
|
+
* Renders SVG icons with customizable color and size.
|
|
12
|
+
* Supports both inline SVG (for full style control) and external references.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* <svgflex src="assets/icons/home.svg" color="red" size="32"></svgflex>
|
|
16
|
+
* <svgflex src="assets/icons/user.svg" color="currentColor" [size]="{width: '2rem', height: '2rem'}"></svgflex>
|
|
17
|
+
*/
|
|
18
|
+
declare class SvgflexComponent implements OnChanges {
|
|
19
|
+
private svgLoader;
|
|
20
|
+
private sanitizer;
|
|
21
|
+
/** Path or URL to the SVG file */
|
|
22
|
+
src: string;
|
|
23
|
+
/** Color for the SVG (defaults to 'currentColor') */
|
|
24
|
+
color?: SvgColor;
|
|
25
|
+
/** Size for the SVG (number in px, string with units, or {width, height} object) */
|
|
26
|
+
size?: SvgSize;
|
|
27
|
+
/** Additional CSS classes */
|
|
28
|
+
class?: string;
|
|
29
|
+
/** Accessibility label */
|
|
30
|
+
ariaLabel?: string;
|
|
31
|
+
/** Whether to inline the SVG (default: true) */
|
|
32
|
+
inline: boolean;
|
|
33
|
+
protected width: string;
|
|
34
|
+
protected height: string;
|
|
35
|
+
protected processedColor: string;
|
|
36
|
+
protected svgContent: any;
|
|
37
|
+
get hostClasses(): string;
|
|
38
|
+
get hostWidth(): string | null;
|
|
39
|
+
get hostHeight(): string | null;
|
|
40
|
+
ngOnChanges(changes: SimpleChanges): void;
|
|
41
|
+
private loadSvgContent;
|
|
42
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SvgflexComponent, never>;
|
|
43
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SvgflexComponent, "svgflex", never, { "src": { "alias": "src"; "required": true; }; "color": { "alias": "color"; "required": false; }; "size": { "alias": "size"; "required": false; }; "class": { "alias": "class"; "required": false; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; }; "inline": { "alias": "inline"; "required": false; }; }, {}, never, never, true, never>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Service for loading and caching SVG content
|
|
48
|
+
*/
|
|
49
|
+
declare class SvgLoaderService {
|
|
50
|
+
private http;
|
|
51
|
+
private cache;
|
|
52
|
+
constructor(http: HttpClient);
|
|
53
|
+
/**
|
|
54
|
+
* Loads SVG content from a URL with caching
|
|
55
|
+
*/
|
|
56
|
+
loadSvg(url: string): Observable<string>;
|
|
57
|
+
/**
|
|
58
|
+
* Clears the SVG cache
|
|
59
|
+
*/
|
|
60
|
+
clearCache(): void;
|
|
61
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SvgLoaderService, never>;
|
|
62
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<SvgLoaderService>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { SvgLoaderService, SvgflexComponent };
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@svgflex/angular",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Angular library for SVG icons with easy color and size customization",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"svg",
|
|
7
|
+
"icons",
|
|
8
|
+
"angular",
|
|
9
|
+
"currentColor",
|
|
10
|
+
"customizable",
|
|
11
|
+
"standalone",
|
|
12
|
+
"component"
|
|
13
|
+
],
|
|
14
|
+
"author": "Kosuke Shimizu",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/KosukeShimizu/svgflex.git",
|
|
19
|
+
"directory": "packages/angular"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/KosukeShimizu/svgflex#readme",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/KosukeShimizu/svgflex/issues"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@angular/common": "^20.0.0",
|
|
30
|
+
"@angular/core": "^20.0.0",
|
|
31
|
+
"@svgflex/core": "^0.1.0"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"tslib": "^2.3.0"
|
|
35
|
+
},
|
|
36
|
+
"sideEffects": false,
|
|
37
|
+
"module": "fesm2022/svgflex-angular.mjs",
|
|
38
|
+
"typings": "index.d.ts",
|
|
39
|
+
"exports": {
|
|
40
|
+
"./package.json": {
|
|
41
|
+
"default": "./package.json"
|
|
42
|
+
},
|
|
43
|
+
".": {
|
|
44
|
+
"types": "./index.d.ts",
|
|
45
|
+
"default": "./fesm2022/svgflex-angular.mjs"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|