Refactor collections and browser profile data-tables (#1505)
- Updates browser profile list styles to match other data table styles - Makes entire collection item clickable - Refactors row click area to fix text overflow
This commit is contained in:
parent
15e410daa1
commit
79645b64fe
@ -1,7 +1,12 @@
|
|||||||
import { LitElement, html, css } from "lit";
|
import { LitElement, html, css } from "lit";
|
||||||
import { customElement, state, queryAssignedElements } from "lit/decorators.js";
|
import {
|
||||||
|
customElement,
|
||||||
|
state,
|
||||||
|
queryAssignedElements,
|
||||||
|
query,
|
||||||
|
} from "lit/decorators.js";
|
||||||
import { msg, localized } from "@lit/localize";
|
import { msg, localized } from "@lit/localize";
|
||||||
import type { SlMenu } from "@shoelace-style/shoelace";
|
import type { SlDropdown, SlMenu } from "@shoelace-style/shoelace";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dropdown for additional actions.
|
* Dropdown for additional actions.
|
||||||
@ -34,12 +39,15 @@ export class OverflowDropdown extends LitElement {
|
|||||||
@state()
|
@state()
|
||||||
private hasMenuItems?: boolean;
|
private hasMenuItems?: boolean;
|
||||||
|
|
||||||
|
@query("sl-dropdown")
|
||||||
|
private dropdown?: SlDropdown;
|
||||||
|
|
||||||
@queryAssignedElements({ selector: "sl-menu", flatten: true })
|
@queryAssignedElements({ selector: "sl-menu", flatten: true })
|
||||||
private menu!: Array<SlMenu>;
|
private menu!: Array<SlMenu>;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<sl-dropdown ?disabled=${!this.hasMenuItems}>
|
<sl-dropdown ?disabled=${!this.hasMenuItems} hoist>
|
||||||
<sl-icon-button
|
<sl-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
class="trigger"
|
class="trigger"
|
||||||
@ -54,4 +62,8 @@ export class OverflowDropdown extends LitElement {
|
|||||||
</sl-dropdown>
|
</sl-dropdown>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this.dropdown?.hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,23 +40,22 @@ export class TableCell extends LitElement {
|
|||||||
role = "cell";
|
role = "cell";
|
||||||
|
|
||||||
@property({ type: String })
|
@property({ type: String })
|
||||||
rowClickTarget?: (typeof ALLOWED_ROW_CLICK_TARGET_TAG)[number] | "" = "";
|
rowClickTarget?: (typeof ALLOWED_ROW_CLICK_TARGET_TAG)[number];
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`${this.rowClickTarget &&
|
return html`<slot @slotchange=${this.handleSlotChange}></slot>`;
|
||||||
ALLOWED_ROW_CLICK_TARGET_TAG.includes(this.rowClickTarget)
|
|
||||||
? html`<style>
|
|
||||||
:host {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: subgrid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::slotted(${unsafeCSS(this.rowClickTarget)}) {
|
private handleSlotChange(e: Event) {
|
||||||
position: absolute;
|
if (!this.rowClickTarget) return;
|
||||||
inset: 0;
|
const elems = (e.target as HTMLSlotElement).assignedElements();
|
||||||
grid-column: clickable-start / clickable-end;
|
const rowClickTarget = elems.find(
|
||||||
}
|
(el) => el.tagName.toLowerCase() === this.rowClickTarget
|
||||||
</style>`
|
);
|
||||||
: nothing} <slot></slot>`;
|
|
||||||
|
if (!rowClickTarget) return;
|
||||||
|
|
||||||
|
// Styled in table.css
|
||||||
|
rowClickTarget.classList.add("rowClickTarget");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ export class TableRow extends LitElement {
|
|||||||
grid-column: var(--btrix-table-grid-column);
|
grid-column: var(--btrix-table-grid-column);
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: subgrid;
|
grid-template-columns: subgrid;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
24
frontend/src/components/ui/table/table.stylesheet.css
Normal file
24
frontend/src/components/ui/table/table.stylesheet.css
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
btrix-table-cell[rowClickTarget] {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: subgrid;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rowClickTarget {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rowClickTarget::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
grid-column: clickable-start / clickable-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rowClickTarget:focus-visible {
|
||||||
|
outline: var(--sl-focus-ring);
|
||||||
|
outline-offset: -0.25rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
@ -6,9 +6,18 @@ import {
|
|||||||
queryAssignedElements,
|
queryAssignedElements,
|
||||||
} from "lit/decorators.js";
|
} from "lit/decorators.js";
|
||||||
|
|
||||||
import { TailwindElement } from "@/classes/TailwindElement";
|
import { theme } from "@/theme";
|
||||||
|
import tableCSS from "./table.stylesheet.css";
|
||||||
import { type TableHead } from "./table-head";
|
import { type TableHead } from "./table-head";
|
||||||
|
|
||||||
|
// Add table CSS to theme CSS to make it available throughout the app,
|
||||||
|
// to both shadow and light dom components.
|
||||||
|
// TODO Remove once all `LiteElement`s are migrated over to `TailwindElement`
|
||||||
|
tableCSS.split("}").forEach((rule: string) => {
|
||||||
|
if (!rule.trim()) return;
|
||||||
|
theme.insertRule(`${rule}}`);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Low-level component for displaying content as a table.
|
* Low-level component for displaying content as a table.
|
||||||
* To style tables, use TailwindCSS utility classes.
|
* To style tables, use TailwindCSS utility classes.
|
||||||
@ -46,7 +55,7 @@ import { type TableHead } from "./table-head";
|
|||||||
* @cssproperty --btrix-cell-padding-bottom
|
* @cssproperty --btrix-cell-padding-bottom
|
||||||
*/
|
*/
|
||||||
@customElement("btrix-table")
|
@customElement("btrix-table")
|
||||||
export class Table extends TailwindElement {
|
export class Table extends LitElement {
|
||||||
static styles = css`
|
static styles = css`
|
||||||
:host {
|
:host {
|
||||||
--btrix-cell-gap: 0;
|
--btrix-cell-gap: 0;
|
||||||
|
|||||||
@ -6,15 +6,14 @@ import {
|
|||||||
queryAssignedElements,
|
queryAssignedElements,
|
||||||
query,
|
query,
|
||||||
} from "lit/decorators.js";
|
} from "lit/decorators.js";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
import { msg, localized, str } from "@lit/localize";
|
import { msg, localized, str } from "@lit/localize";
|
||||||
|
import { type SlCheckbox } from "@shoelace-style/shoelace";
|
||||||
|
|
||||||
import { TailwindElement } from "@/classes/TailwindElement";
|
import { TailwindElement } from "@/classes/TailwindElement";
|
||||||
import type { ArchivedItem } from "@/types/crawler";
|
import type { ArchivedItem } from "@/types/crawler";
|
||||||
import { renderName } from "@/utils/crawler";
|
import { renderName } from "@/utils/crawler";
|
||||||
import { NavigateController } from "@/controllers/navigate";
|
import { NavigateController } from "@/controllers/navigate";
|
||||||
import { type SlCheckbox } from "@shoelace-style/shoelace";
|
|
||||||
|
|
||||||
const NAME_WIDTH_CSS = css`26rem`;
|
|
||||||
|
|
||||||
export type CheckboxChangeEventDetail = {
|
export type CheckboxChangeEventDetail = {
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
@ -38,29 +37,8 @@ export class ArchivedItemListItem extends TailwindElement {
|
|||||||
border-radius: var(--btrix-border-radius-top, 0)
|
border-radius: var(--btrix-border-radius-top, 0)
|
||||||
var(--btrix-border-radius-to, 0) var(--btrix-border-radius-bottom, 0)
|
var(--btrix-border-radius-to, 0) var(--btrix-border-radius-bottom, 0)
|
||||||
var(--btrix-border-radius-bottom, 0);
|
var(--btrix-border-radius-bottom, 0);
|
||||||
position: relative;
|
|
||||||
height: 2.5rem;
|
height: 2.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO consolidate data-table variations
|
|
||||||
* https://github.com/webrecorder/browsertrix-cloud/issues/1504
|
|
||||||
*/
|
|
||||||
btrix-table-cell {
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clickLabel {
|
|
||||||
width: ${NAME_WIDTH_CSS};
|
|
||||||
display: flex;
|
|
||||||
gap: var(--btrix-cell-gap, 0);
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: var(--btrix-cell-padding-top) var(--btrix-cell-padding-right)
|
|
||||||
var(--btrix-cell-padding-bottom) var(--btrix-cell-padding-left);
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@property({ type: Object })
|
@property({ type: Object })
|
||||||
@ -87,7 +65,7 @@ export class ArchivedItemListItem extends TailwindElement {
|
|||||||
if (!this.item) return;
|
if (!this.item) return;
|
||||||
const checkboxId = `${this.item.id}-checkbox`;
|
const checkboxId = `${this.item.id}-checkbox`;
|
||||||
const rowName = html`
|
const rowName = html`
|
||||||
<div class="clickLabel">
|
<div class="flex items-center gap-3">
|
||||||
<slot name="namePrefix"></slot>
|
<slot name="namePrefix"></slot>
|
||||||
${renderName(this.item)}
|
${renderName(this.item)}
|
||||||
</div>
|
</div>
|
||||||
@ -122,7 +100,9 @@ export class ArchivedItemListItem extends TailwindElement {
|
|||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
<btrix-table-cell
|
<btrix-table-cell
|
||||||
rowClickTarget=${this.href ? "a" : this.checkbox ? "label" : ""}
|
rowClickTarget=${ifDefined(
|
||||||
|
this.href ? "a" : this.checkbox ? "label" : undefined
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
${this.href
|
${this.href
|
||||||
? html`<a href=${this.href} @click=${this.navigate.link}>
|
? html`<a href=${this.href} @click=${this.navigate.link}>
|
||||||
@ -225,7 +205,7 @@ export class ArchivedItemList extends TailwindElement {
|
|||||||
btrix-table {
|
btrix-table {
|
||||||
grid-template-columns: ${`${
|
grid-template-columns: ${`${
|
||||||
this.hasCheckboxCell ? "min-content" : ""
|
this.hasCheckboxCell ? "min-content" : ""
|
||||||
} [clickable-start] ${NAME_WIDTH_CSS} 12rem 1fr 1fr 1fr [clickable-end] ${
|
} [clickable-start] 60ch 12rem 1fr 1fr 30ch [clickable-end] ${
|
||||||
this.hasActionCell ? "min-content" : ""
|
this.hasActionCell ? "min-content" : ""
|
||||||
}`.trim()};
|
}`.trim()};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import {
|
|||||||
query,
|
query,
|
||||||
queryAssignedElements,
|
queryAssignedElements,
|
||||||
} from "lit/decorators.js";
|
} from "lit/decorators.js";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
import { msg, localized, str } from "@lit/localize";
|
import { msg, localized, str } from "@lit/localize";
|
||||||
|
|
||||||
import { RelativeDuration } from "@/components/ui/relative-duration";
|
import { RelativeDuration } from "@/components/ui/relative-duration";
|
||||||
@ -28,8 +29,6 @@ import { renderName } from "@/utils/crawler";
|
|||||||
import { TailwindElement } from "@/classes/TailwindElement";
|
import { TailwindElement } from "@/classes/TailwindElement";
|
||||||
import { NavigateController } from "@/controllers/navigate";
|
import { NavigateController } from "@/controllers/navigate";
|
||||||
|
|
||||||
const NAME_WIDTH_CSS = css`16rem`;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @slot menu
|
* @slot menu
|
||||||
*/
|
*/
|
||||||
@ -46,28 +45,6 @@ export class CrawlListItem extends TailwindElement {
|
|||||||
border-radius: var(--btrix-border-radius-top, 0)
|
border-radius: var(--btrix-border-radius-top, 0)
|
||||||
var(--btrix-border-radius-to, 0) var(--btrix-border-radius-bottom, 0)
|
var(--btrix-border-radius-to, 0) var(--btrix-border-radius-bottom, 0)
|
||||||
var(--btrix-border-radius-bottom, 0);
|
var(--btrix-border-radius-bottom, 0);
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO consolidate data-table variations
|
|
||||||
* https://github.com/webrecorder/browsertrix-cloud/issues/1504
|
|
||||||
*/
|
|
||||||
btrix-table-cell {
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clickLabel {
|
|
||||||
width: ${NAME_WIDTH_CSS};
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
gap: var(--btrix-cell-gap, 0);
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: var(--btrix-cell-padding-top) var(--btrix-cell-padding-right)
|
|
||||||
var(--btrix-cell-padding-bottom) var(--btrix-cell-padding-left);
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -102,7 +79,7 @@ export class CrawlListItem extends TailwindElement {
|
|||||||
|
|
||||||
if (this.workflowId) {
|
if (this.workflowId) {
|
||||||
const label = html`
|
const label = html`
|
||||||
<div class="clickLabel">
|
<div>
|
||||||
${this.safeRender(
|
${this.safeRender(
|
||||||
(crawl) => html`
|
(crawl) => html`
|
||||||
<sl-format-date
|
<sl-format-date
|
||||||
@ -118,7 +95,9 @@ export class CrawlListItem extends TailwindElement {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
idCell = html`
|
idCell = html`
|
||||||
<btrix-table-cell rowClickTarget=${this.href ? "a" : ""}>
|
<btrix-table-cell
|
||||||
|
rowClickTarget=${ifDefined(this.href ? "a" : undefined)}
|
||||||
|
>
|
||||||
${this.href
|
${this.href
|
||||||
? html`<a href=${this.href} @click=${this.navigate.link}>
|
? html`<a href=${this.href} @click=${this.navigate.link}>
|
||||||
${label}
|
${label}
|
||||||
@ -320,7 +299,7 @@ export class CrawlList extends TailwindElement {
|
|||||||
btrix-table {
|
btrix-table {
|
||||||
grid-template-columns:
|
grid-template-columns:
|
||||||
min-content [clickable-start]
|
min-content [clickable-start]
|
||||||
${this.workflowId ? "" : `${NAME_WIDTH_CSS} `}${NAME_WIDTH_CSS} auto
|
${this.workflowId ? "" : `auto `}auto auto
|
||||||
auto auto auto auto [clickable-end] min-content;
|
auto auto auto auto [clickable-end] min-content;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import type { Profile } from "./types";
|
|||||||
import type { APIPaginatedList } from "@/types/api";
|
import type { APIPaginatedList } from "@/types/api";
|
||||||
import type { SelectNewDialogEvent } from "./index";
|
import type { SelectNewDialogEvent } from "./index";
|
||||||
import type { Browser } from "@/types/browser";
|
import type { Browser } from "@/types/browser";
|
||||||
|
import { nothing } from "lit";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Usage:
|
* Usage:
|
||||||
@ -54,133 +55,129 @@ export class BrowserProfilesList extends LiteElement {
|
|||||||
</sl-button>
|
</sl-button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
<div class="overflow-auto pb-1 px-2">${this.renderTable()}</div>`;
|
||||||
${this.renderTable()}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderTable() {
|
private renderTable() {
|
||||||
return html`
|
return html`
|
||||||
<div role="table">
|
<btrix-table
|
||||||
<div class="mb-2 px-1" role="rowgroup">
|
style="grid-template-columns: min-content [clickable-start] 60ch repeat(2, auto) [clickable-end] min-content; --btrix-cell-padding-left: var(--sl-spacing-x-small); --btrix-cell-padding-right: var(--sl-spacing-x-small);"
|
||||||
<div
|
|
||||||
class="hidden md:grid grid-cols-8 gap-3 md:gap-5 text-sm text-neutral-500"
|
|
||||||
role="row"
|
|
||||||
>
|
>
|
||||||
<div class="col-span-3 px-2" role="columnheader" aria-sort="none">
|
<btrix-table-head class="mb-2">
|
||||||
${msg("Description")}
|
<btrix-table-header-cell>
|
||||||
</div>
|
<span class="sr-only">${msg("Backed up status")}</span>
|
||||||
<div class="col-span-1 px-2" role="columnheader" aria-sort="none">
|
</btrix-table-header-cell>
|
||||||
${msg("Created")}
|
<btrix-table-header-cell class="pl-0">
|
||||||
</div>
|
${msg("Name")}
|
||||||
<div class="col-span-2 px-2" role="columnheader" aria-sort="none">
|
</btrix-table-header-cell>
|
||||||
|
<btrix-table-header-cell>
|
||||||
|
${msg("Date Created")}
|
||||||
|
</btrix-table-header-cell>
|
||||||
|
<btrix-table-header-cell>
|
||||||
${msg("Visited URLs")}
|
${msg("Visited URLs")}
|
||||||
</div>
|
</btrix-table-header-cell>
|
||||||
</div>
|
<btrix-table-header-cell>
|
||||||
</div>
|
<span class="sr-only">${msg("Row Actions")}</span>
|
||||||
${this.browserProfiles
|
</btrix-table-header-cell>
|
||||||
? this.browserProfiles.length
|
</btrix-table-head>
|
||||||
? html`<div class="border rounded" role="rowgroup">
|
${this.browserProfiles?.length
|
||||||
${this.browserProfiles.map(this.renderItem.bind(this))}
|
? html`
|
||||||
</div>`
|
<btrix-table-body
|
||||||
|
style="--btrix-row-gap: var(--sl-spacing-x-small); --btrix-cell-padding-top: var(--sl-spacing-2x-small); --btrix-cell-padding-bottom: var(--sl-spacing-2x-small);"
|
||||||
|
>
|
||||||
|
${this.browserProfiles.map(this.renderItem)}
|
||||||
|
</btrix-table-body>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
</btrix-table>
|
||||||
|
${this.browserProfiles?.length
|
||||||
|
? nothing
|
||||||
: html`
|
: html`
|
||||||
<div class="border-t border-b py-5">
|
<div class="border-t border-b py-5">
|
||||||
<p class="text-center text-0-500">
|
<p class="text-center text-0-500">
|
||||||
${msg("No browser profiles yet.")}
|
${msg("No browser profiles yet.")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
`
|
`}
|
||||||
: ""}
|
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderItem(data: Profile) {
|
private renderItem = (data: Profile) => {
|
||||||
|
const isBackedUp = data.resource && data.resource.replicas.length > 0;
|
||||||
return html`
|
return html`
|
||||||
|
<btrix-table-row
|
||||||
|
class="border rounded cursor-pointer select-none transition-all shadow hover:shadow-none hover:bg-neutral-50 focus-within:bg-neutral-50"
|
||||||
|
>
|
||||||
|
<btrix-table-cell class="p-3">
|
||||||
|
<sl-tooltip
|
||||||
|
content=${isBackedUp ? msg("Backed up") : msg("Not backed up")}
|
||||||
|
>
|
||||||
|
<sl-icon
|
||||||
|
name=${isBackedUp ? "clouds" : "cloud-slash"}
|
||||||
|
class="${isBackedUp ? "text-success" : "text-neutral-500"}"
|
||||||
|
></sl-icon>
|
||||||
|
</sl-tooltip>
|
||||||
|
</btrix-table-cell>
|
||||||
|
<btrix-table-cell
|
||||||
|
class="flex-col items-start justify-center pl-0"
|
||||||
|
rowClickTarget="a"
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
class="block p-1 leading-none hover:bg-zinc-50 hover:text-primary border-t first:border-t-0 transition-colors"
|
class="flex items-center gap-3"
|
||||||
href=${`${this.orgBasePath}/browser-profiles/profile/${data.id}`}
|
href=${`${this.orgBasePath}/browser-profiles/profile/${data.id}`}
|
||||||
@click=${this.navLink}
|
@click=${this.navLink}
|
||||||
title=${data.name}
|
|
||||||
>
|
>
|
||||||
<div class="grid grid-cols-8 gap-3 md:gap-5" role="row">
|
${data.name}
|
||||||
<div class="col-span-8 md:col-span-3 p-2" role="cell">
|
</a>
|
||||||
<div class="font-medium text-sm">
|
<div class="text-xs text-neutral-500 w-full">
|
||||||
<span>${data.name}</span>
|
<div class="truncate">
|
||||||
${when(
|
${data.description} ${data.description} ${data.description}
|
||||||
data.resource && data.resource.replicas.length > 0,
|
${data.description} ${data.description} ${data.description}
|
||||||
() => html` <sl-tooltip content=${msg("Backed up")}>
|
|
||||||
<sl-icon
|
|
||||||
name="clouds"
|
|
||||||
class="w-4 h-4 ml-2 align-text-bottom text-success"
|
|
||||||
></sl-icon>
|
|
||||||
</sl-tooltip>`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div class="text-sm truncate" title=${data.description}>
|
|
||||||
${data.description}
|
${data.description}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-span-8 md:col-span-1 p-2 text-sm" role="cell">
|
</btrix-table-cell>
|
||||||
${new Date(data.created).toLocaleDateString()}
|
<btrix-table-cell class="whitespace-nowrap">
|
||||||
</div>
|
<sl-format-date
|
||||||
<div class="col-span-7 md:col-span-3 p-2 text-sm" role="cell">
|
date=${`${data.created}Z`}
|
||||||
${data.origins.join(", ")}
|
month="2-digit"
|
||||||
</div>
|
day="2-digit"
|
||||||
<div class="col-span-1 md:col-span-1 flex items-center justify-end">
|
year="2-digit"
|
||||||
${this.renderMenu(data)}
|
hour="2-digit"
|
||||||
</div>
|
minute="2-digit"
|
||||||
</div>
|
></sl-format-date>
|
||||||
</a>
|
</btrix-table-cell>
|
||||||
`;
|
<btrix-table-cell>${data.origins.join(", ")}</btrix-table-cell>
|
||||||
}
|
<btrix-table-cell class="px-1"
|
||||||
|
>${this.renderActions(data)}</btrix-table-cell
|
||||||
private renderMenu(data: Profile) {
|
|
||||||
return html`
|
|
||||||
<sl-dropdown hoist @click=${(e: Event) => e.preventDefault()}>
|
|
||||||
<sl-icon-button
|
|
||||||
slot="trigger"
|
|
||||||
name="three-dots"
|
|
||||||
label=${msg("Actions")}
|
|
||||||
style="font-size: 1rem"
|
|
||||||
></sl-icon-button>
|
|
||||||
<ul
|
|
||||||
class="text-sm text-neutral-800 bg-white whitespace-nowrap"
|
|
||||||
role="menu"
|
|
||||||
>
|
>
|
||||||
<li
|
</btrix-table-row>
|
||||||
class="p-2 hover:bg-zinc-100 cursor-pointer"
|
`;
|
||||||
role="menuitem"
|
};
|
||||||
|
|
||||||
|
private renderActions(data: Profile) {
|
||||||
|
return html`
|
||||||
|
<btrix-overflow-dropdown @click=${(e: Event) => e.preventDefault()}>
|
||||||
|
<sl-menu>
|
||||||
|
<sl-menu-item
|
||||||
@click=${(e: any) => {
|
@click=${(e: any) => {
|
||||||
this.duplicateProfile(data);
|
this.duplicateProfile(data);
|
||||||
e.target.closest("sl-dropdown").hide();
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<sl-icon
|
<sl-icon slot="prefix" name="files"></sl-icon>
|
||||||
class="inline-block align-middle px-1"
|
${msg("Duplicate profile")}
|
||||||
name="files"
|
</sl-menu-item>
|
||||||
></sl-icon>
|
<sl-menu-item
|
||||||
<span class="inline-block align-middle pr-2"
|
style="--sl-color-neutral-700: var(--danger)"
|
||||||
>${msg("Duplicate profile")}</span
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
class="p-2 text-danger hover:bg-danger hover:text-white cursor-pointer"
|
|
||||||
role="menuitem"
|
|
||||||
@click=${(e: any) => {
|
@click=${(e: any) => {
|
||||||
// Close dropdown before deleting template
|
|
||||||
e.target.closest("sl-dropdown").hide();
|
|
||||||
|
|
||||||
this.deleteProfile(data);
|
this.deleteProfile(data);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<sl-icon
|
<sl-icon slot="prefix" name="trash3"></sl-icon>
|
||||||
class="inline-block align-middle px-1"
|
${msg("Delete")}
|
||||||
name="trash3"
|
</sl-menu-item>
|
||||||
></sl-icon>
|
</sl-menu>
|
||||||
<span class="inline-block align-middle pr-2">${msg("Delete")}</span>
|
</btrix-overflow-dropdown>
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</sl-dropdown>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import type { Collection, CollectionSearchValues } from "@/types/collection";
|
|||||||
import type { CollectionSavedEvent } from "@/features/collections/collection-metadata-dialog";
|
import type { CollectionSavedEvent } from "@/features/collections/collection-metadata-dialog";
|
||||||
import noCollectionsImg from "~assets/images/no-collections-found.webp";
|
import noCollectionsImg from "~assets/images/no-collections-found.webp";
|
||||||
import type { SelectNewDialogEvent } from "./index";
|
import type { SelectNewDialogEvent } from "./index";
|
||||||
|
import type { OverflowDropdown } from "@/components/ui/overflow-dropdown";
|
||||||
|
|
||||||
type Collections = APIPaginatedList<Collection>;
|
type Collections = APIPaginatedList<Collection>;
|
||||||
type SearchFields = "name";
|
type SearchFields = "name";
|
||||||
@ -151,7 +152,9 @@ export class CollectionsList extends LiteElement {
|
|||||||
>
|
>
|
||||||
${this.renderControls()}
|
${this.renderControls()}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="overflow-auto pb-1 px-2">
|
||||||
${guard([this.collections], this.renderList)}
|
${guard([this.collections], this.renderList)}
|
||||||
|
</div>
|
||||||
`
|
`
|
||||||
: this.renderLoading()
|
: this.renderLoading()
|
||||||
)}
|
)}
|
||||||
@ -391,7 +394,7 @@ export class CollectionsList extends LiteElement {
|
|||||||
if (this.collections?.items.length) {
|
if (this.collections?.items.length) {
|
||||||
return html`
|
return html`
|
||||||
<btrix-table
|
<btrix-table
|
||||||
style="grid-template-columns: min-content 24rem repeat(3, 1fr) 12rem min-content"
|
style="grid-template-columns: min-content [clickable-start] 60ch repeat(3, 1fr) 12rem [clickable-end] min-content"
|
||||||
>
|
>
|
||||||
<btrix-table-head class="mb-2">
|
<btrix-table-head class="mb-2">
|
||||||
<btrix-table-header-cell>
|
<btrix-table-header-cell>
|
||||||
@ -485,7 +488,9 @@ export class CollectionsList extends LiteElement {
|
|||||||
|
|
||||||
private renderItem = (col: Collection) =>
|
private renderItem = (col: Collection) =>
|
||||||
html`
|
html`
|
||||||
<btrix-table-row class="border rounded">
|
<btrix-table-row
|
||||||
|
class="border rounded cursor-pointer select-none transition-all shadow hover:shadow-none hover:bg-neutral-50 focus-within:bg-neutral-50"
|
||||||
|
>
|
||||||
<btrix-table-cell class="p-3">
|
<btrix-table-cell class="p-3">
|
||||||
${col?.isPublic
|
${col?.isPublic
|
||||||
? html`
|
? html`
|
||||||
@ -507,10 +512,10 @@ export class CollectionsList extends LiteElement {
|
|||||||
</sl-tooltip>
|
</sl-tooltip>
|
||||||
`}
|
`}
|
||||||
</btrix-table-cell>
|
</btrix-table-cell>
|
||||||
<btrix-table-cell>
|
<btrix-table-cell rowClickTarget="a">
|
||||||
<a
|
<a
|
||||||
|
class="block py-2 truncate"
|
||||||
href=${`${this.orgBasePath}/collections/view/${col.id}`}
|
href=${`${this.orgBasePath}/collections/view/${col.id}`}
|
||||||
class="block text-primary hover:text-indigo-500"
|
|
||||||
@click=${this.navLink}
|
@click=${this.navLink}
|
||||||
>
|
>
|
||||||
${col.name}
|
${col.name}
|
||||||
@ -542,7 +547,7 @@ export class CollectionsList extends LiteElement {
|
|||||||
minute="2-digit"
|
minute="2-digit"
|
||||||
></sl-format-date>
|
></sl-format-date>
|
||||||
</btrix-table-cell>
|
</btrix-table-cell>
|
||||||
<btrix-table-cell>
|
<btrix-table-cell class="px-1">
|
||||||
${this.isCrawler ? this.renderActions(col) : ""}
|
${this.isCrawler ? this.renderActions(col) : ""}
|
||||||
</btrix-table-cell>
|
</btrix-table-cell>
|
||||||
</btrix-table-row>
|
</btrix-table-row>
|
||||||
@ -552,10 +557,7 @@ export class CollectionsList extends LiteElement {
|
|||||||
const authToken = this.authState!.headers.Authorization.split(" ")[1];
|
const authToken = this.authState!.headers.Authorization.split(" ")[1];
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<sl-dropdown distance="4">
|
<btrix-overflow-dropdown>
|
||||||
<btrix-button class="p-2" slot="trigger" label=${msg("Actions")} icon>
|
|
||||||
<sl-icon class="font-base" name="three-dots-vertical"></sl-icon>
|
|
||||||
</btrix-button>
|
|
||||||
<sl-menu>
|
<sl-menu>
|
||||||
<sl-menu-item
|
<sl-menu-item
|
||||||
@click=${() => this.manageCollection(col, "editMetadata")}
|
@click=${() => this.manageCollection(col, "editMetadata")}
|
||||||
@ -602,7 +604,11 @@ export class CollectionsList extends LiteElement {
|
|||||||
class="px-6 py-[0.6rem] flex gap-2 items-center whitespace-nowrap hover:bg-neutral-100"
|
class="px-6 py-[0.6rem] flex gap-2 items-center whitespace-nowrap hover:bg-neutral-100"
|
||||||
download
|
download
|
||||||
@click=${(e: MouseEvent) => {
|
@click=${(e: MouseEvent) => {
|
||||||
(e.target as HTMLAnchorElement).closest("sl-dropdown")?.hide();
|
(
|
||||||
|
(e.target as HTMLAnchorElement).closest(
|
||||||
|
"btrix-overflow-dropdown"
|
||||||
|
) as OverflowDropdown
|
||||||
|
)?.hide();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<sl-icon name="cloud-download" slot="prefix"></sl-icon>
|
<sl-icon name="cloud-download" slot="prefix"></sl-icon>
|
||||||
@ -617,7 +623,7 @@ export class CollectionsList extends LiteElement {
|
|||||||
${msg("Delete Collection")}
|
${msg("Delete Collection")}
|
||||||
</sl-menu-item>
|
</sl-menu-item>
|
||||||
</sl-menu>
|
</sl-menu>
|
||||||
</sl-dropdown>
|
</btrix-overflow-dropdown>
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import themeCSS from "./theme.css";
|
import themeCSS from "./theme.stylesheet.css";
|
||||||
|
|
||||||
// Create a new style sheet from the compiled theme CSS
|
// Create a new style sheet from the compiled theme CSS
|
||||||
export const theme = new CSSStyleSheet();
|
export const theme = new CSSStyleSheet();
|
||||||
|
|||||||
@ -104,13 +104,14 @@ const main = {
|
|||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Non-theme styles and assets like fonts and Shoelace
|
// Global styles and assets, like fonts and Shoelace,
|
||||||
|
// that get added to document styles
|
||||||
test: /\.css$/,
|
test: /\.css$/,
|
||||||
include: [
|
include: [
|
||||||
path.resolve(__dirname, "src"),
|
path.resolve(__dirname, "src"),
|
||||||
path.resolve(__dirname, "node_modules/@shoelace-style/shoelace"),
|
path.resolve(__dirname, "node_modules/@shoelace-style/shoelace"),
|
||||||
],
|
],
|
||||||
exclude: [path.resolve(__dirname, "src/theme.css")],
|
exclude: /\.stylesheet\.css$/,
|
||||||
use: [
|
use: [
|
||||||
"style-loader",
|
"style-loader",
|
||||||
{ loader: "css-loader", options: { importLoaders: 1 } },
|
{ loader: "css-loader", options: { importLoaders: 1 } },
|
||||||
@ -118,8 +119,8 @@ const main = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Theme CSS loaded as raw string and used as a CSSStyleSheet
|
// CSS loaded as raw string and used as a CSSStyleSheet
|
||||||
test: /theme\.css$/,
|
test: /\.stylesheet\.css$/,
|
||||||
type: "asset/source",
|
type: "asset/source",
|
||||||
include: [path.resolve(__dirname, "src")],
|
include: [path.resolve(__dirname, "src")],
|
||||||
use: ["postcss-loader"],
|
use: ["postcss-loader"],
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user