Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions frontend/src/app/layout/breadcrumb/breadcrumb.component.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
<div class="breadcrumb">
<div class="breadcrumb-content">
<div #breadcrumbContent class="breadcrumb-content">
@for (item of levels; track $index) {
@if (!$first) {
<svg
[class.breadcrumb-mobile-hidden]="!$last"
[lucideIcon]="icons.LucideChevronRight">
</svg>
}
<li
[class.breadcrumb-mobile-active]="!$first && $last"
[class.breadcrumb-mobile-hidden]="!$first && !$last"
routerLinkActive="active"
[routerLinkActiveOptions]="{matrixParams: 'ignored', queryParams: 'ignored', paths: 'exact', fragment: 'ignored'}">
<a [routerLink]="item.link">
Expand All @@ -18,6 +16,9 @@
}
<span>{{ item.title }}</span>
</a>
@if ($first && !$last) {
<span class="breadcrumb-overflow-marker" aria-hidden="true">...</span>
}
</li>
}
</div>
Expand Down
31 changes: 29 additions & 2 deletions frontend/src/app/layout/breadcrumb/breadcrumb.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, inject, OnDestroy } from '@angular/core'
import { AfterViewChecked, AfterViewInit, Component, ElementRef, inject, OnDestroy, ViewChild } from '@angular/core'
import { RouterLink, RouterLinkActive } from '@angular/router'
import type { LucideIcon } from '@lucide/angular'
import { LucideChevronRight, LucideDynamicIcon } from '@lucide/angular'
Expand All @@ -11,12 +11,15 @@ import { BreadCrumbUrl } from './breadcrumb.interfaces'
templateUrl: 'breadcrumb.component.html',
imports: [RouterLink, RouterLinkActive, LucideDynamicIcon]
})
export class BreadcrumbComponent implements OnDestroy {
export class BreadcrumbComponent implements AfterViewChecked, AfterViewInit, OnDestroy {
@ViewChild('breadcrumbContent') private breadcrumbContent!: ElementRef<HTMLDivElement>
protected readonly icons = { LucideChevronRight }
protected breadcrumbIcon: LucideIcon = null
protected levels: { link: string[] | string; title: string }[] = []
private readonly layout = inject(LayoutService)
private subscriptions: Subscription[] = []
private scrollToCurrentFolder = false
private resizeObserver: ResizeObserver | null = null

constructor() {
this.subscriptions.push(this.layout.breadcrumbNav.subscribe((url) => this.updateNav(url)))
Expand All @@ -25,9 +28,33 @@ export class BreadcrumbComponent implements OnDestroy {

ngOnDestroy() {
this.subscriptions.forEach((s) => s.unsubscribe())
this.resizeObserver?.disconnect()
}

ngAfterViewInit() {
const content = this.breadcrumbContent?.nativeElement
if (content) {
this.resizeObserver = new ResizeObserver(() => this.updateOverflow(content))
this.resizeObserver.observe(content)
this.updateOverflow(content)
}
}

ngAfterViewChecked() {
const content = this.breadcrumbContent?.nativeElement
if (content) this.updateOverflow(content)
if (this.scrollToCurrentFolder) {
this.scrollToCurrentFolder = false
if (content) content.scrollLeft = content.scrollWidth
}
}

private updateOverflow(content: HTMLDivElement) {
content.classList.toggle('breadcrumb-content--overflowing', content.scrollWidth > content.clientWidth)
}

updateNav(url: BreadCrumbUrl) {
this.scrollToCurrentFolder = true
const splicing = url.splicing || 1
const translating = url.translating || false
const sameLink = url.sameLink || false
Expand Down
38 changes: 28 additions & 10 deletions frontend/src/styles/components/_header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -496,25 +496,28 @@
justify-content: flex-start;
width: 100%;
min-width: 0;
@include text-truncate-utility;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;

&::-webkit-scrollbar {
display: none;
}

@include no-select-pointer-utility;

@include media-breakpoint-down(sm) {
.breadcrumb-mobile-active > a {
color: $gray-lightest !important;
}

.breadcrumb-mobile-hidden {
display: none;
}
}

> li {
&:not(:first-child) {
@include text-truncate-utility;
}

display: inline;
flex: 0 0 auto;
list-style: none;

&.active {
Expand All @@ -524,12 +527,23 @@
}

&:first-child {
position: sticky;
left: 0;
z-index: 1;
display: flex;
align-items: center;
overflow: visible;
background-color: $app-header-bg;

> a {
gap: .4rem;
margin-right: .3rem;
}

> .breadcrumb-overflow-marker {
display: none;
margin-right: .3rem;
}
}

> a {
Expand All @@ -550,10 +564,14 @@
}

> span {
min-width: 0;
@include text-truncate-utility;
white-space: nowrap;
}
}

}

&.breadcrumb-content--overflowing > li:first-child > .breadcrumb-overflow-marker {
display: inline;
}

> .lucide {
Expand Down
Loading