Эх сурвалжийг харах

Classic Material navigation

Lukas Angerer 3 жил өмнө
parent
commit
31614b8418

+ 18 - 23
src/RunnersMeet.Client/src/app/app.component.html

@@ -1,23 +1,18 @@
-<h1>{{title}}</h1>
-<ul *ngIf="notificationService.hasMessages">
-	<li *ngFor="let msg of notificationService.messages" [ngClass]="msg.classes">
-		{{ msg.message }} <button *ngIf="msg.isDismissable" (click)="msg.dispose()">Dismiss</button>
-	</li>
-</ul>
-<ul>
-	<li><a [routerLink]="['/']">Home</a></li>
-	<li><a [routerLink]="['/tracks']">Tracks</a></li>
-	<li><a [routerLink]="['/tracks/edit/new']">Create Track</a></li>
-	<li><a href="#" (click)="logout()">Logout</a></li>
-</ul>
-<div *ngIf="showLoadingBlock()">
-	Loading...
-</div>
-<router-outlet *ngIf="showRouterOutlet()"></router-outlet>
-<div *ngIf="showLoginBlock()">
-	Not authenticated
-	<button type="button" (click)="login()">Login</button>
-</div>
-<div *ngIf="showPermissionBlock()">
-	Your account has not yet been granted the necessary permissions to use this application, please contact your administrator.
-</div>
+<app-main-menu>
+	<ul *ngIf="notificationService.hasMessages">
+		<li *ngFor="let msg of notificationService.messages" [ngClass]="msg.classes">
+			{{ msg.message }} <button *ngIf="msg.isDismissable" (click)="msg.dispose()">Dismiss</button>
+		</li>
+	</ul>
+	<div *ngIf="showLoadingBlock()">
+		Loading...
+	</div>
+	<router-outlet *ngIf="showRouterOutlet()"></router-outlet>
+	<div *ngIf="showLoginBlock()">
+		Not authenticated
+		<button type="button" (click)="login()">Login</button>
+	</div>
+	<div *ngIf="showPermissionBlock()">
+		Your account has not yet been granted the necessary permissions to use this application, please contact your administrator.
+	</div>
+</app-main-menu>

+ 0 - 7
src/RunnersMeet.Client/src/app/app.component.ts

@@ -10,7 +10,6 @@ import { UserState } from './users/user-state';
 	styleUrls: ['./app.component.scss']
 })
 export class AppComponent {
-	public title = 'RunnersMeet';
 	public state: UserState = UserState.Loading;
 
 	public constructor(
@@ -43,10 +42,4 @@ export class AppComponent {
 	public login(): void {
 		this.authService.loginWithRedirect();
 	}
-
-	public logout(): void {
-		this.authService.logout({
-			federated: true,
-		});
-	}
 }

+ 15 - 1
src/RunnersMeet.Client/src/app/app.module.ts

@@ -22,6 +22,13 @@ import { PermissionService } from './users/permission.service';
 import { GlobalErrorHandler } from './global-error-handler';
 import { NotificationService } from './notification.service';
 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { MainMenuComponent } from './shell/main-menu/main-menu.component';
+import { LayoutModule } from '@angular/cdk/layout';
+import { MatToolbarModule } from '@angular/material/toolbar';
+import { MatButtonModule } from '@angular/material/button';
+import { MatSidenavModule } from '@angular/material/sidenav';
+import { MatIconModule } from '@angular/material/icon';
+import { MatListModule } from '@angular/material/list';
 
 @NgModule({
 	declarations: [
@@ -34,6 +41,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
 		TrackEditComponent,
 		TrackViewComponent,
 		ViewTrackPageComponent,
+		MainMenuComponent,
 	],
 	imports: [
 		BrowserModule,
@@ -41,7 +49,13 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
 		HttpClientModule,
 		AuthModule.forRoot(),
 		FormsModule,
-		BrowserAnimationsModule
+		BrowserAnimationsModule,
+		LayoutModule,
+		MatToolbarModule,
+		MatButtonModule,
+		MatSidenavModule,
+		MatIconModule,
+		MatListModule
 	],
 	providers: [
 		ConfigService,

+ 22 - 0
src/RunnersMeet.Client/src/app/shell/main-menu/main-menu.component.html

@@ -0,0 +1,22 @@
+<mat-sidenav-container class="sidenav-container">
+	<mat-sidenav #drawer class="sidenav" fixedInViewport [attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'"
+		[mode]="(isHandset$ | async) ? 'over' : 'side'" [opened]="(isHandset$ | async) === false">
+		<mat-toolbar>Menu</mat-toolbar>
+		<mat-nav-list>
+			<a mat-list-item [routerLink]="['/']">Home</a>
+			<a mat-list-item [routerLink]="['/tracks']">Tracks</a>
+			<a mat-list-item [routerLink]="['/tracks/edit/new']">Create Track</a>
+			<a mat-list-item href="#" (click)="logout()">Logout</a>
+		</mat-nav-list>
+	</mat-sidenav>
+	<mat-sidenav-content>
+		<mat-toolbar color="primary">
+			<button type="button" aria-label="Toggle sidenav" mat-icon-button (click)="drawer.toggle()"
+				*ngIf="isHandset$ | async">
+				<mat-icon aria-label="Side nav toggle icon">menu</mat-icon>
+			</button>
+			<span>RunnersMeet</span>
+		</mat-toolbar>
+		<ng-content></ng-content>
+	</mat-sidenav-content>
+</mat-sidenav-container>

+ 17 - 0
src/RunnersMeet.Client/src/app/shell/main-menu/main-menu.component.scss

@@ -0,0 +1,17 @@
+.sidenav-container {
+	height: 100%;
+}
+
+.sidenav {
+	width: 200px;
+}
+
+.sidenav .mat-toolbar {
+	background: inherit;
+}
+
+.mat-toolbar.mat-primary {
+	position: sticky;
+	top: 0;
+	z-index: 1;
+}

+ 40 - 0
src/RunnersMeet.Client/src/app/shell/main-menu/main-menu.component.spec.ts

@@ -0,0 +1,40 @@
+import { LayoutModule } from '@angular/cdk/layout';
+import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { MatButtonModule } from '@angular/material/button';
+import { MatIconModule } from '@angular/material/icon';
+import { MatListModule } from '@angular/material/list';
+import { MatSidenavModule } from '@angular/material/sidenav';
+import { MatToolbarModule } from '@angular/material/toolbar';
+
+import { MainMenuComponent } from './main-menu.component';
+
+describe('MainMenuComponentComponent', () => {
+	let component: MainMenuComponent;
+	let fixture: ComponentFixture<MainMenuComponent>;
+
+	beforeEach(waitForAsync(() => {
+		TestBed.configureTestingModule({
+			declarations: [MainMenuComponent],
+			imports: [
+				NoopAnimationsModule,
+				LayoutModule,
+				MatButtonModule,
+				MatIconModule,
+				MatListModule,
+				MatSidenavModule,
+				MatToolbarModule,
+			]
+		}).compileComponents();
+	}));
+
+	beforeEach(() => {
+		fixture = TestBed.createComponent(MainMenuComponent);
+		component = fixture.componentInstance;
+		fixture.detectChanges();
+	});
+
+	it('should compile', () => {
+		expect(component).toBeTruthy();
+	});
+});

+ 30 - 0
src/RunnersMeet.Client/src/app/shell/main-menu/main-menu.component.ts

@@ -0,0 +1,30 @@
+import { Component } from '@angular/core';
+import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
+import { Observable } from 'rxjs';
+import { map, shareReplay } from 'rxjs/operators';
+import { AuthService } from '@auth0/auth0-angular';
+
+@Component({
+	selector: 'app-main-menu',
+	templateUrl: './main-menu.component.html',
+	styleUrls: ['./main-menu.component.scss']
+})
+export class MainMenuComponent {
+
+	isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset)
+		.pipe(
+			map(result => result.matches),
+			shareReplay()
+		);
+
+	constructor(
+		private breakpointObserver: BreakpointObserver,
+		private readonly authService: AuthService,
+	) { }
+
+	public logout(): void {
+		this.authService.logout({
+			federated: true,
+		});
+	}
+}