Cleaning up navigation height & edit page

This commit is contained in:
Zoe Roux 2020-10-31 22:57:16 +01:00
parent 3de2e74ecf
commit f764d8bbae
14 changed files with 153 additions and 84 deletions

View File

@ -38,7 +38,10 @@
"./node_modules/jquery/dist/jquery.min.js", "./node_modules/jquery/dist/jquery.min.js",
"./node_modules/bootstrap/dist/js/bootstrap.bundle.min.js", "./node_modules/bootstrap/dist/js/bootstrap.bundle.min.js",
"./node_modules/hls.js/dist/hls.js" "./node_modules/hls.js/dist/hls.js"
] ],
"stylePreprocessorOptions": {
"includePaths": ["src"]
}
}, },
"configurations": { "configurations": {
"production": { "production": {

View File

@ -1,4 +1,4 @@
<header id="nav" style="height: 68px;"> <header id="nav">
<div class="fixed-top"> <div class="fixed-top">
<nav id="toolbar" class="navbar navbar-dark bg-secondary"> <nav id="toolbar" class="navbar navbar-dark bg-secondary">
<a class="navbar-brand nav-item ml-3" routerLink="/"> <a class="navbar-brand nav-item ml-3" routerLink="/">
@ -7,7 +7,7 @@
<ul class="navbar-nav flex-row"> <ul class="navbar-nav flex-row">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" routerLink="/browse" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">All</a> <a class="nav-link" routerLink="/browse" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">All</a>
</li> </li>
<li class="nav-item" *ngFor="let library of this.libraries"> <li class="nav-item" *ngFor="let library of this.libraries">
<a class="nav-link" routerLink="/browse/{{library.slug}}" routerLinkActive="active">{{library.name}}</a> <a class="nav-link" routerLink="/browse/{{library.slug}}" routerLinkActive="active">{{library.name}}</a>
@ -47,6 +47,6 @@
</div> </div>
</header> </header>
<main> <main id="main">
<router-outlet></router-outlet> <router-outlet></router-outlet>
</main> </main>

View File

@ -1,6 +1,12 @@
@import "~bootstrap/scss/functions"; @import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables"; @import "~bootstrap/scss/variables";
@import "~bootstrap/scss/mixins/breakpoints"; @import "~bootstrap/scss/mixins/breakpoints";
@import "variables";
#toolbar
{
height: $nav-bar-height;
}
.navbar .navbar
{ {
@ -111,29 +117,27 @@ input::-webkit-search-cancel-button
main main
{ {
max-height: calc(100vh - 68px) !important; margin-top: $nav-bar-height;
padding-top: 4px;
max-height: calc(100vh - #{$nav-bar-height});
display: block;
overflow-y: auto;
scrollbar-color: #999 transparent;
position: relative;
&:last-child &::-webkit-scrollbar
{ {
display: block; width: 8px;
overflow-y: auto; background: transparent;
}
scrollbar-color: #999 transparent; &::-webkit-scrollbar-thumb
{
background-color: #999;
&::-webkit-scrollbar &:host-context(.hoverEnabled) &:hover
{ {
width: 8px; background-color: rgb(134, 127, 127);
background: transparent;
}
&::-webkit-scrollbar-thumb
{
background-color: #999;
&:host-context(.hoverEnabled) &:hover
{
background-color: rgb(134, 127, 127);
}
} }
} }
} }

View File

@ -1,6 +1,6 @@
@import "../../../../node_modules/bootstrap/scss/functions"; @import "~bootstrap/scss/functions";
@import "../../../../node_modules/bootstrap/scss/variables"; @import "~bootstrap/scss/variables";
@import "../../../../node_modules/bootstrap/scss/mixins/breakpoints"; @import "~bootstrap/scss/mixins/breakpoints";
.root .root
{ {

View File

@ -1,6 +1,6 @@
@import "../../../../node_modules/bootstrap/scss/functions"; @import "~bootstrap/scss/functions";
@import "../../../../node_modules/bootstrap/scss/variables"; @import "~bootstrap/scss/variables";
@import "../../../../node_modules/bootstrap/scss/mixins/breakpoints"; @import "~bootstrap/scss/mixins/breakpoints";
button button
{ {

View File

@ -1,6 +1,6 @@
@import "../../../../node_modules/bootstrap/scss/functions"; @import "~bootstrap/scss/functions";
@import "../../../../node_modules/bootstrap/scss/variables"; @import "~bootstrap/scss/variables";
@import "../../../../node_modules/bootstrap/scss/mixins/breakpoints"; @import "~bootstrap/scss/mixins/breakpoints";
.container .container
{ {

View File

@ -1,6 +1,6 @@
@import "../../../../node_modules/bootstrap/scss/functions"; @import "~bootstrap/scss/functions";
@import "../../../../node_modules/bootstrap/scss/variables"; @import "~bootstrap/scss/variables";
@import "../../../../node_modules/bootstrap/scss/mixins/breakpoints"; @import "~bootstrap/scss/mixins/breakpoints";
.people-container .people-container
{ {

View File

@ -53,10 +53,14 @@
{{genre.name}} {{genre.name}}
<mat-icon matChipRemove>cancel</mat-icon> <mat-icon matChipRemove>cancel</mat-icon>
</mat-chip> </mat-chip>
<input #genreInput placeholder="New genre..." [matChipInputFor]="genreList" (matChipInputTokenEnd)="addGenre($event)" <input #genreInput placeholder="New genre..."
[formControl]="genreForm"
[matChipInputFor]="genreList"
(matChipInputTokenEnd)="addGenre($event); $event.input.value = null;"
[matAutocomplete]="genreAuto" /> [matAutocomplete]="genreAuto" />
<mat-autocomplete #genreAuto="matAutocomplete" (optionSelected)="autocompleteGenre($event)"> <mat-autocomplete #genreAuto="matAutocomplete"
<mat-option *ngFor="let genre of this.allGenres" [value]="genre"> (optionSelected)="autocompleteGenre($event); genreInput.value = null;">
<mat-option *ngFor="let genre of this.filteredGenres | async" [value]="genre">
{{genre.name}} {{genre.name}}
</mat-option> </mat-option>
</mat-autocomplete> </mat-autocomplete>
@ -73,8 +77,8 @@
<input matInput [value]="this.show.studio?.name" <input matInput [value]="this.show.studio?.name"
(input)="this.show.studio = {id: 0, slug: null, name: $event.target.value};" (input)="this.show.studio = {id: 0, slug: null, name: $event.target.value};"
[matAutocomplete]="studioAuto" name="studio"> [matAutocomplete]="studioAuto" name="studio">
<mat-autocomplete #studioAuto="matAutocomplete" (optionSelected)="this.show.studio = $event.option.value"> <mat-autocomplete #studioAuto="matAutocomplete" (optionSelected)="this.show.studio = $event.option.value">
<mat-option *ngFor="let studio of this.allStudios" [value]="studio"> <mat-option *ngFor="let studio of this.filteredStudios | async" [value]="studio">
{{studio.name}} {{studio.name}}
</mat-option> </mat-option>
</mat-autocomplete> </mat-autocomplete>

View File

@ -1,6 +1,6 @@
@import "../../../../node_modules/bootstrap/scss/functions"; @import "~bootstrap/scss/functions";
@import "../../../../node_modules/bootstrap/scss/variables"; @import "~bootstrap/scss/variables";
@import "../../../../node_modules/bootstrap/scss/mixins/breakpoints"; @import "~bootstrap/scss/mixins/breakpoints";
.provider .provider
{ {

View File

@ -1,53 +1,84 @@
import {Component, ElementRef, Inject, ViewChild} from '@angular/core'; import { Component, Inject, OnInit, ViewChild } from "@angular/core";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; import { FormControl } from "@angular/forms";
import {HttpClient} from "@angular/common/http"; import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog";
import {Show} from "../../models/resources/show"; import { HttpClient } from "@angular/common/http";
import {Genre} from "../../models/resources/genre"; import { Page } from "../../models/page";
import {MatChipInputEvent} from "@angular/material/chips"; import { Show } from "../../models/resources/show";
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete"; import { Genre } from "../../models/resources/genre";
import {Observable, of} from "rxjs"; import { MatChipInputEvent } from "@angular/material/chips";
import {tap} from "rxjs/operators"; import { MatAutocompleteSelectedEvent } from "@angular/material/autocomplete";
import {Studio} from "../../models/resources/studio"; import { Observable, of} from "rxjs";
import {Provider} from "../../models/provider"; import { catchError, filter, map, mergeAll, tap } from "rxjs/operators";
import {MatSnackBar} from "@angular/material/snack-bar"; import { Studio } from "../../models/resources/studio";
import {ShowGridComponent} from "../../components/show-grid/show-grid.component"; import { Provider } from "../../models/provider";
import { MatSnackBar } from "@angular/material/snack-bar";
import { ShowGridComponent } from "../../components/show-grid/show-grid.component";
import { GenreService, StudioService } from "../../services/api.service";
@Component({ @Component({
selector: 'app-metadata-edit', selector: 'app-metadata-edit',
templateUrl: './metadata-edit.component.html', templateUrl: './metadata-edit.component.html',
styleUrls: ['./metadata-edit.component.scss'] styleUrls: ['./metadata-edit.component.scss']
}) })
export class MetadataEditComponent export class MetadataEditComponent implements OnInit
{ {
@ViewChild("genreInput") private genreInput: ElementRef<HTMLInputElement>; studioForm: FormControl = new FormControl();
public allGenres: Genre[]; filteredStudios: Observable<Studio[]>;
public allStudios: Studio[];
genreForm: FormControl = new FormControl();
filteredGenres: Observable<Genre[]>;
@ViewChild("identifyGrid") private identifyGrid: ShowGridComponent; @ViewChild("identifyGrid") private identifyGrid: ShowGridComponent;
private identifying: Observable<Show[]>; private identifying: Observable<Show[]>;
private identifiedShows: [string, Show[]]; private identifiedShows: [string, Show[]];
public providers: Provider[]; public providers: Provider[] = [];
public metadataChanged: boolean = false; public metadataChanged: boolean = false;
constructor(public dialogRef: MatDialogRef<MetadataEditComponent>, @Inject(MAT_DIALOG_DATA) public show: Show, private http: HttpClient, private snackBar: MatSnackBar) constructor(public dialogRef: MatDialogRef<MetadataEditComponent>,
@Inject(MAT_DIALOG_DATA) public show: Show,
private http: HttpClient,
private studioApi: StudioService,
private genreApi: GenreService,
private snackBar: MatSnackBar)
{ {
this.http.get<Genre[]>("/api/genres").subscribe(result => this.http.get<Page<Provider>>("/api/providers").subscribe(result =>
{ {
this.allGenres = result; this.providers = result.items;
});
this.http.get<Studio[]>("/api/studios").subscribe(result =>
{
this.allStudios = result;
});
this.http.get<Provider[]>("/api/providers").subscribe(result =>
{
this.providers = result;
}); });
this.reIdentify(this.show.title); this.reIdentify(this.show.title);
} }
ngOnInit()
{
this.filteredGenres = this.genreForm.valueChanges
.pipe(
filter(x => x),
map(x => typeof x === "string" ? x : x.name),
map(x => this.genreApi.search(x)),
mergeAll(),
catchError(x =>
{
console.log(x);
return [];
})
);
this.filteredStudios = this.studioForm.valueChanges
.pipe(
filter(x => x),
map(x => typeof x === "string" ? x : x.name),
map(x => this.studioApi.search(x)),
mergeAll(),
catchError(x =>
{
console.log(x);
return [];
})
);
}
apply(): void apply(): void
{ {
if (this.metadataChanged) if (this.metadataChanged)
@ -59,7 +90,7 @@ export class MetadataEditComponent
}, },
() => () =>
{ {
this.snackBar.open("An unknown error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 }); this.snackBar.open("An unknown error occurred.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
} }
); );
this.dialogRef.close(this.show); this.dialogRef.close(this.show);
@ -109,7 +140,6 @@ export class MetadataEditComponent
autocompleteGenre(event: MatAutocompleteSelectedEvent): void autocompleteGenre(event: MatAutocompleteSelectedEvent): void
{ {
this.show.genres.push(event.option.value); this.show.genres.push(event.option.value);
this.genreInput.nativeElement.value = '';
} }
identityShow(name: string): Observable<Show[]> identityShow(name: string): Observable<Show[]>

View File

@ -1,6 +1,7 @@
@import "../../../../node_modules/bootstrap/scss/functions"; @import "~bootstrap/scss/functions";
@import "../../../../node_modules/bootstrap/scss/variables"; @import "~bootstrap/scss/variables";
@import "../../../../node_modules/bootstrap/scss/mixins/breakpoints"; @import "~bootstrap/scss/mixins/breakpoints";
@import "variables";
a a
{ {

View File

@ -1,4 +1,4 @@
import { Component, HostListener, OnInit } from "@angular/core"; import { AfterViewInit, Component, HostListener, OnInit } from "@angular/core";
import { MatSnackBar } from "@angular/material/snack-bar"; import { MatSnackBar } from "@angular/material/snack-bar";
import { DomSanitizer, Title } from "@angular/platform-browser"; import { DomSanitizer, Title } from "@angular/platform-browser";
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, Router} from '@angular/router';
@ -17,7 +17,7 @@ import {People} from "../../models/resources/people";
templateUrl: './show-details.component.html', templateUrl: './show-details.component.html',
styleUrls: ['./show-details.component.scss'] styleUrls: ['./show-details.component.scss']
}) })
export class ShowDetailsComponent implements OnInit export class ShowDetailsComponent implements AfterViewInit
{ {
show: Show; show: Show;
seasons: Season[]; seasons: Season[];
@ -25,6 +25,7 @@ export class ShowDetailsComponent implements OnInit
episodes: Page<Episode>[] = []; episodes: Page<Episode>[] = [];
people: Page<People>; people: Page<People>;
private scrollZone: HTMLElement;
private toolbar: HTMLElement; private toolbar: HTMLElement;
private backdrop: HTMLElement; private backdrop: HTMLElement;
@ -65,23 +66,30 @@ export class ShowDetailsComponent implements OnInit
this.getEpisodes(this.season);}); this.getEpisodes(this.season);});
} }
ngOnInit() ngAfterViewInit()
{ {
this.scrollZone = document.getElementById("main");
this.toolbar = document.getElementById("toolbar"); this.toolbar = document.getElementById("toolbar");
this.backdrop = document.getElementById("backdrop"); this.backdrop = document.getElementById("backdrop");
this.toolbar.setAttribute("style", `background-color: rgba(0, 0, 0, 0) !important`); this.toolbar.setAttribute("style", `background-color: rgba(0, 0, 0, 0) !important`);
this.scrollZone.style.marginTop = "0";
this.scrollZone.style.maxHeight = "100vh";
this.scrollZone.addEventListener("scroll", () => this.scroll());
} }
ngOnDestroy() ngOnDestroy()
{ {
this.title.setTitle("Kyoo"); this.title.setTitle("Kyoo");
this.toolbar.setAttribute("style", `background-color: #000000 !important`); this.toolbar.setAttribute("style", `background-color: #000000 !important`);
this.scrollZone.style.marginTop = null;
this.scrollZone.style.maxHeight = null;
this.scrollZone.removeEventListener("scroll", () => this.scroll());
} }
@HostListener("window:scroll")
scroll() scroll()
{ {
let opacity: number = 2 * window.scrollY / this.backdrop.clientHeight; console.log("scroll");
let opacity: number = 2 * this.scrollZone.scrollTop / this.backdrop.clientHeight;
this.toolbar.setAttribute("style", `background-color: rgba(0, 0, 0, ${opacity}) !important`); this.toolbar.setAttribute("style", `background-color: rgba(0, 0, 0, ${opacity}) !important`);
}; };

View File

@ -2,6 +2,7 @@ import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http"; import {HttpClient} from "@angular/common/http";
import {Observable} from "rxjs" import {Observable} from "rxjs"
import {Page} from "../models/page"; import {Page} from "../models/page";
import { Genre } from "../models/resources/genre";
import {IResource} from "../models/resources/resource"; import {IResource} from "../models/resources/resource";
import {Library} from "../models/resources/library"; import {Library} from "../models/resources/library";
import {LibraryItem} from "../models/resources/library-item"; import {LibraryItem} from "../models/resources/library-item";
@ -144,6 +145,23 @@ export class PeopleService extends CrudApi<People>
} }
} }
@Injectable({
providedIn: 'root'
})
export class GenreService extends CrudApi<Genre>
{
constructor(client: HttpClient)
{
super(client, "genres");
}
getFromShow(show: string | number, args?: ApiArgs): Observable<Page<Genre>>
{
return this.client.get<Page<Genre>>(`/api/shows/${show}/genres${this.ArgsAsQuery(args)}`)
.pipe(map(x => Object.assign(new Page<Genre>(), x)));
}
}
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })

1
src/variables.scss Normal file
View File

@ -0,0 +1 @@
$nav-bar-height: 64px;