Skip to content

Commit c8af0e2

Browse files
committed
✨ add breadcrumbs
1 parent 3a3530e commit c8af0e2

13 files changed

+294
-2
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface IBreadcrumb {
2+
name: string;
3+
_id: string;
4+
color: string;
5+
}

src/app/core/interfaces/response.interface.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { File } from "@models/file.model";
22
import { Folder } from "@models/folder.model";
33
import { User } from "@models/user.model";
44

5+
import { IBreadcrumb } from "./breadcrumb.interface";
6+
57
export interface IUserCreated {
68
ok: boolean;
79
user: User;
@@ -20,6 +22,7 @@ export interface IFolderCreated {
2022
export interface IFolderResponse {
2123
ok: boolean,
2224
folder: Folder,
25+
breadcrumb?: IBreadcrumb[],
2326
}
2427

2528
export interface IFileResponse {

src/app/core/services/folder.service.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Observable, catchError, map, of } from 'rxjs';
66

77
import { Folder } from '@models/folder.model';
88

9+
import { IBreadcrumb } from '@interfaces/breadcrumb.interface';
910
import { IFolderCreated, IFolderResponse } from '@interfaces/response.interface';
1011

1112
import Storage from '@utils/storage.util';
@@ -18,6 +19,7 @@ const base_url = environment.base_url;
1819
export class FolderService {
1920
folderCreated: EventEmitter<{folder: Folder, isNew: boolean}> = new EventEmitter();
2021
folderTemp: Folder;
22+
breadcrumb: IBreadcrumb[] = [];
2123

2224
get headers() {
2325
return {
@@ -47,6 +49,7 @@ export class FolderService {
4749
const { folder } = resp;
4850
if(!folder) return false;
4951
this.folderTemp = folder;
52+
this.breadcrumb = resp.breadcrumb || [];
5053
return true;
5154
}), catchError(() => of(false)));
5255
}

src/app/features/home/home.component.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ export class HomeComponent implements OnInit, OnDestroy {
3030

3131
ngOnInit(): void {
3232
this.root = this.authService.userActive.rootFolder;
33+
this.folderService.breadcrumb = [
34+
{ _id: this.root._id as string,
35+
name: this.root.name,
36+
color: this.root.color
37+
}
38+
];
3339
this.folderCreatedSubscription = this.folderService
3440
.folderCreated.subscribe(({folder, isNew}) => {
3541
if (isNew) this.root.folders.push(folder);
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<div class="items">
2+
<div class="more" *ngIf="breadcrumbDropdown.length > 0">
3+
<button class="options" (click)="changeVisibility()">
4+
<i class="bx bx-dots-vertical-rounded"></i>
5+
</button>
6+
<ul class="dropdown-list" #dropdown>
7+
<li
8+
class="dropdown-item"
9+
*ngFor="let item of breadcrumbDropdown; let i = index"
10+
[routerLink]="[i === 0 ? '/' : '/folder', item._id]"
11+
>
12+
<i class="bx bxs-folder" [ngStyle]="{'color': item.color}"></i>
13+
<span class="title">{{ item.name }}</span>
14+
</li>
15+
</ul>
16+
</div>
17+
<div class="item" *ngFor="let item of breadcrumbShown; let i = index">
18+
<a (click)="navigateToFolder(item._id)" [ngClass]="{'active': i === breadcrumbShown.length - 1}">
19+
{{ item.name }}
20+
</a>
21+
<i class="bx bx-chevron-right" *ngIf="i !== breadcrumbShown.length - 1"></i>
22+
</div>
23+
</div>
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
@import 'settings/_colors.scss';
2+
@import 'settings/_typography.scss';
3+
4+
.items {
5+
display: flex;
6+
flex-wrap: wrap;
7+
align-items: center;
8+
list-style: none;
9+
padding: 0;
10+
margin: 0;
11+
12+
& .item {
13+
@include fs-2;
14+
@include fw-500;
15+
color: var(--fc-primary);
16+
display: flex;
17+
align-items: center;
18+
max-width: 300px;
19+
20+
& a {
21+
color: var(--fc-primary);
22+
text-decoration: none;
23+
padding: 0.2rem 0.5rem;
24+
border-radius: 0.5rem;
25+
transition: 0.3s ease;
26+
text-overflow: ellipsis;
27+
overflow: hidden;
28+
white-space: nowrap;
29+
cursor: pointer;
30+
31+
&::selection {
32+
background: transparent;
33+
}
34+
35+
&.active {
36+
color: var(--fc-purple);
37+
}
38+
39+
&:hover {
40+
color: var(--fc-purple);
41+
background: var(--bg-hover-3)
42+
}
43+
44+
}
45+
46+
& i {
47+
@include fs-3;
48+
}
49+
}
50+
}
51+
52+
.more {
53+
position: relative;
54+
}
55+
56+
.options {
57+
@include fs-3;
58+
display: flex;
59+
justify-content: center;
60+
align-items: center;
61+
border: none;
62+
border-radius: 50%;
63+
height: 2.2rem;
64+
width: 2.2rem;
65+
background: transparent;
66+
transition: 0.4s ease;
67+
cursor: pointer;
68+
69+
&:hover {
70+
background: var(--bg-hover-2);
71+
}
72+
}
73+
74+
ul {
75+
display: none;
76+
z-index: 2;
77+
list-style-type: none;
78+
position: absolute;
79+
opacity: 0;
80+
top: 40px;
81+
left: 5px;
82+
background-color: #f7f7f7;
83+
padding: 0;
84+
font-size: 16px;
85+
border-radius: 10px;
86+
box-shadow: none;
87+
visibility: hidden;
88+
height: fit-content;
89+
transition: visibility 0.2s, opacity 0.2s linear, bottom 0.2s linear,
90+
box-shadow 0.2s linear;
91+
min-width: 15rem;
92+
93+
&.show {
94+
opacity: 1;
95+
bottom: 0;
96+
display: block;
97+
visibility: visible;
98+
box-shadow: 0px 1px 12px 0px rgba(0, 0, 0, 0.25);
99+
}
100+
101+
.dropdown-item {
102+
display: flex;
103+
align-items: center;
104+
list-style: none;
105+
height: 50px;
106+
cursor: pointer;
107+
transition: 0.3s ease;
108+
padding: 0 1rem;
109+
gap: 1.2rem;
110+
111+
&:hover {
112+
background-color: #b8c1ce77;
113+
color: var(--font-color-4);
114+
}
115+
116+
&:first-child {
117+
border-radius: 10px 10px 0 0;
118+
}
119+
120+
&:last-child {
121+
border-radius: 0 0 10px 10px;
122+
}
123+
124+
&:only-child {
125+
border-radius: 10px;
126+
}
127+
128+
& .title {
129+
@include fs-5;
130+
@include fw-400;
131+
color: var(--fc-primary);
132+
text-overflow: ellipsis;
133+
overflow: hidden;
134+
white-space: nowrap;
135+
}
136+
137+
& i {
138+
@include fs-3;
139+
@include fw-500;
140+
color: var(--fc-primary);
141+
}
142+
}
143+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { BreadcrumbComponent } from './breadcrumb.component';
4+
5+
describe('BreadcrumbComponent', () => {
6+
let component: BreadcrumbComponent;
7+
let fixture: ComponentFixture<BreadcrumbComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
declarations: [ BreadcrumbComponent ]
12+
})
13+
.compileComponents();
14+
});
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(BreadcrumbComponent);
18+
component = fixture.componentInstance;
19+
fixture.detectChanges();
20+
});
21+
22+
it('should create', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
});
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
2+
import { Router } from '@angular/router';
3+
4+
import { FolderService } from '@services/folder.service';
5+
6+
import { IBreadcrumb } from '@interfaces/breadcrumb.interface';
7+
8+
@Component({
9+
selector: 'app-breadcrumb',
10+
templateUrl: './breadcrumb.component.html',
11+
styleUrls: ['./breadcrumb.component.scss']
12+
})
13+
export class BreadcrumbComponent implements OnInit {
14+
@ViewChild('dropdown') dropdown: ElementRef;
15+
private breadcrumb: IBreadcrumb[];
16+
breadcrumbShown: IBreadcrumb[] = [];
17+
breadcrumbDropdown: IBreadcrumb[] = [];
18+
private rootFolder: IBreadcrumb;
19+
constructor(
20+
private folderService: FolderService,
21+
private router: Router,
22+
) { }
23+
24+
ngOnInit(): void {
25+
this.buildBreadcrumb();
26+
this.router.events.subscribe(() => {
27+
this.buildBreadcrumb();
28+
});
29+
}
30+
31+
buildBreadcrumb(): void {
32+
this.breadcrumb = this.folderService.breadcrumb || [];
33+
this.rootFolder = this.breadcrumb[0];
34+
if (this.breadcrumb.length > 2) {
35+
this.breadcrumbShown = this.breadcrumb.slice(this.breadcrumb.length - 2);
36+
this.breadcrumbDropdown = this.breadcrumb.slice(0, this.breadcrumb.length - 2);
37+
} else {
38+
this.breadcrumbShown = this.breadcrumb;
39+
this.breadcrumbDropdown = [];
40+
}
41+
}
42+
43+
navigateToFolder(folderID: string): void {
44+
if (folderID === this.rootFolder._id) {
45+
this.router.navigate(['/']);
46+
} else {
47+
this.router.navigate(['/folder', folderID]);
48+
}
49+
}
50+
51+
changeVisibility(): void {
52+
this.dropdown.nativeElement.classList.toggle('show');
53+
}
54+
55+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { CommonModule } from '@angular/common';
2+
import { NgModule } from '@angular/core';
3+
import { RouterModule } from '@angular/router';
4+
5+
import { BreadcrumbComponent } from './breadcrumb/breadcrumb.component';
6+
7+
@NgModule({
8+
declarations: [
9+
BreadcrumbComponent
10+
],
11+
imports: [
12+
CommonModule,
13+
RouterModule
14+
],
15+
exports: [
16+
BreadcrumbComponent
17+
]
18+
})
19+
export class BreadcrumbsModule { }

src/app/shared/headers/actions/actions.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<header class="actions">
2+
<app-breadcrumb></app-breadcrumb>
23
<div class="new">
34
<button #btnDrop class="btn btn-primary btn-select" (click)="openDrop(btnDrop)">
45
Nuevo

0 commit comments

Comments
 (0)