import { t } from "@lingui/macro"; import { CSSResult, html, LitElement, property, TemplateResult } from "lit-element"; import { AKResponse } from "../../api/Client"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; import PFTable from "@patternfly/patternfly/components/Table/table.css"; import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css"; import PFButton from "@patternfly/patternfly/components/Button/button.css"; import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css"; import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css"; import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css"; import AKGlobal from "../../authentik.css"; import "./TablePagination"; import "./TableSearch"; import "../EmptyState"; import "../chips/Chip"; import "../chips/ChipGroup"; import { EVENT_REFRESH } from "../../constants"; import { ifDefined } from "lit-html/directives/if-defined"; export class TableColumn { title: string; orderBy?: string; onClick?: () => void; constructor(title: string, orderBy?: string) { this.title = title; this.orderBy = orderBy; } headerClickHandler(table: Table): void { if (!this.orderBy) { return; } if (table.order === this.orderBy) { table.order = `-${this.orderBy}`; } else { table.order = this.orderBy; } table.fetch(); } private getSortIndicator(table: Table): string { switch (table.order) { case this.orderBy: return "fa-long-arrow-alt-down"; case `-${this.orderBy}`: return "fa-long-arrow-alt-up"; default: return "fa-arrows-alt-v"; } } renderSortable(table: Table): TemplateResult { return html` `; } render(table: Table): TemplateResult { return html` ${this.orderBy ? this.renderSortable(table) : html`${this.title}`} `; } } export abstract class Table extends LitElement { abstract apiEndpoint(page: number): Promise>; abstract columns(): TableColumn[]; abstract row(item: T): TemplateResult[]; private isLoading = false; searchEnabled(): boolean { return false; } // eslint-disable-next-line @typescript-eslint/no-unused-vars renderExpanded(item: T): TemplateResult { if (this.expandable) { throw new Error("Expandable is enabled but renderExpanded is not overridden!"); } return html``; } @property({ attribute: false }) data?: AKResponse; @property({ type: Number }) page = 1; @property({ type: String }) order?: string; @property({ type: String }) search?: string; @property({ type: Boolean }) checkbox = false; @property({ type: Boolean }) checkboxChip = false; @property({ attribute: false }) selectedElements: T[] = []; @property({ type: Boolean }) paginated = true; @property({ type: Boolean }) expandable = false; @property({ attribute: false }) expandedElements: T[] = []; static get styles(): CSSResult[] { return [ PFBase, PFTable, PFBullseye, PFButton, PFToolbar, PFDropdown, PFPagination, AKGlobal, ]; } constructor() { super(); this.addEventListener(EVENT_REFRESH, () => { this.fetch(); }); } public fetch(): void { if (this.isLoading) { return; } this.isLoading = true; this.apiEndpoint(this.page) .then((r) => { this.data = r; this.page = r.pagination.current; r.results.forEach((res) => { const selectedIndex = this.selectedElements.indexOf(res); if (selectedIndex <= -1) { this.selectedElements.splice(selectedIndex, 1); } const expandedIndex = this.expandedElements.indexOf(res); if (expandedIndex <= -1) { this.expandedElements.splice(expandedIndex, 1); } }); this.isLoading = false; }) .catch(() => { this.isLoading = false; }); } private renderLoading(): TemplateResult { return html`
`; } renderEmpty(inner?: TemplateResult): TemplateResult { return html`
${inner ? inner : html``}
`; } private renderRows(): TemplateResult[] | undefined { if (!this.data) { return; } if (this.data.pagination.count === 0) { return [this.renderEmpty()]; } return this.data.results.map((item: T) => { return html` ${this.checkbox ? html` = 0} @input=${(ev: InputEvent) => { if ((ev.target as HTMLInputElement).checked) { // Add item to selected this.selectedElements.push(item); } else { // Get index of item and remove if selected const index = this.selectedElements.indexOf(item); if (index <= -1) return; this.selectedElements.splice(index, 1); } this.requestUpdate(); }} /> ` : html``} ${this.expandable ? html` ` : html``} ${this.row(item).map((col) => { return html`${col}`; })} ${this.expandedElements.indexOf(item) > -1 ? this.renderExpanded(item) : html``} `; }); } renderToolbar(): TemplateResult { return html``; } renderToolbarSelected(): TemplateResult { return html``; } renderToolbarAfter(): TemplateResult { return html``; } renderSearch(): TemplateResult { if (!this.searchEnabled()) { return html``; } return html` { this.search = value; this.dispatchEvent( new CustomEvent(EVENT_REFRESH, { bubbles: true, composed: true, }), ); }} > `; } // eslint-disable-next-line @typescript-eslint/no-unused-vars renderSelectedChip(item: T): TemplateResult { return html``; } renderToolbarContainer(): TemplateResult { return html`
${this.renderSearch()}
${this.renderToolbar()}
${this.renderToolbarAfter()}
${this.renderToolbarSelected()}
${this.paginated ? html` { this.page = page; this.dispatchEvent( new CustomEvent(EVENT_REFRESH, { bubbles: true, composed: true, }), ); }} > ` : html``}
`; } firstUpdated(): void { this.fetch(); } renderTable(): TemplateResult { return html` ${this.checkbox && this.checkboxChip ? html` ${this.selectedElements.map((el) => { return html`${this.renderSelectedChip(el)}`; })} ` : html``} ${this.renderToolbarContainer()} ${this.checkbox ? html`` : html``} ${this.expandable ? html`` : html``} ${this.columns().map((col) => col.render(this))} ${this.isLoading || !this.data ? this.renderLoading() : this.renderRows()}
{ if ((ev.target as HTMLInputElement).checked) { this.selectedElements = this.data?.results || []; } else { this.selectedElements = []; } }} />
${this.paginated ? html`
{ this.page = page; this.dispatchEvent( new CustomEvent(EVENT_REFRESH, { bubbles: true, composed: true, }), ); }} >
` : html``}`; } render(): TemplateResult { return this.renderTable(); } }