Skip to content

Commit

Permalink
Add mobile navbar
Browse files Browse the repository at this point in the history
  • Loading branch information
jvyden committed Aug 31, 2024
1 parent b429f47 commit 19de1c6
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 103 deletions.
6 changes: 5 additions & 1 deletion src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<app-header class="z-40"></app-header>
@if(!(layout.isMobile | async)) {
<app-header></app-header>
} @else {
<app-header-mobile></app-header-mobile>
}
@defer (when bannerService.banners.length > 0) {
<app-popup-banner-container></app-popup-banner-container>
}
Expand Down
6 changes: 5 additions & 1 deletion src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ import {AuthenticationService} from "./api/authentication.service";
import {PopupBannerContainerComponent} from "./banners/popup-banner-container.component";
import {BannerService} from "./banners/banner.service";
import {animate, group, query, style, transition, trigger} from "@angular/animations";
import {LayoutService} from "./services/layout.service";
import {AsyncPipe} from "@angular/common";
import {HeaderMobileComponent} from "./components/ui/header/mobile/header-mobile.component";

const fadeLength: string = "100ms";

@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, HeaderComponent, PopupBannerContainerComponent],
imports: [RouterOutlet, HeaderComponent, PopupBannerContainerComponent, AsyncPipe, HeaderMobileComponent],
templateUrl: './app.component.html',
animations: [
trigger('routeAnimations', [
Expand All @@ -38,6 +41,7 @@ export class AppComponent {
constructor(private title: TitleService,
private embed: EmbedService,
private auth: AuthenticationService,
protected layout: LayoutService,
protected bannerService: BannerService,
library: FaIconLibrary) {
library.addIconPacks(fas)
Expand Down
20 changes: 20 additions & 0 deletions src/app/components/ui/header/header-logo.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Component } from '@angular/core';
import {NgOptimizedImage} from "@angular/common";
import {RouterLink} from "@angular/router";

@Component({
selector: 'app-header-logo',
standalone: true,
imports: [
NgOptimizedImage,
RouterLink
],
template: `
<a routerLink="/" title="Home" class="py-1">
<img ngSrc="/assets/logo.svg" alt="Refresh Logo" width="48" height="48" priority>
</a>
`
})
export class HeaderLogoComponent {

}
10 changes: 6 additions & 4 deletions src/app/components/ui/header/header-me.component.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { Component } from '@angular/core';
import {Component, Input} from '@angular/core';
import {UserAvatarComponent} from "../photos/user-avatar.component";
import {AuthenticationService} from "../../../api/authentication.service";
import {AsyncPipe} from "@angular/common";
import {faCaretDown, faChevronDown, faSignInAlt} from "@fortawesome/free-solid-svg-icons";
import {faCaretDown, faSignInAlt} from "@fortawesome/free-solid-svg-icons";
import {NavbarItemComponent} from "./navbar-item.component";
import {UserRouterLinkComponent} from "../text/links/user-router-link.component";
import {FaIconComponent} from "@fortawesome/angular-fontawesome";
import {HeaderMeMenuComponent} from "./header-me-menu.component";
import {NavItem} from "./navtypes";

@Component({
selector: 'app-header-me',
Expand All @@ -25,7 +24,9 @@ import {NavItem} from "./navtypes";
<div class="relative">
<div (click)="toggleMenu()">
<app-user-avatar [user]="user" [size]="44"></app-user-avatar>
<fa-icon [icon]="faCaretDown" class="ml-2.5"></fa-icon>
@if(arrow) {
<fa-icon [icon]="faCaretDown" class="ml-2.5"></fa-icon>
}
</div>
@if(showMenu) {
Expand All @@ -40,6 +41,7 @@ import {NavItem} from "./navtypes";
})
export class HeaderMeComponent {
protected showMenu: boolean = false;
@Input() arrow: boolean = false;

toggleMenu(): void {
this.showMenu = !this.showMenu;
Expand Down
105 changes: 8 additions & 97 deletions src/app/components/ui/header/header.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,19 @@ import { Component } from '@angular/core';
import { AsyncPipe, NgOptimizedImage } from "@angular/common";
import {NavbarItemComponent} from "./navbar-item.component";
import {Router, RouterLink} from "@angular/router";
import {
faBookOpen,
faCheckCircle,
faEnvelope, faFire,
faFireAlt,
faImages,
faPlay, faQuestionCircle, faRandom, faSearch,
faShareAlt,
faSignInAlt, faThList,
faTools, faTrophy
} from "@fortawesome/free-solid-svg-icons";
import {faSignInAlt} from "@fortawesome/free-solid-svg-icons";
import {FormComponent} from "../form/form.component";
import {FormControl, FormGroup} from "@angular/forms";
import {TextboxComponent} from "../form/textbox.component";
import {SearchBarComponent} from "../form/search-bar.component";
import {LayoutService} from "../../../services/layout.service";
import {NavCategory} from "./navtypes";
import {NavbarCategoryComponent} from "./navbar-category.component";
import {FaIconComponent} from "@fortawesome/angular-fontawesome";
import {SearchComponent} from "../../../overlays/search.component";
import {AuthenticationService} from "../../../api/authentication.service";
import {UserLinkComponent} from "../text/links/user-link.component";
import {UserAvatarComponent} from "../photos/user-avatar.component";
import {HeaderMeComponent} from "./header-me.component";
import {HeaderLogoComponent} from "./header-logo.component";
import {navTree, rightNavTree} from './navtypes';

@Component({
selector: 'header-vertical-divider',
Expand All @@ -52,14 +41,13 @@ class VerticalDividerComponent {}
SearchComponent,
UserLinkComponent,
UserAvatarComponent,
HeaderMeComponent
HeaderMeComponent,
HeaderLogoComponent
],
template: `
<header
class="flex items-center bg-header-background gap-x-2.5 sm:gap-x-1 px-5 leading-none sticky top-0 left-0 w-full z-[1000]">
<a routerLink="/" title="Home" class="py-1">
<img ngSrc="/assets/logo.svg" alt="Refresh Logo" width="48" height="48" priority>
</a>
<app-header-logo></app-header-logo>
<header-vertical-divider></header-vertical-divider>
<nav class="flex gap-x-5 h-[60px] items-center">
Expand All @@ -86,85 +74,8 @@ class VerticalDividerComponent {}
})
export class HeaderComponent {
protected readonly faSignInAlt = faSignInAlt;

protected readonly navTree: NavCategory[] = [
{
name: "Play",
icon: faPlay,
defaultRoute: "/levels",
items: [
{
name: "Team Picks",
icon: faCheckCircle,
route: "/levels/teamPicks"
},
{
name: "Cool Levels",
icon: faFire,
route: "/levels/coolLevels"
},
{
name: "Lucky Dip",
icon: faRandom,
route: "/levels/random"
},
]
},
{
name: "Create",
icon: faTools,
defaultRoute: "/contests",
items: [
{
name: "Contests",
icon: faTrophy,
route: "/contests"
},
{
name: "Playlists",
icon: faThList,
route: "/playlists"
}
]
},
{
name: "Share",
icon: faShareAlt,
defaultRoute: "/activity",
items: [
{
name: "Recent Activity",
icon: faFireAlt,
route: "/activity"
},
{
name: "Photos",
icon: faImages,
route: "/photos"
}
]
}
]

protected readonly rightNavTree: NavCategory[] = [
{
name: "Other",
icon: faQuestionCircle,
defaultRoute: null,
items: [
{
name: "API Documentation",
icon: faBookOpen,
route: "/docs"
},
{
name: "Contact Us",
icon: faEnvelope,
route: "/contact"
},
]
}
]
protected readonly navTree = navTree;
protected readonly rightNavTree = rightNavTree;

constructor(private router: Router, protected layout: LayoutService) {}
}
36 changes: 36 additions & 0 deletions src/app/components/ui/header/mobile/header-mobile.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Component } from '@angular/core';
import {HeaderLogoComponent} from "../header-logo.component";
import {FaIconComponent} from "@fortawesome/angular-fontawesome";
import {faBars, faSignInAlt} from "@fortawesome/free-solid-svg-icons";
import {HeaderMeComponent} from "../header-me.component";
import {NavbarItemComponent} from "../navbar-item.component";
import {HeaderNavbarMobileComponent} from "./header-navbar-mobile.component";

@Component({
selector: 'app-header-mobile',
standalone: true,
imports: [
HeaderLogoComponent,
FaIconComponent,
HeaderMeComponent,
NavbarItemComponent,
HeaderNavbarMobileComponent
],
template: `
<header class="flex items-center bg-header-background gap-x-1 px-5 sticky top-0 left-0 w-full z-[1000] text-xl h-[60px] justify-between">
<app-header-navbar-mobile></app-header-navbar-mobile>
<app-header-logo class="absolute left-1/2 -translate-x-1/2"></app-header-logo>
@defer {
<app-header-me [arrow]="false" class=""></app-header-me>
} @placeholder {
<app-navbar-item href="/login" [icon]=faSignInAlt class=""></app-navbar-item>
}
</header>
`
})
export class HeaderMobileComponent {
protected readonly faBars = faBars;
protected readonly faSignInAlt = faSignInAlt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {Component} from '@angular/core';
import {DialogComponent} from "../../dialog.component";
import {FaIconComponent} from "@fortawesome/angular-fontawesome";
import {faBars, faXmark} from "@fortawesome/free-solid-svg-icons";
import {navTree, rightNavTree} from '../navtypes';
import {HeaderNavbarSectionMobileComponent} from "./header-navbar-section-mobile.component";
import {DividerComponent} from "../../divider.component";

@Component({
selector: 'app-header-navbar-mobile',
standalone: true,
imports: [
DialogComponent,
FaIconComponent,
HeaderNavbarSectionMobileComponent,
DividerComponent
],
template: `
<fa-icon [icon]="show ? faXmark : faBars" class="text-3xl" (click)="toggleModal()"></fa-icon>
@defer (when show) { @if (show) {
<div class="backdrop-brightness-50 absolute left-0 top-[60px] z-[1001] w-full">
<div class="bg-header-background h-[100vh] w-64 font-normal px-5 py-2.5 flex flex-col gap-y-4">
@for(category of navTree; track category.name) {
<app-header-navbar-section-mobile [category]="category"></app-header-navbar-section-mobile>
}
<app-divider></app-divider>
@for(category of rightNavTree; track category.name) {
<app-header-navbar-section-mobile [category]="category"></app-header-navbar-section-mobile>
}
</div>
</div>
}}
`
})
export class HeaderNavbarMobileComponent {
protected show: boolean = true;

toggleModal(): void {
this.show = !this.show;
}

protected readonly navTree = navTree;
protected readonly rightNavTree = rightNavTree;

protected readonly faBars = faBars;
protected readonly faXmark = faXmark;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {Component, Input} from '@angular/core';
import {NavCategory} from "../navtypes";
import {DarkContainerComponent} from "../../dark-container.component";
import {ContainerTitleComponent} from "../../text/container-title.component";
import {FaIconComponent} from "@fortawesome/angular-fontawesome";
import {NavbarItemComponent} from "../navbar-item.component";

@Component({
selector: 'app-header-navbar-section-mobile',
standalone: true,
imports: [
DarkContainerComponent,
ContainerTitleComponent,
FaIconComponent,
NavbarItemComponent
],
template: `
<div class="bg-backdrop rounded-md px-2.5 py-1 flex flex-row gap-x-1.5 text-lg">
<fa-icon [icon]="category.icon"></fa-icon>
<span>{{category.name}}</span>
</div>
<div class="flex flex-col gap-y-1 pt-2">
@for (item of category.items; track item.route) {
<app-navbar-item [icon]="item.icon" [title]="item.name" [href]="item.route" iconClass="w-4 text-[1.1rem]" labelClass="text-md"></app-navbar-item>
}
</div>
`
})
export class HeaderNavbarSectionMobileComponent {
@Input({required: true}) category: NavCategory = null!;
}
Loading

0 comments on commit 19de1c6

Please sign in to comment.