Add side menu for header.
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
<div class="category {{category.isOpenned ? 'openned' : ''}}" *ngFor="let category of categories$ | async">
|
||||
<div id="category-{{category.id}}" class="category-header" (click)="setOpenned(category)">
|
||||
{{category.name}}
|
||||
<mat-icon>chevron_right</mat-icon>
|
||||
</div>
|
||||
<div class="sub-category-container {{category.isOpenned ? 'displayed' : ''}}">
|
||||
<a [routerLink]="['/']" class="sub-category" *ngFor="let subCategory of category.subCategories">
|
||||
{{subCategory.name}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,57 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.category {
|
||||
transition: background-color .2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: #5c6bc0;
|
||||
}
|
||||
|
||||
.category-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: .5em 1em;
|
||||
|
||||
mat-icon {
|
||||
transition: transform .2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
&.openned {
|
||||
.category-header {
|
||||
mat-icon {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
.sub-category-container {
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
|
||||
.sub-category-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
max-height: 0;
|
||||
transition: max-height .2s ease-in-out;
|
||||
background-color: #303f9f;
|
||||
|
||||
.sub-category {
|
||||
padding: .5em 1em .5em 2em;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: background-color .2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: #5c6bc0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, inject } from "@angular/core";
|
||||
import { MatIconModule } from "@angular/material/icon";
|
||||
import { DisplayableCategory, SideMenuService } from "../side-menu.service";
|
||||
import { Observable } from "rxjs";
|
||||
import { RouterModule } from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'app-categories-menu',
|
||||
standalone: true,
|
||||
imports: [CommonModule, RouterModule, MatIconModule],
|
||||
templateUrl: './categories-menu.component.html',
|
||||
styleUrl: './categories-menu.component.scss'
|
||||
})
|
||||
export class CategoriesMenuComponent {
|
||||
private sideMenuService = inject(SideMenuService);
|
||||
|
||||
get categories$(): Observable<DisplayableCategory[]> {
|
||||
return this.sideMenuService.categories$;
|
||||
}
|
||||
|
||||
setOpenned(category: DisplayableCategory): void {
|
||||
if (category.isOpenned) {
|
||||
const categoryDiv = document.getElementById(`category-${category.id}`);
|
||||
if (categoryDiv) {
|
||||
this.closeAccordion(categoryDiv);
|
||||
}
|
||||
} else {
|
||||
const categoriesDivs = document.getElementsByClassName('category-header');
|
||||
Array.from(categoriesDivs)
|
||||
.map(category => category as HTMLElement)
|
||||
.forEach(categoryDiv => this.closeAccordion(categoryDiv));
|
||||
|
||||
const categoryDiv = document.getElementById(`category-${category.id}`);
|
||||
if (categoryDiv) {
|
||||
this.openAccordion(categoryDiv);
|
||||
}
|
||||
}
|
||||
|
||||
this.sideMenuService.setOpenned(category);
|
||||
}
|
||||
|
||||
private closeAccordion(categoryDiv: HTMLElement): void {
|
||||
const divContent = categoryDiv?.nextElementSibling as HTMLElement;
|
||||
divContent.style.maxHeight = '0';
|
||||
}
|
||||
|
||||
private openAccordion(categoryDiv: HTMLElement): void {
|
||||
const divContent = categoryDiv?.nextElementSibling as HTMLElement;
|
||||
divContent.style.maxHeight = `${divContent.scrollHeight}px`;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
<h1>Codiki</h1>
|
||||
<h2>Catégories</h2>
|
||||
<div class="categories-container">
|
||||
<div class="category {{category.isOpenned ? 'openned' : ''}}" *ngFor="let category of categories$ | async">
|
||||
<div id="category-{{category.id}}" class="category-header" (click)="setOpenned(category)">
|
||||
{{category.name}}
|
||||
<mat-icon>chevron_right</mat-icon>
|
||||
</div>
|
||||
<div class="sub-category-container {{category.isOpenned ? 'displayed' : ''}}">
|
||||
<div class="sub-category" *ngFor="let subCategory of category.subCategories">
|
||||
{{subCategory.name}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu {{ isOpenned ? 'displayed' : '' }}">
|
||||
<h1>
|
||||
<span>
|
||||
<img src="assets/images/codiki.png" alt="logo"/>
|
||||
Codiki
|
||||
</span>
|
||||
<button type="button" (click)="close()" matTooltip="Close the menu">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</h1>
|
||||
<h2>Catégories</h2>
|
||||
<app-categories-menu></app-categories-menu>
|
||||
</div>
|
||||
<div class="overlay {{ isOpenned ? 'displayed' : ''}}" (click)="close()"></div>
|
||||
|
||||
@@ -1,52 +1,83 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.categories-container {
|
||||
.menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #3f51b5;
|
||||
color: white;
|
||||
|
||||
.category {
|
||||
border: 1px solid blue;
|
||||
$categoriesMenuWidth: 20em;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: -$categoriesMenuWidth - 1em - 1;
|
||||
bottom: 0;
|
||||
transition: left .2s ease-in-out;
|
||||
width: $categoriesMenuWidth;
|
||||
z-index: 2;
|
||||
padding: 1em 0;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
&.displayed {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.category-header {
|
||||
h1 {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 1em;
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
padding: .5em 1em;
|
||||
gap: .5em;
|
||||
|
||||
mat-icon {
|
||||
transition: transform .2s ease-in-out;
|
||||
img {
|
||||
$imageSize: 1.2em;
|
||||
width: $imageSize;
|
||||
height: $imageSize;
|
||||
}
|
||||
}
|
||||
|
||||
&.openned {
|
||||
.category-header {
|
||||
mat-icon {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
button {
|
||||
border-radius: 10em;
|
||||
border: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
color: white;
|
||||
background-color: #3f51b5;
|
||||
transition: background-color .2s ease-in-out;
|
||||
|
||||
.sub-category-container {
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
|
||||
.sub-category-container {
|
||||
border: 1px solid red;
|
||||
overflow: hidden;
|
||||
max-height: 0;
|
||||
transition: max-height .2s ease-in-out;
|
||||
|
||||
.sub-category {
|
||||
padding: .5em 1em .5em 2em;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: #5c6bc0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
padding: 0 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000;
|
||||
opacity: .2;
|
||||
|
||||
&.displayed {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +1,23 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { DisplayableCategory, SideMenuService } from './side-menu.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import {Component} from '@angular/core';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {CategoriesMenuComponent} from './categories-menu/categories-menu.component';
|
||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
|
||||
@Component({
|
||||
selector: 'app-side-menu',
|
||||
standalone: true,
|
||||
imports: [CommonModule, MatIconModule],
|
||||
imports: [CategoriesMenuComponent, MatIconModule, MatTooltipModule],
|
||||
templateUrl: './side-menu.component.html',
|
||||
styleUrl: './side-menu.component.scss'
|
||||
})
|
||||
export class SideMenuComponent {
|
||||
private sideMenuService = inject(SideMenuService);
|
||||
isOpenned: boolean = false;
|
||||
|
||||
get categories$(): Observable<DisplayableCategory[]> {
|
||||
return this.sideMenuService.categories$;
|
||||
open(): void {
|
||||
this.isOpenned = true;
|
||||
}
|
||||
|
||||
setOpenned(category: DisplayableCategory): void {
|
||||
if (category.isOpenned) {
|
||||
const categoryDiv = document.getElementById(`category-${category.id}`);
|
||||
if (categoryDiv) {
|
||||
this.closeAccordion(categoryDiv);
|
||||
}
|
||||
} else {
|
||||
const categoriesDivs = document.getElementsByClassName('category-header');
|
||||
Array.from(categoriesDivs)
|
||||
.map(category => category as HTMLElement)
|
||||
.forEach(categoryDiv => this.closeAccordion(categoryDiv));
|
||||
|
||||
const categoryDiv = document.getElementById(`category-${category.id}`);
|
||||
if (categoryDiv) {
|
||||
this.openAccordion(categoryDiv);
|
||||
}
|
||||
}
|
||||
|
||||
this.sideMenuService.setOpenned(category);
|
||||
}
|
||||
|
||||
private closeAccordion(categoryDiv: HTMLElement): void {
|
||||
const divContent = categoryDiv?.nextElementSibling as HTMLElement;
|
||||
divContent.style.maxHeight = '0';
|
||||
}
|
||||
|
||||
private openAccordion(categoryDiv: HTMLElement): void {
|
||||
const divContent = categoryDiv?.nextElementSibling as HTMLElement;
|
||||
divContent.style.maxHeight = `${divContent.scrollHeight}px`;
|
||||
close(): void {
|
||||
this.isOpenned = false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user