styles
1
public/icons/bolt.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#666666"><path d="m422-232 207-248H469l29-227-185 267h139l-30 208ZM320-80l40-280H160l360-520h80l-40 320h240L400-80h-80Zm151-390Z"/></svg>
|
||||
|
After Width: | Height: | Size: 235 B |
1
public/icons/delete.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#666666"><path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"/></svg>
|
||||
|
After Width: | Height: | Size: 319 B |
1
public/icons/edit.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#666666"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h357l-80 80H200v560h560v-278l80-80v358q0 33-23.5 56.5T760-120H200Zm280-360ZM360-360v-170l367-367q12-12 27-18t30-6q16 0 30.5 6t26.5 18l56 57q11 12 17 26.5t6 29.5q0 15-5.5 29.5T897-728L530-360H360Zm481-424-56-56 56 56ZM440-440h56l232-232-28-28-29-28-231 231v57Zm260-260-29-28 29 28 28 28-28-28Z"/></svg>
|
||||
|
After Width: | Height: | Size: 477 B |
1
public/icons/eye.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#666666"><path d="M480-320q75 0 127.5-52.5T660-500q0-75-52.5-127.5T480-680q-75 0-127.5 52.5T300-500q0 75 52.5 127.5T480-320Zm0-72q-45 0-76.5-31.5T372-500q0-45 31.5-76.5T480-608q45 0 76.5 31.5T588-500q0 45-31.5 76.5T480-392Zm0 192q-146 0-266-81.5T40-500q54-137 174-218.5T480-800q146 0 266 81.5T920-500q-54 137-174 218.5T480-200Zm0-300Zm0 220q113 0 207.5-59.5T832-500q-50-101-144.5-160.5T480-720q-113 0-207.5 59.5T128-500q50 101 144.5 160.5T480-280Z"/></svg>
|
||||
|
After Width: | Height: | Size: 554 B |
1
public/icons/group.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#666666"><path d="M40-160v-112q0-34 17.5-62.5T104-378q62-31 126-46.5T360-440q66 0 130 15.5T616-378q29 15 46.5 43.5T680-272v112H40Zm720 0v-120q0-44-24.5-84.5T666-434q51 6 96 20.5t84 35.5q36 20 55 44.5t19 53.5v120H760ZM360-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47Zm400-160q0 66-47 113t-113 47q-11 0-28-2.5t-28-5.5q27-32 41.5-71t14.5-81q0-42-14.5-81T544-792q14-5 28-6.5t28-1.5q66 0 113 47t47 113ZM120-240h480v-32q0-11-5.5-20T580-306q-54-27-109-40.5T360-360q-56 0-111 13.5T140-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T440-640q0-33-23.5-56.5T360-720q-33 0-56.5 23.5T280-640q0 33 23.5 56.5T360-560Zm0 320Zm0-400Z"/></svg>
|
||||
|
After Width: | Height: | Size: 766 B |
1
public/icons/money.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#666666"><path d="M441-120v-86q-53-12-91.5-46T293-348l74-30q15 48 44.5 73t77.5 25q41 0 69.5-18.5T587-356q0-35-22-55.5T463-458q-86-27-118-64.5T313-614q0-65 42-101t86-41v-84h80v84q50 8 82.5 36.5T651-650l-74 32q-12-32-34-48t-60-16q-44 0-67 19.5T393-614q0 33 30 52t104 40q69 20 104.5 63.5T667-358q0 71-42 108t-104 46v84h-80Z"/></svg>
|
||||
|
After Width: | Height: | Size: 427 B |
1
public/icons/shopping_bag.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#666666"><path d="M240-80q-33 0-56.5-23.5T160-160v-480q0-33 23.5-56.5T240-720h80q0-66 47-113t113-47q66 0 113 47t47 113h80q33 0 56.5 23.5T800-640v480q0 33-23.5 56.5T720-80H240Zm0-80h480v-480h-80v80q0 17-11.5 28.5T600-520q-17 0-28.5-11.5T560-560v-80H400v80q0 17-11.5 28.5T360-520q-17 0-28.5-11.5T320-560v-80h-80v480Zm160-560h160q0-33-23.5-56.5T480-800q-33 0-56.5 23.5T400-720ZM240-160v-480 480Z"/></svg>
|
||||
|
After Width: | Height: | Size: 499 B |
@@ -1,15 +1,34 @@
|
||||
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { provideAnimations } from '@angular/platform-browser/animations'; // Importieren
|
||||
import { provideAnimations } from '@angular/platform-browser/animations';
|
||||
|
||||
import { provideHttpClient, withFetch } from '@angular/common/http';
|
||||
import { provideClientHydration } from '@angular/platform-browser';
|
||||
|
||||
// +++ HIER IST DIE DEFINITIVE KORREKTUR FÜR FORMS-PROVIDER +++
|
||||
import {
|
||||
ReactiveFormsModule, // <-- Importieren des Moduls selbst
|
||||
FormsModule // <-- Importieren des Moduls für Template-driven forms (falls benötigt)
|
||||
} from '@angular/forms';
|
||||
|
||||
import { routes } from './app.routes';
|
||||
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||
provideRouter(routes),
|
||||
provideClientHydration(withEventReplay()),
|
||||
provideAnimations() // Hier hinzufügen
|
||||
provideAnimations(),
|
||||
|
||||
provideHttpClient(withFetch()),
|
||||
provideClientHydration(),
|
||||
|
||||
// +++ Korrigierte Verwendung der Forms-Provider +++
|
||||
// Dies macht die Provider für Reactive Forms global verfügbar
|
||||
ReactiveFormsModule.withConfig({
|
||||
warnOnNgModelWithFormControl: 'never' // Optional: Schaltet eine NgModel Warnung aus
|
||||
}).providers!, // <--- Korrigiert
|
||||
|
||||
// Falls Sie Template-Driven Forms (mit NgModel) benötigen, würden Sie das hinzufügen:
|
||||
// FormsModule.withConfig({}).providers!
|
||||
]
|
||||
};
|
||||
@@ -88,76 +88,48 @@
|
||||
<app-page-header></app-page-header>
|
||||
|
||||
<div class="dashboard-grid">
|
||||
<!-- Sektion: KPI-Karten -->
|
||||
<app-kpi-card value="€ 14.750" label="Umsatz (Monat)" color="sales"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<line x1="12" y1="1" x2="12" y2="23"></line>
|
||||
<path
|
||||
d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"
|
||||
></path></svg
|
||||
></app-kpi-card>
|
||||
<app-kpi-card value="1.284" label="Neue Nutzer" color="users"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="9" cy="7" r="4"></circle>
|
||||
<path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
|
||||
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg
|
||||
></app-kpi-card>
|
||||
<app-kpi-card value="312" label="Bestellungen" color="orders"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"></path>
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<path d="M16 10a4 4 0 0 1-8 0"></path></svg
|
||||
></app-kpi-card>
|
||||
<app-kpi-card value="99.8%" label="Verfügbarkeit" color="performance"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<polygon
|
||||
points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"
|
||||
></polygon></svg
|
||||
<app-kpi-card
|
||||
value="€ 14.750"
|
||||
label="Umsatz"
|
||||
color="green"
|
||||
iconName="money"
|
||||
></app-kpi-card>
|
||||
|
||||
<app-card>
|
||||
<h3 card-header>Letzte Bestellungen</h3>
|
||||
<app-kpi-card
|
||||
value="1.284"
|
||||
label="Neue Nutzer"
|
||||
color="blue"
|
||||
iconName="group"
|
||||
></app-kpi-card>
|
||||
|
||||
<app-kpi-card
|
||||
value="312"
|
||||
label="Bestellungen"
|
||||
color="orange"
|
||||
iconName="shopping_bag"
|
||||
></app-kpi-card>
|
||||
|
||||
<app-kpi-card
|
||||
value="99.8%"
|
||||
label="Verfügbarkeit"
|
||||
color="purple"
|
||||
iconName="bolt"
|
||||
></app-kpi-card>
|
||||
|
||||
<app-card class="grid-col-span-2">
|
||||
<h3 class="card-header">Umsatzentwicklung</h3>
|
||||
<div class="chart-container">
|
||||
<div class="chart-bar" style="height: 60%"><span>Jan</span></div>
|
||||
<div class="chart-bar" style="height: 75%"><span>Feb</span></div>
|
||||
<div class="chart-bar" style="height: 50%"><span>Mär</span></div>
|
||||
<div class="chart-bar" style="height: 85%"><span>Apr</span></div>
|
||||
<div class="chart-bar" style="height: 90%"><span>Mai</span></div>
|
||||
<div class="chart-bar" style="height: 65%"><span>Jun</span></div>
|
||||
</div>
|
||||
</app-card>
|
||||
|
||||
<app-card class="grid-col-span-2">
|
||||
<h3 class="card-header">Letzte Bestellungen</h3>
|
||||
<div class="table-container">
|
||||
<table class="modern-table">
|
||||
<thead>
|
||||
@@ -212,65 +184,26 @@
|
||||
</td>
|
||||
<td class="text-right amount">€ 129,99</td>
|
||||
<td class="actions-cell">
|
||||
<app-button color="icon" tooltip="Details anzeigen"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path
|
||||
d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"
|
||||
></path>
|
||||
<circle cx="12" cy="12" r="3"></circle></svg></app-button
|
||||
><app-button color="icon" tooltip="Bearbeiten"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path
|
||||
d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"
|
||||
></path>
|
||||
<path
|
||||
d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"
|
||||
></path></svg></app-button
|
||||
><app-button
|
||||
<app-button
|
||||
color="icon"
|
||||
tooltip="Details anzeigen"
|
||||
iconName="eye"
|
||||
></app-button>
|
||||
|
||||
<app-button
|
||||
color="icon"
|
||||
tooltip="Bearbeiten"
|
||||
iconName="edit"
|
||||
></app-button>
|
||||
|
||||
<app-button
|
||||
color="icon-danger"
|
||||
tooltip="Löschen"
|
||||
iconName="delete"
|
||||
(click)="openDialog()"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<polyline points="3 6 5 6 21 6"></polyline>
|
||||
<path
|
||||
d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"
|
||||
></path>
|
||||
<line x1="10" y1="11" x2="10" y2="17"></line>
|
||||
<line x1="14" y1="11" x2="14" y2="17"></line></svg
|
||||
></app-button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Weitere Tabellenzeilen hier... -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -282,24 +215,9 @@
|
||||
></app-paginator>
|
||||
</app-card>
|
||||
|
||||
<!-- Sektion: Diagramm & Tabelle -->
|
||||
<app-card class="grid-col-span-2">
|
||||
<h3 card-header>Umsatzentwicklung</h3>
|
||||
<div class="chart-container">
|
||||
<div class="chart-bar" style="height: 60%"><span>Jan</span></div>
|
||||
<div class="chart-bar" style="height: 75%"><span>Feb</span></div>
|
||||
<div class="chart-bar" style="height: 50%"><span>Mär</span></div>
|
||||
<div class="chart-bar" style="height: 85%"><span>Apr</span></div>
|
||||
<div class="chart-bar" style="height: 90%"><span>Mai</span></div>
|
||||
<div class="chart-bar" style="height: 65%"><span>Jun</span></div>
|
||||
</div>
|
||||
</app-card>
|
||||
|
||||
|
||||
|
||||
<!-- UI Komponenten Showcase -->
|
||||
<app-card class="grid-col-span-2">
|
||||
<h3 card-header>Moderne Formular-Elemente</h3>
|
||||
<h3 class="card-header">Moderne Formular-Elemente</h3>
|
||||
<div class="component-grid">
|
||||
<app-form-field label="Name" type="text"></app-form-field>
|
||||
<div class="form-field">
|
||||
@@ -325,26 +243,21 @@
|
||||
</app-card>
|
||||
|
||||
<app-card class="grid-col-span-2">
|
||||
<h3 card-header>Buttons, Chips & Interaktion</h3>
|
||||
<h3 class="card-header">Buttons, Chips & Interaktion</h3>
|
||||
<div class="component-grid">
|
||||
<div class="button-group">
|
||||
<app-button color="primary">Primary</app-button>
|
||||
<app-button color="secondary">Secondary</app-button>
|
||||
<app-button color="stroked">Stroked</app-button>
|
||||
<app-button color="flat">Flat</app-button>
|
||||
<app-button color="icon" tooltip="Favorit hinzufügen"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>...</app-button
|
||||
>
|
||||
<path
|
||||
d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"
|
||||
></path></svg
|
||||
|
||||
<app-button
|
||||
color="icon"
|
||||
tooltip="Details anzeigen"
|
||||
iconName="eye"
|
||||
></app-button>
|
||||
</div>
|
||||
<div class="chip-set">
|
||||
@@ -373,11 +286,13 @@
|
||||
><span class="badge-dot">3</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</app-card>
|
||||
|
||||
<app-card class="grid-col-span-2">
|
||||
<h3 card-header>Indikatoren & Feedback</h3>
|
||||
<h3 class="card-header">Indikatoren & Feedback</h3>
|
||||
<div class="card-body component-grid">
|
||||
<app-button color="secondary" (click)="triggerSnackbar()"
|
||||
>Snackbar anzeigen</app-button
|
||||
@@ -394,7 +309,7 @@
|
||||
</app-card>
|
||||
|
||||
<app-card class="grid-col-span-2">
|
||||
<h3 card-header>Navigation & Layout</h3>
|
||||
<h3 class="card-header">Navigation & Layout</h3>
|
||||
<div class="component-grid">
|
||||
<div class="tab-group">
|
||||
<button class="tab-link active">Profil</button
|
||||
@@ -437,7 +352,7 @@
|
||||
</app-card>
|
||||
|
||||
<app-card class="grid-col-span-2">
|
||||
<h3 card-header>Ladezustände (Skeleton)</h3>
|
||||
<h3 class="card-header">Ladezustände (Skeleton)</h3>
|
||||
<div *ngIf="isLoading" class="component-grid">
|
||||
<div class="skeleton-card card">
|
||||
<app-skeleton type="avatar"></app-skeleton>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
<div class="card kpi-card">
|
||||
<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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
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' })
|
||||
.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');
|
||||
// Ü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.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');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||