This commit is contained in:
Tizian.Breuch
2025-09-09 11:47:48 +02:00
parent c65aef11ca
commit 5601cd0110
16 changed files with 223 additions and 202 deletions

View File

@@ -16,19 +16,28 @@
border-radius: 50%;
display: grid;
place-items: center;
color: #fff;
flex-shrink: 0;
background-color: var(--color-surface);
}
.kpi-icon ::ng-deep svg {
.kpi-icon app-icon {
width: 24px;
height: 24px;
color: #fff;
}
.icon-sales { background: linear-gradient(135deg, #2ecc71, #27ae60); }
.icon-users { background: linear-gradient(135deg, #3498db, #2980b9); }
.icon-orders { background: linear-gradient(135deg, #f39c12, #f1c40f); }
.icon-performance { background: linear-gradient(135deg, #9b59b6, #8e44ad); }
.icon-blue {
background: linear-gradient(135deg, #3498db, #2980b9);
}
.icon-green {
background: linear-gradient(135deg, #2ecc71, #27ae60);
}
.icon-orange {
background: linear-gradient(135deg, #f39c12, #f1c40f);
}
.icon-purple {
background: linear-gradient(135deg, #9b59b6, #8e44ad);
}
.kpi-content {
display: flex;
@@ -44,4 +53,4 @@
.kpi-label {
font-size: 0.9rem;
color: var(--color-text-light);
}
}

View File

@@ -1,15 +1,19 @@
<div class="card kpi-card">
<div
<div
class="kpi-icon"
[class.icon-sales]="color === 'sales'"
[class.icon-users]="color === 'users'"
[class.icon-orders]="color === 'orders'"
[class.icon-performance]="color === 'performance'">
<!-- ng-content erlaubt es, ein spezifisches SVG-Icon von außen einzufügen -->
<ng-content></ng-content>
[class.icon-blue]="color === 'blue'"
[class.icon-green]="color === 'green'"
[class.icon-orange]="color === 'orange'"
[class.icon-purple]="color === 'purple'"
>
<app-icon
*ngIf="iconName"
[name]="iconName"
[svgColor]="svgColor"
></app-icon>
</div>
<div class="kpi-content">
<span class="kpi-value">{{ value }}</span>
<span class="kpi-label">{{ label }}</span>
</div>
</div>
</div>

View File

@@ -1,17 +1,20 @@
import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IconComponent } from '../../ui/icon/icon.component';
type KpiColor = 'sales' | 'users' | 'orders' | 'performance';
type KpiColor = 'blue' | 'green' | 'orange' | 'purple';
@Component({
selector: 'app-kpi-card',
standalone: true,
imports: [CommonModule],
imports: [CommonModule, IconComponent],
templateUrl: './kpi-card.component.html',
styleUrl: './kpi-card.component.css'
styleUrl: './kpi-card.component.css',
})
export class KpiCardComponent {
@Input() value: string = '';
@Input() label: string = '';
@Input() color: KpiColor = 'users';
@Input() color: KpiColor = 'blue';
@Input() iconName: string | null = null;
@Input() svgColor: string | null = null;
}

View File

@@ -9,8 +9,9 @@
[class.btn-icon]="color === 'icon' || color === 'icon-danger'"
[class.btn-icon-danger]="color === 'icon-danger'"
[class.btn-full-width]="fullWidth"
[attr.data-tooltip]="tooltip">
<!-- ng-content erlaubt es, Text oder SVGs von außen in den Button einzufügen -->
[attr.data-tooltip]="tooltip"
>
<!-- HIER IST DIE KORREKTUR: Binding an [svgColor] statt [iconColor] -->
<app-icon *ngIf="iconName" [name]="iconName" [svgColor]="svgColor"></app-icon>
<ng-content></ng-content>
</button>

View File

@@ -1,13 +1,16 @@
import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IconComponent } from '../icon/icon.component';
// Definiert die erlaubten Varianten für den Button
type ButtonColor = 'primary' | 'secondary' | 'stroked' | 'flat' | 'icon' | 'icon-danger';
@Component({
selector: 'app-button',
imports: [CommonModule],
standalone: true,
imports: [
CommonModule,
IconComponent
],
templateUrl: './button.component.html',
styleUrl: './button.component.css'
})
@@ -17,6 +20,8 @@ export class ButtonComponent {
@Input() disabled = false;
@Input() fullWidth = false;
// Ein spezieller Input für Tooltips
@Input() tooltip: string | null = null;
@Input() iconName: string | null = null;
// --- HIER IST DIE KORREKTUR: Umbenennung zu svgColor ---
@Input() svgColor: string | null = null; // Farbe des SVG-Inhalts im Button
}

View File

@@ -3,12 +3,16 @@
display: inline-flex;
align-items: center;
justify-content: center;
width: 1em; /* Passt sich an die Schriftgröße des Elternelements an */
height: 1em;
width: 1.75rem;
height: 1.75rem;
color: inherit; /* Wichtig, damit currentColor vom Elternelement übernommen wird */
}
/* Wir verwenden ::ng-deep, da das SVG-Element dynamisch eingefügt wird */
::ng-deep .icon-svg {
width: 100%;
height: 100%;
stroke: currentColor;
/* HIER IST DIE KORREKTUR: fill muss ein gültiger CSS-Wert sein */
stroke: currentColor;
fill: currentColor;
}

View File

@@ -1,35 +1,89 @@
import { Component, Input, OnChanges, SimpleChanges, ElementRef, Renderer2 } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
Component,
Input,
OnChanges,
SimpleChanges,
ElementRef,
Renderer2,
Inject,
PLATFORM_ID,
TransferState,
makeStateKey,
StateKey
} from '@angular/core';
import { CommonModule, isPlatformBrowser } from '@angular/common';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { tap } from 'rxjs/operators';
const ICON_SVG_KEY_PREFIX = 'icon-svg-';
@Component({
selector: 'app-icon',
standalone: true,
standalone: true,
imports: [CommonModule, HttpClientModule],
template: '<ng-content></ng-content>', // Leeres Template, da wir das SVG direkt einfügen
template: '',
styleUrl: './icon.component.css'
})
export class IconComponent implements OnChanges {
@Input() name: string = '';
// --- HIER IST DIE KORREKTUR: Umbenennung zu svgColor ---
@Input() svgColor: string | null = null; // Farbe des SVG-Inhalts (Füllung/Linie)
constructor(
private http: HttpClient,
private el: ElementRef,
private renderer: Renderer2
private renderer: Renderer2,
private transferState: TransferState,
@Inject(PLATFORM_ID) private platformId: Object
) {}
ngOnChanges(changes: SimpleChanges): void {
if (changes['name'] && this.name) {
// Lädt das SVG aus dem assets-Ordner und fügt es in die Komponente ein
this.http.get(`assets/icons/${this.name}.svg`, { responseType: 'text' })
// Überprüfe Änderungen an 'name' ODER 'svgColor'
if ((changes['name'] && this.name) || (changes['svgColor'] && this.name)) {
this.loadSvg();
}
}
private loadSvg(): void {
const svgStateKey: StateKey<string> = makeStateKey<string>(ICON_SVG_KEY_PREFIX + this.name);
const cachedSvg = this.transferState.get(svgStateKey, null);
this.el.nativeElement.innerHTML = '';
if (cachedSvg) {
this.setSvg(cachedSvg);
} else {
this.http.get(`icons/${this.name}.svg`, { responseType: 'text' })
.pipe(
tap(svg => {
if (!isPlatformBrowser(this.platformId)) {
this.transferState.set(svgStateKey, svg);
}
})
)
.subscribe(svg => {
this.el.nativeElement.innerHTML = svg;
// Optional: Fügt dem <svg>-Element selbst eine Klasse hinzu
const svgElement = this.el.nativeElement.querySelector('svg');
if (svgElement) {
this.renderer.addClass(svgElement, 'icon-svg');
}
this.setSvg(svg);
});
}
}
private setSvg(svgContent: string): void {
this.el.nativeElement.innerHTML = svgContent;
const svgElement = this.el.nativeElement.querySelector('svg');
if (svgElement) {
this.renderer.addClass(svgElement, 'icon-svg');
// --- HIER IST DIE KORREKTUR: svgColor anwenden ---
if (this.svgColor) {
this.renderer.setStyle(svgElement, 'fill', this.svgColor); // Füllfarbe
this.renderer.setStyle(svgElement, 'stroke', this.svgColor); // Linienfarbe
} else {
// Fallback zu currentColor, wenn keine Farbe übergeben wird
this.renderer.setStyle(svgElement, 'fill', 'currentColor');
this.renderer.setStyle(svgElement, 'stroke', 'currentColor');
}
}
}
}