demo
This commit is contained in:
@@ -1 +1 @@
|
||||
<router-outlet></router-outlet>
|
||||
<router-outlet style="height: 100vh; width: 100vw;"></router-outlet>
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { provideAnimations } from '@angular/platform-browser/animations'; // Importieren
|
||||
|
||||
import { routes } from './app.routes';
|
||||
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideClientHydration(withEventReplay())]
|
||||
};
|
||||
providers: [
|
||||
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||
provideRouter(routes),
|
||||
provideClientHydration(withEventReplay()),
|
||||
provideAnimations() // Hier hinzufügen
|
||||
]
|
||||
};
|
||||
@@ -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">×</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"><</button>
|
||||
<button class="btn btn-icon">></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">
|
||||
×
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
];
|
||||
@@ -1,21 +1,9 @@
|
||||
// src/app/material.ts
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
// ... füge hier alle Material-Module hinzu, die du häufig verwendest
|
||||
|
||||
|
||||
export const MATERIAL_MODULES = [
|
||||
CommonModule,
|
||||
|
||||
MatButtonModule,
|
||||
MatCardModule,
|
||||
MatIconModule,
|
||||
MatTableModule,
|
||||
MatTabsModule,
|
||||
MatToolbarModule,
|
||||
];
|
||||
445
src/styles.css
445
src/styles.css
@@ -831,3 +831,448 @@ body.dark-theme .pill-info {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* =================================================================================
|
||||
* 10. ERWEITERTE UI-KOMPONENTEN-BIBLIOTHEK (TEIL 2)
|
||||
* ================================================================================= */
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* 10.1 DIALOG / MODAL
|
||||
/* -------------------------------------------------- */
|
||||
|
||||
.dialog-backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
z-index: 1000;
|
||||
opacity: 1; /* Für Animationen anpassen */
|
||||
transition: opacity var(--transition-speed);
|
||||
}
|
||||
|
||||
.dialog-container {
|
||||
width: 100%;
|
||||
max-width: 500px; /* Standardbreite für Dialoge */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: var(--box-shadow-md);
|
||||
/* Animation: von leicht unten nach oben sliden */
|
||||
transform: translateY(0);
|
||||
transition: transform var(--transition-speed);
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem 1.5rem;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.dialog-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
padding: 1.5rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.dialog-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem 1.5rem;
|
||||
border-top: 1px solid var(--color-border);
|
||||
background-color: var(--color-body-bg);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* 10.2 SNACKBAR / TOAST
|
||||
/* -------------------------------------------------- */
|
||||
|
||||
@keyframes snackbar-in {
|
||||
from { transform: translate(-50%, 100px); opacity: 0; }
|
||||
to { transform: translate(-50%, 0); opacity: 1; }
|
||||
}
|
||||
|
||||
.snackbar {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
min-width: 300px;
|
||||
max-width: 500px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 0.75rem 1.25rem;
|
||||
border-radius: var(--border-radius-md);
|
||||
background-color: #2c3e50; /* Typischerweise dunkel */
|
||||
color: #fff;
|
||||
box-shadow: var(--box-shadow-md);
|
||||
z-index: 1050;
|
||||
animation: snackbar-in 0.3s ease-out;
|
||||
}
|
||||
|
||||
body.dark-theme .snackbar {
|
||||
background-color: var(--color-surface);
|
||||
color: var(--color-text);
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.snackbar-action {
|
||||
color: var(--color-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
body.dark-theme .snackbar-action {
|
||||
color: #60a5fa; /* Helleres Blau für Dark Mode */
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* 10.3 MENU / DROPDOWN
|
||||
/* -------------------------------------------------- */
|
||||
|
||||
.menu-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.menu-panel {
|
||||
position: absolute;
|
||||
top: calc(100% + 8px); /* Etwas Abstand zum Auslöser */
|
||||
right: 0;
|
||||
min-width: 200px;
|
||||
padding: 0.5rem 0;
|
||||
z-index: 900;
|
||||
border: 1px solid var(--color-border);
|
||||
box-shadow: var(--box-shadow-md);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translateY(-10px);
|
||||
transition: opacity var(--transition-speed), transform var(--transition-speed);
|
||||
}
|
||||
/* Zustand, wenn das Menü offen ist (z.B. durch eine JS-Klasse) */
|
||||
.menu-container.open .menu-panel {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: block;
|
||||
padding: 0.6rem 1.25rem;
|
||||
color: var(--color-text);
|
||||
text-decoration: none;
|
||||
font-size: 0.95rem;
|
||||
transition: background-color 0.2s ease-out;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background-color: var(--color-body-bg);
|
||||
}
|
||||
|
||||
.menu-item-danger {
|
||||
color: var(--color-danger);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* 10.4 SKELETON LOADER
|
||||
/* -------------------------------------------------- */
|
||||
|
||||
@keyframes skeleton-shimmer {
|
||||
0% { background-position: -200px 0; }
|
||||
100% { background-position: calc(200px + 100%) 0; }
|
||||
}
|
||||
|
||||
.skeleton-item {
|
||||
background-color: var(--color-border);
|
||||
background-image: linear-gradient(
|
||||
90deg,
|
||||
var(--color-border) 0px,
|
||||
var(--color-body-bg) 40px,
|
||||
var(--color-border) 80px
|
||||
);
|
||||
background-size: 200px 100%;
|
||||
background-repeat: no-repeat;
|
||||
animation: skeleton-shimmer 1.5s infinite;
|
||||
}
|
||||
|
||||
.skeleton-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
.skeleton-avatar {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.skeleton-content {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.skeleton-line {
|
||||
width: 100%;
|
||||
height: 1rem;
|
||||
border-radius: var(--border-radius-sm);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* 10.5 STEPPER
|
||||
/* -------------------------------------------------- */
|
||||
|
||||
.stepper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: var(--color-text-light);
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--color-border);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-weight: bold;
|
||||
transition: all var(--transition-speed);
|
||||
}
|
||||
.step.step-active {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
.step.step-active .step-icon {
|
||||
border-color: var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
.step.step-complete {
|
||||
color: var(--color-text);
|
||||
}
|
||||
.step.step-complete .step-icon {
|
||||
background-color: var(--color-primary);
|
||||
border-color: var(--color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.step-connector {
|
||||
flex-grow: 1;
|
||||
height: 2px;
|
||||
background-color: var(--color-border);
|
||||
margin: 0 1rem;
|
||||
transform: translateY(-12px); /* Auf Höhe der Icons ausrichten */
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* 10.6 PAGINATOR
|
||||
/* -------------------------------------------------- */
|
||||
|
||||
.paginator {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
padding: 1rem;
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
.paginator-info {
|
||||
font-size: 0.9rem;
|
||||
color: var(--color-text-light);
|
||||
font-weight: 500;
|
||||
}
|
||||
.paginator-controls {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* 10.7 TREE
|
||||
/* -------------------------------------------------- */
|
||||
|
||||
.tree {
|
||||
list-style: none;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.tree-node {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tree-node-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border-radius: var(--border-radius-sm);
|
||||
cursor: pointer;
|
||||
}
|
||||
.tree-node-label:hover {
|
||||
background-color: var(--color-body-bg);
|
||||
}
|
||||
.tree-node details > summary::-webkit-details-marker {
|
||||
display: none; /* Icon entfernen */
|
||||
}
|
||||
.tree-node details > summary {
|
||||
list-style: none;
|
||||
}
|
||||
.tree-node details > summary::before { /* Chevron-Icon */
|
||||
content: '▶';
|
||||
display: inline-block;
|
||||
font-size: 0.7rem;
|
||||
margin-right: 0.5rem;
|
||||
transition: transform 0.2s ease-out;
|
||||
}
|
||||
.tree-node details[open] > summary::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.tree-node-children {
|
||||
list-style: none;
|
||||
padding-left: 1.75rem;
|
||||
position: relative;
|
||||
}
|
||||
/* Verbindungs-Linie */
|
||||
.tree-node-children::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
background-color: var(--color-border);
|
||||
}
|
||||
|
||||
/* =================================================================================
|
||||
* 11. VERBESSERTES DIALOG-STYLING & HELFER-KLASSEN
|
||||
* ================================================================================= */
|
||||
|
||||
/* Verhindert das Scrollen des Body, wenn ein Overlay aktiv ist */
|
||||
body.no-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Neuer Button-Typ für gefährliche Aktionen */
|
||||
.btn-icon-danger:hover {
|
||||
background-color: #fee2e2; /* Rötlicher Hover-Effekt */
|
||||
color: var(--color-danger);
|
||||
}
|
||||
body.dark-theme .btn-icon-danger:hover {
|
||||
background-color: #991b1b;
|
||||
}
|
||||
|
||||
/* Verbessertes Dialog-Styling */
|
||||
.dialog-backdrop {
|
||||
/* ... bestehende Regeln (position: fixed, z-index etc.) bleiben gleich ... */
|
||||
backdrop-filter: blur(4px); /* Moderner Blur-Effekt */
|
||||
}
|
||||
|
||||
.dialog-container {
|
||||
/* ... bestehende Regeln (max-width, display: flex etc.) bleiben gleich ... */
|
||||
border-radius: var(--border-radius-md);
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
/* Icon-Styling innerhalb des Dialogs */
|
||||
.dialog-content {
|
||||
text-align: center; /* Zentriert den Inhalt für einen besseren Look */
|
||||
}
|
||||
|
||||
.dialog-icon-container {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: 0 auto 1rem auto;
|
||||
border-radius: 50%;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background-color: #fee2e2; /* Roter Hintergrund für das Icon */
|
||||
color: var(--color-danger);
|
||||
}
|
||||
body.dark-theme .dialog-icon-container {
|
||||
background-color: #7f1d1d;
|
||||
}
|
||||
|
||||
.dialog-content p {
|
||||
font-size: 1rem;
|
||||
color: var(--color-text-light);
|
||||
}
|
||||
.dialog-content strong {
|
||||
color: var(--color-text);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* =================================================================================
|
||||
* 12. VERBESSERTES SNACKBAR-STYLING
|
||||
* ================================================================================= */
|
||||
|
||||
.snackbar {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%); /* Startposition für Animation */
|
||||
|
||||
min-width: 320px;
|
||||
max-width: 500px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 0.75rem; /* Etwas weniger Padding, da der Button eigenes hat */
|
||||
border-radius: var(--border-radius-md);
|
||||
|
||||
background-color: var(--color-surface);
|
||||
color: var(--color-text);
|
||||
border: 1px solid var(--color-border);
|
||||
box-shadow: var(--box-shadow-md);
|
||||
z-index: 1050;
|
||||
}
|
||||
|
||||
/* Der @keyframes-Block wird nicht mehr benötigt, da wir Angular Animations nutzen */
|
||||
|
||||
.snackbar-icon-container {
|
||||
flex-shrink: 0; /* Verhindert, dass das Icon schrumpft */
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background-color: #dcfce7; /* Success-Farbe */
|
||||
color: #166534;
|
||||
}
|
||||
body.dark-theme .snackbar-icon-container {
|
||||
background-color: #166534;
|
||||
color: #dcfce7;
|
||||
}
|
||||
|
||||
.snackbar-message {
|
||||
flex-grow: 1; /* Nimmt den verfügbaren Platz ein */
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.snackbar-close-btn {
|
||||
flex-shrink: 0;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 1.5rem; /* Größeres "x" */
|
||||
color: var(--color-text-light);
|
||||
}
|
||||
.snackbar-close-btn:hover {
|
||||
background-color: var(--color-body-bg);
|
||||
}
|
||||
Reference in New Issue
Block a user