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