styles
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
.expansion-panel {
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-md);
|
||||
transition: box-shadow var(--transition-speed);
|
||||
}
|
||||
.expansion-panel.is-open {
|
||||
box-shadow: var(--box-shadow-sm);
|
||||
}
|
||||
.expansion-panel-header {
|
||||
background: none;
|
||||
border: none;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 0.75rem 1.25rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
.expansion-panel-icon {
|
||||
transition: transform 0.3s ease-out;
|
||||
}
|
||||
.expansion-panel.is-open .expansion-panel-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.expansion-panel-content-wrapper {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.35s ease-in-out;
|
||||
}
|
||||
.expansion-panel.is-open .expansion-panel-content-wrapper {
|
||||
max-height: 200px;
|
||||
}
|
||||
.expansion-panel-content {
|
||||
padding: 0 1.25rem 1.25rem 1.25rem;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<div class="expansion-panel" [class.is-open]="isOpen">
|
||||
<button type="button" class="expansion-panel-header" (click)="togglePanel()">
|
||||
<span>{{ title }}</span>
|
||||
<svg class="expansion-panel-icon" 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"><polyline points="6 9 12 15 18 9"></polyline></svg>
|
||||
</button>
|
||||
<div class="expansion-panel-content-wrapper">
|
||||
<div class="expansion-panel-content">
|
||||
<!-- Der Inhalt des Panels wird von außen über ng-content eingefügt -->
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-expansion-panel',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
templateUrl: './expansion-panel.component.html',
|
||||
styleUrl: './expansion-panel.component.css'
|
||||
})
|
||||
export class ExpansionPanelComponent {
|
||||
@Input() title: string = 'Details anzeigen'; // Titel für den Header
|
||||
isOpen = false; // Interner Zustand, ob das Panel geöffnet ist
|
||||
|
||||
togglePanel(): void {
|
||||
this.isOpen = !this.isOpen;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* Verschieben Sie diese Stile aus der globalen styles.css hierher */
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.main-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
/* Responsivität für den Header */
|
||||
@media (max-width: 768px) {
|
||||
.main-header {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
@@ -1 +1,14 @@
|
||||
<p>page-header works!</p>
|
||||
<header class="main-header">
|
||||
|
||||
<!-- Linker Bereich: Hier könnte später ein Titel oder Breadcrumbs via ng-content platziert werden -->
|
||||
<div class="header-left">
|
||||
<app-search-bar></app-search-bar>
|
||||
</div>
|
||||
|
||||
<!-- Rechter Bereich mit den Aktionen -->
|
||||
<div class="header-actions">
|
||||
<app-theme-switcher></app-theme-switcher>
|
||||
<app-user-profile></app-user-profile>
|
||||
</div>
|
||||
|
||||
</header>
|
||||
@@ -1,11 +1,21 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SearchBarComponent } from '../search-bar/search-bar.component';
|
||||
import { ThemeSwitcherComponent } from '../theme-switcher/theme-switcher.component';
|
||||
import { UserProfileComponent } from '../user-profile/user-profile.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-header',
|
||||
imports: [],
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
SearchBarComponent,
|
||||
ThemeSwitcherComponent,
|
||||
UserProfileComponent
|
||||
],
|
||||
templateUrl: './page-header.component.html',
|
||||
styleUrl: './page-header.component.css'
|
||||
})
|
||||
export class PageHeaderComponent {
|
||||
|
||||
}
|
||||
// Hier könnten später z.B. der Seitentitel oder Breadcrumbs übergeben werden
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Verschieben Sie diese Stile aus styles.css hierher */
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
}
|
||||
.search-bar svg {
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--color-text-light);
|
||||
pointer-events: none; /* Verhindert, dass das Icon Klicks abfängt */
|
||||
}
|
||||
.search-bar input {
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem 0.75rem 3rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-md);
|
||||
background-color: var(--color-surface);
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
transition: all var(--transition-speed);
|
||||
}
|
||||
.search-bar input:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.3);
|
||||
}
|
||||
@@ -1 +1,7 @@
|
||||
<p>search-bar works!</p>
|
||||
<div class="search-bar">
|
||||
<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">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<input type="text" placeholder="Dashboard durchsuchen..." (input)="onSearch($event)" />
|
||||
</div>
|
||||
@@ -1,11 +1,18 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, Output, EventEmitter } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-search-bar',
|
||||
standalone: true,
|
||||
imports: [],
|
||||
templateUrl: './search-bar.component.html',
|
||||
styleUrl: './search-bar.component.css'
|
||||
})
|
||||
export class SearchBarComponent {
|
||||
// Gibt den Suchbegriff aus, wenn der Benutzer tippt (mit debounce) oder Enter drückt
|
||||
@Output() search = new EventEmitter<string>();
|
||||
|
||||
}
|
||||
onSearch(event: Event): void {
|
||||
const input = event.target as HTMLInputElement;
|
||||
this.search.emit(input.value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.theme-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
color: var(--color-text-light);
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="theme-switcher">
|
||||
<label>Dark Mode</label>
|
||||
|
||||
<app-slide-toggle [(ngModel)]="isDarkMode" (ngModelChange)="toggleTheme($event)"></app-slide-toggle>
|
||||
</div>
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Component, Renderer2, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms'; // FormsModule importieren
|
||||
import { SlideToggleComponent } from '../../form/slide-toggle/slide-toggle.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-theme-switcher',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
SlideToggleComponent,
|
||||
FormsModule // <-- HIER IST DIE KORREKTUR
|
||||
],
|
||||
templateUrl: './theme-switcher.component.html',
|
||||
styleUrl: './theme-switcher.component.css'
|
||||
})
|
||||
export class ThemeSwitcherComponent implements OnInit {
|
||||
isDarkMode = false;
|
||||
|
||||
constructor(private renderer: Renderer2) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
// Hier könnte man den Zustand aus dem localStorage laden
|
||||
}
|
||||
|
||||
// Die Methode akzeptiert jetzt direkt den neuen boolean-Wert
|
||||
toggleTheme(isDarkMode: boolean): void {
|
||||
this.isDarkMode = isDarkMode;
|
||||
if (this.isDarkMode) {
|
||||
this.renderer.addClass(document.body, 'dark-theme');
|
||||
} else {
|
||||
this.renderer.removeClass(document.body, 'dark-theme');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/* Verschieben Sie diese Stile aus styles.css hierher */
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.user-profile img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
cursor: pointer;
|
||||
border: 2px solid var(--color-border);
|
||||
transition: border-color var(--transition-speed);
|
||||
}
|
||||
|
||||
.user-profile img:hover {
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
@@ -1 +1,3 @@
|
||||
<p>user-profile works!</p>
|
||||
<div class="user-profile">
|
||||
<img [src]="avatarUrl" alt="User Avatar" />
|
||||
</div>
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-profile',
|
||||
standalone: true,
|
||||
imports: [],
|
||||
templateUrl: './user-profile.component.html',
|
||||
styleUrl: './user-profile.component.css'
|
||||
})
|
||||
export class UserProfileComponent {
|
||||
|
||||
}
|
||||
// Später könnte hier ein User-Objekt übergeben werden
|
||||
@Input() avatarUrl = 'https://i.pravatar.cc/40';
|
||||
}
|
||||
Reference in New Issue
Block a user