Skip to content

Commit 880e273

Browse files
authored
feat: Breadcrumb navigation (#2053)
- Adds breadcrumb navigation to all detail views, returning to correct origin for workflow and collection items - Refactors list page headers into layout utility - Refactors crawl tab labels and renames "Files" tab to "WACZ Files"
1 parent e4107d0 commit 880e273

20 files changed

+666
-452
lines changed

docs/user-guide/archived-items.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ For more details on navigating web archives within ReplayWeb.page, see the [Repl
5858

5959
While crawling, Browsertrix will output one or more WACZ files — the crawler aims to output files in consistently sized chunks, and each [crawler instance](workflow-setup.md#crawler-instances) will output separate WACZ files.
6060

61-
The files tab lists the individually downloadable WACZ files that make up the archived item as well as their file sizes and backup status.
61+
The **WACZ Files** tab lists the individually downloadable WACZ files that make up the archived item as well as their file sizes and backup status.
6262

63-
To download an entire archived item as a single WACZ file, click the _Download Item_ button at the top of the files tab or the _Download Item_ entry in the crawl's _Actions_ menu.
63+
To download an entire archived item as a single WACZ file, click the _Download Item_ button at the top of the **WACZ Files** tab or the _Download Item_ entry in the crawl's _Actions_ menu.
6464

6565
To combine multiple archived items and download them all as a single WACZ file, add them to a collection and [download the collection](collections.md#downloading-collections).
6666

frontend/src/controllers/navigate.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ const NAVIGATE_EVENT_NAME: keyof NavigateEventMap = "btrix-navigate";
1919
* Manage app navigation
2020
*/
2121
export class NavigateController implements ReactiveController {
22+
static createNavigateEvent = (detail: NavigateEventDetail) =>
23+
new CustomEvent<NavigateEventDetail>(NAVIGATE_EVENT_NAME, {
24+
detail,
25+
bubbles: true,
26+
composed: true,
27+
});
28+
2229
private readonly host: ReactiveControllerHost & EventTarget;
2330

2431
get orgBasePath() {
@@ -43,10 +50,11 @@ export class NavigateController implements ReactiveController {
4350
resetScroll = true,
4451
replace = false,
4552
): void => {
46-
const evt = new CustomEvent<NavigateEventDetail>(NAVIGATE_EVENT_NAME, {
47-
detail: { url, state, resetScroll, replace },
48-
bubbles: true,
49-
composed: true,
53+
const evt = NavigateController.createNavigateEvent({
54+
url,
55+
state,
56+
resetScroll,
57+
replace,
5058
});
5159
this.host.dispatchEvent(evt);
5260
};
@@ -88,13 +96,9 @@ export class NavigateController implements ReactiveController {
8896
return;
8997
}
9098

91-
const evt = new CustomEvent<NavigateEventDetail>(NAVIGATE_EVENT_NAME, {
92-
detail: {
93-
url: (event.currentTarget as HTMLAnchorElement).href,
94-
resetScroll,
95-
},
96-
bubbles: true,
97-
composed: true,
99+
const evt = NavigateController.createNavigateEvent({
100+
url: (event.currentTarget as HTMLAnchorElement).href,
101+
resetScroll,
98102
});
99103
this.host.dispatchEvent(evt);
100104
};

frontend/src/layouts/pageHeader.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import type { SlBreadcrumb } from "@shoelace-style/shoelace";
2+
import clsx from "clsx";
3+
import { html, nothing, type TemplateResult } from "lit";
4+
import { ifDefined } from "lit/directives/if-defined.js";
5+
6+
import { NavigateController } from "@/controllers/navigate";
7+
8+
export type Breadcrumb = {
9+
href?: string;
10+
content?: string | TemplateResult;
11+
};
12+
13+
function navigateBreadcrumb(e: MouseEvent, href: string) {
14+
e.preventDefault();
15+
16+
const el = e.target as SlBreadcrumb;
17+
const evt = NavigateController.createNavigateEvent({
18+
url: href,
19+
resetScroll: true,
20+
});
21+
22+
el.dispatchEvent(evt);
23+
}
24+
25+
export function pageBreadcrumbs(breadcrumbs: Breadcrumb[]) {
26+
return html`
27+
<sl-breadcrumb class="part-[base]:h-6">
28+
${breadcrumbs.length
29+
? breadcrumbs.map(
30+
({ href, content }) => html`
31+
<sl-breadcrumb-item
32+
href=${ifDefined(href)}
33+
@click=${href
34+
? (e: MouseEvent) => navigateBreadcrumb(e, href)
35+
: undefined}
36+
>
37+
${content || html`<sl-skeleton class="w-48"></sl-skeleton>`}
38+
</sl-breadcrumb-item>
39+
`,
40+
)
41+
: html`<sl-breadcrumb-item>
42+
<sl-skeleton class="w-48"></sl-skeleton>
43+
</sl-breadcrumb-item>
44+
<sl-breadcrumb-item>
45+
<sl-skeleton class="w-48"></sl-skeleton>
46+
</sl-breadcrumb-item>`}
47+
</sl-breadcrumb>
48+
`;
49+
}
50+
51+
export function pageTitle(title?: string | TemplateResult) {
52+
return html`
53+
<h1 class="min-w-0 text-xl font-semibold leading-8">
54+
${title || html`<sl-skeleton class="my-.5 h-5 w-60"></sl-skeleton>`}
55+
</h1>
56+
`;
57+
}
58+
59+
export function pageHeader(
60+
title?: string | TemplateResult,
61+
suffix?: TemplateResult<1>,
62+
classNames?: string,
63+
) {
64+
return html`
65+
<header
66+
class=${clsx(
67+
"mt-5 flex items-end flex-wrap justify-between gap-2 border-b pb-3",
68+
classNames,
69+
)}
70+
>
71+
${pageTitle(title)}
72+
${suffix
73+
? html`<div class="ml-auto flex items-center gap-2">${suffix}</div>`
74+
: nothing}
75+
</header>
76+
`;
77+
}

0 commit comments

Comments
 (0)