From a1f12d369b71b596bdbe2e290236c1af30dfe816 Mon Sep 17 00:00:00 2001 From: emma Date: Thu, 13 Feb 2025 11:18:07 -0500 Subject: [PATCH 1/9] wip --- frontend/src/pages/org/collections-list.ts | 101 ++++++++++++++++++--- 1 file changed, 88 insertions(+), 13 deletions(-) diff --git a/frontend/src/pages/org/collections-list.ts b/frontend/src/pages/org/collections-list.ts index e8a788dc42..fc8526cf67 100644 --- a/frontend/src/pages/org/collections-list.ts +++ b/frontend/src/pages/org/collections-list.ts @@ -1,5 +1,10 @@ import { localized, msg } from "@lit/localize"; -import type { SlInput, SlMenuItem } from "@shoelace-style/shoelace"; +import type { + SlChangeEvent, + SlInput, + SlMenuItem, + SlRadioGroup, +} from "@shoelace-style/shoelace"; import Fuse from "fuse.js"; import { html, nothing, type PropertyValues } from "lit"; import { customElement, property, query, state } from "lit/decorators.js"; @@ -79,6 +84,11 @@ const sortableFields: Record< }; const MIN_SEARCH_LENGTH = 2; +enum ListView { + List = "list", + Grid = "grid", +} + @customElement("btrix-collections-list") @localized() export class CollectionsList extends BtrixElement { @@ -97,6 +107,9 @@ export class CollectionsList extends BtrixElement { direction: sortableFields["modified"].defaultDirection!, }; + @state() + private listView = ListView.List; + @state() private filterBy: Partial> = {}; @@ -178,8 +191,13 @@ export class CollectionsList extends BtrixElement { > ${this.renderControls()} -
- ${guard([this.collections], this.renderList)} +
+ ${guard( + [this.collections, this.listView], + this.listView === ListView.List + ? this.renderList + : this.renderGrid, + )}
` : this.renderLoading(), @@ -291,17 +309,45 @@ export class CollectionsList extends BtrixElement { `, )} - { - this.orderBy = { - ...this.orderBy, - direction: -1 * this.orderBy.direction, - }; - }} - > + + { + this.orderBy = { + ...this.orderBy, + direction: -1 * this.orderBy.direction, + }; + }} + > +
+ + { + this.listView = (e.target as SlRadioGroup).value as ListView; + }} + > + + + + + + + `; @@ -386,6 +432,35 @@ export class CollectionsList extends BtrixElement { `; } + private readonly renderGrid = () => { + return html` + ${this.collections && + this.collections.total > this.collections.items.length + ? html` + { + await this.fetchCollections({ + page: e.detail.page, + }); + + // Scroll to top of list + // TODO once deep-linking is implemented, scroll to top of pushstate + this.scrollIntoView({ behavior: "smooth" }); + }} + slot="pagination" + > + + ` + : nothing} + `; + }; + private readonly renderList = () => { if (this.collections?.items.length) { return html` From c7eb2e2e50185a973250e320900c459fc1519e6d Mon Sep 17 00:00:00 2001 From: emma Date: Thu, 13 Feb 2025 11:30:29 -0500 Subject: [PATCH 2/9] handle refreshing collections a little better --- frontend/src/pages/org/collections-list.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/org/collections-list.ts b/frontend/src/pages/org/collections-list.ts index fc8526cf67..d570fcd410 100644 --- a/frontend/src/pages/org/collections-list.ts +++ b/frontend/src/pages/org/collections-list.ts @@ -128,6 +128,9 @@ export class CollectionsList extends BtrixElement { @state() private selectedCollection?: Collection; + @state() + collectionRefreshing: string | null = null; + @state() private fetchErrorStatusCode?: number; @@ -193,7 +196,7 @@ export class CollectionsList extends BtrixElement {
${guard( - [this.collections, this.listView], + [this.collections, this.listView, this.collectionRefreshing], this.listView === ListView.List ? this.renderList : this.renderGrid, @@ -436,6 +439,14 @@ export class CollectionsList extends BtrixElement { return html` { + this.collectionRefreshing = detail.id; + await this.fetchCollections(); + this.collectionRefreshing = null; + }} > ${this.collections && this.collections.total > this.collections.items.length @@ -468,7 +479,7 @@ export class CollectionsList extends BtrixElement { class="[--btrix-column-gap:var(--sl-spacing-small)]" style="grid-template-columns: min-content [clickable-start] 45em repeat(4, 1fr) [clickable-end] min-content" > - + ${msg("Collection Access")} From 3d0fed71fab19597507133a692aa2e3c3729a65b Mon Sep 17 00:00:00 2001 From: emma Date: Thu, 13 Feb 2025 11:35:42 -0500 Subject: [PATCH 3/9] make access icons more consistent with list --- .../features/collections/collections-grid.ts | 74 ++++++++++++++++--- 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/frontend/src/features/collections/collections-grid.ts b/frontend/src/features/collections/collections-grid.ts index 989ebce01b..017384758e 100644 --- a/frontend/src/features/collections/collections-grid.ts +++ b/frontend/src/features/collections/collections-grid.ts @@ -7,6 +7,7 @@ import { queryAssignedNodes, state, } from "lit/decorators.js"; +import { choose } from "lit/directives/choose.js"; import { ifDefined } from "lit/directives/if-defined.js"; import { when } from "lit/directives/when.js"; @@ -16,7 +17,7 @@ import { SelectCollectionAccess } from "./select-collection-access"; import { BtrixElement } from "@/classes/BtrixElement"; import { textSeparator } from "@/layouts/separator"; import { RouteNamespace } from "@/routes"; -import type { PublicCollection } from "@/types/collection"; +import { CollectionAccess, type PublicCollection } from "@/types/collection"; import { pluralOf } from "@/utils/pluralize"; import { tw } from "@/utils/tailwind"; @@ -107,15 +108,68 @@ export class CollectionsGrid extends BtrixElement {
${this.showVisibility - ? html`` - : nothing} + ? choose(collection.access, [ + [ + CollectionAccess.Private, + () => html` + + + + `, + ], + [ + CollectionAccess.Unlisted, + () => html` + + + + `, + ], + [ + CollectionAccess.Public, + () => html` + + + + `, + ], + ]) + : // ? html`` + nothing} From 714bc2b624ed9609113c415655c92cbd3575009e Mon Sep 17 00:00:00 2001 From: emma Date: Thu, 17 Apr 2025 15:06:55 -0400 Subject: [PATCH 4/9] refactor collection editing dialog into separate component this makes collection grid simpler: it doesn't include extra editing dialog when not needed Extract collection edit dialog into dedicated component - Move dialog from collections-grid to collections-grid-with-edit-dialog - Update dashboard page to use new composite component --- .../collections-grid-with-edit-dialog.ts | 52 ++++++++ .../features/collections/collections-grid.ts | 28 +---- frontend/src/features/collections/index.ts | 1 + frontend/src/pages/org/dashboard.ts | 116 ++++++++++-------- 4 files changed, 121 insertions(+), 76 deletions(-) create mode 100644 frontend/src/features/collections/collections-grid-with-edit-dialog.ts diff --git a/frontend/src/features/collections/collections-grid-with-edit-dialog.ts b/frontend/src/features/collections/collections-grid-with-edit-dialog.ts new file mode 100644 index 0000000000..3623aae104 --- /dev/null +++ b/frontend/src/features/collections/collections-grid-with-edit-dialog.ts @@ -0,0 +1,52 @@ +import { localized } from "@lit/localize"; +import { html } from "lit"; +import { customElement, property, state } from "lit/decorators.js"; +import { when } from "lit/directives/when.js"; + +import { BtrixElement } from "@/classes/BtrixElement"; +import { type PublicCollection } from "@/types/collection"; + +@customElement("btrix-collections-grid-with-edit-dialog") +@localized() +export class CollectionsGridWithEditDialog extends BtrixElement { + @property({ type: Array }) + collections?: PublicCollection[]; + + @state() + collectionBeingEdited: string | null = null; + + @property({ type: String }) + collectionRefreshing: string | null = null; + + @property({ type: Boolean }) + showVisibility = false; + + render() { + const showActions = !this.navigate.isPublicPage && this.appState.isCrawler; + return html` + ) => { + this.collectionBeingEdited = e.detail; + }} + > + + + + ${when( + showActions, + () => + html` { + this.collectionBeingEdited = null; + }} + >`, + )} + `; + } +} diff --git a/frontend/src/features/collections/collections-grid.ts b/frontend/src/features/collections/collections-grid.ts index 017384758e..93dce950d3 100644 --- a/frontend/src/features/collections/collections-grid.ts +++ b/frontend/src/features/collections/collections-grid.ts @@ -1,12 +1,7 @@ import { localized, msg } from "@lit/localize"; import clsx from "clsx"; import { html, nothing } from "lit"; -import { - customElement, - property, - queryAssignedNodes, - state, -} from "lit/decorators.js"; +import { customElement, property, queryAssignedNodes } from "lit/decorators.js"; import { choose } from "lit/directives/choose.js"; import { ifDefined } from "lit/directives/if-defined.js"; import { when } from "lit/directives/when.js"; @@ -35,9 +30,6 @@ export class CollectionsGrid extends BtrixElement { @property({ type: Array }) collections?: PublicCollection[]; - @state() - collectionBeingEdited: string | null = null; - @property({ type: String }) collectionRefreshing: string | null = null; @@ -212,18 +204,6 @@ export class CollectionsGrid extends BtrixElement { class=${clsx("justify-center flex", this.pagination.length && "mt-10")} name="pagination" > - - ${when( - showActions, - () => - html` { - this.collectionBeingEdited = null; - }} - >`, - )} `; } @@ -235,7 +215,11 @@ export class CollectionsGrid extends BtrixElement { raised size="small" @click=${() => { - this.collectionBeingEdited = collection.id; + this.dispatchEvent( + new CustomEvent("btrix-edit-collection", { + detail: collection.id, + }), + ); }} > diff --git a/frontend/src/features/collections/index.ts b/frontend/src/features/collections/index.ts index 2ed846c3b7..a4fbc66b0a 100644 --- a/frontend/src/features/collections/index.ts +++ b/frontend/src/features/collections/index.ts @@ -1,5 +1,6 @@ import("./collections-add"); import("./collections-grid"); +import("./collections-grid-with-edit-dialog"); import("./collection-items-dialog"); import("./collection-edit-dialog"); import("./collection-create-dialog"); diff --git a/frontend/src/pages/org/dashboard.ts b/frontend/src/pages/org/dashboard.ts index 3354cec1ca..10fc0441d4 100644 --- a/frontend/src/pages/org/dashboard.ts +++ b/frontend/src/pages/org/dashboard.ts @@ -376,33 +376,35 @@ export class Dashboard extends BtrixElement { ? msg("Public Collections") : msg("All Collections"), })} - ${this.collectionsView === CollectionGridView.Public - ? html` — - + ${ + this.collectionsView === CollectionGridView.Public + ? html` — + + ${this.org?.enablePublicProfile + ? msg("Visit public collections gallery") + : msg("Preview public collections gallery")} + + ${this.org?.enablePublicProfile - ? msg("Visit public collections gallery") - : msg("Preview public collections gallery")} - - - ${this.org?.enablePublicProfile - ? html`` - : nothing} - ` - : nothing} + ? html`` + : nothing} + ` + : nothing + }
${when( @@ -446,8 +448,7 @@ export class Dashboard extends BtrixElement {
- ${this.renderNoPublicCollections()} ${this.collectionsView === CollectionGridView.Public - ? msg("No public collections yet.") - : msg("No collections yet.")}${ + this.collectionsView === CollectionGridView.Public + ? msg("No public collections yet.") + : msg("No collections yet.") + } - ${this.collections.value && - this.collections.value.total > this.collections.value.items.length - ? html` - { - this.collectionPage = e.detail.page; - }} - slot="pagination" - > - - ` - : nothing} + ${ + this.collections.value && + this.collections.value.total > + this.collections.value.items.length + ? html` + { + this.collectionPage = e.detail.page; + }} + slot="pagination" + > + + ` + : nothing + } - ${this.collections.status === TaskStatus.PENDING && - this.collections.value - ? html`
- -
` - : nothing} + ${ + this.collections.status === TaskStatus.PENDING && + this.collections.value + ? html`
+ +
` + : nothing + }
From 022141eb7b3a51f0a0fe63e411eed14bc9fbebc6 Mon Sep 17 00:00:00 2001 From: emma Date: Thu, 17 Apr 2025 15:21:22 -0400 Subject: [PATCH 5/9] add getter/setter for page in pagination component --- frontend/src/components/ui/pagination.ts | 63 +++++++++++++++--------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/frontend/src/components/ui/pagination.ts b/frontend/src/components/ui/pagination.ts index af57c7b34e..59a88256cd 100644 --- a/frontend/src/components/ui/pagination.ts +++ b/frontend/src/components/ui/pagination.ts @@ -141,19 +141,30 @@ export class Pagination extends LitElement { searchParams = new SearchParamsController(this, (params) => { const page = parsePage(params.get(this.name)); - if (this.page !== page) { + if (this._page !== page) { this.dispatchEvent( new CustomEvent("page-change", { detail: { page: page, pages: this.pages }, composed: true, }), ); - this.page = page; + this._page = page; } }); @state() - page = 1; + private _page = 1; + + @property({ type: Number }) + set page(page: number) { + if (page !== this._page) { + this.setPage(page); + } + } + + get page() { + return this._page; + } @property({ type: String }) name = "page"; @@ -174,7 +185,7 @@ export class Pagination extends LitElement { private pages = 0; connectedCallback() { - this.inputValue = `${this.page}`; + this.inputValue = `${this._page}`; super.connectedCallback(); } @@ -186,14 +197,14 @@ export class Pagination extends LitElement { const parsedPage = parseFloat( this.searchParams.searchParams.get(this.name) ?? "1", ); - if (parsedPage != this.page) { + if (parsedPage != this._page) { const page = parsePage(this.searchParams.searchParams.get(this.name)); const constrainedPage = Math.max(1, Math.min(this.pages, page)); this.onPageChange(constrainedPage); } - if (changedProperties.get("page") && this.page) { - this.inputValue = `${this.page}`; + if (changedProperties.get("page") && this._page) { + this.inputValue = `${this._page}`; } } @@ -208,7 +219,7 @@ export class Pagination extends LitElement {