Add reusable head bar component

- implement the head bar in all apps
- work on the documentation
This commit is contained in:
Sean Engelhardt 2018-08-28 13:54:25 +02:00
parent 65a945841c
commit 897488f3a4
22 changed files with 434 additions and 80 deletions

View File

@ -1,6 +1,25 @@
import { Injectable } from '@angular/core';
import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout';
/**
* Viewport Service
*
* Uses breakpoint observers to determine the size of the users/operators viewport size (the device)
*
* ## Example:
*
* Provide the service via constructor and just use it like
*
* ```html
* <div *ngIf="!vp.isMobile">Will only be shown of not mobile</div>
* ```
* or
* ```ts
* if (this.vp.isMobile) {
* ...
* }
* ```
*/
@Injectable({
providedIn: 'root'
})
@ -10,8 +29,17 @@ export class ViewportService {
*/
private _isMobile = false;
/**
* Get the BreakpointObserver
*
* @param breakpointObserver
*/
constructor(private breakpointObserver: BreakpointObserver) {}
/**
* Needs to be called (exactly) once.
* Will observe breakpoints and updates the _isMobile variable
*/
checkForChange() {
this.breakpointObserver
.observe([Breakpoints.Small, Breakpoints.HandsetPortrait])

View File

@ -0,0 +1,22 @@
<mat-toolbar color='primary'>
<button *ngIf="plusButton" class='generic-plus-button on-transition-fade' (click)=clickPlusButton() mat-fab>
<fa-icon icon='plus'></fa-icon>
</button>
<span class='app-name on-transition-fade'>
{{ appName | translate }}
</span>
<span class='spacer'></span>
<button *ngIf="menuList" class='on-transition-fade' [matMenuTriggerFor]="ellipsisMenu" mat-icon-button>
<fa-icon icon='ellipsis-v'></fa-icon>
</button>
</mat-toolbar>
<mat-menu #ellipsisMenu="matMenu">
<button mat-menu-item *ngFor="let item of menuList" (click)=clickMenu(item)>
<fa-icon *ngIf="item.icon" [icon]='item.icon'></fa-icon>
{{item.text | translate}}
</button>
</mat-menu>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HeadBarComponent } from './head-bar.component';
describe('HeadBarComponent', () => {
let component: HeadBarComponent;
let fixture: ComponentFixture<HeadBarComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [HeadBarComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HeadBarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,105 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
/**
* Reusable head bar component for Apps.
*
* Will translate the title automatically.
*
* Use `PlusButton=true` and `(plusButtonClicked)=myFunction()` if a plus button is needed
*
* Use `[menuLust]=myArray` and `(ellipsisMenuItem)=myFunction($event)` if a menu is needed
*
* ## Examples:
*
* ### Usage of the selector:
*
* ```html
* <app-head-bar
* appName="Files"
* PlusButton=true
* [menuList]=myMenu
* (plusButtonClicked)=onPlusButton()
* (ellipsisMenuItem)=onEllipsisItem($event)>
* </app-head-bar>
* ```
*
* ### Declaration of a menu provided as `[menuList]=myMenu`:
*
* ```ts
* myMenu = [
* {
* text: 'Download All',
* icon: 'download',
* action: 'downloadAllFiles'
* },
* ];
* ```
* The parent needs to react to `action` like the following.
* This will execute a function with the name provided in the
* `action` field.
* ```ts
* onEllipsisItem(event: any) {
* if (event.action) {
* this[event.action]();
* }
* }
* ```
*/
@Component({
selector: 'app-head-bar',
templateUrl: './head-bar.component.html',
styleUrls: ['./head-bar.component.scss']
})
export class HeadBarComponent implements OnInit {
/**
* Input declaration for the app name
*/
@Input() appName: string;
/**
* Determine if there should be a plus button.
*/
@Input() plusButton: false;
/**
* If not empty shows a ellipsis menu on the right side
*
* The parent needs to provide a menu, i.e `[menuList]=myMenu`.
*/
@Input() menuList: any[];
/**
* Emit a signal to the parent component if the plus button was clicked
*/
@Output() plusButtonClicked = new EventEmitter<boolean>();
/**
* Emit a signal to the parent of an item in the menuList was selected.
*/
@Output() ellipsisMenuItem = new EventEmitter<any>();
/**
* Empty constructor
*/
constructor() {}
/**
* empty onInit
*/
ngOnInit() {}
/**
* Emits a signal to the parent if an item in the menu was clicked.
* @param item
*/
clickMenu(item: any) {
this.ellipsisMenuItem.emit(item);
}
/**
* Emits a signal to the parent if
*/
clickPlusButton() {
this.plusButtonClicked.emit(true);
}
}

View File

@ -34,6 +34,7 @@ import { TranslateModule } from '@ngx-translate/core';
// directives
import { OsPermsDirective } from './directives/os-perms.directive';
import { DomChangeDirective } from './directives/dom-change.directive';
import { HeadBarComponent } from './components/head-bar/head-bar.component';
library.add(fas);
@ -68,6 +69,7 @@ library.add(fas);
MatMenuModule,
MatSnackBarModule,
MatDialogModule,
TranslateModule.forChild(),
FontAwesomeModule
],
exports: [
@ -92,8 +94,10 @@ library.add(fas);
MatSnackBarModule,
FontAwesomeModule,
TranslateModule,
OsPermsDirective
OsPermsDirective,
DomChangeDirective,
HeadBarComponent
],
declarations: [OsPermsDirective, DomChangeDirective]
declarations: [OsPermsDirective, DomChangeDirective, HeadBarComponent]
})
export class SharedModule {}

View File

@ -1,18 +1,4 @@
<mat-toolbar color='primary'>
<button class='generic-plus-button on-transition-fade' mat-fab>
<fa-icon icon='plus'></fa-icon>
</button>
<span class='app-name on-transition-fade' translate>Agenda</span>
<!-- download button on the right -->
<span class='spacer'></span>
<button class='on-transition-fade' mat-icon-button (click)='downloadAgendaButton()'>
<fa-icon icon='download'></fa-icon>
</button>
</mat-toolbar>
<app-head-bar appName="Agenda" plusButton=true (plusButtonClicked)=onPlusButton()></app-head-bar>
<mat-card class="os-card card-plus-distance">
<div class="app-content">
@ -26,4 +12,4 @@
Only permitted users should see this
</div>
</div>
</mat-card>
</mat-card>

View File

@ -3,21 +3,39 @@ import { Title } from '@angular/platform-browser';
import { BaseComponent } from 'app/base.component';
import { TranslateService } from '@ngx-translate/core';
/**
* List view for the agenda.
*
* TODO: Not yet implemented
*/
@Component({
selector: 'app-agenda-list',
templateUrl: './agenda-list.component.html',
styleUrls: ['./agenda-list.component.css']
})
export class AgendaListComponent extends BaseComponent implements OnInit {
/**
* The usual constructor for components
* @param titleService
* @param translate
*/
constructor(titleService: Title, protected translate: TranslateService) {
super(titleService, translate);
}
/**
* Init function.
* Sets the title
*/
ngOnInit() {
super.setTitle('Agenda');
}
downloadAgendaButton() {
console.log('Clock Download Button');
/**
* Handler for the plus button.
* Comes from the HeadBar Component
*/
onPlusButton() {
console.log('create new motion');
}
}

View File

@ -1,4 +1,4 @@
<mat-toolbar color='primary'>
<!-- <mat-toolbar color='primary'>
<button class='generic-plus-button on-transition-fade' mat-fab>
<fa-icon icon='plus'></fa-icon>
@ -6,14 +6,15 @@
<span class='app-name on-transition-fade' translate>Assignments</span>
<!-- download button on the right -->
<span class='spacer'></span>
<button class='on-transition-fade' mat-icon-button (click)='downloadAssignmentButton()'>
<fa-icon icon='download'></fa-icon>
</button>
</mat-toolbar>
</mat-toolbar> -->
<app-head-bar appName="Assignments" plusButton=true [menuList]=assignmentMenu (plusButtonClicked)=onPlusButton() (ellipsisMenuItem)=onEllipsisItem($event)></app-head-bar>
<mat-card class="os-card card-plus-distance">
assignment-list works!
</mat-card>
</mat-card>

View File

@ -3,21 +3,68 @@ import { BaseComponent } from '../../../base.component';
import { TranslateService } from '@ngx-translate/core';
import { Title } from '@angular/platform-browser';
/**
* Listview for the assignments
*
* TODO: not yet implemented
*/
@Component({
selector: 'app-assignment-list',
templateUrl: './assignment-list.component.html',
styleUrls: ['./assignment-list.component.css']
})
export class AssignmentListComponent extends BaseComponent implements OnInit {
/**
* Constructor.
* @param titleService
* @param translate
*/
constructor(titleService: Title, protected translate: TranslateService) {
super(titleService, translate);
}
/**
* Define the content of the ellipsis menu.
* Give it to the HeadBar to display them.
*/
assignmentMenu = [
{
text: 'Download All',
icon: 'download',
action: 'downloadAssignmentButton'
}
];
/**
* Click on the plus button delegated from head-bar
*/
onPlusButton() {
console.log('create new assignments');
}
/**
* Init function. Sets the title.
*/
ngOnInit() {
super.setTitle('Assignments');
}
/**
* Function to download the assignment list
* TODO: Not yet implemented
*/
downloadAssignmentButton(): void {
console.log('Hello World');
}
/**
* handler function for clicking on items in the ellipsis menu.
*
* @param event clicked entry from ellipsis menu
*/
onEllipsisItem(event: any) {
if (event.action) {
this[event.action]();
}
}
}

View File

@ -1,13 +1,7 @@
<mat-toolbar color='primary'>
<app-head-bar appName="Files" plusButton=true [menuList]=extraMenu (plusButtonClicked)=onPlusButton() (ellipsisMenuItem)=onEllipsisItem($event)>
</app-head-bar>
<button class='generic-plus-button on-transition-fade' mat-fab>
<fa-icon icon='plus'></fa-icon>
</button>
<span class='app-name on-transition-fade' translate>Files</span>
</mat-toolbar>
<mat-card class="os-card card-plus-distance">
mediafile-list works!
</mat-card>
</mat-card>

View File

@ -1,19 +1,76 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, ViewChild } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { BaseComponent } from '../../../base.component';
/**
* Lists all the uploaded mediafiles.
*
* Not yet implemented
*/
@Component({
selector: 'app-mediafile-list',
templateUrl: './mediafile-list.component.html',
styleUrls: ['./mediafile-list.component.css']
})
export class MediafileListComponent extends BaseComponent implements OnInit {
/**
* Constructor
*
* @param titleService
* @param translate
*/
constructor(titleService: Title, protected translate: TranslateService) {
super(titleService, translate);
}
/**
* Define the content of the ellipsis menu.
* Give it to the HeadBar to display them.
*/
extraMenu = [
{
text: 'Download',
icon: 'download',
action: 'downloadAllFiles'
}
];
/**
* Init.
* Set the title
*/
ngOnInit() {
super.setTitle('Files');
}
/**
* Click on the plus button delegated from head-bar
*/
onPlusButton() {
console.log('clicked plus (mediafile)');
}
/**
* function to Download all files
* (serves as example to use functions on head bar)
*
* TODO: Not yet implemented, might not even be required
*/
deleteAllFiles() {
console.log('do download');
}
/**
* handler function for clicking on items in the ellipsis menu.
*
* @param event clicked entry from ellipsis menu
*/
onEllipsisItem(event: any) {
if (event.action) {
this[event.action]();
}
}
}

View File

@ -1,24 +1,4 @@
<mat-toolbar color='primary'>
<button class='generic-plus-button on-transition-fade' routerLink='new' mat-fab>
<fa-icon icon='plus'></fa-icon>
</button>
<span class='app-name on-transition-fade' translate>Category</span>
<!-- <button class='on-transition-fade' mat-icon-button (click)='downloadMotionsButton()'> -->
<span class='spacer'></span>
<button class='on-transition-fade' mat-icon-button [matMenuTriggerFor]="motionExtraMenu">
<fa-icon icon='ellipsis-v'></fa-icon>
</button>
<mat-menu #motionExtraMenu="matMenu">
<!-- TODO the functions for the buttons -->
<button mat-menu-item translate><fa-icon icon='download'></fa-icon> Export As...</button>
<button mat-menu-item routerLink='category' translate>Categories</button>
</mat-menu>
</mat-toolbar>
<app-head-bar appName="Category" plusButton=true (plusButtonClicked)=onPlusButton()></app-head-bar>
<mat-table class='on-transition-fade' [dataSource]="dataSource" matSort>
<!-- name column -->

View File

@ -1,11 +1,16 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { Category } from '../../../shared/models/motions/category';
import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { Router } from '@angular/router';
import { BaseComponent } from '../../../base.component';
import { MatSort, MatTable, MatTableDataSource } from '@angular/material';
import { TranslateService } from '@ngx-translate/core';
import { BaseComponent } from '../../../base.component';
import { Category } from '../../../shared/models/motions/category';
/**
* List view for the categories.
*
* TODO: Creation of new Categories
*/
@Component({
selector: 'app-category-list',
templateUrl: './category-list.component.html',
@ -32,10 +37,20 @@ export class CategoryListComponent extends BaseComponent implements OnInit {
*/
@ViewChild(MatSort) sort: MatSort;
constructor(protected titleService: Title, protected translate: TranslateService, private router: Router) {
/**
* The usual component constructor
* @param titleService
* @param translate
*/
constructor(protected titleService: Title, protected translate: TranslateService) {
super(titleService, translate);
}
/**
* Init function.
*
* Sets the title and gets/observes categories from DataStore
*/
ngOnInit() {
super.setTitle('Category');
this.categoryArray = this.DS.get(Category) as Category[];
@ -51,4 +66,13 @@ export class CategoryListComponent extends BaseComponent implements OnInit {
}
});
}
/**
* Add a new Category.
*
* TODO: Not yet implemented
*/
onPlusButton() {
console.log('Add New Category');
}
}

View File

@ -1,4 +1,4 @@
<mat-toolbar color='primary'>
<!-- <mat-toolbar color='primary'>
<button class='generic-plus-button on-transition-fade' routerLink='new' mat-fab>
<fa-icon icon='plus'></fa-icon>
@ -6,19 +6,20 @@
<span class='app-name on-transition-fade' translate>Motions</span>
<!-- <button class='on-transition-fade' mat-icon-button (click)='downloadMotionsButton()'> -->
<span class='spacer'></span>
<button class='on-transition-fade' mat-icon-button [matMenuTriggerFor]="motionExtraMenu">
<fa-icon icon='ellipsis-v'></fa-icon>
</button>
<mat-menu #motionExtraMenu="matMenu">
<!-- TODO the functions for the buttons -->
<button mat-menu-item translate><fa-icon icon='download'></fa-icon> Export As...</button>
<button mat-menu-item routerLink='category' translate>Categories</button>
</mat-menu>
</mat-toolbar>
</mat-toolbar> -->
<app-head-bar appName="Motions" plusButton=true (plusButtonClicked)=onPlusButton() [menuList]=motionMenuList (ellipsisMenuItem)=onEllipsisItem($event)></app-head-bar>
<div class='custom-table-header on-transition-fade'>
<button mat-button>

View File

@ -56,6 +56,21 @@ export class MotionListComponent extends BaseComponent implements OnInit {
*/
columnsToDisplayFullWidth = ['identifier', 'title', 'meta', 'state'];
/**
* content of the ellipsis menu
*/
motionMenuList = [
{
text: 'Download',
icon: 'download',
action: 'downloadMotions'
},
{
text: 'Categories',
action: 'toCategories'
}
];
/**
* Constructor implements title and translation Module.
*
@ -121,16 +136,45 @@ export class MotionListComponent extends BaseComponent implements OnInit {
}
}
/**
* Determines if an icon should be shown in the list view
* @param state
*/
isDisplayIcon(state): boolean {
return state.name === 'accepted' || state.name === 'rejected' || state.name === 'not decided';
}
/**
* Handler for the plus button
*/
onPlusButton() {
this.router.navigate(['./new'], { relativeTo: this.route });
}
/**
* navigate to 'motion/category'
*/
toCategories() {
this.router.navigate(['./category'], { relativeTo: this.route });
}
/**
* Download all motions As PDF and DocX
*
* TODO: Currently does nothing
*/
downloadMotionsButton() {
downloadMotions() {
console.log('Download Motions Button');
}
/**
* handler function for clicking on items in the ellipsis menu.
*
* @param event clicked entry from ellipsis menu
*/
onEllipsisItem(event: any) {
if (event.action) {
this[event.action]();
}
}
}

View File

@ -1,6 +1,4 @@
<mat-toolbar color='primary'>
<span class='app-name on-transition-fade' translate>Settings</span>
</mat-toolbar>
<app-head-bar appName="Settings"></app-head-bar>
<mat-card class="os-card">
<div *appOsPerms="['core.can_manage_config']" class="app-content">
@ -18,4 +16,4 @@
</mat-accordion>
</div>
</mat-card>
</mat-card>

View File

@ -3,16 +3,29 @@ import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { BaseComponent } from '../../../base.component';
/**
* List view for the global settings
*
* TODO: Not yet implemented
*/
@Component({
selector: 'app-settings-list',
templateUrl: './settings-list.component.html',
styleUrls: ['./settings-list.component.css']
})
export class SettingsListComponent extends BaseComponent implements OnInit {
/**
* The usual component constructor
* @param titleService
* @param translate
*/
constructor(titleService: Title, protected translate: TranslateService) {
super(titleService, translate);
}
/**
* Init function. Sets the title
*/
ngOnInit() {
super.setTitle('Settings');
}

View File

@ -3,7 +3,6 @@ import { CommonModule } from '@angular/common';
import { SiteRoutingModule } from './site-routing.module';
import { SharedModule } from 'app/shared/shared.module';
import { TranslateModule } from '@ngx-translate/core';
import { SiteComponent } from './site.component';
import { StartComponent } from './start/start.component';
@ -11,7 +10,7 @@ import { LegalNoticeComponent } from './legal-notice/legal-notice.component';
import { PrivacyPolicyComponent } from './privacy-policy/privacy-policy.component';
@NgModule({
imports: [CommonModule, SharedModule, SiteRoutingModule, TranslateModule.forChild()],
imports: [CommonModule, SharedModule, SiteRoutingModule],
declarations: [SiteComponent, StartComponent, LegalNoticeComponent, PrivacyPolicyComponent]
})
export class SiteModule {}

View File

@ -1,6 +1,4 @@
<mat-toolbar color='primary'>
<span class='app-name on-transition-fade' translate>Home</span>
</mat-toolbar>
<app-head-bar appName="Home"></app-head-bar>
<mat-card class="os-card">
<div class="app-content" translate>
@ -20,4 +18,4 @@
<input matInput #motionNumber placeholder="Number of Motions to add" value="100">
<button mat-button (click)="createMotions(motionNumber.value)">Add Random Motions</button>
</div>
</mat-card>
</mat-card>

View File

@ -1,7 +1,5 @@
<mat-toolbar color='primary'>
<span class='app-name on-transition-fade' translate>Users</span>
</mat-toolbar>
<app-head-bar appName="Users"></app-head-bar>
<mat-card class="os-card">
UserList Works!
</mat-card>
</mat-card>

View File

@ -3,16 +3,29 @@ import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { BaseComponent } from '../../../base.component';
/**
* Component for the user list view.
*
* TODO: Not yet implemented
*/
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
styleUrls: ['./user-list.component.css']
})
export class UserListComponent extends BaseComponent implements OnInit {
/**
* The usual constructor for components
* @param titleService
* @param translate
*/
constructor(titleService: Title, protected translate: TranslateService) {
super(titleService, translate);
}
/**
* Init function, sets the title
*/
ngOnInit() {
super.setTitle('Users');
}