add swiper view to show

This commit is contained in:
2023-05-18 11:54:47 +02:00
parent e94766898d
commit 551bed9a77
16 changed files with 162 additions and 22 deletions

40
package-lock.json generated
View File

@@ -31,6 +31,7 @@
"ngx-perfect-scrollbar": "^10.1.1", "ngx-perfect-scrollbar": "^10.1.1",
"rxfire": "^6.0.3", "rxfire": "^6.0.3",
"rxjs": "~7.5.6", "rxjs": "~7.5.6",
"swiper": "^9.3.2",
"tslib": "^2.4.0", "tslib": "^2.4.0",
"uuid": "^8.3.2", "uuid": "^8.3.2",
"zone.js": "~0.11.8" "zone.js": "~0.11.8"
@@ -23178,6 +23179,11 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/ssr-window": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-4.0.2.tgz",
"integrity": "sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ=="
},
"node_modules/ssri": { "node_modules/ssri": {
"version": "9.0.1", "version": "9.0.1",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz",
@@ -23415,6 +23421,27 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/swiper": {
"version": "9.3.2",
"resolved": "https://registry.npmjs.org/swiper/-/swiper-9.3.2.tgz",
"integrity": "sha512-Kj9Z4kXRmJR3YT/Wj+XLWj8P6IcRt+WG38uL8M3/Wny7+6sV0TlP9vnE1X+Co9c7VzNooojWGnFa+Wf/9+CUMA==",
"funding": [
{
"type": "patreon",
"url": "https://www.patreon.com/swiperjs"
},
{
"type": "open_collective",
"url": "http://opencollective.com/swiper"
}
],
"dependencies": {
"ssr-window": "^4.0.2"
},
"engines": {
"node": ">= 4.7.0"
}
},
"node_modules/symbol-observable": { "node_modules/symbol-observable": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
@@ -42174,6 +42201,11 @@
"tweetnacl": "~0.14.0" "tweetnacl": "~0.14.0"
} }
}, },
"ssr-window": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-4.0.2.tgz",
"integrity": "sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ=="
},
"ssri": { "ssri": {
"version": "9.0.1", "version": "9.0.1",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz",
@@ -42335,6 +42367,14 @@
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true "dev": true
}, },
"swiper": {
"version": "9.3.2",
"resolved": "https://registry.npmjs.org/swiper/-/swiper-9.3.2.tgz",
"integrity": "sha512-Kj9Z4kXRmJR3YT/Wj+XLWj8P6IcRt+WG38uL8M3/Wny7+6sV0TlP9vnE1X+Co9c7VzNooojWGnFa+Wf/9+CUMA==",
"requires": {
"ssr-window": "^4.0.2"
}
},
"symbol-observable": { "symbol-observable": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",

View File

@@ -33,6 +33,7 @@
"ngx-perfect-scrollbar": "^10.1.1", "ngx-perfect-scrollbar": "^10.1.1",
"rxfire": "^6.0.3", "rxfire": "^6.0.3",
"rxjs": "~7.5.6", "rxjs": "~7.5.6",
"swiper": "^9.3.2",
"tslib": "^2.4.0", "tslib": "^2.4.0",
"uuid": "^8.3.2", "uuid": "^8.3.2",
"zone.js": "~0.11.8" "zone.js": "~0.11.8"

View File

@@ -2,6 +2,7 @@ import {ChangeDetectionStrategy, Component, OnInit, ViewChild} from '@angular/co
import {fader} from './animations'; import {fader} from './animations';
import {ScrollService} from './services/scroll.service'; import {ScrollService} from './services/scroll.service';
import {PerfectScrollbarComponent} from 'ngx-perfect-scrollbar'; import {PerfectScrollbarComponent} from 'ngx-perfect-scrollbar';
import {register} from 'swiper/element/bundle';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@@ -17,6 +18,7 @@ export class AppComponent implements OnInit {
scrollService.restoreScrollPosition$.subscribe(pos => { scrollService.restoreScrollPosition$.subscribe(pos => {
if (this.scrollbar && pos) this.scrollbar.directiveRef?.scrollTo(0, pos, 300); if (this.scrollbar && pos) this.scrollbar.directiveRef?.scrollTo(0, pos, 300);
}); });
register();
} }
public ngOnInit(): void { public ngOnInit(): void {

View File

@@ -12,6 +12,7 @@ import {TextRenderingService} from '../../songs/services/text-rendering.service'
import {PresentationBackground, Show} from '../../shows/services/show'; import {PresentationBackground, Show} from '../../shows/services/show';
import {GlobalSettings} from '../../../services/global-settings'; import {GlobalSettings} from '../../../services/global-settings';
import {ShowSongService} from '../../shows/services/show-song.service'; import {ShowSongService} from '../../shows/services/show-song.service';
import {openFullscreen} from '../../../services/fullscreen';
@Component({ @Component({
selector: 'app-monitor', selector: 'app-monitor',
@@ -45,6 +46,7 @@ export class MonitorComponent implements OnInit {
} }
public ngOnInit(): void { public ngOnInit(): void {
openFullscreen();
this.globalSettingsService.get$ this.globalSettingsService.get$
.pipe( .pipe(
debounceTime(100), debounceTime(100),

View File

@@ -30,10 +30,6 @@ export class ShowService {
map(s => map(s =>
s.shows s.shows
.sort((a, b) => a.date.toMillis() - b.date.toMillis()) .sort((a, b) => a.date.toMillis() - b.date.toMillis())
.map(_ => {
console.log(_);
return _;
})
.filter(_ => !_.archived) .filter(_ => !_.archived)
.filter(show => show.published || (show.owner === s.user?.id && !publishedOnly)) .filter(show => show.published || (show.owner === s.user?.id && !publishedOnly))
) )

View File

@@ -1,23 +1,30 @@
<div *ngIf="show$ | async as show"> <div *ngIf="show$ | async as show">
<app-card <app-card
[fullscreen]="useSwiper"
closeLink="../" closeLink="../"
heading="{{ show.showType | showType }}, {{ heading="{{ show.showType | showType }}, {{
show.date.toDate() | date: 'dd.MM.yyyy' show.date.toDate() | date: 'dd.MM.yyyy'
}} - {{ getStatus(show) }}" }} - {{ getStatus(show) }}"
> >
<p>{{show.public ? 'öffentliche' : 'geschlossene'}} Veranstaltung von <p *ngIf="!useSwiper">{{show.public ? 'öffentliche' : 'geschlossene'}} Veranstaltung von
<app-user-name [userId]="show.owner"></app-user-name> <app-user-name [userId]="show.owner"></app-user-name>
</p> </p>
<div class="head"> <div class="head">
<mat-checkbox [(ngModel)]="showText">Text anzeigen</mat-checkbox>
<div> <div>
<mat-checkbox *ngIf="!useSwiper" [(ngModel)]="showText">Text anzeigen</mat-checkbox>
</div>
<div [class.floating]="useSwiper">
<app-menu-button @fade (click)="onZoomOut()" [icon]="faZoomOut" class="btn-delete btn-icon" <app-menu-button @fade (click)="onZoomOut()" [icon]="faZoomOut" class="btn-delete btn-icon"
matTooltip="Vergrößern"></app-menu-button>
<app-menu-button @fade (click)="onZoomIn()" [icon]="faZoomIn" class="btn-delete btn-icon"
matTooltip="Verkleinern"></app-menu-button> matTooltip="Verkleinern"></app-menu-button>
<app-menu-button @fade (click)="onZoomIn()" [icon]="faZoomIn" class="btn-delete btn-icon"
matTooltip="Vergrößern"></app-menu-button>
<app-menu-button (click)="useSwiper=!useSwiper;fullscreen(useSwiper)" @fade
[icon]="useSwiper ? faFileLines : faFile" class="btn-delete btn-icon"
matTooltip="Swiper umschalten"></app-menu-button>
</div> </div>
</div> </div>
<div *ngIf="showSongs" class="song-list" cdkDropList [cdkDropListDisabled]="show.published || showText" <div *ngIf="showSongs && !useSwiper" [cdkDropListDisabled]="show.published || showText" cdkDropList
class="song-list"
[style.cursor]="!(show.published || showText) ? 'drag' : 'inherit'" [style.cursor]="!(show.published || showText) ? 'drag' : 'inherit'"
[style.font-size]="textSize + 'em'" [style.font-size]="textSize + 'em'"
(cdkDropListDropped)="drop($event, show)"> (cdkDropListDropped)="drop($event, show)">
@@ -27,19 +34,34 @@
[showId]="showId" [showId]="showId"
[showText]="showText" [showText]="showText"
[show]="show" [show]="show"
[fullscreen]="useSwiper"
[index]="i" [index]="i"
></app-song> ></app-song>
</div> </div>
</div> </div>
<swiper-container *ngIf="useSwiper" scrollbar="true">
<swiper-slide *ngFor="let song of orderedShowSongs(show); let i = index; trackBy: trackBy" [style.font-size]="textSize + 'em'"
class="song-swipe">
<app-song
[fullscreen]="true"
[index]="i"
[showId]="showId"
[showSong]="song"
[showText]="true"
[show]="show"
></app-song>
</swiper-slide>
</swiper-container>
<app-add-song <app-add-song
*ngIf="songs && !show.published" *ngIf="songs && !show.published && !useSwiper"
[show]="show" [show]="show"
[showSongs]="showSongs" [showSongs]="showSongs"
[songs]="songs" [songs]="songs"
></app-add-song> ></app-add-song>
<app-button-row> <app-button-row *ngIf="!useSwiper">
<ng-container *appRole="['leader']"> <ng-container *appRole="['leader']">
<ng-container *appOwner="show.owner"> <ng-container *appOwner="show.owner">
<app-button (click)="onArchive(true)" *ngIf="!show.archived" [icon]="faBox"> <app-button (click)="onArchive(true)" *ngIf="!show.archived" [icon]="faBox">

View File

@@ -8,6 +8,12 @@
border-bottom: 1px solid #0002; border-bottom: 1px solid #0002;
} }
.song-swipe {
margin-bottom: 20px;
min-height: calc(100vh - 60px);
}
.head { .head {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@@ -36,3 +42,10 @@
.example-list.cdk-drop-list-dragging .song-list:not(.cdk-drag-placeholder) { .example-list.cdk-drop-list-dragging .song-list:not(.cdk-drag-placeholder) {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
} }
.floating {
position: fixed;
right: 20px;
top: 12px;
z-index: 5;
}

View File

@@ -1,4 +1,4 @@
import {ChangeDetectorRef, Component, OnDestroy, OnInit} from '@angular/core'; import {ChangeDetectorRef, Component, HostListener, OnDestroy, OnInit} from '@angular/core';
import {filter, map, switchMap, tap} from 'rxjs/operators'; import {filter, map, switchMap, tap} from 'rxjs/operators';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, Router} from '@angular/router';
import {ShowService} from '../services/show.service'; import {ShowService} from '../services/show.service';
@@ -13,7 +13,9 @@ import {
faBox, faBox,
faBoxOpen, faBoxOpen,
faExternalLinkAlt, faExternalLinkAlt,
faFile,
faFileDownload, faFileDownload,
faFileLines,
faLock, faLock,
faMagnifyingGlassMinus, faMagnifyingGlassMinus,
faMagnifyingGlassPlus, faMagnifyingGlassPlus,
@@ -25,6 +27,7 @@ import {CdkDragDrop, moveItemInArray} from '@angular/cdk/drag-drop';
import {fade} from '../../../animations'; import {fade} from '../../../animations';
import {MatDialog} from '@angular/material/dialog'; import {MatDialog} from '@angular/material/dialog';
import {ArchiveDialogComponent} from '../dialog/archive-dialog/archive-dialog.component'; import {ArchiveDialogComponent} from '../dialog/archive-dialog/archive-dialog.component';
import {closeFullscreen, openFullscreen} from '../../../services/fullscreen';
@Component({ @Component({
selector: 'app-show', selector: 'app-show',
@@ -49,7 +52,10 @@ export class ShowComponent implements OnInit, OnDestroy {
public faUsers = faUsers; public faUsers = faUsers;
public faZoomIn = faMagnifyingGlassPlus; public faZoomIn = faMagnifyingGlassPlus;
public faZoomOut = faMagnifyingGlassMinus; public faZoomOut = faMagnifyingGlassMinus;
public faPage = faFile;
public faLines = faFileLines;
private subs: Subscription[] = []; private subs: Subscription[] = [];
public useSwiper = false;
public constructor( public constructor(
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
@@ -161,4 +167,34 @@ export class ShowComponent implements OnInit, OnDestroy {
public async onChange(showId: string) { public async onChange(showId: string) {
await this.router.navigateByUrl('/shows/' + showId + '/edit'); await this.router.navigateByUrl('/shows/' + showId + '/edit');
} }
public faFileLines = faFileLines;
public faFile = faFile;
@HostListener('document:keydown', ['$event'])
public handleKeyboardEvent(event: KeyboardEvent) {
const swiperEl = document.querySelector('swiper-container') as unknown as Swiper;
switch (event.code) {
case 'ArrowRight':
case 'ArrowDown':
swiperEl.swiper.slideNext();
break;
case 'ArrowLeft':
case 'ArrowUp':
swiperEl.swiper.slidePrev();
break;
}
}
public fullscreen(useSwiper: boolean) {
if (useSwiper) openFullscreen();
else closeFullscreen();
}
}
export interface Swiper {
swiper: {
slideNext(): void;
slidePrev(): void;
};
} }

View File

@@ -1,10 +1,10 @@
<div *ngIf="iSong && iSong && show"> <div *ngIf="iSong && iSong && show">
<div *ngIf="show.published" class="title published"> <div *ngIf="show.published || fullscreen" class="title published">
<div class="key">{{ iSong.key }}</div> <div class="key">{{ iSong.key }}</div>
<div>{{ iSong.title }}</div> <div>{{ iSong.title }}</div>
</div> </div>
<div *ngIf="!show.published" class="song"> <div *ngIf="!show.published && !fullscreen" class="song">
<span class="title">{{ iSong.title }}</span> <span class="title">{{ iSong.title }}</span>
<span *ngIf="!edit" class="keys"> <span *ngIf="!edit" class="keys">
<span *ngIf="iSong.keyOriginal !== iSong.key">{{ iSong.keyOriginal }}&nbsp;&nbsp;</span> <span *ngIf="iSong.keyOriginal !== iSong.key">{{ iSong.keyOriginal }}&nbsp;&nbsp;</span>

View File

@@ -19,6 +19,7 @@ export class SongComponent implements OnInit {
@Input() public showId: string | null = null; @Input() public showId: string | null = null;
@Input() public showText: boolean | null = null; @Input() public showText: boolean | null = null;
@Input() public index = -1; @Input() public index = -1;
@Input() public fullscreen = false;
public keys: string[] = []; public keys: string[] = [];
public faDelete = faTrash; public faDelete = faTrash;

View File

@@ -1,4 +1,4 @@
import {NgModule} from '@angular/core'; import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from '@angular/core';
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {ShowsRoutingModule} from './shows-routing.module'; import {ShowsRoutingModule} from './shows-routing.module';
@@ -70,5 +70,6 @@ import {MatDialogModule} from '@angular/material/dialog';
MatTooltipModule, MatTooltipModule,
MatDialogModule, MatDialogModule,
], ],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}) })
export class ShowsModule {} export class ShowsModule {}

View File

@@ -0,0 +1,13 @@
const elem = document.documentElement;
export const openFullscreen = () => {
if (elem.requestFullscreen) {
void elem.requestFullscreen();
}
};
export const closeFullscreen = () => {
if (document.exitFullscreen) {
void document.exitFullscreen();
}
};

View File

@@ -1,13 +1,13 @@
<div [class.padding]="padding" class="card"> <div [class.fullscreen]="fullscreen" [class.padding]="padding" class="card">
<button <button
*ngIf="closeLink" *ngIf="closeLink && !fullscreen"
[routerLink]="closeLink" [routerLink]="closeLink"
class="btn-close" class="btn-close"
mat-icon-button mat-icon-button
> >
<fa-icon [icon]="closeIcon"></fa-icon> <fa-icon [icon]="closeIcon"></fa-icon>
</button> </button>
<div *ngIf="heading" class="heading">{{ heading }}</div> <div *ngIf="heading && !fullscreen" class="heading">{{ heading }}</div>
<div *ngIf="subheading" class="subheading">{{ subheading }}</div> <div *ngIf="subheading && !fullscreen" class="subheading">{{ subheading }}</div>
<ng-content></ng-content> <ng-content></ng-content>
</div> </div>

View File

@@ -23,6 +23,20 @@
} }
box-sizing: border-box; box-sizing: border-box;
&.fullscreen {
border-radius: 0;
background: #ffff;
margin: 0;
color: #000;
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
width: unset;
z-index: 10;
}
} }
.heading { .heading {

View File

@@ -12,4 +12,5 @@ export class CardComponent {
@Input() public subheading: string | null = null; @Input() public subheading: string | null = null;
@Input() public closeLink: string | null = null; @Input() public closeLink: string | null = null;
@Input() public closeIcon = faTimes; @Input() public closeIcon = faTimes;
@Input() public fullscreen = false;
} }

View File

@@ -45,9 +45,7 @@
@media screen and (max-width: 860px) { @media screen and (max-width: 860px) {
margin-bottom: 10px; margin-bottom: 10px;
} }
border-left: 3px solid #0002;
padding-left: 5px;
margin-left: -8px;
} }
.chorus { .chorus {