sidemenu song list
This commit is contained in:
@@ -51,7 +51,7 @@
|
||||
"docx",
|
||||
"qrcode"
|
||||
]
|
||||
},
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
@@ -64,7 +64,7 @@
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kB",
|
||||
"maximumError": "1MB"
|
||||
"maximumError": "10MB"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
@@ -82,7 +82,7 @@
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"serve": {
|
||||
"builder": "@angular/build:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
@@ -99,5 +99,8 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"analytics": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<app-navigation></app-navigation>
|
||||
|
||||
<div [@fader]="o.isActivated ? o.activatedRoute : ''" class="content">
|
||||
<router-outlet #o="outlet"></router-outlet>
|
||||
<div class="content">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
|
||||
@@ -1,83 +1,81 @@
|
||||
@if (show) {
|
||||
<div @fade>
|
||||
<app-card [closeIcon]="faIcon" [heading]="show.showType | showType" [subheading]="show.date.toDate() | date:'dd.MM.yyyy'" closeLink="/presentation/select">
|
||||
@if (!progress) {
|
||||
<div class="song">
|
||||
@if (show) {
|
||||
<div class="song-parts">
|
||||
<div (click)="onSectionClick('title', -1, show.id)" [class.active]="show.presentationSongId === 'title'" class="song-part">
|
||||
<div class="head">Veranstaltung</div>
|
||||
</div>
|
||||
<div (click)="onSectionClick('empty', -1, show.id)" [class.active]="show.presentationSongId === 'empty'" class="song-part">
|
||||
<div class="head">Leer</div>
|
||||
</div>
|
||||
<app-card [closeIcon]="faIcon" [heading]="show.showType | showType" [subheading]="show.date.toDate() | date:'dd.MM.yyyy'" closeLink="/presentation/select">
|
||||
@if (!progress) {
|
||||
<div class="song">
|
||||
@if (show) {
|
||||
<div class="song-parts">
|
||||
<div (click)="onSectionClick('title', -1, show.id)" [class.active]="show.presentationSongId === 'title'" class="song-part">
|
||||
<div class="head">Veranstaltung</div>
|
||||
</div>
|
||||
<div (click)="onSectionClick('empty', -1, show.id)" [class.active]="show.presentationSongId === 'empty'" class="song-part">
|
||||
<div class="head">Leer</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@for (song of presentationSongs; track trackBy($index, song)) {
|
||||
<div class="song">
|
||||
@if (show) {
|
||||
<div [class.active]="show.presentationSongId === song.id" class="title song-part">
|
||||
<div (click)="onSectionClick(song.id, -1, show.id)" class="head">{{ song.title }}</div>
|
||||
</div>
|
||||
} @if (show) {
|
||||
<div class="song-parts">
|
||||
@for (section of song.sections; track section.type + '-' + section.number + '-' + $index; let i = $index) {
|
||||
<div
|
||||
(click)="onSectionClick(song.id, i, show.id)"
|
||||
[class.active]="
|
||||
}
|
||||
</div>
|
||||
@for (song of presentationSongs; track trackBy($index, song)) {
|
||||
<div class="song">
|
||||
@if (show) {
|
||||
<div [class.active]="show.presentationSongId === song.id" class="title song-part">
|
||||
<div (click)="onSectionClick(song.id, -1, show.id)" class="head">{{ song.title }}</div>
|
||||
</div>
|
||||
} @if (show) {
|
||||
<div class="song-parts">
|
||||
@for (section of song.sections; track section.type + '-' + section.number + '-' + $index; let i = $index) {
|
||||
<div
|
||||
(click)="onSectionClick(song.id, i, show.id)"
|
||||
[class.active]="
|
||||
show.presentationSongId === song.id &&
|
||||
show.presentationSection === i
|
||||
"
|
||||
class="song-part"
|
||||
>
|
||||
<div class="head">{{ section.type | sectionType }} {{ section.number + 1 }}</div>
|
||||
<div class="fragment">{{ getFirstLine(section) }}</div>
|
||||
</div>
|
||||
}
|
||||
class="song-part"
|
||||
>
|
||||
<div class="head">{{ section.type | sectionType }} {{ section.number + 1 }}</div>
|
||||
<div class="fragment">{{ getFirstLine(section) }}</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="song">
|
||||
@if (show) {
|
||||
<div [class.active]="show.presentationSongId === 'dynamicText'" class="title song-part">
|
||||
<div (click)="onSectionClick('dynamicText', -1, show.id)" class="head">Freier Text</div>
|
||||
</div>
|
||||
}
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Überschrift</mat-label>
|
||||
<input (ngModelChange)="onDynamicCaption($event, show.id)" [ngModel]="show.presentationDynamicCaption" autocomplete="off" id="dynamic-caption" matInput type="text" />
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Text</mat-label>
|
||||
<textarea (ngModelChange)="onDynamicText($event, show.id)" [ngModel]="show.presentationDynamicText" autocomplete="off" id="dynamic-text" matInput></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="song">
|
||||
@if (show) {
|
||||
<div class="div-bottom">
|
||||
<button class="btn-start-presentation" mat-button routerLink="/presentation/monitor">
|
||||
<fa-icon [icon]="faDesktop"></fa-icon>
|
||||
Präsentation starten
|
||||
</button>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Hintergrund</mat-label>
|
||||
<mat-select (ngModelChange)="onBackground($event, show.id)" [ngModel]="show.presentationBackground">
|
||||
<mat-option value="none">kein Hintergrund</mat-option>
|
||||
<mat-option value="blue">Sternenhimmel</mat-option>
|
||||
<mat-option value="green">Blätter</mat-option>
|
||||
<mat-option value="leder">Leder</mat-option>
|
||||
<mat-option value="praise">Lobpreis</mat-option>
|
||||
<mat-option value="bible">Bibel</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-slider #slider [max]="100" [min]="10" [step]="2" class="zoom-slider" color="primary" ngDefaultControl
|
||||
><input (ngModelChange)="onZoom($event, show.id)" [ngModel]="show.presentationZoom" matSliderThumb />
|
||||
</mat-slider>
|
||||
<div [class.active]="show.presentationSongId === 'dynamicText'" class="title song-part">
|
||||
<div (click)="onSectionClick('dynamicText', -1, show.id)" class="head">Freier Text</div>
|
||||
</div>
|
||||
} @if (show) {
|
||||
<app-add-song [addedLive]="true" [showSongs]="showSongs" [show]="show" [songs]="songs$|async"></app-add-song>
|
||||
} }
|
||||
</app-card>
|
||||
</div>
|
||||
}
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Überschrift</mat-label>
|
||||
<input (ngModelChange)="onDynamicCaption($event, show.id)" [ngModel]="show.presentationDynamicCaption" autocomplete="off" id="dynamic-caption" matInput type="text" />
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Text</mat-label>
|
||||
<textarea (ngModelChange)="onDynamicText($event, show.id)" [ngModel]="show.presentationDynamicText" autocomplete="off" id="dynamic-text" matInput></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
@if (show) {
|
||||
<div class="div-bottom">
|
||||
<button class="btn-start-presentation" mat-button routerLink="/presentation/monitor">
|
||||
<fa-icon [icon]="faDesktop"></fa-icon>
|
||||
Präsentation starten
|
||||
</button>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Hintergrund</mat-label>
|
||||
<mat-select (ngModelChange)="onBackground($event, show.id)" [ngModel]="show.presentationBackground">
|
||||
<mat-option value="none">kein Hintergrund</mat-option>
|
||||
<mat-option value="blue">Sternenhimmel</mat-option>
|
||||
<mat-option value="green">Blätter</mat-option>
|
||||
<mat-option value="leder">Leder</mat-option>
|
||||
<mat-option value="praise">Lobpreis</mat-option>
|
||||
<mat-option value="bible">Bibel</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-slider #slider [max]="100" [min]="10" [step]="2" class="zoom-slider" color="primary" ngDefaultControl
|
||||
><input (ngModelChange)="onZoom($event, show.id)" [ngModel]="show.presentationZoom" matSliderThumb />
|
||||
</mat-slider>
|
||||
</div>
|
||||
} @if (show) {
|
||||
<app-add-song [addedLive]="true" [showSongs]="showSongs" [show]="show" [songs]="songs$|async"></app-add-song>
|
||||
} }
|
||||
</app-card>
|
||||
}
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
.third {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
column-gap: 20px;
|
||||
.third,
|
||||
:host ::ng-deep form,
|
||||
div[formGroup] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.third {
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
:host ::ng-deep .mat-mdc-form-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -1,43 +1,48 @@
|
||||
@if (songs$ | async; as songs) {
|
||||
<div>
|
||||
<app-list-header [anyFilterActive]="anyFilterActive">
|
||||
<app-sidebar>
|
||||
<div sidebar class="sidebar-content">
|
||||
<app-filter [songs]="songs"></app-filter>
|
||||
</app-list-header>
|
||||
<app-card [padding]="false">
|
||||
@for (song of songs; track trackBy($index, song)) {
|
||||
<div [routerLink]="song.id" class="list-item">
|
||||
<div class="number">{{ song.number }}</div>
|
||||
<div class="title">
|
||||
<span>{{ song.title }}</span>
|
||||
@if (song.hasChordValidationIssues) {
|
||||
<span class="validation-star" title="Akkord-Validierungsfehler vorhanden">*</span>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<ng-container *appRole="['contributor']">
|
||||
@if (song.status === 'draft') {
|
||||
<div class="sidebar-actions">
|
||||
<app-button [icon]="faNewSong" routerLink="new">Neuen Song anlegen</app-button>
|
||||
</div>
|
||||
</div>
|
||||
<div content>
|
||||
<app-card [padding]="false">
|
||||
@for (song of songs; track trackBy($index, song)) {
|
||||
<div [routerLink]="song.id" class="list-item">
|
||||
<div class="number">{{ song.number }}</div>
|
||||
<div class="title">
|
||||
<span>{{ song.title }}</span>
|
||||
@if (song.hasChordValidationIssues) {
|
||||
<span class="validation-star" title="Akkord-Validierungsfehler vorhanden">*</span>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<ng-container *appRole="['contributor']">
|
||||
@if (song.status === 'draft') {
|
||||
<div class="warning">
|
||||
<fa-icon [icon]="faDraft"></fa-icon>
|
||||
</div>
|
||||
} @if (song.status === 'set') {
|
||||
<div class="neutral">
|
||||
<fa-icon [icon]="faDraft"></fa-icon>
|
||||
</div>
|
||||
} @if (song.status === 'final') {
|
||||
<div class="success">
|
||||
<fa-icon [icon]="faFinal"></fa-icon>
|
||||
</div>
|
||||
}
|
||||
</ng-container>
|
||||
@if (song.legalType === 'open') {
|
||||
<div class="warning">
|
||||
<fa-icon [icon]="faDraft"></fa-icon>
|
||||
</div>
|
||||
} @if (song.status === 'set') {
|
||||
<div class="neutral">
|
||||
<fa-icon [icon]="faDraft"></fa-icon>
|
||||
</div>
|
||||
} @if (song.status === 'final') {
|
||||
<div class="success">
|
||||
<fa-icon [icon]="faFinal"></fa-icon>
|
||||
<fa-icon [icon]="faLegal"></fa-icon>
|
||||
</div>
|
||||
}
|
||||
</ng-container>
|
||||
@if (song.legalType === 'open') {
|
||||
<div class="warning">
|
||||
<fa-icon [icon]="faLegal"></fa-icon>
|
||||
</div>
|
||||
}
|
||||
<div>{{ song.key }}</div>
|
||||
</div>
|
||||
<div>{{ song.key }}</div>
|
||||
</div>
|
||||
}
|
||||
</app-card>
|
||||
</div>
|
||||
}
|
||||
</app-card>
|
||||
</div>
|
||||
</app-sidebar>
|
||||
}
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
.sidebar-content {
|
||||
padding: 20px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar-actions {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
padding: 5px 20px;
|
||||
display: grid;
|
||||
|
||||
@@ -6,15 +6,16 @@ import {fade} from '../../../animations';
|
||||
import {ActivatedRoute, RouterLink} from '@angular/router';
|
||||
import {filterSong} from '../../../services/filter.helper';
|
||||
import {FilterValues} from './filter/filter-values';
|
||||
import {faBalanceScaleRight, faCheck, faPencilRuler} from '@fortawesome/free-solid-svg-icons';
|
||||
import {faBalanceScaleRight, faCheck, faPencilRuler, faPlus} from '@fortawesome/free-solid-svg-icons';
|
||||
import {TextRenderingService} from '../services/text-rendering.service';
|
||||
import {FilterStoreService} from '../../../services/filter-store.service';
|
||||
import {AsyncPipe} from '@angular/common';
|
||||
import {ListHeaderComponent} from '../../../widget-modules/components/list-header/list-header.component';
|
||||
import {FilterComponent} from './filter/filter.component';
|
||||
import {CardComponent} from '../../../widget-modules/components/card/card.component';
|
||||
import {RoleDirective} from '../../../services/user/role.directive';
|
||||
import {FaIconComponent} from '@fortawesome/angular-fontawesome';
|
||||
import {SidebarComponent} from '../../../widget-modules/components/sidebar/sidebar.component';
|
||||
import {ButtonComponent} from '../../../widget-modules/components/button/button.component';
|
||||
|
||||
interface SongListItem extends Song {
|
||||
hasChordValidationIssues: boolean;
|
||||
@@ -26,20 +27,21 @@ interface SongListItem extends Song {
|
||||
styleUrls: ['./song-list.component.less'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [fade],
|
||||
imports: [ListHeaderComponent, FilterComponent, CardComponent, RouterLink, RoleDirective, FaIconComponent, AsyncPipe],
|
||||
imports: [FilterComponent, CardComponent, RouterLink, RoleDirective, FaIconComponent, AsyncPipe, SidebarComponent, ButtonComponent],
|
||||
})
|
||||
export class SongListComponent {
|
||||
public faLegal = faBalanceScaleRight;
|
||||
public faDraft = faPencilRuler;
|
||||
public faFinal = faCheck;
|
||||
public faNewSong = faPlus;
|
||||
private route = inject(ActivatedRoute);
|
||||
private textRenderingService = inject(TextRenderingService);
|
||||
private filterStore = inject(FilterStoreService);
|
||||
|
||||
public anyFilterActive = false;
|
||||
public songs$: Observable<SongListItem[]> = combineLatest([
|
||||
this.filterStore.songFilter$,
|
||||
this.route.data.pipe(map(data => (data['songs'] as Song[]).slice().sort((a, b) => a.number - b.number))),
|
||||
]).pipe(
|
||||
map(([filter, songs]) => {
|
||||
this.anyFilterActive = this.checkIfFilterActive(filter);
|
||||
return songs
|
||||
.filter(song => this.filter(song, filter))
|
||||
.map(song => ({
|
||||
@@ -49,9 +51,6 @@ export class SongListComponent {
|
||||
.sort((a, b) => a.title?.localeCompare(b.title));
|
||||
})
|
||||
);
|
||||
public faLegal = faBalanceScaleRight;
|
||||
public faDraft = faPencilRuler;
|
||||
public faFinal = faCheck;
|
||||
|
||||
public trackBy = (index: number, show: SongListItem) => show.id;
|
||||
|
||||
@@ -65,10 +64,6 @@ export class SongListComponent {
|
||||
return baseFilter;
|
||||
}
|
||||
|
||||
private checkIfFilterActive(filter: FilterValues): boolean {
|
||||
return !!filter.q || !!filter.type || !!filter.key || !!filter.legalType || !!filter.flag;
|
||||
}
|
||||
|
||||
private checkFlag(flag: string, flags: string) {
|
||||
if (!flags) {
|
||||
return false;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div [class.fullscreen]="fullscreen" [class.padding]="padding" class="card">
|
||||
<div @fade [class.fullscreen]="fullscreen" [class.padding]="padding" class="card">
|
||||
@if (closeLink && !fullscreen) {
|
||||
<button [routerLink]="closeLink" class="btn-close" mat-icon-button>
|
||||
<fa-icon [icon]="closeIcon"></fa-icon>
|
||||
|
||||
@@ -70,9 +70,10 @@
|
||||
}
|
||||
|
||||
.btn-close {
|
||||
--icon-button-color: var(--text-soft);
|
||||
--icon-button-hover-color: var(--text);
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 15px;
|
||||
opacity: 0.7;
|
||||
color: var(--text-soft);
|
||||
}
|
||||
|
||||
@@ -4,12 +4,14 @@ import {faTimes} from '@fortawesome/free-solid-svg-icons';
|
||||
import {MatIconButton} from '@angular/material/button';
|
||||
import {RouterLink} from '@angular/router';
|
||||
import {FaIconComponent} from '@fortawesome/angular-fontawesome';
|
||||
import {fade} from '../../../animations';
|
||||
|
||||
@Component({
|
||||
selector: 'app-card',
|
||||
templateUrl: './card.component.html',
|
||||
styleUrls: ['./card.component.less'],
|
||||
imports: [MatIconButton, RouterLink, FaIconComponent],
|
||||
animations: [fade],
|
||||
})
|
||||
export class CardComponent {
|
||||
@Input() public padding = true;
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
<div class="header">
|
||||
@if (showFilterButton) {
|
||||
<button (click)="onFilterClick()" [class.filter-active]="anyFilterActive" mat-icon-button>
|
||||
<fa-icon [icon]="faFilter"></fa-icon>
|
||||
</button>
|
||||
}
|
||||
<button mat-icon-button routerLink="new">
|
||||
<fa-icon [icon]="faNew"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (filterVisible || anyFilterActive) {
|
||||
@if (showFilterButton && (filterVisible || anyFilterActive)) {
|
||||
<div @fade>
|
||||
<app-card>
|
||||
<ng-content></ng-content>
|
||||
|
||||
@@ -10,11 +10,15 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
color: var(--primary-hover);
|
||||
|
||||
}
|
||||
|
||||
.filter-active {
|
||||
color: var(--danger);
|
||||
.header .mat-mdc-icon-button {
|
||||
--icon-button-color: var(--primary-hover);
|
||||
--icon-button-hover-color: var(--primary-active);
|
||||
}
|
||||
|
||||
.header .mat-mdc-icon-button.filter-active {
|
||||
--icon-button-color: var(--danger);
|
||||
--icon-button-hover-color: var(--danger);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ export class ListHeaderComponent {
|
||||
public filterVisible = false;
|
||||
@Output() public filterVisibleChanged = new EventEmitter<boolean>();
|
||||
@Input() public anyFilterActive = false;
|
||||
@Input() public showFilterButton = true;
|
||||
|
||||
public onFilterClick(): void {
|
||||
this.filterVisible = !this.filterVisible || this.anyFilterActive;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
button {
|
||||
.mat-mdc-button {
|
||||
--icon-button-color: var(--primary-color);
|
||||
--icon-button-hover-color: var(--primary-active);
|
||||
min-width: 0;
|
||||
padding: 0 var(--button-padding, 5px);
|
||||
font-size: var(--button-font-size, 1em);
|
||||
|
||||
@@ -1 +1,12 @@
|
||||
<aside></aside>
|
||||
<button class="sidebar-toggle" (click)="toggle()" [attr.aria-expanded]="!collapsed" aria-label="Sidebar umschalten" mat-icon-button type="button">
|
||||
<fa-icon [icon]="collapsed ? closedIcon : openIcon"></fa-icon>
|
||||
</button>
|
||||
<aside [class.collapsed]="collapsed">
|
||||
<div class="sidebar-toggle-placeholder" aria-hidden="true"></div>
|
||||
<div class="sidebar-body">
|
||||
<ng-content select="[sidebar]"></ng-content>
|
||||
</div>
|
||||
</aside>
|
||||
<div class="content">
|
||||
<ng-content select="[content]"></ng-content>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,48 @@
|
||||
:host {
|
||||
--sidebar-width: 300px;
|
||||
--sidebar-toggle-size: 48px;
|
||||
--sidebar-toggle-offset: 12px;
|
||||
display: grid;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
|
||||
align-items: start;
|
||||
box-sizing: border-box;
|
||||
transition: grid-template-columns 200ms ease;
|
||||
}
|
||||
|
||||
:host.collapsed {
|
||||
grid-template-columns: 0 minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.sidebar-toggle {
|
||||
--icon-button-color: var(--primary-hover);
|
||||
--icon-button-hover-color: var(--primary-active);
|
||||
position: fixed;
|
||||
top: calc(50px + var(--sidebar-toggle-offset));
|
||||
left: var(--sidebar-toggle-offset);
|
||||
z-index: 11;
|
||||
color: var(--icon-button-color);
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:host.collapsed .sidebar-toggle {
|
||||
--icon-button-color: var(--text-inverse);
|
||||
--icon-button-hover-color: var(--text-inverse);
|
||||
}
|
||||
|
||||
.sidebar-toggle fa-icon {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.sidebar-toggle:hover {
|
||||
color: var(--icon-button-hover-color);
|
||||
}
|
||||
|
||||
aside {
|
||||
width: 200px;
|
||||
width: var(--sidebar-width);
|
||||
height: calc(100vh - 50px);
|
||||
|
||||
position: fixed;
|
||||
@@ -8,4 +51,28 @@ aside {
|
||||
bottom: 0;
|
||||
|
||||
background: var(--surface);
|
||||
box-shadow: var(--shadow-card-2);
|
||||
overflow: hidden;
|
||||
transform: translateX(0);
|
||||
transition: transform 200ms ease;
|
||||
}
|
||||
|
||||
aside.collapsed {
|
||||
transform: translateX(calc(-1 * var(--sidebar-width)));
|
||||
}
|
||||
|
||||
.sidebar-toggle-placeholder {
|
||||
height: calc(var(--sidebar-toggle-size) + var(--sidebar-toggle-offset));
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.sidebar-body {
|
||||
height: calc(100% - var(--sidebar-toggle-size) - var(--sidebar-toggle-offset));
|
||||
}
|
||||
|
||||
.content {
|
||||
grid-column: 2;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {MatIconButton} from '@angular/material/button';
|
||||
import {FaIconComponent} from '@fortawesome/angular-fontawesome';
|
||||
import {faBars, faChevronLeft} from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sidebar',
|
||||
imports: [],
|
||||
imports: [MatIconButton, FaIconComponent],
|
||||
templateUrl: './sidebar.component.html',
|
||||
styleUrl: './sidebar.component.less',
|
||||
host: {
|
||||
'[class.collapsed]': 'collapsed',
|
||||
},
|
||||
})
|
||||
export class SidebarComponent {}
|
||||
export class SidebarComponent {
|
||||
public collapsed = true;
|
||||
public openIcon = faChevronLeft;
|
||||
public closedIcon = faBars;
|
||||
|
||||
public toggle(): void {
|
||||
this.collapsed = !this.collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ $wgenerator-theme: mat.m2-define-light-theme((
|
||||
warn: $wgenerator-warn,
|
||||
),
|
||||
typography: mat.m2-define-typography-config(),
|
||||
density: 0,
|
||||
density: -2,
|
||||
));
|
||||
|
||||
@include mat.all-component-themes($wgenerator-theme);
|
||||
|
||||
@@ -34,6 +34,10 @@
|
||||
--focus-ring: 0 0 0 2px rgba(111, 143, 149, 0.28);
|
||||
--transition: all 300ms ease-in-out;
|
||||
--transition-fast: all 150ms ease-in-out;
|
||||
--icon-button-color: var(--primary-color);
|
||||
--icon-button-hover-color: var(--primary-active);
|
||||
--icon-button-opacity: 1;
|
||||
--icon-button-hover-opacity: 1;
|
||||
|
||||
|
||||
--mat-dialog-supporting-text-color: var(--text);
|
||||
@@ -78,11 +82,11 @@ a {
|
||||
}
|
||||
|
||||
.mat-mdc-icon-button {
|
||||
color: var(--primary-color) !important;
|
||||
color: var(--icon-button-color, var(--primary-color));
|
||||
transition: var(--transition);
|
||||
|
||||
&:hover {
|
||||
color: var(--primary-active) !important;
|
||||
color: var(--icon-button-hover-color, var(--primary-active));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,11 +107,11 @@ body .cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing {
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
opacity: 0.2;
|
||||
opacity: var(--icon-button-opacity);
|
||||
transition: var(--transition);
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
opacity: var(--icon-button-hover-opacity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +130,5 @@ body {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
--mat-form-field-container-text-line-height: 16px;
|
||||
--mat-form-field-container-text-size: 16px;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user