@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
297 lines (296 loc) • 20 kB
JavaScript
/* 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
};