diff --git a/frontend/src/components/ui/index.ts b/frontend/src/components/ui/index.ts index f112e380..0a4cd157 100644 --- a/frontend/src/components/ui/index.ts +++ b/frontend/src/components/ui/index.ts @@ -14,6 +14,7 @@ import("./desc-list"); import("./details"); import("./dialog"); import("./file-list"); +import("./inline-input"); import("./input"); import("./language-select"); import("./locale-picker"); diff --git a/frontend/src/components/ui/inline-input.ts b/frontend/src/components/ui/inline-input.ts new file mode 100644 index 00000000..140558e5 --- /dev/null +++ b/frontend/src/components/ui/inline-input.ts @@ -0,0 +1,25 @@ +import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js"; +import inputStyles from "@shoelace-style/shoelace/dist/components/input/input.styles.js"; +import { css } from "lit"; +import { customElement } from "lit/decorators.js"; + +/** + * Input to use inline with text. + */ +@customElement("btrix-inline-input") +export class InlineInput extends SlInput { + static styles = [ + inputStyles, + css` + :host { + --sl-input-height-small: var(--sl-font-size-x-large); + --sl-input-color: var(--sl-color-neutral-500); + } + + .input--small .input__control { + text-align: center; + padding: 0 0.5ch; + } + `, + ] as typeof SlInput.styles; +} diff --git a/frontend/src/components/ui/pagination.ts b/frontend/src/components/ui/pagination.ts index 5ab37a1b..e8a2cf35 100644 --- a/frontend/src/components/ui/pagination.ts +++ b/frontend/src/components/ui/pagination.ts @@ -33,17 +33,12 @@ export class Pagination extends LitElement { static styles = [ srOnly, css` - :host { - --sl-input-height-small: var(--sl-font-size-x-large); - --sl-input-color: var(--sl-color-neutral-500); - } - ul { align-items: center; list-style: none; margin: 0; padding: 0; - color: var(--sl-input-color); + color: var(--sl-color-neutral-500); } ul.compact { @@ -64,11 +59,6 @@ export class Pagination extends LitElement { cursor: pointer; } - sl-input::part(input) { - text-align: center; - padding: 0 0.5ch; - } - .currentPage { display: flex; align-items: center; @@ -211,7 +201,7 @@ export class Pagination extends LitElement { return html`
${this.pages}
- + >
`; } diff --git a/frontend/src/components/utils/observable.ts b/frontend/src/components/utils/observable.ts index b37ef342..5706e616 100644 --- a/frontend/src/components/utils/observable.ts +++ b/frontend/src/components/utils/observable.ts @@ -32,6 +32,7 @@ export class Observable extends LitElement { disconnectedCallback(): void { this.observer?.disconnect(); + super.disconnectedCallback(); } firstUpdated() { diff --git a/frontend/src/features/archived-items/crawl-queue.ts b/frontend/src/features/archived-items/crawl-queue.ts index b226c025..6cab108c 100644 --- a/frontend/src/features/archived-items/crawl-queue.ts +++ b/frontend/src/features/archived-items/crawl-queue.ts @@ -1,4 +1,9 @@ import { localized, msg, str } from "@lit/localize"; +import type { + SlChangeEvent, + SlInput, + SlInputEvent, +} from "@shoelace-style/shoelace"; import type { PropertyValues } from "lit"; import { customElement, property, state } from "lit/decorators.js"; import { when } from "lit/directives/when.js"; @@ -60,6 +65,9 @@ export class CrawlQueue extends LiteElement { @state() private isLoading = false; + @state() + private pageOffset = 0; + @state() private pageSize = 50; @@ -82,7 +90,10 @@ export class CrawlQueue extends LiteElement { changedProperties.has("orgId") || changedProperties.has("crawlId") || changedProperties.has("pageSize") || - changedProperties.has("regex") + changedProperties.has("regex") || + (changedProperties.has("pageOffset") && + // Prevents double-fetch when offset is programmatically changed according to queue total + !changedProperties.has("queue")) ) { void this.fetchOnUpdate(); } @@ -90,13 +101,66 @@ export class CrawlQueue extends LiteElement { render() { return html` - ${msg("Queued URLs")} ${this.renderBadge()} + + ${this.renderOffsetControl()} ${this.renderBadge()} + ${this.renderContent()} `; } + private renderOffsetControl() { + if (!this.queue) { + return msg("Queued URLs"); + } + if (this.pageOffset === 0 && this.queue.total <= this.pageSize) { + return msg( + str`Queued URLs from 1 to ${this.queue.total.toLocaleString()}`, + ); + } + + const offsetValue = this.pageOffset + 1; + const countMax = Math.min( + this.pageOffset + this.pageSize, + this.queue.total, + ); + const getInputWidth = (v: number | string) => + `${Math.max(v.toString().length, 3) + 2}ch`; + + return html` +
+ ${msg(html` + Queued URLs from + { + const input = e.target as SlInput; + + input.style.width = getInputWidth(input.value); + }} + @sl-change=${async (e: SlChangeEvent) => { + const input = e.target as SlInput; + const int = +input.value.replace(/\D/g, ""); + + await this.updateComplete; + + const value = Math.max(1, Math.min(int, this.queue!.total - 1)); + + input.value = value.toString(); + this.pageOffset = value - 1; + }} + > + to ${countMax.toLocaleString()} of + ${this.queue.total.toLocaleString()} + `)} +
+ `; + } + private renderContent() { if (!this.queue?.total) { if (this.isLoading) { @@ -119,9 +183,9 @@ export class CrawlQueue extends LiteElement { const isExcluded = !isMatch && this.isExcluded(url); return html` - ${idx + 1}. + + ${(idx + this.pageOffset + 1).toLocaleString()}. + ${when( - this.queue.total === this.queue.results.length, + this.queue.total <= this.pageOffset + this.pageSize, () => html`
${msg("End of queue")} @@ -165,14 +229,6 @@ export class CrawlQueue extends LiteElement { if (!this.queue) return ""; return html` - - ${this.queue.total - ? this.queue.total > 1 - ? msg(str`${this.queue.total.toLocaleString()} URLs`) - : msg(str`1 URL`) - : msg("No queue")} - - ${this.matchedTotal ? html` @@ -230,10 +286,13 @@ export class CrawlQueue extends LiteElement { } private async getQueue(): Promise { - const offset = "0"; const count = this.pageSize.toString(); const regex = this.regex; - const params = new URLSearchParams({ offset, count, regex }); + const params = new URLSearchParams({ + offset: this.pageOffset.toString(), + count, + regex, + }); const data: ResponseData = await this.apiFetch( `/orgs/${this.orgId}/crawls/${this.crawlId}/queue?${params.toString()}`, this.authState!, diff --git a/frontend/src/pages/org/workflow-detail.ts b/frontend/src/pages/org/workflow-detail.ts index b4b842a2..7d6a4d17 100644 --- a/frontend/src/pages/org/workflow-detail.ts +++ b/frontend/src/pages/org/workflow-detail.ts @@ -1220,7 +1220,7 @@ export class WorkflowDetail extends LiteElement { return html`

- ${msg("Crawl URLs")} + ${msg("Upcoming Pages")}