UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

297 lines (296 loc) • 20 kB
/* COPYRIGHT Esri - https://js.arcgis.com/5.0/LICENSE.txt */ import { c as customElement } from "../../chunks/runtime.js"; import { isServer, css, nothing, html, render } from "lit"; import { createRef, ref } from "lit/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"; 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", dismissButton: "dismiss-button", selectionChipActive: "selection-chip--active", selectionCountChip: "selection-chip", selectionOutOfViewChip: "selection-chip--out-of-view" }; const SLOTS = { selectionActions: "selection-actions", tableHeader: "table-header", tableFooter: "table-footer" }; const ICONS = { hideEmpty: "hide-empty", clear: "x" }; 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(:is([scale=s],[scale=m])){--calcite-internal-table-selection-action-spacing: var(--calcite-spacing-xxs)}:host([scale=l]){--calcite-internal-table-selection-action-spacing: var(--calcite-spacing-xs);--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;flex-direction:column;inline-size:var(--calcite-container-size-content-fluid);block-size:var(--calcite-container-size-content-fluid)}.table-container{overflow:auto;white-space:nowrap;border:var(--calcite-border-width-sm) solid var(--calcite-table-border-color, var(--calcite-color-border-2));border-radius:var(--calcite-table-corner-radius, var(--calcite-corner-radius-sharp));box-shadow:var(--calcite-table-shadow, var(--calcite-shadow-none))}.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{border-collapse:collapse;overflow-x:scroll;inline-size:var(--calcite-container-size-content-fluid)}@-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-table-border-color, var(--calcite-color-border-2))}.striped ::slotted(calcite-table-row:nth-child(2n+1)){--calcite-table-row-background-color: var( --calcite-table-row-background-color-striped, var(--calcite-color-foreground-2) )}.selection-actions{display:flex;flex-direction:row;gap:var(--calcite-internal-table-selection-action-spacing);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}.selection-chip{--calcite-chip-background-color: var(--calcite-table-selection-chip-background-color);--calcite-chip-border-color: var(--calcite-table-selection-chip-border-color);--calcite-chip-corner-radius: var(--calcite-table-selection-chip-corner-radius);--calcite-chip-shadow: var(--calcite-table-selection-chip-shadow);--calcite-chip-text-color: var(--calcite-table-selection-chip-text-color)}.selection-chip.selection-chip--active{--calcite-chip-background-color: var(--calcite-table-selection-chip-background-color-selected);--calcite-chip-border-color: var(--calcite-table-selection-chip-border-color-selected);--calcite-chip-text-color: var(--calcite-table-selection-chip-text-color-selected)}.selection-chip--out-of-view{--calcite-chip-background-color: var(--calcite-table-selection-out-of-view-chip-background-color);--calcite-chip-border-color: var(--calcite-table-selection-out-of-view-chip-border-color);--calcite-chip-corner-radius: var(--calcite-table-selection-out-of-view-chip-corner-radius);--calcite-chip-shadow: var(--calcite-table-selection-out-of-view-chip-shadow);--calcite-chip-text-color: var(--calcite-table-selection-out-of-view-chip-text-color);--calcite-chip-icon-color: var(--calcite-table-selection-out-of-view-chip-icon-color)}.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;--calcite-pagination-color: var(--calcite-table-pagination-color);--calcite-pagination-color-hover: var(--calcite-table-pagination-color-hover);--calcite-pagination-color-border-hover: var(--calcite-table-pagination-color-border-hover);--calcite-pagination-color-border-active: var(--calcite-table-pagination-color-border-active);--calcite-pagination-background-color: var(--calcite-table-pagination-background-color);--calcite-pagination-icon-color-background-hover: var(--calcite-table-pagination-icon-color-background-hover)}.dismiss-button{margin-inline-end:1rem;--calcite-button-background-color: var(--calcite-table-selection-dismiss-button-background-color);--calcite-button-border-color: var(--calcite-table-selection-dismiss-button-border-color);--calcite-button-corner-radius: var(--calcite-table-selection-dismiss-button-corner-radius);--calcite-button-shadow: var(--calcite-table-selection-dismiss-button-shadow);--calcite-button-text-color: var(--calcite-table-selection-dismiss-button-text-color)}.dismiss-button:hover{--calcite-button-background-color: var(--calcite-table-selection-dismiss-button-background-color-hover);--calcite-button-border-color: var(--calcite-table-selection-dismiss-button-border-color-hover);--calcite-button-text-color: var(--calcite-table-selection-dismiss-button-text-color-hover)}.dismiss-button:active{--calcite-button-background-color: var(--calcite-table-selection-dismiss-button-background-color-active);--calcite-button-border-color: var(--calcite-table-selection-dismiss-button-border-color-active);--calcite-button-text-color: var(--calcite-table-selection-dismiss-button-text-color-active)}:host([hidden]){display:none}[hidden]{display:none}`; class Table extends LitElement { constructor() { super(); this.paginationRef = createRef(); this.tableBodySlotRef = createRef(); this.tableFootSlotRef = createRef(); this.tableHeadSlotRef = createRef(); this.messages = useT9n({ blocking: true }); this.colCount = 0; this.pageStartRow = 1; this.selectedCount = 0; this._selectedItems = []; this.bordered = false; this.currentPage = 0; 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, currentPage: [11, {}, { reflect: true, type: Number }], 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") || changes.has("currentPage") && (this.hasUpdated || this.currentPage > 1) && this.pageSize > 0) { 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.tableHeadSlotRef.value) || []; const bodyRows = this.getSlottedRows(this.tableBodySlotRef.value) || []; const footRows = this.getSlottedRows(this.tableFootSlotRef.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.handleCurrentPageRange(); this.updateSelectedItems(); } handleCurrentPageRange() { const requestedPage = this.currentPage; const totalRows = this.bodyRows?.length || 0; const totalPages = this.pageSize > 0 ? Math.ceil(totalRows / this.pageSize) : 1; if (totalPages > 0) { const page = Math.min(Math.max(requestedPage, 1), totalPages); this.currentPage = page; this.pageStartRow = (page - 1) * this.pageSize + 1; } this.paginateRows(); } handlePaginationChange() { const requestedItem = this.paginationRef.value?.startItem; this.pageStartRow = requestedItem || 1; this.currentPage = Math.ceil(this.pageStartRow / this.pageSize); 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 class=${safeClassMap({ [CSS.selectionCountChip]: true, [CSS.selectionChipActive]: this.selectedCount > 0 })} .kind=${this.selectedCount > 0 ? "brand" : "neutral"} .label=${selectionText} .scale=${this.scale} .value=${selectionText}>${selectionText}</calcite-chip>${outOfViewCount > 0 && html`<calcite-chip class=${safeClassMap(CSS.selectionOutOfViewChip)} .icon=${ICONS.hideEmpty} .label=${outOfView} .scale=${this.scale} title=${outOfView ?? nothing} .value=${outOfView}>${localizedOutOfView}</calcite-chip>` || ""}${this.selectedCount > 0 && html`<calcite-button class=${safeClassMap(CSS.dismissButton)} .iconStart=${ICONS.clear} 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} .startItem=${this.pageStartRow} .totalItems=${this.bodyRows?.length} ${ref(this.paginationRef)}></calcite-pagination></div>`; } renderTHead() { return html`<thead><slot name=${SLOTS.tableHeader} ${ref(this.tableHeadSlotRef)}></slot></thead>`; } renderTBody() { return html`<tbody><slot ${ref(this.tableBodySlotRef)}></slot></tbody>`; } renderTFoot() { return html`<tfoot><slot name=${SLOTS.tableFooter} ${ref(this.tableFootSlotRef)}></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 };