Enhanced head bar no 3

This commit is contained in:
Sean Engelhardt 2018-11-06 15:35:51 +01:00
parent 3fde4dc81f
commit 41f2adbc33
18 changed files with 118 additions and 13419 deletions

13205
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout'; import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout';
import { BehaviorSubject } from 'rxjs';
/** /**
* Viewport Service * Viewport Service
@ -8,16 +9,15 @@ import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/l
* *
* ## Example: * ## Example:
* *
* Provide the service via constructor and just use it like * Provide the service via constructor and just use it like this this
* *
* ```html * ```html
* <div *ngIf="!vp.isMobile">Will only be shown of not mobile</div> * <div *ngIf="!vp.isMobile">Will only be shown of not mobile</div>
* ``` * ```
* or * or
* ```ts * ```ts
* if (this.vp.isMobile) { * this.vp.isMobileSubject.subscribe(mobile => (this.isMobile = mobile));
* ... *
* }
* ``` * ```
*/ */
@Injectable({ @Injectable({
@ -25,9 +25,15 @@ import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/l
}) })
export class ViewportService { export class ViewportService {
/** /**
* True if Viewport equals mobile or small resolution. * Simple boolean to determine whether the client is in mobile view or not
* Use in HTML with automatic change detection
*/ */
private _isMobile = false; public isMobile: boolean;
/**
* Returns a subject that contains whether the viewport os mobile or not
*/
public isMobileSubject = new BehaviorSubject<boolean>(false);
/** /**
* Get the BreakpointObserver * Get the BreakpointObserver
@ -45,14 +51,12 @@ export class ViewportService {
.observe([Breakpoints.Small, Breakpoints.HandsetPortrait]) .observe([Breakpoints.Small, Breakpoints.HandsetPortrait])
.subscribe((state: BreakpointState) => { .subscribe((state: BreakpointState) => {
if (state.matches) { if (state.matches) {
this._isMobile = true; this.isMobile = true;
this.isMobileSubject.next(true);
} else { } else {
this._isMobile = false; this.isMobile = false;
this.isMobileSubject.next(false);
} }
}); });
} }
public get isMobile(): boolean {
return this._isMobile;
}
} }

View File

@ -1,58 +1,51 @@
<mat-toolbar color='primary' [ngClass]="{'during-scroll': stickyToolbar}"> <mat-toolbar color='primary' *ngIf="!vp.isMobile"></mat-toolbar>
<mat-toolbar color='primary' class="sticky-toolbar">
<mat-toolbar-row [ngClass]="{'hidden-bar': stickyToolbar}"> <div class="toolbar-left">
<!-- Nav menu --> <!-- Nav menu -->
<button mat-icon-button *ngIf="vp.isMobile && nav" (click)='clickHamburgerMenu()'> <button mat-icon-button class="on-transition-fade" *ngIf="vp.isMobile && nav" (click)='clickHamburgerMenu()'>
<mat-icon>menu</mat-icon> <mat-icon>menu</mat-icon>
</button> </button>
</mat-toolbar-row>
<mat-toolbar-row [ngClass]="{'during-scroll': stickyToolbar}"> <!-- Exit / Back button -->
<div class="toolbar-left on-transition-fade"> <button mat-icon-button class="on-transition-fade" *ngIf="!nav && !editMode" (click)="onBackButton()">
<!-- Fab Button "Plus" --> <mat-icon>arrow_back</mat-icon>
<button mat-fab class="head-button" *ngIf="plusButton && !editMode" (click)=clickPlusButton()> </button>
<mat-icon>add</mat-icon>
</button>
<!-- Exit / Back button --> <!-- Cancel edit button -->
<button mat-icon-button class="on-transition-fade" *ngIf="backButton && !editMode" (click)="onBackButton()"> <button mat-icon-button class="on-transition-fade" *ngIf="editMode" (click)="sendMainEvent()">
<mat-icon>arrow_back</mat-icon> <mat-icon>close</mat-icon>
</button> </button>
<!-- Cancel edit button --> <div class="toolbar-left-text on-transition-fade">
<button mat-icon-button class="on-transition-fade" *ngIf="editMode" (click)="toggleEditMode()"> <!-- Title slot -->
<mat-icon>close</mat-icon> <ng-content select=".title-slot"></ng-content>
</button>
<div class="toolbar-left-text">
<!-- Title slot -->
<ng-content select=".title-slot"></ng-content>
</div>
</div> </div>
<div class="toolbar-right on-transition-fade" [ngClass]="{'toolbar-right-scroll': stickyToolbar, 'toolbar-right-top': !stickyToolbar}"> </div>
<div class=spacer></div>
<div class="toolbar-right">
<!-- Extra controls slot --> <!-- Extra controls slot -->
<div class="extra-controls-wrapper on-transition-fade"> <div class="extra-controls-wrapper on-transition-fade">
<ng-content select=".extra-controls-slot"></ng-content> <ng-content select=".extra-controls-slot"></ng-content>
</div>
<!-- Save button -->
<button mat-button *ngIf="editMode" (click)="save()">
<strong translate class="upper">Save</strong>
</button>
<!-- Edit button-->
<button mat-icon-button *ngIf="!editMode && allowEdit" (click)="toggleEditMode()">
<mat-icon>{{ editIcon }}</mat-icon>
</button>
<!-- Menu button slot -->
<ng-content *ngIf="!editMode" select=".menu-slot"></ng-content>
</div> </div>
</mat-toolbar-row> <!-- Main action button - desktop -->
<button mat-mini-fab color="accent" class="on-transition-fade" *ngIf="mainButton && !editMode && !vp.isMobile"
(click)="sendMainEvent()">
<mat-icon>{{ buttonIcon }}</mat-icon>
</button>
<!-- Save button -->
<button mat-button *ngIf="editMode" (click)="save()">
<strong translate class="upper">Save</strong>
</button>
<!-- Menu button slot -->
<ng-content class="on-transition-fade" *ngIf="!editMode" select=".menu-slot"></ng-content>
</div>
</mat-toolbar> </mat-toolbar>
<!-- fake mat-toolbar to keep the distance when the real one gets a fixed position --> <!-- Main action button - mobile-->
<div class="fake-bar" [ngClass]="{'hidden-bar': !stickyToolbar}"></div> <button mat-fab class="head-button on-transition-fade" *ngIf="mainButton && !editMode && vp.isMobile" (click)=sendMainEvent()>
<mat-icon>{{ buttonIcon }}</mat-icon>
</button>

View File

@ -1,46 +1,26 @@
.during-scroll { .head-button {
position: fixed; position: fixed;
z-index: 1; right: 25px;
bottom: 25px;
}
.sticky-toolbar {
position: -webkit-sticky;
position: sticky;
top: 0px;
z-index: 2;
} }
.toolbar-left { .toolbar-left {
position: absolute; display: contents;
display: inherit;
.head-button {
bottom: -30px;
}
.toolbar-left-text { .toolbar-left-text {
margin: auto 0 5px 20px; display: initial;
margin-left: 10px;
} }
} }
.toolbar-right { .toolbar-right {
display: inherit; display: contents;
}
.toolbar-right-scroll {
position: fixed;
right: 30px; // fixed and absolute somehow have different ideas of distance
}
.toolbar-right-top {
position: absolute;
right: 15px;
}
// to hide the first mat-toolbar-row while scrolling and the fake-bar while on top
.hidden-bar {
display: none;
position: inline;
}
// fake bar to simulate the size of the other one, show it when the position changes to fixed
.fake-bar {
width: 100%;
height: 120px; // height of two normal mat-toolbars
z-index: -1;
} }
.extra-controls-wrapper { .extra-controls-wrapper {

View File

@ -1,8 +1,6 @@
import { Component, Input, Output, EventEmitter, OnInit, NgZone } from '@angular/core'; import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { Router, ActivatedRoute } from '@angular/router'; import { Router, ActivatedRoute } from '@angular/router';
import { ScrollDispatcher, CdkScrollable } from '@angular/cdk/scrolling';
import { map } from 'rxjs/operators';
import { ViewportService } from '../../../core/services/viewport.service'; import { ViewportService } from '../../../core/services/viewport.service';
import { MainMenuService } from '../../../core/services/main-menu.service'; import { MainMenuService } from '../../../core/services/main-menu.service';
@ -21,10 +19,24 @@ import { MainMenuService } from '../../../core/services/main-menu.service';
* *
* ```html * ```html
* <os-head-bar * <os-head-bar
* appName="Files" * [nav]="false"
* plusButton=true * [mainButton]="opCanEdit()"
* (plusButtonClicked)=onPlusButton() * [buttonIcon]="edit"
* (ellipsisMenuItem)=onEllipsisItem($event)> * [editMode]="editMotion"
* (mainEvent)="setEditMode(!editMotion)"
* (saveEvent)="saveMotion()">
*
* <!-- Title -->
* <div class="title-slot">
* My Component Title
* </div>
*
* <!-- Menu -->
* <div class="menu-slot">
* <button type="button" mat-icon-button [matMenuTriggerFor]="myComponentMenu">
* <mat-icon>more_vert</mat-icon>
* </button>
* </div>
* </os-head-bar> * </os-head-bar>
* ``` * ```
*/ */
@ -33,12 +45,7 @@ import { MainMenuService } from '../../../core/services/main-menu.service';
templateUrl: './head-bar.component.html', templateUrl: './head-bar.component.html',
styleUrls: ['./head-bar.component.scss'] styleUrls: ['./head-bar.component.scss']
}) })
export class HeadBarComponent implements OnInit { export class HeadBarComponent {
/**
* determine weather the toolbar should be sticky or not
*/
public stickyToolbar = false;
/** /**
* Determine if the the navigation "hamburger" icon should be displayed in mobile mode * Determine if the the navigation "hamburger" icon should be displayed in mobile mode
*/ */
@ -46,16 +53,10 @@ export class HeadBarComponent implements OnInit {
public nav = true; public nav = true;
/** /**
* Show or hide edit features * Custom icon if necessary
*/ */
@Input() @Input()
public allowEdit = false; public buttonIcon = 'add';
/**
* Custom edit icon if necessary
*/
@Input()
public editIcon = 'edit';
/** /**
* Determine edit mode * Determine edit mode
@ -64,16 +65,10 @@ export class HeadBarComponent implements OnInit {
public editMode = false; public editMode = false;
/** /**
* Determine if there should be a plus button. * Determine if there should be the main action button
*/ */
@Input() @Input()
public plusButton = false; public mainButton = false;
/**
* Determine if there should be a back button.
*/
@Input()
public backButton = false;
/** /**
* Set to true if the component should use location.back instead * Set to true if the component should use location.back instead
@ -83,16 +78,10 @@ export class HeadBarComponent implements OnInit {
public goBack = false; public goBack = false;
/** /**
* Emit a signal to the parent component if the plus button was clicked * Emit a signal to the parent component if the main button was clicked
*/ */
@Output() @Output()
public plusButtonClicked = new EventEmitter<boolean>(); public mainEvent = new EventEmitter<void>();
/**
* Sends a signal if a detail view should be edited or editing should be canceled
*/
@Output()
public editEvent = new EventEmitter<boolean>();
/** /**
* Sends a signal if a detail view should be saved * Sends a signal if a detail view should be saved
@ -105,8 +94,6 @@ export class HeadBarComponent implements OnInit {
*/ */
public constructor( public constructor(
public vp: ViewportService, public vp: ViewportService,
private scrollDispatcher: ScrollDispatcher,
private ngZone: NgZone,
private menu: MainMenuService, private menu: MainMenuService,
private router: Router, private router: Router,
private route: ActivatedRoute, private route: ActivatedRoute,
@ -116,8 +103,8 @@ export class HeadBarComponent implements OnInit {
/** /**
* Emits a signal to the parent if * Emits a signal to the parent if
*/ */
public clickPlusButton(): void { public sendMainEvent(): void {
this.plusButtonClicked.emit(true); this.mainEvent.next();
} }
/** /**
@ -127,20 +114,11 @@ export class HeadBarComponent implements OnInit {
this.menu.toggleMenu(); this.menu.toggleMenu();
} }
/**
* Toggle edit mode and send a signal to listeners
*/
public toggleEditMode(): void {
this.editEvent.next(!this.editMode);
}
/** /**
* Send a save signal and set edit mode * Send a save signal and set edit mode
*/ */
public save(): void { public save(): void {
if (this.editMode) { this.saveEvent.next(true);
this.saveEvent.next(true);
}
} }
/** /**
@ -154,36 +132,4 @@ export class HeadBarComponent implements OnInit {
this.router.navigate(['../'], { relativeTo: this.route }); this.router.navigate(['../'], { relativeTo: this.route });
} }
} }
/**
* Init function. Subscribe to the scrollDispatcher and decide when to set the top bar to fixed
*
* Not working for now.
*/
public ngOnInit(): void {
this.scrollDispatcher
.scrolled()
.pipe(map((event: CdkScrollable) => this.getScrollPosition(event)))
.subscribe(scrollTop => {
this.ngZone.run(() => {
if (scrollTop > 60) {
this.stickyToolbar = true;
} else {
this.stickyToolbar = false;
}
});
});
}
/**
* returns the scroll position
* @param event
*/
public getScrollPosition(event: CdkScrollable): number {
if (event) {
return event.getElementRef().nativeElement.scrollTop;
} else {
return window.scrollY;
}
}
} }

View File

@ -1,4 +1,4 @@
<os-head-bar plusButton=true (plusButtonClicked)=onPlusButton()> <os-head-bar [mainButton]="true" (mainEvent)=onPlusButton()>
<!-- Title --> <!-- Title -->
<div class="title-slot"> <div class="title-slot">
<h2 translate>Agenda</h2> <h2 translate>Agenda</h2>

View File

@ -1,4 +1,4 @@
<os-head-bar [backButton]=true [goBack]=true> <os-head-bar [nav]=false [goBack]=true>
<div class="title-slot"> <div class="title-slot">
<h2 translate>Legal Notice</h2> <h2 translate>Legal Notice</h2>
</div> </div>

View File

@ -1,4 +1,4 @@
<os-head-bar [backButton]=true [goBack]=true> <os-head-bar [nav]=false [goBack]=true>
<div class="title-slot"> <div class="title-slot">
<h2 translate>Privacy Policy</h2> <h2 translate>Privacy Policy</h2>
</div> </div>

View File

@ -1,17 +1,8 @@
<os-head-bar [nav]="false" [backButton]=true [allowEdit]="false"> <os-head-bar [nav]="false" [mainButton]="true" (mainEvent)="onPlusButton()">
<!-- Title --> <!-- Title -->
<div class="title-slot"> <div class="title-slot">
<h2 translate>Categories</h2> <h2 translate>Categories</h2>
</div> </div>
<!-- Use the menu slot for an add button -->
<div class="menu-slot">
<button type="button" mat-icon-button (click)="onPlusButton()">
<mat-icon>add</mat-icon>
</button>
</div>
</os-head-bar> </os-head-bar>
<div class="custom-table-header"></div> <div class="custom-table-header"></div>

View File

@ -1,8 +1,14 @@
<ng-container *ngIf="vp.isMobile ; then mobileView; else desktopView"></ng-container> <ng-container *ngIf="vp.isMobile ; then mobileView; else desktopView"></ng-container>
<ng-template #title><ng-content select=".meta-text-block-title"></ng-content></ng-template> <ng-template #title>
<ng-template #content><ng-content select=".meta-text-block-content"></ng-content></ng-template> <ng-content select=".meta-text-block-title"></ng-content>
<ng-template #actionRow><ng-content select=".meta-text-block-action-row"></ng-content></ng-template> </ng-template>
<ng-template #content>
<ng-content select=".meta-text-block-content"></ng-content>
</ng-template>
<ng-template #actionRow>
<ng-content select=".meta-text-block-action-row"></ng-content>
</ng-template>
<ng-template #mobileView> <ng-template #mobileView>
<mat-expansion-panel> <mat-expansion-panel>
@ -42,4 +48,3 @@
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
</ng-template> </ng-template>

View File

@ -1,17 +1,8 @@
<os-head-bar [nav]="false" [backButton]=true [allowEdit]="false"> <os-head-bar [nav]="false" [mainButton]="true" (mainEvent)="onPlusButton()">
<!-- Title --> <!-- Title -->
<div class="title-slot"> <div class="title-slot">
<h2 translate>Comments</h2> <h2 translate>Comments</h2>
</div> </div>
<!-- Use the menu slot for an add button -->
<div class="menu-slot">
<button type="button" mat-icon-button (click)="onPlusButton()">
<mat-icon>add</mat-icon>
</button>
</div>
</os-head-bar> </os-head-bar>
<div class="head-spacer"></div> <div class="head-spacer"></div>

View File

@ -1,4 +1,4 @@
<os-head-bar [nav]="false" [backButton]=true [allowEdit]="opCanEdit()" [editMode]="editMotion" (editEvent)="setEditMode($event)" <os-head-bar [mainButton]="opCanEdit()" buttonIcon="edit" [nav]="false" [editMode]="editMotion" (mainEvent)="setEditMode(!editMotion)"
(saveEvent)="saveMotion()"> (saveEvent)="saveMotion()">
<!-- Title --> <!-- Title -->

View File

@ -1,4 +1,4 @@
<os-head-bar plusButton=true (plusButtonClicked)=onPlusButton()> <os-head-bar [mainButton]="true" (mainEvent)=onPlusButton()>
<!-- Title --> <!-- Title -->
<div class="title-slot"> <div class="title-slot">
<h2 translate>Motions</h2> <h2 translate>Motions</h2>

View File

@ -1,4 +1,4 @@
<os-head-bar [nav]="false" [backButton]=true [allowEdit]="false"> <os-head-bar [nav]="false" [mainButton]="true" (mainEvent)="onPlusButton()">
<!-- Title --> <!-- Title -->
<div class="title-slot"> <div class="title-slot">
@ -7,10 +7,6 @@
<!-- Use the menu slot for an add button --> <!-- Use the menu slot for an add button -->
<div class="menu-slot"> <div class="menu-slot">
<button type="button" mat-icon-button (click)="onPlusButton()">
<mat-icon>add</mat-icon>
</button>
<button type="button" mat-icon-button [matMenuTriggerFor]="commentMenu"> <button type="button" mat-icon-button [matMenuTriggerFor]="commentMenu">
<mat-icon>more_vert</mat-icon> <mat-icon>more_vert</mat-icon>
</button> </button>

View File

@ -1,5 +1,4 @@
<os-head-bar [nav]="false" [backButton]=true [allowEdit]="true" [editMode]="editTag" editIcon="add" (editEvent)="setEditMode($event)" <os-head-bar [mainButton]="true" [nav]="true" [editMode]="editTag" (mainEvent)="setEditMode(!editTag)" (saveEvent)="saveTag()">
(saveEvent)="saveTag()">
<!-- Title --> <!-- Title -->
<div class="title-slot"> <div class="title-slot">

View File

@ -1,5 +1,4 @@
<os-head-bar [nav]="false" [backButton]=true [allowEdit]="true" [editMode]="editGroup" editIcon="add" (editEvent)="setEditMode($event)" <os-head-bar [mainButton]="true" [nav]="false" [editMode]="editGroup" (mainEvent)="setEditMode(!editGroup)" (saveEvent)="saveGroup()">
(saveEvent)="saveGroup()">
<!-- Title --> <!-- Title -->
<div class="title-slot"> <div class="title-slot">

View File

@ -1,4 +1,4 @@
<os-head-bar [nav]="false" [backButton]=true [allowEdit]="isAllowed('manage')" [editMode]="editUser" (editEvent)="setEditMode($event)" <os-head-bar [mainButton]="isAllowed('manage')" buttonIcon="edit" [nav]="false" [editMode]="editUser" (mainEvent)="setEditMode(!editUser)"
(saveEvent)="saveUser()"> (saveEvent)="saveUser()">
<!-- Title --> <!-- Title -->

View File

@ -1,4 +1,4 @@
<os-head-bar plusButton=true (plusButtonClicked)=onPlusButton()> <os-head-bar mainButton=true (mainEvent)=onPlusButton()>
<!-- Title --> <!-- Title -->
<div class="title-slot"> <div class="title-slot">
<h2 translate>Users</h2> <h2 translate>Users</h2>