+
@for (item of levels; track $index) {
@if (!$first) {
}
@@ -18,6 +16,9 @@
}
{{ item.title }}
+ @if ($first && !$last) {
+ ...
+ }
}
diff --git a/frontend/src/app/layout/breadcrumb/breadcrumb.component.ts b/frontend/src/app/layout/breadcrumb/breadcrumb.component.ts
index b4dbac4e..b3326543 100644
--- a/frontend/src/app/layout/breadcrumb/breadcrumb.component.ts
+++ b/frontend/src/app/layout/breadcrumb/breadcrumb.component.ts
@@ -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'
@@ -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
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)))
@@ -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
diff --git a/frontend/src/styles/components/_header.scss b/frontend/src/styles/components/_header.scss
index 957edac0..3409d57c 100644
--- a/frontend/src/styles/components/_header.scss
+++ b/frontend/src/styles/components/_header.scss
@@ -496,7 +496,16 @@
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) {
@@ -504,17 +513,11 @@
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 {
@@ -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 {
@@ -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 {