diff --git a/frontend/src/components/orgs-list.ts b/frontend/src/components/orgs-list.ts index 9c87bbcdcc..f9df083b5c 100644 --- a/frontend/src/components/orgs-list.ts +++ b/frontend/src/components/orgs-list.ts @@ -115,7 +115,9 @@ export class OrgsList extends BtrixElement { library="default" > -
+ @@ -144,7 +146,7 @@ export class OrgsList extends BtrixElement { ${orgs?.map(this.renderOrg)} -
+ ${this.renderOrgQuotas()} ${this.renderOrgProxies()} ${this.renderOrgReadOnly()} ${this.renderOrgDelete()} diff --git a/frontend/src/components/ui/index.ts b/frontend/src/components/ui/index.ts index c7c9f96f61..485b542f6c 100644 --- a/frontend/src/components/ui/index.ts +++ b/frontend/src/components/ui/index.ts @@ -27,6 +27,7 @@ import("./menu-item-link"); import("./meter"); import("./numbered-list"); import("./overflow-dropdown"); +import("./overflow-scroll"); import("./pagination"); import("./pw-strength-alert"); import("./relative-duration"); diff --git a/frontend/src/components/ui/overflow-scroll.ts b/frontend/src/components/ui/overflow-scroll.ts new file mode 100644 index 0000000000..aabba5266b --- /dev/null +++ b/frontend/src/components/ui/overflow-scroll.ts @@ -0,0 +1,105 @@ +import { css, html, LitElement } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +/** + * Overflow scroller. Optionally displays a scrim/shadow (a small gradient + * indicating there's more available content) on supported browsers, + * depending on scroll position. + * @slot + * @cssPart content + * @cssproperty --btrix-overflow-scrim-width The width of the scrim. 3rem by default. + * @cssproperty --btrix-overflow-scroll-scrim-color The color of the scrim. White by default. + */ +@customElement("btrix-overflow-scroll") +export class OverflowScroll extends LitElement { + /** + * The direction of the overflow scroll. Currently just horizontal. + */ + // TODO: Implement vertical overflow scroller + @property({ type: String }) + // eslint-disable-next-line @typescript-eslint/prefer-as-const + direction: "horizontal" = "horizontal"; + + /** + * Whether to show a scrim when the overflow scroll is active. Only appears when the inner content is wider than this element. + * + * Progressive enhancement: only works on Chromium-based browsers currently. + * See https://caniuse.com/mdn-css_properties_scroll-timeline for support. + */ + @property({ type: Boolean }) + scrim = true; + + static styles = css` + :host { + display: block; + position: relative; + } + + [direction="horizontal"] { + overflow-x: auto; + } + + @supports (scroll-timeline-name: --btrix-overflow-scroll-timeline) { + [scrim][direction="horizontal"] { + scroll-timeline-name: --btrix-overflow-scroll-timeline; + scroll-timeline-axis: inline; + } + + [scrim][direction="horizontal"]:before, + [scrim][direction="horizontal"]:after { + content: ""; + width: var(--btrix-overflow-scrim-width, 3rem); + position: absolute; + z-index: 1; + top: 0; + height: 100%; + pointer-events: none; + animation-name: btrix-scroll-scrim; + animation-timeline: --btrix-overflow-scroll-timeline; + opacity: 0; + } + + [scrim][direction="horizontal"]:before { + left: 0; + background: linear-gradient( + to right, + var(--btrix-overflow-scroll-scrim-color, white), + transparent + ); + /* background-color: blue; */ + } + [scrim][direction="horizontal"]:after { + right: 0; + background: linear-gradient( + to right, + transparent, + var(--btrix-overflow-scroll-scrim-color, white) + ); + /* background-color: blue; */ + animation-direction: reverse; + } + @keyframes btrix-scroll-scrim { + 0% { + opacity: 0; + } + 20% { + opacity: 1; + } + 100% { + opacity: 1; + } + } + } + `; + + render() { + return html`
+ +
`; + } +} diff --git a/frontend/src/features/archived-items/archived-item-list.ts b/frontend/src/features/archived-items/archived-item-list.ts index e90ee08144..1dea96a05a 100644 --- a/frontend/src/features/archived-items/archived-item-list.ts +++ b/frontend/src/features/archived-items/archived-item-list.ts @@ -451,7 +451,7 @@ export class ArchivedItemList extends TailwindElement { .join(" ")}; } -
+ -
+ `; } } diff --git a/frontend/src/features/archived-items/crawl-list.ts b/frontend/src/features/archived-items/crawl-list.ts index 0772f2e235..0f8080239f 100644 --- a/frontend/src/features/archived-items/crawl-list.ts +++ b/frontend/src/features/archived-items/crawl-list.ts @@ -283,7 +283,7 @@ export class CrawlList extends TailwindElement { [clickable-end] min-content; } -
+ @@ -320,7 +320,7 @@ export class CrawlList extends TailwindElement { -
`; + `; } private handleSlotchange() { diff --git a/frontend/src/pages/org/browser-profiles-list.ts b/frontend/src/pages/org/browser-profiles-list.ts index 689ad8af15..6ffbea89b1 100644 --- a/frontend/src/pages/org/browser-profiles-list.ts +++ b/frontend/src/pages/org/browser-profiles-list.ts @@ -163,52 +163,54 @@ export class BrowserProfilesList extends BtrixElement { }; return html` - - - ${headerCells.map(({ sortBy, sortDirection, label, className }) => { - const isSorting = sortBy === this.sort.sortBy; - const sortValue = - (isSorting && SortDirection.get(this.sort.sortDirection)) || - "none"; - // TODO implement sort render logic in table-header-cell - return html` - { - if (isSorting) { - this.sort = { - ...this.sort, - sortDirection: this.sort.sortDirection * -1, - }; - } else { - this.sort = { - sortBy, - sortDirection, - }; - } - }} - > - ${label} ${getSortIcon(sortValue)} - - `; - })} - - ${msg("Row Actions")} - - - - ${when(this.browserProfiles, ({ total, items }) => - total ? html` ${items.map(this.renderItem)} ` : nothing, - )} - ${when(this.isLoading, this.renderLoading)} - - + + + + ${headerCells.map(({ sortBy, sortDirection, label, className }) => { + const isSorting = sortBy === this.sort.sortBy; + const sortValue = + (isSorting && SortDirection.get(this.sort.sortDirection)) || + "none"; + // TODO implement sort render logic in table-header-cell + return html` + { + if (isSorting) { + this.sort = { + ...this.sort, + sortDirection: this.sort.sortDirection * -1, + }; + } else { + this.sort = { + sortBy, + sortDirection, + }; + } + }} + > + ${label} ${getSortIcon(sortValue)} + + `; + })} + + ${msg("Row Actions")} + + + + ${when(this.browserProfiles, ({ total, items }) => + total ? html` ${items.map(this.renderItem)} ` : nothing, + )} + ${when(this.isLoading, this.renderLoading)} + + + ${when(this.browserProfiles, ({ total, page, pageSize }) => total ? html` diff --git a/frontend/src/pages/org/collections-list.ts b/frontend/src/pages/org/collections-list.ts index 4897f56d3f..d7d49ff0ed 100644 --- a/frontend/src/pages/org/collections-list.ts +++ b/frontend/src/pages/org/collections-list.ts @@ -195,14 +195,39 @@ export class CollectionsList extends BtrixElement { > ${this.renderControls()} -
+ ${guard( [this.collections, this.listView, this.collectionRefreshing], this.listView === ListView.List ? this.renderList : this.renderGrid, )} -
+ + ${when(this.listView === ListView.List, () => + when( + (this.collections && + this.collections.total > this.collections.pageSize) || + (this.collections && this.collections.page > 1), + () => html` + + `, + ), + )} ` : this.renderLoading(), )} @@ -507,29 +532,6 @@ export class CollectionsList extends BtrixElement { ${this.collections.items.map(this.renderItem)} - - ${when( - this.collections.total > this.collections.pageSize || - this.collections.page > 1, - () => html` - - `, - )} `; } diff --git a/frontend/src/pages/org/index.ts b/frontend/src/pages/org/index.ts index 3b0191a2a7..2f9fe150fd 100644 --- a/frontend/src/pages/org/index.ts +++ b/frontend/src/pages/org/index.ts @@ -355,36 +355,38 @@ export class Org extends BtrixElement {
- + + +

diff --git a/frontend/src/stories/components/OverflowScroll.stories.ts b/frontend/src/stories/components/OverflowScroll.stories.ts new file mode 100644 index 0000000000..837fa5defd --- /dev/null +++ b/frontend/src/stories/components/OverflowScroll.stories.ts @@ -0,0 +1,30 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; + +import { renderOverflowScroll, type RenderProps } from "./OverflowScroll"; + +const meta = { + title: "Components/Overflow Scroll", + component: "btrix-overflow-scroll", + tags: ["autodocs"], + render: renderOverflowScroll, + argTypes: { + direction: { + control: { type: "select" }, + options: ["horizontal"] satisfies RenderProps["direction"][], + }, + scrim: { + control: { type: "boolean" }, + }, + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + direction: "horizontal", + scrim: true, + }, +}; diff --git a/frontend/src/stories/components/OverflowScroll.ts b/frontend/src/stories/components/OverflowScroll.ts new file mode 100644 index 0000000000..22b90fdfe6 --- /dev/null +++ b/frontend/src/stories/components/OverflowScroll.ts @@ -0,0 +1,37 @@ +import clsx from "clsx"; +import { html } from "lit"; +import { ifDefined } from "lit/directives/if-defined.js"; + +import { defaultArgs, renderTable } from "./Table"; + +import "@/components/ui/overflow-scroll"; + +import type { OverflowScroll } from "@/components/ui/overflow-scroll"; +import { tw } from "@/utils/tailwind"; + +export type RenderProps = OverflowScroll; + +export const renderOverflowScroll = ({ + direction, + scrim, +}: Partial) => { + return html` +
+ + + ${renderTable({ + ...defaultArgs, + classes: clsx( + ...defaultArgs.classes, + tw`w-[800px] rounded border bg-neutral-50 p-2 [--btrix-table-cell-padding:var(--sl-spacing-2x-small)]`, + ), + })} + +
+ `; +};