UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

277 lines (276 loc) • 15.4 kB
/*! All material copyright ESRI, All Rights Reserved, unless otherwise specified. See https://github.com/Esri/calcite-design-system/blob/dev/LICENSE.md for details. v3.2.1 */ import { c as customElement } from "../../chunks/runtime.js"; import { isServer, nothing, html } from "lit"; import { render } from "lit-html"; import { createRef, ref } from "lit-html/directives/ref.js"; import { LitElement, createEvent, safeClassMap } from "@arcgis/lumina"; import { n as numberStringFormatter } from "../../chunks/locale.js"; import { u as useT9n } from "../../chunks/useT9n.js"; import { i as isHidden } from "../../chunks/component.js"; import { css } from "@lit/reactive-element/css-tag.js"; function getUserAgentData() { return navigator.userAgentData; } function getUserAgentString() { if (isServer) { return ""; } const uaData = getUserAgentData(); return uaData?.brands ? uaData.brands.map(({ brand, version }) => `${brand}/${version}`).join(" ") : navigator.userAgent; } const CSS = { bordered: "bordered", striped: "striped", selectionArea: "selection-area", paginationArea: "pagination-area", container: "container", tableContainer: "table-container", tableFixed: "table--fixed", assistiveText: "assistive-text", selectionActions: "selection-actions" }; const SLOTS = { selectionActions: "selection-actions", tableHeader: "table-header", tableFooter: "table-footer" }; const styles = css`@charset "UTF-8";:host([scale=s]){--calcite-internal-table-cell-padding: .25rem;--calcite-internal-table-cell-font-size: var(--calcite-font-size--2);--calcite-internal-table-cell-font-size-secondary: var(--calcite-font-size--3)}:host([scale=m]){--calcite-internal-table-cell-padding: .5rem;--calcite-internal-table-cell-font-size: var(--calcite-font-size--1);--calcite-internal-table-cell-font-size-secondary: var(--calcite-font-size--2)}:host([scale=l]){--calcite-internal-table-cell-padding: 1rem;--calcite-internal-table-cell-font-size: var(--calcite-font-size-0);--calcite-internal-table-cell-font-size-secondary: var(--calcite-font-size--1)}:host{display:flex}.container{display:flex;block-size:100%;inline-size:100%;flex-direction:column}.table-container{overflow:auto;white-space:nowrap;border:1px solid var(--calcite-color-border-3)}.assistive-text{position:absolute;inline-size:1px;block-size:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}table{inline-size:100%;border-collapse:collapse;overflow-x:scroll}@-moz-document url-prefix(){table{border-collapse:separate;border-spacing:0}}.table--fixed{table-layout:fixed}.bordered ::slotted(calcite-table-row){--calcite-table-row-border-color: var(--calcite-color-border-3)}.striped ::slotted(calcite-table-row:nth-child(2n+1)){--calcite-table-row-background: var(--calcite-color-foreground-2)}.selection-actions{display:flex;flex-direction:row;margin-inline-start:auto}.selection-area{display:flex;flex-direction:row;align-items:center;padding-block:var(--calcite-internal-table-cell-padding)}.selection-area calcite-chip:last-of-type{margin-inline-end:.5rem}.selection-area calcite-chip:last-of-type:not(:first-of-type){margin-inline-start:.5rem}.selection-area calcite-button{margin-inline-end:1rem}.pagination-area{display:flex;inline-size:100%;flex-direction:row;justify-content:center;padding-block:var(--calcite-internal-table-cell-padding)}calcite-pagination{flex:1;justify-content:center}:host([hidden]){display:none}[hidden]{display:none}`; class Table extends LitElement { constructor() { super(); this.paginationEl = createRef(); this.tableBodySlotEl = createRef(); this.tableFootSlotEl = createRef(); this.tableHeadSlotEl = createRef(); this.messages = useT9n({ blocking: true }); this.colCount = 0; this.pageStartRow = 1; this.selectedCount = 0; this._selectedItems = []; this.bordered = false; this.groupSeparator = false; this.interactionMode = "interactive"; this.layout = "auto"; this.numbered = false; this.pageSize = 0; this.scale = "m"; this.selectionDisplay = "top"; this.selectionMode = "none"; this.striped = false; this.calciteInternalTableRowFocusChange = createEvent({ cancelable: false }); this.calciteTablePageChange = createEvent({ cancelable: false }); this.calciteTableSelect = createEvent({ cancelable: false }); this.listen("calciteTableRowSelect", this.calciteTableRowSelectListener); this.listen("calciteInternalTableRowSelect", this.calciteInternalTableRowSelectListener); this.listen("calciteInternalTableRowFocusRequest", this.calciteInternalTableRowFocusEvent); } static { this.properties = { colCount: [16, {}, { state: true }], pageStartRow: [16, {}, { state: true }], readCellContentsToAT: [16, {}, { state: true }], selectedCount: [16, {}, { state: true }], _selectedItems: [16, {}, { state: true }], bordered: [7, {}, { reflect: true, type: Boolean }], caption: 1, groupSeparator: [7, {}, { reflect: true, type: Boolean }], interactionMode: [3, {}, { reflect: true }], layout: [3, {}, { reflect: true }], messageOverrides: [0, {}, { attribute: false }], numbered: [7, {}, { reflect: true, type: Boolean }], numberingSystem: [3, {}, { reflect: true }], pageSize: [11, {}, { reflect: true, type: Number }], scale: [3, {}, { reflect: true }], selectedItems: [32, {}, { attribute: false, readOnly: true }], selectionDisplay: [3, {}, { reflect: true }], selectionMode: [3, {}, { reflect: true }], striped: [7, {}, { reflect: true, type: Boolean }] }; } static { this.styles = styles; } get selectedItems() { return this._selectedItems; } async load() { this.readCellContentsToAT = /safari/i.test(getUserAgentString()); this.listenOn(this.el.shadowRoot, "slotchange", this.handleSlotChange); } willUpdate(changes) { if (changes.has("groupSeparator") && (this.hasUpdated || this.groupSeparator !== false) || changes.has("interactionMode") && (this.hasUpdated || this.interactionMode !== "interactive") || changes.has("numbered") && (this.hasUpdated || this.numbered !== false) || changes.has("numberingSystem") || changes.has("pageSize") && (this.hasUpdated || this.pageSize !== 0) || changes.has("scale") && (this.hasUpdated || this.scale !== "m") || changes.has("selectionMode") && (this.hasUpdated || this.selectionMode !== "none")) { this.updateRows(); } } handleSlotChange() { this.updateRows(); } calciteTableRowSelectListener(event) { if (event.composedPath().includes(this.el)) { this.setSelectedItems(event.target); } } calciteInternalTableRowSelectListener(event) { if (!event.composedPath().includes(this.el)) { return; } this.updateSelectedItems(false); event.stopPropagation(); } calciteInternalTableRowFocusEvent(event) { const cellPosition = event.detail.cellPosition; const rowPos = event.detail.rowPosition; const destination = event.detail.destination; const lastCell = event.detail.lastCell; const visibleBody = this.bodyRows?.filter((row) => !isHidden(row)); const visibleAll = this.allRows?.filter((row) => !isHidden(row)); const lastHeadRow = this.headRows[this.headRows.length - 1]?.positionAll; const firstBodyRow = visibleBody[0]?.positionAll; const lastBodyRow = visibleBody[visibleBody.length - 1]?.positionAll; const firstFootRow = this.footRows[0]?.positionAll; const lastTableRow = visibleAll[visibleAll.length - 1]?.positionAll; const leavingHeader = destination === "next" && rowPos === lastHeadRow; const leavingFooter = destination === "previous" && rowPos === firstFootRow; const enteringHeader = destination === "previous" && rowPos === firstBodyRow; const enteringFooter = destination === "next" && rowPos === lastBodyRow; let rowPosition; switch (destination) { case "first": rowPosition = 0; break; case "last": rowPosition = lastTableRow; break; case "next": rowPosition = leavingHeader ? firstBodyRow : enteringFooter ? firstFootRow : rowPos + 1; break; case "previous": rowPosition = leavingFooter ? lastBodyRow : enteringHeader ? lastHeadRow : rowPos - 1; break; } const destinationCount = this.allRows?.find((row) => row.positionAll === rowPosition)?.cellCount; const adjustedPos = cellPosition > destinationCount ? destinationCount : cellPosition; if (rowPosition !== void 0) { this.calciteInternalTableRowFocusChange.emit({ cellPosition: adjustedPos, rowPosition, destination, lastCell }); } } getSlottedRows(el) { return el?.assignedElements({ flatten: true })?.filter((el2) => el2?.matches("calcite-table-row")); } updateRows() { const headRows = this.getSlottedRows(this.tableHeadSlotEl.value) || []; const bodyRows = this.getSlottedRows(this.tableBodySlotEl.value) || []; const footRows = this.getSlottedRows(this.tableFootSlotEl.value) || []; const allRows = [...headRows, ...bodyRows, ...footRows]; headRows?.forEach((row) => { const position = headRows?.indexOf(row); row.rowType = "head"; row.positionSection = position; row.positionSectionLocalized = this.localizeNumber((position + 1).toString()); }); bodyRows?.forEach((row) => { const position = bodyRows?.indexOf(row); row.rowType = "body"; row.positionSection = position; row.positionSectionLocalized = this.localizeNumber((position + 1).toString()); }); footRows?.forEach((row) => { const position = footRows?.indexOf(row); row.rowType = "foot"; row.positionSection = position; row.positionSectionLocalized = this.localizeNumber((position + 1).toString()); }); allRows?.forEach((row) => { row.interactionMode = this.interactionMode; row.selectionMode = this.selectionMode; row.bodyRowCount = bodyRows?.length; row.positionAll = allRows?.indexOf(row); row.numbered = this.numbered; row.scale = this.scale; row.readCellContentsToAT = this.readCellContentsToAT; row.lastVisibleRow = allRows?.indexOf(row) === allRows.length - 1; }); const colCount = headRows[0]?.cellCount || headRows[0]?.querySelectorAll("calcite-table-header")?.length; this.colCount = colCount; this.headRows = headRows; this.bodyRows = bodyRows; this.footRows = footRows; this.allRows = allRows; this.updateSelectedItems(); this.paginateRows(); } handlePaginationChange() { const requestedItem = this.paginationEl.value?.startItem; this.pageStartRow = requestedItem || 1; this.calciteTablePageChange.emit(); this.updateRows(); } paginateRows() { this.bodyRows?.forEach((row) => { const rowPos = row.positionSection + 1; const inView = rowPos >= this.pageStartRow && rowPos < this.pageStartRow + this.pageSize; row.itemHidden = this.pageSize > 0 && !inView && !this.footRows.includes(row); row.lastVisibleRow = rowPos === this.pageStartRow + this.pageSize - 1 || rowPos === this.bodyRows.length; }); } async updateSelectedItems(emit) { const selectedItems = this.bodyRows?.filter((el) => el.selected); this._selectedItems = selectedItems; this.selectedCount = selectedItems?.length; this.allRows?.forEach((row) => { row.selectedRowCount = this.selectedCount; row.selectedRowCountLocalized = this.localizeNumber(this.selectedCount); }); if (emit) { this.calciteTableSelect.emit(); } } handleDeselectAllRows() { this.bodyRows?.forEach((row) => { row.selected = false; }); this.updateSelectedItems(true); } setSelectedItems(elToMatch) { this.bodyRows?.forEach((el) => { if (elToMatch?.rowType === "head") { el.selected = this.selectedCount !== this.bodyRows?.length; } else { el.selected = this.selectionMode === "multiple" || elToMatch === el ? el.selected : false; } }); this.updateSelectedItems(true); } localizeNumber(value) { numberStringFormatter.numberFormatOptions = { locale: this.messages._lang, numberingSystem: this.numberingSystem, useGrouping: this.groupSeparator }; return numberStringFormatter.localize(value.toString()); } renderSelectionArea() { const outOfViewCount = this._selectedItems?.filter((el) => isHidden(el))?.length; const localizedOutOfView = this.localizeNumber(outOfViewCount?.toString()); const localizedSelectedCount = this.localizeNumber(this.selectedCount?.toString()); const selectionText = `${localizedSelectedCount} ${this.messages.selected}`; const outOfView = `${localizedOutOfView} ${this.messages.hiddenSelected}`; return html`<div class=${safeClassMap(CSS.selectionArea)}><calcite-chip .kind=${this.selectedCount > 0 ? "brand" : "neutral"} .label=${selectionText} .scale=${this.scale} .value=${selectionText}>${selectionText}</calcite-chip>${outOfViewCount > 0 && html`<calcite-chip icon=hide-empty .label=${outOfView} .scale=${this.scale} title=${outOfView ?? nothing} .value=${outOfView}>${localizedOutOfView}</calcite-chip>` || ""}${this.selectedCount > 0 && html`<calcite-button icon-start=x kind=neutral @click=${this.handleDeselectAllRows} round .scale=${this.scale} .title=${`${this.messages.clear} ${selectionText} ${this.messages.row}`}>${this.messages.clear}</calcite-button>` || ""}<div class=${safeClassMap(CSS.selectionActions)}><slot name=${SLOTS.selectionActions}></slot></div></div>`; } renderPaginationArea() { return html`<div class=${safeClassMap(CSS.paginationArea)}><calcite-pagination .groupSeparator=${this.groupSeparator} .numberingSystem=${this.numberingSystem} @calcitePaginationChange=${this.handlePaginationChange} .pageSize=${this.pageSize} .scale=${this.scale} start-item=1 .totalItems=${this.bodyRows?.length} ${ref(this.paginationEl)}></calcite-pagination></div>`; } renderTHead() { return html`<thead><slot name=${SLOTS.tableHeader} ${ref(this.tableHeadSlotEl)}></slot></thead>`; } renderTBody() { return html`<tbody><slot ${ref(this.tableBodySlotEl)}></slot></tbody>`; } renderTFoot() { return html`<tfoot><slot name=${SLOTS.tableFooter} ${ref(this.tableFootSlotEl)}></slot></tfoot>`; } render() { return html`<div class=${safeClassMap(CSS.container)}>${this.selectionMode !== "none" && this.selectionDisplay !== "none" && this.renderSelectionArea() || ""}<div class=${safeClassMap({ [CSS.bordered]: this.bordered, [CSS.striped]: this.striped, [CSS.tableContainer]: true })}><table .ariaColCount=${this.colCount} .ariaMultiSelectable=${/* workaround to ensure the attr gets removed; we should be able to avoid the ternary when fixed */ this.selectionMode === "multiple" ? "true" : null} .ariaRowCount=${this.allRows?.length} class=${safeClassMap({ [CSS.tableFixed]: this.layout === "fixed" })} .role=${this.interactionMode === "interactive" ? "grid" : "table"} ${ref((el) => { if (!el) { return; } render(html`<caption class=${safeClassMap(CSS.assistiveText)}>${this.caption}</caption>${this.renderTHead()}${this.renderTBody()}${this.renderTFoot()}`, el); })}></table></div>${this.pageSize > 0 && this.renderPaginationArea() || ""}</div>`; } } customElement("calcite-table", Table); export { Table };