refactor: Update component used in tabbed views (#2300)
- Refactors instances of `btrix-tab-list` except in workflow editor in preparation for https://github.com/webrecorder/browsertrix/issues/2169 - Removes the visual space above navigation item since many tab headings describe the first section in the tab, rather than the entire tab itself
This commit is contained in:
parent
a028ed1808
commit
c563b622fe
@ -1,3 +1,4 @@
|
||||
import clsx from "clsx";
|
||||
import { html, type PropertyValues } from "lit";
|
||||
import {
|
||||
customElement,
|
||||
@ -9,9 +10,18 @@ import type { TabClickDetail, TabGroupTab } from "./tab";
|
||||
import { type TabGroupPanel } from "./tab-panel";
|
||||
|
||||
import { TailwindElement } from "@/classes/TailwindElement";
|
||||
import { tw } from "@/utils/tailwind";
|
||||
|
||||
/**
|
||||
* TODO consolidate with btrix-tab-list
|
||||
* @example Usage:
|
||||
* ```ts
|
||||
* <btrix-tab-group>
|
||||
* <btrix-tab-group-tab slot="nav" panel="first">First</btrix-tab-group-tab>
|
||||
* <btrix-tab-group-tab slot="nav" panel="second">Second</btrix-tab-group-tab>
|
||||
* <btrix-tab-group-panel name="first">First tab content</btrix-tab-group-panel>
|
||||
* <btrix-tab-group-panel name="second">First tab content</btrix-tab-group-panel>
|
||||
* </btrix-tab-group>
|
||||
* ```
|
||||
*/
|
||||
@customElement("btrix-tab-group")
|
||||
export class TabGroup extends TailwindElement {
|
||||
@ -19,6 +29,10 @@ export class TabGroup extends TailwindElement {
|
||||
@property({ type: String, reflect: false })
|
||||
active = "";
|
||||
|
||||
/* Nav placement */
|
||||
@property({ type: String })
|
||||
placement: "top" | "start" = "top";
|
||||
|
||||
@property({ type: String, noAccessor: true, reflect: true })
|
||||
role = "tablist";
|
||||
|
||||
@ -48,10 +62,27 @@ export class TabGroup extends TailwindElement {
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="mb-4 flex gap-2" @keydown=${this.onKeyDown}>
|
||||
<slot name="nav" @btrix-select-tab=${this.onSelectTab}></slot>
|
||||
<div
|
||||
class=${clsx(
|
||||
tw`flex flex-col`,
|
||||
this.placement === "start" && tw`gap-8 lg:flex-row`,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
class=${clsx(
|
||||
tw`flex flex-1 flex-col gap-2`,
|
||||
this.placement === "start"
|
||||
? tw`lg:sticky lg:top-2 lg:max-w-[16.5rem] lg:self-start`
|
||||
: tw`lg:flex-row`,
|
||||
)}
|
||||
@keydown=${this.onKeyDown}
|
||||
>
|
||||
<slot name="nav" @btrix-select-tab=${this.onSelectTab}></slot>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
<slot></slot>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -3,9 +3,6 @@ import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import { TailwindElement } from "@/classes/TailwindElement";
|
||||
|
||||
/**
|
||||
* TODO consolidate with btrix-tab-list btrix-tab-panel
|
||||
*/
|
||||
@customElement("btrix-tab-group-panel")
|
||||
export class TabGroupPanel extends TailwindElement {
|
||||
@property({ type: String })
|
||||
|
@ -6,8 +6,6 @@ import { NavigationButton } from "@/components/ui/navigation/navigation-button";
|
||||
export type TabClickDetail = { panel: string };
|
||||
|
||||
/**
|
||||
* TODO consolidate with btrix-tab-list btrix-tab
|
||||
*
|
||||
* @fires btrix-select-tab
|
||||
*/
|
||||
@customElement("btrix-tab-group-tab")
|
||||
|
@ -9,6 +9,8 @@ const DEFAULT_PANEL_ID = "default-panel";
|
||||
export const TWO_COL_SCREEN_MIN_CSS = css`64.5rem`;
|
||||
|
||||
/**
|
||||
* @deprecated Use `btrix-tab-group`
|
||||
*
|
||||
* Tab list
|
||||
*
|
||||
* Usage example:
|
||||
@ -45,6 +47,9 @@ export class TabPanel extends TailwindElement {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `btrix-tab-group`
|
||||
*/
|
||||
@customElement("btrix-tab")
|
||||
export class Tab extends TailwindElement {
|
||||
// ID of panel the tab labels/controls
|
||||
@ -76,6 +81,9 @@ export class Tab extends TailwindElement {
|
||||
type TabElement = Tab & HTMLElement;
|
||||
type TabPanelElement = TabPanel & HTMLElement;
|
||||
|
||||
/**
|
||||
* @deprecated Use `btrix-tab-group`
|
||||
*/
|
||||
@customElement("btrix-tab-list")
|
||||
export class TabList extends TailwindElement {
|
||||
static styles = css`
|
||||
|
@ -149,25 +149,15 @@ export class AccountSettings extends BtrixElement {
|
||||
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">
|
||||
${choose(
|
||||
this.activeTab,
|
||||
[
|
||||
[Tab.Profile, () => html`<h2>${msg("Display Name")}</h2>`],
|
||||
[Tab.Security, () => html`<h2>${msg("Password")}</h2>`],
|
||||
],
|
||||
() => html`<h2>${this.tabLabels[this.activeTab]}</h2>`,
|
||||
)}
|
||||
</header>
|
||||
<btrix-tab-group active=${this.activeTab} placement="start">
|
||||
${this.renderTab(Tab.Profile)} ${this.renderTab(Tab.Security)}
|
||||
<btrix-tab-panel name=${Tab.Profile}>
|
||||
<btrix-tab-group-panel name=${Tab.Profile}>
|
||||
${this.renderProfile()}
|
||||
</btrix-tab-panel>
|
||||
<btrix-tab-panel name=${Tab.Security}>
|
||||
</btrix-tab-group-panel>
|
||||
<btrix-tab-group-panel name=${Tab.Security}>
|
||||
${this.renderSecurity()}
|
||||
</btrix-tab-panel>
|
||||
</btrix-tab-list>
|
||||
</btrix-tab-group-panel>
|
||||
</btrix-tab-group>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -175,6 +165,7 @@ export class AccountSettings extends BtrixElement {
|
||||
if (!this.userInfo) return;
|
||||
|
||||
return html`
|
||||
<h2 class="mb-2 text-lg font-medium">${msg("Display Name")}</h2>
|
||||
<form class="mb-5 rounded-lg border" @submit=${this.onSubmitName}>
|
||||
<div class="p-4">
|
||||
<p class="mb-2">
|
||||
@ -257,6 +248,7 @@ export class AccountSettings extends BtrixElement {
|
||||
|
||||
private renderSecurity() {
|
||||
return html`
|
||||
<h2 class="mb-2 text-lg font-medium">${msg("Password")}</h2>
|
||||
<form class="rounded-lg border" @submit=${this.onSubmitPassword}>
|
||||
<div class="p-4">
|
||||
<sl-input
|
||||
@ -304,14 +296,11 @@ export class AccountSettings extends BtrixElement {
|
||||
}
|
||||
|
||||
private renderTab(name: Tab) {
|
||||
const isActive = name === this.activeTab;
|
||||
|
||||
return html`
|
||||
<btrix-navigation-button
|
||||
<btrix-tab-group-tab
|
||||
slot="nav"
|
||||
panel=${name}
|
||||
href=${`/account/settings/${name}`}
|
||||
.active=${isActive}
|
||||
aria-selected=${isActive}
|
||||
@click=${this.navigate.link}
|
||||
>
|
||||
${choose(name, [
|
||||
@ -325,7 +314,7 @@ export class AccountSettings extends BtrixElement {
|
||||
],
|
||||
])}
|
||||
${this.tabLabels[name]}
|
||||
</btrix-navigation-button>
|
||||
</btrix-tab-group-tab>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -543,7 +543,7 @@ export class ArchivedItemDetail extends BtrixElement {
|
||||
};
|
||||
return html`
|
||||
<nav
|
||||
class="sticky top-0 -mx-3 flex flex-row gap-2 overflow-x-auto px-3 pb-4 text-center md:mt-10 md:flex-col md:text-start"
|
||||
class="sticky top-0 -mx-3 flex flex-row gap-2 overflow-x-auto px-3 pb-4 text-center md:flex-col md:text-start"
|
||||
role="menu"
|
||||
>
|
||||
${renderNavItem({
|
||||
@ -716,7 +716,7 @@ export class ArchivedItemDetail extends BtrixElement {
|
||||
|
||||
private renderTitle(title: string | TemplateResult<1>) {
|
||||
return html`<h2
|
||||
class="flex items-center gap-2 text-lg font-semibold leading-8"
|
||||
class="flex items-center gap-2 text-lg font-medium leading-8"
|
||||
>
|
||||
${title}
|
||||
</h2>`;
|
||||
|
@ -1,7 +1,13 @@
|
||||
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, nothing, unsafeCSS, type PropertyValues } from "lit";
|
||||
import {
|
||||
html,
|
||||
nothing,
|
||||
unsafeCSS,
|
||||
type PropertyValues,
|
||||
type TemplateResult,
|
||||
} 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";
|
||||
@ -154,53 +160,7 @@ export class OrgSettings extends BtrixElement {
|
||||
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">
|
||||
${choose(
|
||||
this.activePanel,
|
||||
[
|
||||
[
|
||||
"members",
|
||||
() => html`
|
||||
<h3>${msg("Active Members")}</h3>
|
||||
<sl-button
|
||||
href=${`${this.navigate.orgBasePath}/settings/members?invite`}
|
||||
variant="primary"
|
||||
size="small"
|
||||
@click=${this.navigate.link}
|
||||
>
|
||||
<sl-icon
|
||||
slot="prefix"
|
||||
name="person-add"
|
||||
aria-hidden="true"
|
||||
library="default"
|
||||
></sl-icon>
|
||||
${msg("Invite New Member")}
|
||||
</sl-button>
|
||||
`,
|
||||
],
|
||||
["billing", () => html`<h3>${msg("Current Plan")}</h3> `],
|
||||
[
|
||||
"crawling-defaults",
|
||||
() =>
|
||||
html`<h3 class="flex items-center gap-2">
|
||||
${msg("Crawling Defaults")}
|
||||
<sl-tooltip
|
||||
content=${msg(
|
||||
"Default settings for all new crawl workflows. Existing workflows will not be affected.",
|
||||
)}
|
||||
>
|
||||
<sl-icon
|
||||
class="text-base text-neutral-500"
|
||||
name="info-circle"
|
||||
></sl-icon>
|
||||
</sl-tooltip>
|
||||
</h3>`,
|
||||
],
|
||||
],
|
||||
() => html`<h3>${this.tabLabels[this.activePanel]}</h3>`,
|
||||
)}
|
||||
</header>
|
||||
<btrix-tab-group active=${this.activePanel} placement="start">
|
||||
${this.renderTab("information", "settings")}
|
||||
${this.renderTab("members", "settings/members")}
|
||||
${when(this.appState.settings?.billingEnabled, () =>
|
||||
@ -208,34 +168,83 @@ export class OrgSettings extends BtrixElement {
|
||||
)}
|
||||
${this.renderTab("crawling-defaults", "settings/crawling-defaults")}
|
||||
|
||||
<btrix-tab-panel name="information">
|
||||
<btrix-tab-group-panel name="information">
|
||||
${this.renderPanelHeader({ title: msg("General") })}
|
||||
${this.renderInformation()}
|
||||
<btrix-org-settings-profile></btrix-org-settings-profile>
|
||||
${this.renderApi()}
|
||||
</btrix-tab-panel>
|
||||
<btrix-tab-panel name="members">
|
||||
</btrix-tab-group-panel>
|
||||
<btrix-tab-group-panel name="members">
|
||||
${this.renderPanelHeader({
|
||||
title: msg("Active Members"),
|
||||
actions: html`
|
||||
<sl-button
|
||||
href=${`${this.navigate.orgBasePath}/settings/members?invite`}
|
||||
variant="primary"
|
||||
size="small"
|
||||
@click=${this.navigate.link}
|
||||
>
|
||||
<sl-icon
|
||||
slot="prefix"
|
||||
name="person-add"
|
||||
aria-hidden="true"
|
||||
library="default"
|
||||
></sl-icon>
|
||||
${msg("Invite New Member")}
|
||||
</sl-button>
|
||||
`,
|
||||
})}
|
||||
${this.renderMembers()}
|
||||
</btrix-tab-panel>
|
||||
<btrix-tab-panel name="billing">
|
||||
</btrix-tab-group-panel>
|
||||
<btrix-tab-group-panel name="billing">
|
||||
${this.renderPanelHeader({ title: msg("Current Plan") })}
|
||||
<btrix-org-settings-billing
|
||||
.salesEmail=${this.appState.settings?.salesEmail}
|
||||
></btrix-org-settings-billing>
|
||||
</btrix-tab-panel>
|
||||
<btrix-tab-panel name="crawling-defaults">
|
||||
</btrix-tab-group-panel>
|
||||
<btrix-tab-group-panel name="crawling-defaults">
|
||||
${this.renderPanelHeader({
|
||||
title: msg("Crawling Defaults"),
|
||||
actions: html`
|
||||
<sl-tooltip
|
||||
content=${msg(
|
||||
"Default settings for all new crawl workflows. Existing workflows will not be affected.",
|
||||
)}
|
||||
>
|
||||
<sl-icon
|
||||
class="text-base text-neutral-500"
|
||||
name="info-circle"
|
||||
></sl-icon>
|
||||
</sl-tooltip>
|
||||
`,
|
||||
})}
|
||||
<btrix-org-settings-crawling-defaults></btrix-org-settings-crawling-defaults>
|
||||
</btrix-tab-panel>
|
||||
</btrix-tab-list>`;
|
||||
</btrix-tab-group-panel>
|
||||
</btrix-tab-group>`;
|
||||
}
|
||||
|
||||
private renderPanelHeader({
|
||||
title,
|
||||
actions,
|
||||
}: {
|
||||
title: string;
|
||||
actions?: TemplateResult;
|
||||
}) {
|
||||
return html`
|
||||
<header class="mb-2 flex items-center justify-between">
|
||||
<h3 class="text-lg font-medium">${title}</h3>
|
||||
${actions}
|
||||
</header>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderTab(name: Tab, path: string) {
|
||||
const isActive = name === this.activePanel;
|
||||
return html`
|
||||
<btrix-navigation-button
|
||||
<btrix-tab-group-tab
|
||||
slot="nav"
|
||||
panel=${name}
|
||||
href=${`${this.navigate.orgBasePath}/${path}`}
|
||||
.active=${isActive}
|
||||
@click=${this.navigate.link}
|
||||
aria-selected=${isActive}
|
||||
>
|
||||
${choose(name, [
|
||||
[
|
||||
@ -250,7 +259,7 @@ export class OrgSettings extends BtrixElement {
|
||||
],
|
||||
])}
|
||||
${this.tabLabels[name]}
|
||||
</btrix-navigation-button>
|
||||
</btrix-tab-group-tab>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ import type { Crawl, Seed, Workflow, WorkflowParams } from "./types";
|
||||
|
||||
import { BtrixElement } from "@/classes/BtrixElement";
|
||||
import type { PageChangeEvent } from "@/components/ui/pagination";
|
||||
import { type IntersectEvent } from "@/components/utils/observable";
|
||||
import { ClipboardController } from "@/controllers/clipboard";
|
||||
import type { CrawlLog } from "@/features/archived-items/crawl-logs";
|
||||
import { CrawlStatus } from "@/features/archived-items/crawl-status";
|
||||
@ -113,8 +112,6 @@ export class WorkflowDetail extends BtrixElement {
|
||||
|
||||
private timerId?: number;
|
||||
|
||||
private isPanelHeaderVisible?: boolean;
|
||||
|
||||
private getWorkflowPromise?: Promise<Workflow>;
|
||||
private getSeedsPromise?: Promise<APIPaginatedList<Seed>>;
|
||||
|
||||
@ -180,13 +177,6 @@ export class WorkflowDetail extends BtrixElement {
|
||||
changedProperties.has("activePanel") &&
|
||||
this.activePanel
|
||||
) {
|
||||
if (!this.isPanelHeaderVisible) {
|
||||
// Scroll panel header into view
|
||||
this.querySelector("btrix-tab-list")?.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
|
||||
if (this.activePanel === "crawls") {
|
||||
void this.fetchCrawls();
|
||||
}
|
||||
@ -456,22 +446,20 @@ export class WorkflowDetail extends BtrixElement {
|
||||
}
|
||||
|
||||
private readonly renderTabList = () => html`
|
||||
<btrix-tab-list activePanel=${ifDefined(this.activePanel)} hideIndicator>
|
||||
<btrix-observable
|
||||
slot="header"
|
||||
@intersect=${({ detail }: IntersectEvent) =>
|
||||
(this.isPanelHeaderVisible = detail.entry.isIntersecting)}
|
||||
<btrix-tab-group active=${ifDefined(this.activePanel)} placement="start">
|
||||
<header
|
||||
class="mb-2 flex h-7 items-center justify-between text-lg font-medium"
|
||||
>
|
||||
<header class="flex h-5 items-center justify-between">
|
||||
${this.renderPanelHeader()}
|
||||
</header>
|
||||
</btrix-observable>
|
||||
${this.renderPanelHeader()}
|
||||
</header>
|
||||
|
||||
${this.renderTab("crawls")} ${this.renderTab("watch")}
|
||||
${this.renderTab("logs")} ${this.renderTab("settings")}
|
||||
|
||||
<btrix-tab-panel name="crawls">${this.renderCrawls()}</btrix-tab-panel>
|
||||
<btrix-tab-panel name="watch">
|
||||
<btrix-tab-group-panel name="crawls">
|
||||
${this.renderCrawls()}
|
||||
</btrix-tab-group-panel>
|
||||
<btrix-tab-group-panel name="watch">
|
||||
${until(
|
||||
this.getWorkflowPromise?.then(
|
||||
() => html`
|
||||
@ -486,12 +474,14 @@ export class WorkflowDetail extends BtrixElement {
|
||||
`,
|
||||
),
|
||||
)}
|
||||
</btrix-tab-panel>
|
||||
<btrix-tab-panel name="logs">${this.renderLogs()}</btrix-tab-panel>
|
||||
<btrix-tab-panel name="settings">
|
||||
</btrix-tab-group-panel>
|
||||
<btrix-tab-group-panel name="logs">
|
||||
${this.renderLogs()}
|
||||
</btrix-tab-group-panel>
|
||||
<btrix-tab-group-panel name="settings">
|
||||
${this.renderSettings()}
|
||||
</btrix-tab-panel>
|
||||
</btrix-tab-list>
|
||||
</btrix-tab-group-panel>
|
||||
</btrix-tab-group>
|
||||
`;
|
||||
|
||||
private renderPanelHeader() {
|
||||
@ -582,11 +572,11 @@ export class WorkflowDetail extends BtrixElement {
|
||||
private renderTab(tabName: Tab, { disabled = false } = {}) {
|
||||
const isActive = tabName === this.activePanel;
|
||||
return html`
|
||||
<btrix-navigation-button
|
||||
<btrix-tab-group-tab
|
||||
slot="nav"
|
||||
panel=${tabName}
|
||||
href=${`${window.location.pathname}#${tabName}`}
|
||||
.active=${isActive}
|
||||
.disabled=${disabled}
|
||||
?disabled=${disabled}
|
||||
aria-selected=${isActive}
|
||||
aria-disabled=${disabled}
|
||||
@click=${(e: MouseEvent) => {
|
||||
@ -603,7 +593,7 @@ export class WorkflowDetail extends BtrixElement {
|
||||
["settings", () => html`<sl-icon name="file-code-fill"></sl-icon>`],
|
||||
])}
|
||||
${this.tabLabels[tabName]}
|
||||
</btrix-navigation-button>
|
||||
</btrix-tab-group-tab>
|
||||
`;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user