This commit is contained in:
Tizian.Breuch
2025-09-08 12:11:30 +02:00
parent 99dee65a79
commit dd4e2b7d9d
7 changed files with 672 additions and 25 deletions

View File

@@ -674,3 +674,122 @@
<!-- Ende .dashboard-grid -->
</main>
</div>
<!-- =================================================================== -->
<!-- DYNAMISCHER DIALOG (Standardmäßig unsichtbar) -->
<!-- =================================================================== -->
<div
class="dialog-backdrop"
*ngIf="isDialogOpen"
(click)="closeDialog()"
[@fade]> <!-- Angular Animation -->
<!-- (click.stop) verhindert, dass der Klick auf den Container den Backdrop schließt -->
<div class="dialog-container card" (click)="$event.stopPropagation()" [@slideIn]>
<div class="dialog-header">
<h3 class="dialog-title">Aktion bestätigen</h3>
<!-- (click) ruft die closeDialog Methode auf -->
<button class="btn btn-icon" (click)="closeDialog()" data-tooltip="Schließen">&times;</button>
</div>
<div class="dialog-content">
<div class="dialog-icon-container">
<!-- Ein Icon, um die Art des Dialogs zu verdeutlichen -->
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
</div>
<p>Sind Sie sicher, dass Sie die Bestellung <strong>#10543</strong> löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.</p>
</div>
<div class="dialog-actions">
<!-- (click) ruft die closeDialog Methode auf -->
<button class="btn btn-stroked" (click)="closeDialog()">Abbrechen</button>
<button class="btn btn-danger" (click)="closeDialog()">Ja, endgültig löschen</button>
</div>
</div>
</div>
<div class="menu-container">
<button class="btn btn-icon">...</button> <!-- Der Auslöser -->
<div class="menu-panel card"> <!-- Das Panel, das erscheint -->
<a href="#" class="menu-item">Bearbeiten</a>
<a href="#" class="menu-item">Kopieren</a>
<hr class="divider">
<a href="#" class="menu-item menu-item-danger">Löschen</a>
</div>
</div>
<div class="skeleton-card card">
<div class="skeleton-item skeleton-avatar"></div>
<div class="skeleton-content">
<div class="skeleton-item skeleton-line"></div>
<div class="skeleton-item skeleton-line" style="width: 70%;"></div>
</div>
</div>
<div class="stepper">
<div class="step step-complete"><div class="step-icon"></div><span>Profil</span></div>
<div class="step-connector"></div>
<div class="step step-active"><div class="step-icon">2</div><span>Konto</span></div>
<div class="step-connector"></div>
<div class="step"><div class="step-icon">3</div><span>Abschluss</span></div>
</div>
<div class="paginator">
<span class="paginator-info">1-10 von 100</span>
<div class="paginator-controls">
<button class="btn btn-icon">&lt;</button>
<button class="btn btn-icon">&gt;</button>
</div>
</div>
<ul class="tree">
<li class="tree-node">
<details open>
<summary class="tree-node-label">Ordner 1</summary>
<ul class="tree-node-children">
<li class="tree-node"><span class="tree-node-label">Datei 1.1</span></li>
<li class="tree-node"><span class="tree-node-label">Datei 1.2</span></li>
</ul>
</details>
</li>
</ul>
<div *ngIf="isLoading; else content">
<div class="skeleton-card">...</div>
</div>
<ng-template #content>
<!-- Echte Daten hier -->
</ng-template>
<button class="btn btn-icon btn-icon-danger" data-tooltip="Löschen" (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>
</button>
<!-- =================================================================== -->
<!-- DYNAMISCHE SNACKBAR (Standardmäßig unsichtbar) -->
<!-- =================================================================== -->
<div
class="snackbar"
*ngIf="isSnackbarVisible"
[@fadeSlideIn]> <!-- Angular Animation -->
<!-- Das Icon ist optional, wertet die Snackbar aber auf -->
<div class="snackbar-icon-container">
<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="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
</div>
<span class="snackbar-message">{{ snackbarMessage }}</span>
<button class="btn btn-icon snackbar-close-btn" (click)="closeSnackbar()" data-tooltip="Schließen">
&times;
</button>
</div>

View File

@@ -1,17 +1,47 @@
import { Component, Renderer2 } from '@angular/core';
import { CommonModule } from '@angular/common';
import { trigger, state, style, transition, animate } from '@angular/animations';
@Component({
selector: 'app-demo',
imports: [],
imports: [CommonModule],
templateUrl: './demo.component.html',
styleUrl: './demo.component.css'
styleUrl: './demo.component.css',
animations: [
trigger('fade', [
transition(':enter', [
style({ opacity: 0 }),
animate('300ms ease-out', style({ opacity: 1 }))
]),
transition(':leave', [
animate('300ms ease-in', style({ opacity: 0 }))
])
]),
trigger('slideIn', [
transition(':enter', [
style({ transform: 'translateY(20px)', opacity: 0 }),
animate('300ms ease-out', style({ transform: 'translateY(0)', opacity: 1 }))
]),
transition(':leave', [
animate('300ms ease-in', style({ transform: 'translateY(20px)', opacity: 0 }))
])
]),
trigger('fadeSlideIn', [
transition(':enter', [
style({ bottom: '-100px', opacity: 0 }),
animate('300ms ease-out', style({ bottom: '2rem', opacity: 1 }))
]),
transition(':leave', [
animate('300ms ease-in', style({ bottom: '-100px', opacity: 0 }))
])
])
]
})
export class DemoComponent {
// 1. Eine Eigenschaft, die den aktuellen Zustand des Dark Mode speichert.
// Sie ist an [checked]="isDarkMode" in der HTML gebunden.
isDarkMode = false;
isLoading = true;
// 2. Wir "injizieren" den Renderer2. Das ist der sichere Weg in Angular,
// um das DOM (z.B. den <body>-Tag) zu manipulieren.
constructor(private renderer: Renderer2) {}
@@ -29,4 +59,55 @@ export class DemoComponent {
this.renderer.removeClass(document.body, 'dark-theme');
}
}
}
isDialogOpen = false;
// 2. Eine Methode, um den Dialog zu öffnen
openDialog(): void {
this.isDialogOpen = true;
// Optional: Verhindert das Scrollen der Seite im Hintergrund
this.renderer.addClass(document.body, 'no-scroll');
}
// 3. Eine Methode, um den Dialog zu schließen
closeDialog(): void {
this.isDialogOpen = false;
// Optional: Gibt das Scrollen der Seite wieder frei
this.renderer.removeClass(document.body, 'no-scroll');
}
isSnackbarVisible = false;
snackbarMessage = '';
private snackbarTimer: any; // Hier speichern wir den Timer
// 2. Eine Methode, um die Snackbar anzuzeigen
// Wir übergeben eine Nachricht, um sie wiederverwendbar zu machen
showSnackbar(message: string): void {
// Falls bereits eine Snackbar sichtbar ist, schließen wir sie zuerst
if (this.isSnackbarVisible) {
this.closeSnackbar();
// Kurze Verzögerung, damit die Animation sauber neustartet
setTimeout(() => this.displaySnackbar(message), 300);
} else {
this.displaySnackbar(message);
}
}
private displaySnackbar(message: string): void {
this.snackbarMessage = message;
this.isSnackbarVisible = true;
// Setze einen Timer, um die Snackbar nach 5 Sekunden automatisch zu schließen
this.snackbarTimer = setTimeout(() => {
this.closeSnackbar();
}, 5000); // 5000 Millisekunden = 5 Sekunden
}
// 3. Eine Methode, um die Snackbar zu schließen (manuell oder durch Timer)
closeSnackbar(): void {
// Lösche den Timer, falls der Benutzer manuell schließt
clearTimeout(this.snackbarTimer);
this.isSnackbarVisible = false;
}
}

View File

@@ -4,11 +4,19 @@ import { DemoComponent } from './components/demo/demo.component';
export const DEMO_ROUTES: Routes = [
{
// Diese Route ist relativ zu '/demo'.
// Ein leerer Pfad leitet direkt weiter zu '/demo/1'.
path: '',
redirectTo: '1',
pathMatch: 'full'
},
{
// Diese Route passt auf '/demo/1' und lädt die Komponente genau einmal.
path: '1',
component: DemoComponent,
children: [
{ path: '', redirectTo: '1', pathMatch: 'full' },
{ path: '1', component: DemoComponent, title: 'Demo' },
]
title: 'Demo'
}
// Hier könntest du weitere Routen wie '2', '3' etc. hinzufügen,
// die andere Komponenten laden.
// { path: '2', component: AnotherDemoComponent },
];