chore: Refactor page headers (#2282)

- Refactors all page headers to use new `pageHeader`
- Removes border under org name/title in the org dashboard
This commit is contained in:
sua yoo 2025-01-07 16:21:32 -08:00 committed by sua yoo
parent b36ed9f730
commit 0c81a2f89e
No known key found for this signature in database
GPG Key ID: 5AD1B4C02D4F0567
10 changed files with 119 additions and 142 deletions

View File

@ -1,14 +1,11 @@
import clsx from "clsx";
import { html, nothing, type TemplateResult } from "lit";
import { ifDefined } from "lit/directives/if-defined.js";
import { html as staticHtml, unsafeStatic } from "lit/static-html.js";
import { pageNav, pageTitle } from "./pageHeader";
import { pageHeader, pageNav } from "./pageHeader";
import { tw } from "@/utils/tailwind";
type Content = string | TemplateResult | typeof nothing;
export function pageHeading({
content,
level = 2,
@ -29,48 +26,6 @@ export function pageHeading({
`;
}
// TODO consolidate with pageHeader.ts https://github.com/webrecorder/browsertrix/issues/2197
export function pageHeader({
title,
prefix,
suffix,
secondary,
actions,
border = true,
classNames,
}: {
title?: Content;
prefix?: Content;
suffix?: Content;
secondary?: Content;
actions?: Content;
border?: boolean;
classNames?: typeof tw;
}) {
return html`
<header
class=${clsx(
tw`mt-5 flex flex-col gap-3 lg:flex-row`,
border && tw`border-b pb-3`,
classNames,
)}
>
<div class="flex flex-1 flex-col gap-2">
<div class="flex flex-wrap items-center gap-2.5">
${prefix}${pageTitle(title)}${suffix}
</div>
${secondary}
</div>
${actions
? html`<div class="ml-auto flex flex-shrink-0 items-center gap-2">
${actions}
</div>`
: nothing}
</header>
`;
}
export function page(
header: Parameters<typeof pageHeader>[0] & {
breadcrumbs?: Parameters<typeof pageNav>[0];

View File

@ -7,6 +7,8 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { NavigateController } from "@/controllers/navigate";
import { tw } from "@/utils/tailwind";
type Content = string | TemplateResult | typeof nothing;
export type Breadcrumb = {
href?: string;
content?: string | TemplateResult;
@ -101,22 +103,42 @@ export function pageNav(breadcrumbs: Breadcrumb[]) {
return pageBreadcrumbs(breadcrumbs);
}
// TODO consolidate with page.ts https://github.com/webrecorder/browsertrix/issues/2197
export function pageHeader(
title?: string | TemplateResult,
suffix?: TemplateResult<1>,
classNames?: string,
) {
export function pageHeader({
title,
prefix,
suffix,
secondary,
actions,
border = true,
classNames,
}: {
title?: Content;
prefix?: Content;
suffix?: Content;
secondary?: Content;
actions?: Content;
border?: boolean;
classNames?: typeof tw | string;
}) {
return html`
<header
class=${clsx(
"mt-5 flex items-end flex-wrap justify-between gap-2 border-b pb-3",
tw`mt-5 flex flex-col gap-3 lg:flex-row`,
border && tw`border-b pb-3`,
classNames,
)}
>
${pageTitle(title)}
${suffix
? html`<div class="ml-auto flex items-center gap-2">${suffix}</div>`
<div class="flex flex-1 flex-col gap-2">
<div class="flex flex-wrap items-center gap-2.5">
${prefix}${pageTitle(title)}${suffix}
</div>
${secondary}
</div>
${actions
? html`<div class="ml-auto flex flex-shrink-0 items-center gap-2">
${actions}
</div>`
: nothing}
</header>
`;

View File

@ -144,7 +144,10 @@ export class AccountSettings extends BtrixElement {
title=${msg("Account Settings")}
></btrix-document-title>
${pageHeader(msg("Account Settings"), undefined, tw`mb-3 lg:mb-5`)}
${pageHeader({
title: msg("Account Settings"),
classNames: tw`mb-3 lg:mb-5`,
})}
<btrix-tab-list activePanel=${this.activeTab} hideIndicator>
<header slot="header" class="flex h-7 items-end justify-between">

View File

@ -264,29 +264,28 @@ export class CrawlsList extends BtrixElement {
return html`
<main>
<div class="contents">
${pageHeader(
msg("Archived Items"),
when(
this.isCrawler,
() => html`
<sl-tooltip
content=${msg("Org Storage Full")}
?disabled=${!this.org?.storageQuotaReached}
>
<sl-button
size="small"
variant="primary"
@click=${() => (this.isUploadingArchive = true)}
?disabled=${isArchivingDisabled(this.org)}
${pageHeader({
title: msg("Archived Items"),
actions: this.isCrawler
? html`
<sl-tooltip
content=${msg("Org Storage Full")}
?disabled=${!this.org?.storageQuotaReached}
>
<sl-icon slot="prefix" name="upload"></sl-icon>
${msg("Upload WACZ")}
</sl-button>
</sl-tooltip>
`,
),
tw`mb-3`,
)}
<sl-button
size="small"
variant="primary"
@click=${() => (this.isUploadingArchive = true)}
?disabled=${isArchivingDisabled(this.org)}
>
<sl-icon slot="prefix" name="upload"></sl-icon>
${msg("Upload WACZ")}
</sl-button>
</sl-tooltip>
`
: nothing,
classNames: tw`mb-3`,
})}
<div class="mb-3 flex gap-2">
${listTypes.map(({ label, itemType, icon }) => {
const isSelected = itemType === this.itemType;

View File

@ -93,30 +93,29 @@ export class BrowserProfilesList extends BtrixElement {
}
render() {
return html`${pageHeader(
msg("Browser Profiles"),
when(
this.isCrawler,
() => html`
<sl-button
variant="primary"
size="small"
?disabled=${isArchivingDisabled(this.org)}
@click=${() => {
this.dispatchEvent(
new CustomEvent("select-new-dialog", {
detail: "browser-profile",
}) as SelectNewDialogEvent,
);
}}
>
<sl-icon slot="prefix" name="plus-lg"></sl-icon>
${msg("New Browser Profile")}
</sl-button>
`,
),
tw`mb-3`,
)}
return html`${pageHeader({
title: msg("Browser Profiles"),
actions: this.isCrawler
? html`
<sl-button
variant="primary"
size="small"
?disabled=${isArchivingDisabled(this.org)}
@click=${() => {
this.dispatchEvent(
new CustomEvent("select-new-dialog", {
detail: "browser-profile",
}) as SelectNewDialogEvent,
);
}}
>
<sl-icon slot="prefix" name="plus-lg"></sl-icon>
${msg("New Browser Profile")}
</sl-button>
`
: undefined,
classNames: tw`mb-3`,
})}
<div class="pb-1">${this.renderTable()}</div>`;
}

View File

@ -13,8 +13,7 @@ import type { MarkdownEditor } from "@/components/ui/markdown-editor";
import type { PageChangeEvent } from "@/components/ui/pagination";
import { SelectCollectionAccess } from "@/features/collections/select-collection-access";
import type { ShareCollection } from "@/features/collections/share-collection";
import { pageHeader } from "@/layouts/page";
import { pageNav, type Breadcrumb } from "@/layouts/pageHeader";
import { pageHeader, pageNav, type Breadcrumb } from "@/layouts/pageHeader";
import type {
APIPaginatedList,
APIPaginationQuery,

View File

@ -132,12 +132,10 @@ export class CollectionsList extends BtrixElement {
render() {
return html`
<div class="contents">
${pageHeader(
msg("Collections"),
when(
this.isCrawler,
() => html`
<sl-button
${pageHeader({
title: msg("Collections"),
actions: this.isCrawler
? html` <sl-button
variant="primary"
size="small"
?disabled=${!this.org || this.org.readOnly}
@ -145,11 +143,10 @@ export class CollectionsList extends BtrixElement {
>
<sl-icon slot="prefix" name="plus-lg"></sl-icon>
${msg("New Collection")}
</sl-button>
`,
),
tw`border-b-transparent`,
)}
</sl-button>`
: nothing,
classNames: tw`border-b-transparent`,
})}
</div>
<link rel="preload" as="image" href=${noCollectionsImg} />

View File

@ -15,6 +15,7 @@ import { RouteNamespace } from "@/routes";
import type { PublicCollection } from "@/types/collection";
import type { PublicOrgCollections } from "@/types/org";
import { humanizeExecutionSeconds } from "@/utils/executionTimeFormatter";
import { tw } from "@/utils/tailwind";
type Metrics = {
storageUsedBytes: number;
@ -84,9 +85,9 @@ export class Dashboard extends BtrixElement {
this.metrics.storageUsedBytes >= this.metrics.storageQuotaBytes;
return html`
${pageHeader(
this.userOrg?.name,
html`
${pageHeader({
title: this.userOrg?.name,
actions: html`
${when(
this.appState.isAdmin,
() =>
@ -153,8 +154,8 @@ export class Dashboard extends BtrixElement {
</sl-dropdown>`,
)}
`,
"mb-6",
)}
classNames: tw`border-b-transparent lg:mb-2`,
})}
<main>
<div class="mb-10 flex flex-col gap-6 md:flex-row">
${this.renderCard(

View File

@ -1,7 +1,7 @@
import { localized, msg, str } from "@lit/localize";
import type { SlInput } from "@shoelace-style/shoelace";
import { serialize } from "@shoelace-style/shoelace/dist/utilities/form.js";
import { html, unsafeCSS, type PropertyValues } from "lit";
import { html, nothing, unsafeCSS, type PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { choose } from "lit/directives/choose.js";
import { ifDefined } from "lit/directives/if-defined.js";
@ -136,21 +136,23 @@ export class OrgSettings extends BtrixElement {
}
render() {
return html` ${pageHeader(
msg("Org Settings"),
when(
this.userInfo?.orgs && this.userInfo.orgs.length > 1 && this.userOrg,
(userOrg) => html`
<div class="text-neutral-400">
${msg(
html`Viewing
<strong class="font-medium">${userOrg.name}</strong>`,
)}
</div>
`,
),
tw`mb-3 lg:mb-5`,
)}
return html` ${pageHeader({
title: msg("Org Settings"),
actions:
this.userInfo?.orgs && this.userInfo.orgs.length > 1 && this.userOrg
? html`
<div class="text-neutral-400">
${msg(
html`Viewing
<strong class="font-medium"
>${this.userOrg.name}</strong
>`,
)}
</div>
`
: nothing,
classNames: tw`mb-3 lg:mb-5`,
})}
<btrix-tab-list activePanel=${this.activePanel} hideIndicator>
<header slot="header" class="flex h-7 items-end justify-between">

View File

@ -208,9 +208,9 @@ export class WorkflowsList extends BtrixElement {
render() {
return html`
<div class="contents">
${pageHeader(
msg("Crawl Workflows"),
html`
${pageHeader({
title: msg("Crawl Workflows"),
actions: html`
${when(
this.appState.isAdmin,
() =>
@ -304,8 +304,8 @@ export class WorkflowsList extends BtrixElement {
`,
)}
`,
tw`border-b-transparent`,
)}
classNames: tw`border-b-transparent`,
})}
<div class="sticky top-2 z-10 mb-3 rounded-lg border bg-neutral-50 p-4">
${this.renderControls()}
</div>