<template> <div :class="cx('root')" data-scrollselectors=".p-datatable-wrapper" v-bind="ptm('root')" data-pc-name="datatable"> <slot></slot> <div v-if="loading" :class="cx('loadingOverlay')" v-bind="ptm('loadingOverlay')"> <slot v-if="$slots.loading" name="loading"></slot> <template v-else> <component v-if="$slots.loadingicon" :is="$slots.loadingicon" :class="cx('loadingIcon')" /> <i v-else-if="loadingIcon" :class="[cx('loadingIcon'), 'pi-spin', loadingIcon]" v-bind="ptm('loadingIcon')" /> <SpinnerIcon v-else spin :class="cx('loadingIcon')" v-bind="ptm('loadingIcon')" /> </template> </div> <div v-if="$slots.header" :class="cx('header')" v-bind="ptm('header')"> <slot name="header"></slot> </div> <DTPaginator v-if="paginatorTop" :rows="d_rows" :first="d_first" :totalRecords="totalRecordsLength" :pageLinkSize="pageLinkSize" :template="paginatorTemplate" :rowsPerPageOptions="rowsPerPageOptions" :currentPageReportTemplate="currentPageReportTemplate" :class="cx('paginator')" @page="onPage($event)" :alwaysShow="alwaysShowPaginator" :unstyled="unstyled" :pt="ptm('paginator')" > <template v-if="$slots.paginatorstart" #start> <slot name="paginatorstart"></slot> </template> <template v-if="$slots.paginatorend" #end> <slot name="paginatorend"></slot> </template> <template v-if="$slots.paginatorfirstpagelinkicon" #firstpagelinkicon="slotProps"> <slot name="paginatorfirstpagelinkicon" :class="slotProps.class"></slot> </template> <template v-if="$slots.paginatorprevpagelinkicon" #prevpagelinkicon="slotProps"> <slot name="paginatorprevpagelinkicon" :class="slotProps.class"></slot> </template> <template v-if="$slots.paginatornextpagelinkicon" #nextpagelinkicon="slotProps"> <slot name="paginatornextpagelinkicon" :class="slotProps.class"></slot> </template> <template v-if="$slots.paginatorlastpagelinkicon" #lastpagelinkicon="slotProps"> <slot name="paginatorlastpagelinkicon" :class="slotProps.class"></slot> </template> <template v-if="$slots.paginatorjumptopagedropdownicon" #jumptopagedropdownicon="slotProps"> <slot name="paginatorjumptopagedropdownicon" :class="slotProps.class"></slot> </template> <template v-if="$slots.paginatorrowsperpagedropdownicon" #rowsperpagedropdownicon="slotProps"> <slot name="paginatorrowsperpagedropdownicon" :class="slotProps.class"></slot> </template> </DTPaginator> <div :class="cx('wrapper')" :style="[sx('wrapper'), { maxHeight: virtualScrollerDisabled ? scrollHeight : '' }]" v-bind="ptm('wrapper')"> <DTVirtualScroller ref="virtualScroller" v-bind="virtualScrollerOptions" :items="processedData" :columns="columns" :style="scrollHeight !== 'flex' ? { height: scrollHeight } : undefined" :scrollHeight="scrollHeight !== 'flex' ? undefined : '100%'" :disabled="virtualScrollerDisabled" loaderDisabled inline autoSize :showSpacer="false" :pt="ptm('virtualScroller')" > <template #content="slotProps"> <table ref="table" role="table" :class="[cx('table'), tableClass]" :style="[tableStyle, slotProps.spacerStyle]" v-bind="{ ...tableProps, ...ptm('table') }"> <DTTableHeader :columnGroup="headerColumnGroup" :columns="slotProps.columns" :rowGroupMode="rowGroupMode" :groupRowsBy="groupRowsBy" :groupRowSortField="groupRowSortField" :reorderableColumns="reorderableColumns" :resizableColumns="resizableColumns" :allRowsSelected="allRowsSelected" :empty="empty" :sortMode="sortMode" :sortField="d_sortField" :sortOrder="d_sortOrder" :multiSortMeta="d_multiSortMeta" :filters="d_filters" :filtersStore="filters" :filterDisplay="filterDisplay" :filterInputProps="filterInputProps" @column-click="onColumnHeaderClick($event)" @column-mousedown="onColumnHeaderMouseDown($event)" @filter-change="onFilterChange" @filter-apply="onFilterApply" @column-dragstart="onColumnHeaderDragStart($event)" @column-dragover="onColumnHeaderDragOver($event)" @column-dragleave="onColumnHeaderDragLeave($event)" @column-drop="onColumnHeaderDrop($event)" @column-resizestart="onColumnResizeStart($event)" @checkbox-change="toggleRowsWithCheckbox($event)" :unstyled="unstyled" :pt="pt" /> <DTTableBody v-if="frozenValue" ref="frozenBodyRef" :value="frozenValue" :frozenRow="true" :columns="slotProps.columns" :first="d_first" :dataKey="dataKey" :selection="selection" :selectionKeys="d_selectionKeys" :selectionMode="selectionMode" :contextMenu="contextMenu" :contextMenuSelection="contextMenuSelection" :rowGroupMode="rowGroupMode" :groupRowsBy="groupRowsBy" :expandableRowGroups="expandableRowGroups" :rowClass="rowClass" :rowStyle="rowStyle" :editMode="editMode" :compareSelectionBy="compareSelectionBy" :scrollable="scrollable" :expandedRowIcon="expandedRowIcon" :collapsedRowIcon="collapsedRowIcon" :expandedRows="expandedRows" :expandedRowKeys="d_expandedRowKeys" :expandedRowGroups="expandedRowGroups" :editingRows="editingRows" :editingRowKeys="d_editingRowKeys" :templates="$slots" :responsiveLayout="responsiveLayout" :isVirtualScrollerDisabled="true" @rowgroup-toggle="toggleRowGroup" @row-click="onRowClick($event)" @row-dblclick="onRowDblClick($event)" @row-rightclick="onRowRightClick($event)" @row-touchend="onRowTouchEnd" @row-keydown="onRowKeyDown" @row-mousedown="onRowMouseDown" @row-dragstart="onRowDragStart($event)" @row-dragover="onRowDragOver($event)" @row-dragleave="onRowDragLeave($event)" @row-dragend="onRowDragEnd($event)" @row-drop="onRowDrop($event)" @row-toggle="toggleRow($event)" @radio-change="toggleRowWithRadio($event)" @checkbox-change="toggleRowWithCheckbox($event)" @cell-edit-init="onCellEditInit($event)" @cell-edit-complete="onCellEditComplete($event)" @cell-edit-cancel="onCellEditCancel($event)" @row-edit-init="onRowEditInit($event)" @row-edit-save="onRowEditSave($event)" @row-edit-cancel="onRowEditCancel($event)" :editingMeta="d_editingMeta" @editing-meta-change="onEditingMetaChange" :unstyled="unstyled" :pt="pt" /> <DTTableBody ref="bodyRef" :value="dataToRender(slotProps.rows)" :class="slotProps.styleClass" :columns="slotProps.columns" :empty="empty" :first="d_first" :dataKey="dataKey" :selection="selection" :selectionKeys="d_selectionKeys" :selectionMode="selectionMode" :contextMenu="contextMenu" :contextMenuSelection="contextMenuSelection" :rowGroupMode="rowGroupMode" :groupRowsBy="groupRowsBy" :expandableRowGroups="expandableRowGroups" :rowClass="rowClass" :rowStyle="rowStyle" :editMode="editMode" :compareSelectionBy="compareSelectionBy" :scrollable="scrollable" :expandedRowIcon="expandedRowIcon" :collapsedRowIcon="collapsedRowIcon" :expandedRows="expandedRows" :expandedRowKeys="d_expandedRowKeys" :expandedRowGroups="expandedRowGroups" :editingRows="editingRows" :editingRowKeys="d_editingRowKeys" :templates="$slots" :responsiveLayout="responsiveLayout" :virtualScrollerContentProps="slotProps" :isVirtualScrollerDisabled="virtualScrollerDisabled" @rowgroup-toggle="toggleRowGroup" @row-click="onRowClick($event)" @row-dblclick="onRowDblClick($event)" @row-rightclick="onRowRightClick($event)" @row-touchend="onRowTouchEnd" @row-keydown="onRowKeyDown($event, slotProps)" @row-mousedown="onRowMouseDown" @row-dragstart="onRowDragStart($event)" @row-dragover="onRowDragOver($event)" @row-dragleave="onRowDragLeave($event)" @row-dragend="onRowDragEnd($event)" @row-drop="onRowDrop($event)" @row-toggle="toggleRow($event)" @radio-change="toggleRowWithRadio($event)" @checkbox-change="toggleRowWithCheckbox($event)" @cell-edit-init="onCellEditInit($event)" @cell-edit-complete="onCellEditComplete($event)" @cell-edit-cancel="onCellEditCancel($event)" @row-edit-init="onRowEditInit($event)" @row-edit-save="onRowEditSave($event)" @row-edit-cancel="onRowEditCancel($event)" :editingMeta="d_editingMeta" @editing-meta-change="onEditingMetaChange" :unstyled="unstyled" :pt="pt" /> <tbody v-if="hasSpacerStyle(slotProps.spacerStyle)" :class="cx('virtualScrollerSpacer')" :style="{ height: `calc(${slotProps.spacerStyle.height} - ${slotProps.rows.length * slotProps.itemSize}px)` }" v-bind="ptm('virtualScrollerSpacer')" ></tbody> <DTTableFooter :columnGroup="footerColumnGroup" :columns="slotProps.columns" :pt="pt" /> </table> </template> </DTVirtualScroller> </div> <div v-if="$slots.footer" :class="cx('footer')" v-bind="ptm('footer')"> <slot name="footer"></slot> </div> <DTPaginator v-if="paginatorBottom" :rows="d_rows" :first="d_first" :totalRecords="totalRecordsLength" :pageLinkSize="pageLinkSize" :template="paginatorTemplate" :rowsPerPageOptions="rowsPerPageOptions" :currentPageReportTemplate="currentPageReportTemplate" :class="cx('paginator')" @page="onPage($event)" :alwaysShow="alwaysShowPaginator" :unstyled="unstyled" :pt="ptm('paginator')" > <template v-if="$slots.paginatorstart" #start> <slot name="paginatorstart"></slot> </template> <template v-if="$slots.paginatorend" #end> <slot name="paginatorend"></slot> </template> <template v-if="$slots.paginatorfirstpagelinkicon" #firstpagelinkicon="slotProps"> <slot name="paginatorfirstpagelinkicon" :class="slotProps.class"></slot> </template> <template v-if="$slots.paginatorprevpagelinkicon" #prevpagelinkicon="slotProps"> <slot name="paginatorprevpagelinkicon" :class="slotProps.class"></slot> </template> <template v-if="$slots.paginatornextpagelinkicon" #nextpagelinkicon="slotProps"> <slot name="paginatornextpagelinkicon" :class="slotProps.class"></slot> </template> <template v-if="$slots.paginatorlastpagelinkicon" #lastpagelinkicon="slotProps"> <slot name="paginatorlastpagelinkicon" :class="slotProps.class"></slot> </template> <template v-if="$slots.paginatorjumptopagedropdownicon" #jumptopagedropdownicon="slotProps"> <slot name="paginatorjumptopagedropdownicon" :class="slotProps.class"></slot> </template> <template v-if="$slots.paginatorrowsperpagedropdownicon" #rowsperpagedropdownicon="slotProps"> <slot name="paginatorrowsperpagedropdownicon" :class="slotProps.class"></slot> </template> </DTPaginator> <div ref="resizeHelper" :class="cx('resizeHelper')" style="display: none" v-bind="ptm('resizeHelper')"></div> <span v-if="reorderableColumns" ref="reorderIndicatorUp" :class="cx('reorderIndicatorUp')" style="position: absolute; display: none" v-bind="ptm('reorderIndicatorUp')"> <component :is="$slots.reorderindicatorupicon || 'ArrowDownIcon'" /> </span> <span v-if="reorderableColumns" ref="reorderIndicatorDown" :class="cx('reorderIndicatorDown')" style="position: absolute; display: none" v-bind="ptm('reorderIndicatorDown')"> <component :is="$slots.reorderindicatordownicon || 'ArrowUpIcon'" /> </span> </div> </template> <script> import { FilterMatchMode, FilterOperator, FilterService } from 'primevue/api'; import ArrowDownIcon from 'primevue/icons/arrowdown'; import ArrowUpIcon from 'primevue/icons/arrowup'; import SpinnerIcon from 'primevue/icons/spinner'; import Paginator from 'primevue/paginator'; import { DomHandler, ObjectUtils, UniqueComponentId } from 'primevue/utils'; import VirtualScroller from 'primevue/virtualscroller'; import BaseDataTable from './BaseDataTable.vue'; import TableBody from './TableBody.vue'; import TableFooter from './TableFooter.vue'; import TableHeader from './TableHeader.vue'; export default { name: 'DataTable', extends: BaseDataTable, emits: [ 'value-change', 'update:first', 'update:rows', 'page', 'update:sortField', 'update:sortOrder', 'update:multiSortMeta', 'sort', 'filter', 'row-click', 'row-dblclick', 'update:selection', 'row-select', 'row-unselect', 'update:contextMenuSelection', 'row-contextmenu', 'row-unselect-all', 'row-select-all', 'select-all-change', 'column-resize-end', 'column-reorder', 'row-reorder', 'update:expandedRows', 'row-collapse', 'row-expand', 'update:expandedRowGroups', 'rowgroup-collapse', 'rowgroup-expand', 'update:filters', 'state-restore', 'state-save', 'cell-edit-init', 'cell-edit-complete', 'cell-edit-cancel', 'update:editingRows', 'row-edit-init', 'row-edit-save', 'row-edit-cancel' ], data() { return { d_first: this.first, d_rows: this.rows, d_sortField: this.sortField, d_sortOrder: this.sortOrder, d_nullSortOrder: this.nullSortOrder, d_multiSortMeta: this.multiSortMeta ? [...this.multiSortMeta] : [], d_groupRowsSortMeta: null, d_selectionKeys: null, d_expandedRowKeys: null, d_columnOrder: null, d_editingRowKeys: null, d_editingMeta: {}, d_filters: this.cloneFilters(this.filters) }; }, rowTouched: false, anchorRowIndex: null, rangeRowIndex: null, documentColumnResizeListener: null, documentColumnResizeEndListener: null, lastResizeHelperX: null, resizeColumnElement: null, columnResizing: false, colReorderIconWidth: null, colReorderIconHeight: null, draggedColumn: null, draggedRowIndex: null, droppedRowIndex: null, rowDragging: null, columnWidthsState: null, tableWidthState: null, columnWidthsRestored: false, watch: { first(newValue) { this.d_first = newValue; }, rows(newValue) { this.d_rows = newValue; }, sortField(newValue) { this.d_sortField = newValue; }, sortOrder(newValue) { this.d_sortOrder = newValue; }, nullSortOrder(newValue) { this.d_nullSortOrder = newValue; }, multiSortMeta(newValue) { this.d_multiSortMeta = newValue; }, selection: { immediate: true, handler(newValue) { if (this.dataKey) { this.updateSelectionKeys(newValue); } } }, expandedRows(newValue) { if (this.dataKey) { this.updateExpandedRowKeys(newValue); } }, editingRows: { deep: true, handler(newValue) { if (this.dataKey) { this.updateEditingRowKeys(newValue); } } }, filters: { deep: true, handler: function (newValue) { this.d_filters = this.cloneFilters(newValue); } } }, beforeMount() { if (this.isStateful()) { this.restoreState(); } }, mounted() { this.$el.setAttribute(this.attributeSelector, ''); if (this.responsiveLayout === 'stack' && !this.scrollable && !this.unstyled) { this.createResponsiveStyle(); } if (this.isStateful() && this.resizableColumns) { this.restoreColumnWidths(); } if (this.editMode === 'row' && this.dataKey && !this.d_editingRowKeys) { this.updateEditingRowKeys(this.editingRows); } }, beforeUnmount() { this.unbindColumnResizeEvents(); this.destroyStyleElement(); this.destroyResponsiveStyle(); }, updated() { if (this.isStateful()) { this.saveState(); } if (this.editMode === 'row' && this.dataKey && !this.d_editingRowKeys) { this.updateEditingRowKeys(this.editingRows); } }, methods: { columnProp(col, prop) { return ObjectUtils.getVNodeProp(col, prop); }, onPage(event) { this.clearEditingMetaData(); this.d_first = event.first; this.d_rows = event.rows; let pageEvent = this.createLazyLoadEvent(event); pageEvent.pageCount = event.pageCount; pageEvent.page = event.page; this.$emit('update:first', this.d_first); this.$emit('update:rows', this.d_rows); this.$emit('page', pageEvent); this.$emit('value-change', this.processedData); }, onColumnHeaderClick(e) { const event = e.originalEvent; const column = e.column; if (this.columnProp(column, 'sortable')) { const targetNode = event.target; const columnField = this.columnProp(column, 'sortField') || this.columnProp(column, 'field'); if ( DomHandler.getAttribute(targetNode, 'data-p-sortable-column') === true || DomHandler.getAttribute(targetNode, 'data-pc-section') === 'headertitle' || DomHandler.getAttribute(targetNode, 'data-pc-section') === 'headercontent' || DomHandler.getAttribute(targetNode, 'data-pc-section') === 'sorticon' || DomHandler.getAttribute(targetNode.parentElement, 'data-pc-section') === 'sorticon' || DomHandler.getAttribute(targetNode.parentElement.parentElement, 'data-pc-section') === 'sorticon' || (targetNode.closest('[data-p-sortable-column="true"]') && !targetNode.closest('[data-pc-section="filtermenubutton"]')) ) { DomHandler.clearSelection(); if (this.sortMode === 'single') { if (this.d_sortField === columnField) { if (this.removableSort && this.d_sortOrder * -1 === this.defaultSortOrder) { this.d_sortOrder = null; this.d_sortField = null; } else { this.d_sortOrder = this.d_sortOrder * -1; } } else { this.d_sortOrder = this.defaultSortOrder; this.d_sortField = columnField; } this.$emit('update:sortField', this.d_sortField); this.$emit('update:sortOrder', this.d_sortOrder); this.resetPage(); } else if (this.sortMode === 'multiple') { let metaKey = event.metaKey || event.ctrlKey; if (!metaKey) { this.d_multiSortMeta = this.d_multiSortMeta.filter((meta) => meta.field === columnField); } this.addMultiSortField(columnField); this.$emit('update:multiSortMeta', this.d_multiSortMeta); } this.$emit('sort', this.createLazyLoadEvent(event)); this.$emit('value-change', this.processedData); } } }, sortSingle(value) { this.clearEditingMetaData(); if (this.groupRowsBy && this.groupRowsBy === this.sortField) { this.d_multiSortMeta = [ { field: this.sortField, order: this.sortOrder || this.defaultSortOrder }, { field: this.d_sortField, order: this.d_sortOrder } ]; return this.sortMultiple(value); } let data = [...value]; let resolvedFieldData = new Map(); for (let item of data) { resolvedFieldData.set(item, ObjectUtils.resolveFieldData(item, this.d_sortField)); } const comparer = ObjectUtils.localeComparator(); data.sort((data1, data2) => { let value1 = resolvedFieldData.get(data1); let value2 = resolvedFieldData.get(data2); return ObjectUtils.sort(value1, value2, this.d_sortOrder, comparer, this.d_nullSortOrder); }); return data; }, sortMultiple(value) { this.clearEditingMetaData(); if (this.groupRowsBy && (this.d_groupRowsSortMeta || (this.d_multiSortMeta.length && this.groupRowsBy === this.d_multiSortMeta[0].field))) { const firstSortMeta = this.d_multiSortMeta[0]; !this.d_groupRowsSortMeta && (this.d_groupRowsSortMeta = firstSortMeta); if (firstSortMeta.field !== this.d_groupRowsSortMeta.field) { this.d_multiSortMeta = [this.d_groupRowsSortMeta, ...this.d_multiSortMeta]; } } let data = [...value]; data.sort((data1, data2) => { return this.multisortField(data1, data2, 0); }); return data; }, multisortField(data1, data2, index) { const value1 = ObjectUtils.resolveFieldData(data1, this.d_multiSortMeta[index].field); const value2 = ObjectUtils.resolveFieldData(data2, this.d_multiSortMeta[index].field); const comparer = ObjectUtils.localeComparator(); if (value1 === value2) { return this.d_multiSortMeta.length - 1 > index ? this.multisortField(data1, data2, index + 1) : 0; } return ObjectUtils.sort(value1, value2, this.d_multiSortMeta[index].order, comparer, this.d_nullSortOrder); }, addMultiSortField(field) { let index = this.d_multiSortMeta.findIndex((meta) => meta.field === field); if (index >= 0) { if (this.removableSort && this.d_multiSortMeta[index].order * -1 === this.defaultSortOrder) this.d_multiSortMeta.splice(index, 1); else this.d_multiSortMeta[index] = { field: field, order: this.d_multiSortMeta[index].order * -1 }; } else { this.d_multiSortMeta.push({ field: field, order: this.defaultSortOrder }); } this.d_multiSortMeta = [...this.d_multiSortMeta]; }, getActiveFilters(filters) { const removeEmptyFilters = ([key, value]) => { if (value.constraints) { const filteredConstraints = value.constraints.filter((constraint) => constraint.value !== null); if (filteredConstraints.length > 0) { return [key, { ...value, constraints: filteredConstraints }]; } } else if (value.value !== null) { return [key, value]; } return undefined; }; const filterValidEntries = (entry) => entry !== undefined; const entries = Object.entries(filters).map(removeEmptyFilters).filter(filterValidEntries); return Object.fromEntries(entries); }, filter(data) { if (!data) { return; } this.clearEditingMetaData(); let activeFilters = this.getActiveFilters(this.filters); let globalFilterFieldsArray; if (activeFilters['global']) { globalFilterFieldsArray = this.globalFilterFields || this.columns.map((col) => this.columnProp(col, 'filterField') || this.columnProp(col, 'field')); } let filteredValue = []; for (let i = 0; i < data.length; i++) { let localMatch = true; let globalMatch = false; let localFiltered = false; for (let prop in activeFilters) { if (Object.prototype.hasOwnProperty.call(activeFilters, prop) && prop !== 'global') { localFiltered = true; let filterField = prop; let filterMeta = activeFilters[filterField]; if (filterMeta.operator) { for (let filterConstraint of filterMeta.constraints) { localMatch = this.executeLocalFilter(filterField, data[i], filterConstraint); if ((filterMeta.operator === FilterOperator.OR && localMatch) || (filterMeta.operator === FilterOperator.AND && !localMatch)) { break; } } } else { localMatch = this.executeLocalFilter(filterField, data[i], filterMeta); } if (!localMatch) { break; } } } if (localMatch && activeFilters['global'] && !globalMatch && globalFilterFieldsArray) { for (let j = 0; j < globalFilterFieldsArray.length; j++) { let globalFilterField = globalFilterFieldsArray[j]; globalMatch = FilterService.filters[activeFilters['global'].matchMode || FilterMatchMode.CONTAINS](ObjectUtils.resolveFieldData(data[i], globalFilterField), activeFilters['global'].value, this.filterLocale); if (globalMatch) { break; } } } let matches; if (activeFilters['global']) { matches = localFiltered ? localFiltered && localMatch && globalMatch : globalMatch; } else { matches = localFiltered && localMatch; } if (matches) { filteredValue.push(data[i]); } } if (filteredValue.length === this.value.length || Object.keys(activeFilters).length == 0) { filteredValue = data; } let filterEvent = this.createLazyLoadEvent(); filterEvent.filteredValue = filteredValue; this.$emit('filter', filterEvent); this.$emit('value-change', filteredValue); return filteredValue; }, executeLocalFilter(field, rowData, filterMeta) { let filterValue = filterMeta.value; let filterMatchMode = filterMeta.matchMode || FilterMatchMode.STARTS_WITH; let dataFieldValue = ObjectUtils.resolveFieldData(rowData, field); let filterConstraint = FilterService.filters[filterMatchMode]; return filterConstraint(dataFieldValue, filterValue, this.filterLocale); }, onRowClick(e) { const event = e.originalEvent; const body = this.$refs.bodyRef && this.$refs.bodyRef.$el; const focusedItem = DomHandler.findSingle(body, 'tr[data-p-selectable-row="true"][tabindex="0"]'); if (DomHandler.isClickable(event.target)) { return; } this.$emit('row-click', e); if (this.selectionMode) { const rowData = e.data; const rowIndex = this.d_first + e.index; if (this.isMultipleSelectionMode() && event.shiftKey && this.anchorRowIndex != null) { DomHandler.clearSelection(); this.rangeRowIndex = rowIndex; this.selectRange(event); } else { const selected = this.isSelected(rowData); const metaSelection = this.rowTouched ? false : this.metaKeySelection; this.anchorRowIndex = rowIndex; this.rangeRowIndex = rowIndex; if (metaSelection) { let metaKey = event.metaKey || event.ctrlKey; if (selected && metaKey) { if (this.isSingleSelectionMode()) { this.$emit('update:selection', null); } else { const selectionIndex = this.findIndexInSelection(rowData); const _selection = this.selection.filter((val, i) => i != selectionIndex); this.$emit('update:selection', _selection); } this.$emit('row-unselect', { originalEvent: event, data: rowData, index: rowIndex, type: 'row' }); } else { if (this.isSingleSelectionMode()) { this.$emit('update:selection', rowData); } else if (this.isMultipleSelectionMode()) { let _selection = metaKey ? this.selection || [] : []; _selection = [..._selection, rowData]; this.$emit('update:selection', _selection); } this.$emit('row-select', { originalEvent: event, data: rowData, index: rowIndex, type: 'row' }); } } else { if (this.selectionMode === 'single') { if (selected) { this.$emit('update:selection', null); this.$emit('row-unselect', { originalEvent: event, data: rowData, index: rowIndex, type: 'row' }); } else { this.$emit('update:selection', rowData); this.$emit('row-select', { originalEvent: event, data: rowData, index: rowIndex, type: 'row' }); } } else if (this.selectionMode === 'multiple') { if (selected) { const selectionIndex = this.findIndexInSelection(rowData); const _selection = this.selection.filter((val, i) => i != selectionIndex); this.$emit('update:selection', _selection); this.$emit('row-unselect', { originalEvent: event, data: rowData, index: rowIndex, type: 'row' }); } else { const _selection = this.selection ? [...this.selection, rowData] : [rowData]; this.$emit('update:selection', _selection); this.$emit('row-select', { originalEvent: event, data: rowData, index: rowIndex, type: 'row' }); } } } } } this.rowTouched = false; if (focusedItem) { const targetRow = e.originalEvent.target.closest('tr[data-p-selectable-row="true"]'); focusedItem.tabIndex = '-1'; targetRow.tabIndex = '0'; } }, onRowDblClick(e) { const event = e.originalEvent; if (DomHandler.isClickable(event.target)) { return; } this.$emit('row-dblclick', e); }, onRowRightClick(event) { if (this.contextMenu) { DomHandler.clearSelection(); event.originalEvent.target.focus(); } this.$emit('update:contextMenuSelection', event.data); this.$emit('row-contextmenu', event); }, onRowTouchEnd() { this.rowTouched = true; }, onRowKeyDown(e, slotProps) { const event = e.originalEvent; const rowData = e.data; const rowIndex = e.index; const metaKey = event.metaKey || event.ctrlKey; if (this.selectionMode) { const row = event.target; switch (event.code) { case 'ArrowDown': this.onArrowDownKey(event, row, rowIndex, slotProps); break; case 'ArrowUp': this.onArrowUpKey(event, row, rowIndex, slotProps); break; case 'Home': this.onHomeKey(event, row, rowIndex, slotProps); break; case 'End': this.onEndKey(event, row, rowIndex, slotProps); break; case 'Enter': this.onEnterKey(event, rowData, rowIndex); break; case 'Space': this.onSpaceKey(event, rowData, rowIndex, slotProps); break; case 'Tab': this.onTabKey(event, rowIndex); break; default: if (event.code === 'KeyA' && metaKey) { const data = this.dataToRender(slotProps.rows); this.$emit('update:selection', data); } break; } } }, onArrowDownKey(event, row, rowIndex, slotProps) { const nextRow = this.findNextSelectableRow(row); nextRow && this.focusRowChange(row, nextRow); if (event.shiftKey) { const data = this.dataToRender(slotProps.rows); const nextRowIndex = rowIndex + 1 >= data.length ? data.length - 1 : rowIndex + 1; this.onRowClick({ originalEvent: event, data: data[nextRowIndex], index: nextRowIndex }); } event.preventDefault(); }, onArrowUpKey(event, row, rowIndex, slotProps) { const prevRow = this.findPrevSelectableRow(row); prevRow && this.focusRowChange(row, prevRow); if (event.shiftKey) { const data = this.dataToRender(slotProps.rows); const prevRowIndex = rowIndex - 1 <= 0 ? 0 : rowIndex - 1; this.onRowClick({ originalEvent: event, data: data[prevRowIndex], index: prevRowIndex }); } event.preventDefault(); }, onHomeKey(event, row, rowIndex, slotProps) { const firstRow = this.findFirstSelectableRow(); firstRow && this.focusRowChange(row, firstRow); if (event.ctrlKey && event.shiftKey) { const data = this.dataToRender(slotProps.rows); this.$emit('update:selection', data.slice(0, rowIndex + 1)); } event.preventDefault(); }, onEndKey(event, row, rowIndex, slotProps) { const lastRow = this.findLastSelectableRow(); lastRow && this.focusRowChange(row, lastRow); if (event.ctrlKey && event.shiftKey) { const data = this.dataToRender(slotProps.rows); this.$emit('update:selection', data.slice(rowIndex, data.length)); } event.preventDefault(); }, onEnterKey(event, rowData, rowIndex) { this.onRowClick({ originalEvent: event, data: rowData, index: rowIndex }); event.preventDefault(); }, onSpaceKey(event, rowData, rowIndex, slotProps) { this.onEnterKey(event, rowData, rowIndex); if (event.shiftKey && this.selection !== null) { const data = this.dataToRender(slotProps.rows); let index; if (this.selection.length > 0) { let firstSelectedRowIndex, lastSelectedRowIndex; firstSelectedRowIndex = ObjectUtils.findIndexInList(this.selection[0], data); lastSelectedRowIndex = ObjectUtils.findIndexInList(this.selection[this.selection.length - 1], data); index = rowIndex <= firstSelectedRowIndex ? lastSelectedRowIndex : firstSelectedRowIndex; } else { index = ObjectUtils.findIndexInList(this.selection, data); } const _selection = index !== rowIndex ? data.slice(Math.min(index, rowIndex), Math.max(index, rowIndex) + 1) : rowData; this.$emit('update:selection', _selection); } }, onTabKey(event, rowIndex) { const body = this.$refs.bodyRef && this.$refs.bodyRef.$el; const rows = DomHandler.find(body, 'tr[data-p-selectable-row="true"]'); if (event.code === 'Tab' && rows && rows.length > 0) { const firstSelectedRow = DomHandler.findSingle(body, 'tr[data-p-highlight="true"]'); const focusedItem = DomHandler.findSingle(body, 'tr[data-p-selectable-row="true"][tabindex="0"]'); if (firstSelectedRow) { firstSelectedRow.tabIndex = '0'; focusedItem && focusedItem !== firstSelectedRow && (focusedItem.tabIndex = '-1'); } else { rows[0].tabIndex = '0'; focusedItem !== rows[0] && (rows[rowIndex].tabIndex = '-1'); } } }, findNextSelectableRow(row) { let nextRow = row.nextElementSibling; if (nextRow) { if (DomHandler.getAttribute(nextRow, 'data-p-selectable-row') === true) return nextRow; else return this.findNextSelectableRow(nextRow); } else { return null; } }, findPrevSelectableRow(row) { let prevRow = row.previousElementSibling; if (prevRow) { if (DomHandler.getAttribute(prevRow, 'data-p-selectable-row') === true) return prevRow; else return this.findPrevSelectableRow(prevRow); } else { return null; } }, findFirstSelectableRow() { const firstRow = DomHandler.findSingle(this.$refs.table, 'tr[data-p-selectable-row="true"]'); return firstRow; }, findLastSelectableRow() { const rows = DomHandler.find(this.$refs.table, 'tr[data-p-selectable-row="true"]'); return rows ? rows[rows.length - 1] : null; }, focusRowChange(firstFocusableRow, currentFocusedRow) { firstFocusableRow.tabIndex = '-1'; currentFocusedRow.tabIndex = '0'; DomHandler.focus(currentFocusedRow); }, toggleRowWithRadio(event) { const rowData = event.data; if (this.isSelected(rowData)) { this.$emit('update:selection', null); this.$emit('row-unselect', { originalEvent: event.originalEvent, data: rowData, index: event.index, type: 'radiobutton' }); } else { this.$emit('update:selection', rowData); this.$emit('row-select', { originalEvent: event.originalEvent, data: rowData, index: event.index, type: 'radiobutton' }); } }, toggleRowWithCheckbox(event) { const rowData = event.data; if (this.isSelected(rowData)) { const selectionIndex = this.findIndexInSelection(rowData); const _selection = this.selection.filter((val, i) => i != selectionIndex); this.$emit('update:selection', _selection); this.$emit('row-unselect', { originalEvent: event.originalEvent, data: rowData, index: event.index, type: 'checkbox' }); } else { let _selection = this.selection ? [...this.selection] : []; _selection = [..._selection, rowData]; this.$emit('update:selection', _selection); this.$emit('row-select', { originalEvent: event.originalEvent, data: rowData, index: event.index, type: 'checkbox' }); } }, toggleRowsWithCheckbox(event) { if (this.selectAll !== null) { this.$emit('select-all-change', event); } else { const { originalEvent, checked } = event; let _selection = []; if (checked) { _selection = this.frozenValue ? [...this.frozenValue, ...this.processedData] : this.processedData; this.$emit('row-select-all', { originalEvent, data: _selection }); } else { this.$emit('row-unselect-all', { originalEvent }); } this.$emit('update:selection', _selection); } }, isSingleSelectionMode() { return this.selectionMode === 'single'; }, isMultipleSelectionMode() { return this.selectionMode === 'multiple'; }, isSelected(rowData) { if (rowData && this.selection) { if (this.dataKey) { return this.d_selectionKeys ? this.d_selectionKeys[ObjectUtils.resolveFieldData(rowData, this.dataKey)] !== undefined : false; } else { if (this.selection instanceof Array) return this.findIndexInSelection(rowData) > -1; else return this.equals(rowData, this.selection); } } return false; }, findIndexInSelection(rowData) { return this.findIndex(rowData, this.selection); }, findIndex(rowData, collection) { let index = -1; if (collection && collection.length) { for (let i = 0; i < collection.length; i++) { if (this.equals(rowData, collection[i])) { index = i; break; } } } return index; }, updateSelectionKeys(selection) { this.d_selectionKeys = {}; if (Array.isArray(selection)) { for (let data of selection) { this.d_selectionKeys[String(ObjectUtils.resolveFieldData(data, this.dataKey))] = 1; } } else { this.d_selectionKeys[String(ObjectUtils.resolveFieldData(selection, this.dataKey))] = 1; } }, updateExpandedRowKeys(expandedRows) { if (expandedRows && expandedRows.length) { this.d_expandedRowKeys = {}; for (let data of expandedRows) { this.d_expandedRowKeys[String(ObjectUtils.resolveFieldData(data, this.dataKey))] = 1; } } else { this.d_expandedRowKeys = null; } }, updateEditingRowKeys(editingRows) { if (editingRows && editingRows.length) { this.d_editingRowKeys = {}; for (let data of editingRows) { this.d_editingRowKeys[String(ObjectUtils.resolveFieldData(data, this.dataKey))] = 1; } } else { this.d_editingRowKeys = null; } }, equals(data1, data2) { return this.compareSelectionBy === 'equals' ? data1 === data2 : ObjectUtils.equals(data1, data2, this.dataKey); }, selectRange(event) { let rangeStart, rangeEnd; if (this.rangeRowIndex > this.anchorRowIndex) { rangeStart = this.anchorRowIndex; rangeEnd = this.rangeRowIndex; } else if (this.rangeRowIndex < this.anchorRowIndex) { rangeStart = this.rangeRowIndex; rangeEnd = this.anchorRowIndex; } else { rangeStart = this.rangeRowIndex; rangeEnd = this.rangeRowIndex; } if (this.lazy && this.paginator) { rangeStart -= this.first; rangeEnd -= this.first; } const value = this.processedData; let _selection = []; for (let i = rangeStart; i <= rangeEnd; i++) { let rangeRowData = value[i]; _selection.push(rangeRowData); this.$emit('row-select', { originalEvent: event, data: rangeRowData, type: 'row' }); } this.$emit('update:selection', _selection); }, exportCSV(options, data) { let csv = '\ufeff'; if (!data) { data = this.processedData; if (options && options.selectionOnly) data = this.selection || []; else if (this.frozenValue) data = data ? [...this.frozenValue, ...data] : this.frozenValue; } //headers let headerInitiated = false; for (let i = 0; i < this.columns.length; i++) { let column = this.columns[i]; if (this.columnProp(column, 'exportable') !== false && this.columnProp(column, 'field')) { if (headerInitiated) csv += this.csvSeparator; else headerInitiated = true; csv += '"' + (this.columnProp(column, 'exportHeader') || this.columnProp(column, 'header') || this.columnProp(column, 'field')) + '"'; } } //body if (data) { data.forEach((record) => { csv += '\n'; let rowInitiated = false; for (let i = 0; i < this.columns.length; i++) { let column = this.columns[i]; if (this.columnProp(column, 'exportable') !== false && this.columnProp(column, 'field')) { if (rowInitiated) csv += this.csvSeparator; else rowInitiated = true; let cellData = ObjectUtils.resolveFieldData(record, this.columnProp(column, 'field')); if (cellData != null) { if (this.exportFunction) { cellData = this.exportFunction({ data: cellData, field: this.columnProp(column, 'field') }); } else cellData = String(cellData).replace(/"/g, '""'); } else cellData = ''; csv += '"' + cellData + '"'; } } }); } //footers let footerInitiated = false; for (let i = 0; i < this.columns.length; i++) { let column = this.columns[i]; if (i === 0) csv += '\n'; if (this.columnProp(column, 'exportable') !== false && this.columnProp(column, 'exportFooter')) { if (footerInitiated) csv += this.csvSeparator; else footerInitiated = true; csv += '"' + (this.columnProp(column, 'exportFooter') || this.columnProp(column, 'footer') || this.columnProp(column, 'field')) + '"'; } } DomHandler.exportCSV(csv, this.exportFilename); }, resetPage() { this.d_first = 0; this.$emit('update:first', this.d_first); }, onColumnResizeStart(event) { let containerLeft = DomHandler.getOffset(this.$el).left; this.resizeColumnElement = event.target.parentElement; this.columnResizing = true; this.lastResizeHelperX = event.pageX - containerLeft + this.$el.scrollLeft; this.bindColumnResizeEvents(); }, onColumnResize(event) { let containerLeft = DomHandler.getOffset(this.$el).left; this.$el.setAttribute('data-p-unselectable-text', 'true'); !this.isUnstyled && DomHandler.addClass(this.$el, 'p-unselectable-text'); this.$refs.resizeHelper.style.height = this.$el.offsetHeight + 'px'; this.$refs.resizeHelper.style.top = 0 + 'px'; this.$refs.resizeHelper.style.left = event.pageX - containerLeft + this.$el.scrollLeft + 'px'; this.$refs.resizeHelper.style.display = 'block'; }, onColumnResizeEnd() { let delta = this.$refs.resizeHelper.offsetLeft - this.lastResizeHelperX; let columnWidth = this.resizeColumnElement.offsetWidth; let newColumnWidth = columnWidth + delta; let minWidth = this.resizeColumnElement.style.minWidth || 15; if (columnWidth + delta > parseInt(minWidth, 10)) { if (this.columnResizeMode === 'fit') { let nextColumn = this.resizeColumnElement.nextElementSibling; let nextColumnWidth = nextColumn.offsetWidth - delta; if (newColumnWidth > 15 && nextColumnWidth > 15) { this.resizeTableCells(newColumnWidth, nextColumnWidth); } } else if (this.columnResizeMode === 'expand') { const tableWidth = this.$refs.table.offsetWidth + delta + 'px'; const updateTableWidth = (el) => { !!el && (el.style.width = el.style.minWidth = tableWidth); }; // Reasoning: resize table cells before updating the table width so that it can use existing computed cell widths and adjust only the one column. this.resizeTableCells(newColumnWidth); updateTableWidth(this.$refs.table); if (!this.virtualScrollerDisabled) { const body = this.$refs.bodyRef && this.$refs.bodyRef.$el; const frozenBody = this.$refs.frozenBodyRef && this.$refs.frozenBodyRef.$el; updateTableWidth(body); updateTableWidth(frozenBody); } } this.$emit('column-resize-end', { element: this.resizeColumnElement, delta: delta }); } this.$refs.resizeHelper.style.display = 'none'; this.resizeColumn = null; this.$el.setAttribute('data-p-unselectable-text', 'true'); !this.isUnstyled && DomHandler.removeClass(this.$el, 'p-unselectable-text'); this.unbindColumnResizeEvents(); if (this.isStateful()) { this.saveState(); } }, resizeTableCells(newColumnWidth, nextColumnWidth) { let colIndex = DomHandler.index(this.resizeColumnElement); let widths = []; let headers = DomHandler.find(this.$refs.table, 'thead[data-pc-section="thead"] > tr > th'); headers.forEach((header) => widths.push(DomHandler.getOuterWidth(header))); this.destroyStyleElement(); this.createStyleElement(); let innerHTML = ''; let selector = `[data-pc-name="datatable"][${this.attributeSelector}] > [data-pc-section="wrapper"] ${this.virtualScrollerDisabled ? '' : '> [data-pc-section="virtualscroller"]'} > table[data-pc-section="table"]`; widths.forEach((width, index) => { let colWidth = index === colIndex ? newColumnWidth : nextColumnWidth && index === colIndex + 1 ? nextColumnWidth : width; let style = `width: ${colWidth}px !important; max-width: ${colWidth}px !important`; innerHTML += ` ${selector} > thead[data-pc-section="thead"] > tr > th:nth-child(${index + 1}), ${selector} > tbody[data-pc-section="tbody"] > tr > td:nth-child(${index + 1}), ${selector} > tfoot[data-pc-section="tfoot"] > tr > td:nth-child(${index + 1}) { ${style} } `; }); this.styleElement.innerHTML = innerHTML; }, bindColumnResizeEvents() { if (!this.documentColumnResizeListener) { this.documentColumnResizeListener = document.addEventListener('mousemove', () => { if (this.columnResizing) { this.onColumnResize(event); } }); } if (!this.documentColumnResizeEndListener) { this.documentColumnResizeEndListener = document.addEventListener('mouseup', () => { if (this.columnResizing) { this.columnResizing = false; this.onColumnResizeEnd(); } }); } }, unbindColumnResizeEvents() { if (this.documentColumnResizeListener) { document.removeEventListener('document', this.documentColumnResizeListener); this.documentColumnResizeListener = null; } if (this.documentColumnResizeEndListener) { document.removeEventListener('document', this.documentColumnResizeEndListener); this.documentColumnResizeEndListener = null; } }, onColumnHeaderMouseDown(e) { const event = e.originalEvent; const column = e.column; if (this.reorderableColumns && this.columnProp(column, 'reorderableColumn') !== false) { if (event.target.nodeName === 'INPUT' || event.target.nodeName === 'TEXTAREA' || DomHandler.getAttribute(event.target, '[data-pc-section="columnresizer"]')) event.currentTarget.draggable = false; else event.currentTarget.draggable = true; } }, onColumnHeaderDragStart(event) { if (this.columnResizing) { event.preventDefault(); return; } this.colReorderIconWidth = DomHandler.getHiddenElementOuterWidth(this.$refs.reorderIndicatorUp); this.colReorderIconHeight = DomHandler.getHiddenElementOuterHeight(this.$refs.reorderIndicatorUp); this.draggedColumn = this.findParentHeader(event.target); event.dataTransfer.setData('text', 'b'); // Firefox requires this to make dragging possible }, onColumnHeaderDragOver(event) { let dropHeader = this.findParentHeader(event.target); if (this.reorderableColumns && this.draggedColumn && dropHeader) { event.preventDefault(); let containerOffset = DomHandler.getOffset(this.$el); let dropHeaderOffset = DomHandler.getOffset(dropHeader); if (this.draggedColumn !== dropHeader) { let targetLeft = dropHeaderOffset.left - containerOffset.left; let columnCenter = dropHeaderOffset.left + dropHeader.offsetWidth / 2; this.$refs.reorderIndicatorUp.style.top = dropHeaderOffset.top - containerOffset.top - (this.colReorderIconHeight - 1) + 'px'; this.$refs.reorderIndicatorDown.style.top = dropHeaderOffset.top - containerOffset.top + dropHeader.offsetHeight + 'px'; if (event.pageX > columnCenter) { this.$refs.reorderIndicatorUp.style.left = targetLeft + dropHeader.offsetWidth - Math.ceil(this.colReorderIconWidth / 2) + 'px'; this.$refs.reorderIndicatorDown.style.left = targetLeft + dropHeader.offsetWidth - Math.ceil(this.colReorderIconWidth / 2) + 'px'; this.dropPosition = 1; } else { this.$refs.reorderIndicatorUp.style.left = targetLeft - Math.ceil(this.colReorderIconWidth / 2) + 'px'; this.$refs.reorderIndicatorDown.style.left = targetLeft - Math.ceil(this.colReorderIconWidth / 2) + 'px'; this.dropPosition = -1; } this.$refs.reorderIndicatorUp.style.display = 'block'; this.$refs.reorderIndicatorDown.style.display = 'block'; } } }, onColumnHeaderDragLeave(event) { if (this.reorderableColumns && this.draggedColumn) { event.preventDefault(); this.$refs.reorderIndicatorUp.style.display = 'none'; this.$refs.reorderIndicatorDown.style.display = 'none'; } }, onColumnHeaderDrop(event) { event.preventDefault(); if (this.draggedColumn) { let dragIndex = DomHandler.index(this.draggedColumn); let dropIndex = DomHandler.index(this.findParentHeader(event.target)); let allowDrop = dragIndex !== dropIndex; if (allowDrop && ((dropIndex - dragIndex === 1 && this.dropPosition === -1) || (dropIndex - dragIndex === -1 && this.dropPosition === 1))) { allowDrop = false; } if (allowDrop) { ObjectUtils.reorderArray(this.columns, dragIndex, dropIndex); this.updateReorderableColumns(); this.$emit('column-reorder', { originalEvent: event, dragIndex: dragIndex, dropIndex: dropIndex }); } this.$refs.reorderIndicatorUp.style.display = 'none'; this.$refs.reorderIndicatorDown.style.display = 'none'; this.draggedColumn.draggable = false; this.draggedColumn = null; this.dropPosition = null; } }, findParentHeader(element) { if (element.nodeName === 'TH') { return element; } else { let parent = element.parentElement; while (parent.nodeName !== 'TH') { parent = parent.parentElement; if (!parent) break; } return parent; } }, findColumnByKey(columns, key) { if (columns && columns.length) { for (let i = 0; i < columns.length; i++) { let column = columns[i]; if (this.columnProp(column, 'columnKey') === key || this.columnProp(column, 'field') === key) { return column; } } } return null; }, onRowMouseDown(event) { if (DomHandler.getAttribute(event.target, 'data-pc-section') === 'rowreordericon' || DomHandler.getAttribute(event.target.parentElement, 'data-pc-section') === 'rowreordericon') event.currentTarget.draggable = true; else event.currentTarget.draggable = false; }, onRowDragStart(e) { const event = e.originalEvent; const index = e.index; this.rowDragging = true; this.draggedRowIndex = index; event.dataTransfer.setData('text', 'b'); // For firefox }, onRowDragOver(e) { const event = e.originalEvent; const index = e.index; if (this.rowDragging && this.draggedRowIndex !== index) { let rowElement = event.currentTarget; let rowY = DomHandler.getOffset(rowElement).top + DomHandler.getWindowScrollTop(); let pageY = event.pageY; let rowMidY = rowY + DomHandler.getOuterHeight(rowElement) / 2; let prevRowElement = rowElement.previousElementSibling; if (pageY < rowMidY) { rowElement.setAttribute('data-p-datatable-dragpoint-bottom', 'false'); !this.isUnstyled && DomHandler.removeClass(rowElement, 'p-datatable-dragpoint-bottom'); this.droppedRowIndex = index; if (prevRowElement) { prevRowElement.setAttribute('data-p-datatable-dragpoint-bottom', 'true'); !this.isUnstyled && DomHandler.addClass(prevRowElement, 'p-datatable-dragpoint-bottom'); } else { rowElement.setAttribute('data-p-datatable-dragpoint-top', 'true'); !this.isUnstyled && DomHandler.addClass(rowElement, 'p-datatable-dragpoint-top'); } } else { if (prevRowElement) { prevRowElement.setAttribute('data-p-datatable-dragpoint-bottom', 'false'); !this.isUnstyled && DomHandler.removeClass(prevRowElement, 'p-datatable-dragpoint-bottom'); } else { rowElement.setAttribute('data-p-datatable-dragpoint-top', 'true'); !this.isUnstyled && DomHandler.addClass(rowElement, 'p-datatable-dragpoint-top'); } this.droppedRowIndex = index + 1; rowElement.setAttribute('data-p-datatable-dragpoint-bottom', 'true'); !this.isUnstyled && DomHandler.addClass(rowElement, 'p-datatable-dragpoint-bottom'); } event.preventDefault(); } }, onRowDragLeave(event) { let rowElement = event.currentTarget; let prevRowElement = rowElement.previousElementSibling; if (prevRowElement) { prevRowElement.setAttribute('data-p-datatable-dragpoint-bottom', 'false'); !this.isUnstyled && DomHandler.removeClass(prevRowElement, 'p-datatable-dragpoint-bottom'); } rowElement.setAttribute('data-p-datatable-dragpoint-bottom', 'false'); !this.isUnstyled && DomHandler.removeClass(rowElement, 'p-datatable-dragpoint-bottom'); rowElement.setAttribute('data-p-datatable-dragpoint-top', 'false'); !this.isUnstyled && DomHandler.removeClass(rowElement, 'p-datatable-dragpoint-top'); }, onRowDragEnd(event) { this.rowDragging = false; this.draggedRowIndex = null; this.droppedRowIndex = null; event.currentTarget.draggable = false; }, onRowDrop(event) { if (this.droppedRowIndex != null) { let dropIndex = this.draggedRowIndex > this.droppedRowIndex ? this.droppedRowIndex : this.droppedRowIndex === 0 ? 0 : this.droppedRowIndex - 1; let processedData = [...this.processedData]; ObjectUtils.reorderArray(processedData, this.draggedRowIndex + this.d_first, dropIndex + this.d_first); this.$emit('row-reorder', { originalEvent: event, dragIndex: this.draggedRowIndex, dropIndex: dropIndex, value: processedData }); } //cleanup this.onRowDragLeave(event); this.onRowDragEnd(event); event.preventDefault(); }, toggleRow(event) { let rowData = event.data; let expanded; let expandedRowIndex; let _expandedRows = this.expandedRows ? [...this.expandedRows] : []; if (this.dataKey) { expanded = this.d_expandedRowKeys ? this.d_expandedRowKeys[ObjectUtils.resolveFieldData(rowData, this.dataKey)] !== undefined : false; } else { expandedRowIndex = this.findIndex(rowData, this.expandedRows); expanded = expandedRowIndex > -1; } if (expanded) { if (expandedRowIndex == null) { expandedRowIndex = this.findIndex(rowData, this.expandedRows); } _expandedRows.splice(expandedRowIndex, 1); this.$emit('update:expandedRows', _expandedRows); this.$emit('row-collapse', event); } else { _expandedRows.push(rowData); this.$emit('update:expandedRows', _expandedRows); this.$emit('row-expand', event); } }, toggleRowGroup(e) { const event = e.originalEvent; const data = e.data; const groupFieldValue = ObjectUtils.resolveFieldData(data, this.groupRowsBy); let _expandedRowGroups = this.expandedRowGroups ? [...this.expandedRowGroups] : []; if (this.isRowGroupExpanded(data)) { _expandedRowGroups = _expandedRowGroups.filter((group) => group !== groupFieldValue); this.$emit('update:expandedRowGroups', _expandedRowGroups); this.$emit('rowgroup-collapse', { originalEvent: event, data: groupFieldValue }); } else { _expandedRowGroups.push(groupFieldValue); this.$emit('update:expandedRowGroups', _expandedRowGroups); this.$emit('rowgroup-expand', { originalEvent: event, data: groupFieldValue }); } }, isRowGroupExpanded(rowData) { if (this.expandableRowGroups && this.expandedRowGroups) { let groupFieldValue = ObjectUtils.resolveFieldData(rowData, this.groupRowsBy); return this.expandedRowGroups.indexOf(groupFieldValue) > -1; } return false; }, isStateful() { return this.stateKey != null; }, getStorage() { switch (this.stateStorage) { case 'local': return window.localStorage; case 'session': return window.sessionStorage; default: throw new Error(this.stateStorage + ' is not a valid value for the state storage, supported values are "local" and "session".'); } }, saveState() { const storage = this.getStorage(); let state = {}; if (this.paginator) { state.first = this.d_first; state.rows = this.d_rows; } if (this.d_sortField) { state.sortField = this.d_sortField; state.sortOrder = this.d_sortOrder; } if (this.d_multiSortMeta) { state.multiSortMeta = this.d_multiSortMeta; } if (this.hasFilters) { state.filters = this.filters; } if (this.resizableColumns) { this.saveColumnWidths(state); } if (this.reorderableColumns) { state.columnOrder = this.d_columnOrder; } if (this.expandedRows) { state.expandedRows = this.expandedRows; state.expandedRowKeys = this.d_expandedRowKeys; } if (this.expandedRowGroups) { state.expandedRowGroups = this.expandedRowGroups; } if (this.selection) { state.selection = this.selection; state.selectionKeys = this.d_selectionKeys; } if (Object.keys(state).length) { storage.setItem(this.stateKey, JSON.stringify(state)); } this.$emit('state-save', state); }, restoreState() { const storage = this.getStorage(); const stateString = storage.getItem(this.stateKey); const dateFormat = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/; const reviver = function (key, value) { if (typeof value === 'string' && dateFormat.test(value)) { return new Date(value); } return value; }; if (stateString) { let restoredState = JSON.parse(stateString, reviver); if (this.paginator) { this.d_first = restoredState.first; this.d_rows = restoredState.rows; } if (restoredState.sortField) { this.d_sortField = restoredState.sortField; this.d_sortOrder = restoredState.sortOrder; } if (restoredState.multiSortMeta) { this.d_multiSortMeta = restoredState.multiSortMeta; } if (restoredState.filters) { this.$emit('update:filters', restoredState.filters); } if (this.resizableColumns) { this.columnWidthsState = restoredState.columnWidths; this.tableWidthState = restoredState.tableWidth; } if (this.reorderableColumns) { this.d_columnOrder = restoredState.columnOrder; } if (restoredState.expandedRows) { this.d_expandedRowKeys = restoredState.expandedRowKeys; this.$emit('update:expandedRows', restoredState.expandedRows); } if (restoredState.expandedRowGroups) { this.$emit('update:expandedRowGroups', restoredState.expandedRowGroups); } if (restoredState.selection) { this.d_selectionKeys = restoredState.d_selectionKeys; this.$emit('update:selection', restoredState.selection); } this.$emit('state-restore', restoredState); } }, saveColumnWidths(state) { let widths = []; let headers = DomHandler.find(this.$el, 'thead[data-pc-section="thead"] > tr > th'); headers.forEach((header) => widths.push(DomHandler.getOuterWidth(header))); state.columnWidths = widths.join(','); if (this.columnResizeMode === 'expand') { state.tableWidth = DomHandler.getOuterWidth(this.$refs.table) + 'px'; } }, restoreColumnWidths() { if (this.columnWidthsState) { let widths = this.columnWidthsState.split(','); if (this.columnResizeMode === 'expand' && this.tableWidthState) { this.$refs.table.style.width = this.tableWidthState; this.$refs.table.style.minWidth = this.tableWidthState; this.$el.style.width = this.tableWidthState; } if (ObjectUtils.isNotEmpty(widths)) { this.createStyleElement(); let innerHTML = ''; let selector = `[data-pc-name="datatable"][${this.attributeSelector}] > [data-pc-section="wrapper"] ${this.virtualScrollerDisabled ? '' : '> [data-pc-section="virtualscroller"]'} > table[data-pc-section="table"]`; widths.forEach((width, index) => { let style = `width: ${width}px !important; max-width: ${width}px !important`; innerHTML += ` ${selector} > thead[data-pc-section="thead"] > tr > th:nth-child(${index + 1}), ${selector} > tbody[data-pc-section="tbody"] > tr > td:nth-child(${index + 1}), ${selector} > tfoot[data-pc-section="tfoot"] > tr > td:nth-child(${index + 1}) { ${style} } `; }); this.styleElement.innerHTML = innerHTML; } } }, onCellEditInit(event) { this.$emit('cell-edit-init', event); }, onCellEditComplete(event) { this.$emit('cell-edit-complete', event); }, onCellEditCancel(event) { this.$emit('cell-edit-cancel', event); }, onRowEditInit(event) { let _editingRows = this.editingRows ? [...this.editingRows] : []; _editingRows.push(event.data); this.$emit('update:editingRows', _editingRows); this.$emit('row-edit-init', event); }, onRowEditSave(event) { let _editingRows = [...this.editingRows]; _editingRows.splice(this.findIndex(event.data, _editingRows), 1); this.$emit('update:editingRows', _editingRows); this.$emit('row-edit-save', event); }, onRowEditCancel(event) { let _editingRows = [...this.editingRows]; _editingRows.splice(this.findIndex(event.data, _editingRows), 1); this.$emit('update:editingRows', _editingRows); this.$emit('row-edit-cancel', event); }, onEditingMetaChange(event) { let { data, field, index, editing } = event; let editingMeta = { ...this.d_editingMeta }; let meta = editingMeta[index]; if (editing) { !meta && (meta = editingMeta[index] = { data: { ...data }, fields: [] }); meta['fields'].push(field); } else if (meta) { const fields = meta['fields'].filter((f) => f !== field); !fields.length ? delete editingMeta[index] : (meta['fields'] = fields); } this.d_editingMeta = editingMeta; }, clearEditingMetaData() { if (this.editMode) { this.d_editingMeta = {}; } }, createLazyLoadEvent(event) { return { originalEvent: event, first: this.d_first, rows: this.d_rows, sortField: this.d_sortField, sortOrder: this.d_sortOrder, multiSortMeta: this.d_multiSortMeta, filters: this.d_filters }; }, hasGlobalFilter() { return this.filters && Object.prototype.hasOwnProperty.call(this.filters, 'global'); }, getChildren() { return this.$slots.default ? this.$slots.default() : null; }, onFilterChange(filters) { this.d_filters = filters; }, onFilterApply() { this.d_first = 0; this.$emit('update:first', this.d_first); this.$emit('update:filters', this.d_filters); if (this.lazy) { this.$emit('filter', this.createLazyLoadEvent()); } }, cloneFilters() { let cloned = {}; if (this.filters) { Object.entries(this.filters).forEach(([prop, value]) => { cloned[prop] = value.operator ? { operator: value.operator, constraints: value.constraints.map((constraint) => { return { ...constraint }; }) } : { ...value }; }); } return cloned; }, updateReorderableColumns() { let columnOrder = []; this.columns.forEach((col) => columnOrder.push(this.columnProp(col, 'columnKey') || this.columnProp(col, 'field'))); this.d_columnOrder = columnOrder; }, createStyleElement() { this.styleElement = document.createElement('style'); this.styleElement.type = 'text/css'; DomHandler.setAttribute(this.styleElement, 'nonce', this.$primevue?.config?.csp?.nonce); document.head.appendChild(this.styleElement); }, createResponsiveStyle() { if (!this.responsiveStyleElement) { this.responsiveStyleElement = document.createElement('style'); this.responsiveStyleElement.type = 'text/css'; DomHandler.setAttribute(this.responsiveStyleElement, 'nonce', this.$primevue?.config?.csp?.nonce); document.head.appendChild(this.responsiveStyleElement); let tableSelector = `.p-datatable-wrapper ${this.virtualScrollerDisabled ? '' : '> .p-virtualscroller'} > .p-datatable-table`; let selector = `.p-datatable[${this.attributeSelector}] > ${tableSelector}`; let gridLinesSelector = `.p-datatable[${this.attributeSelector}].p-datatable-gridlines > ${tableSelector}`; let innerHTML = ` @media screen and (max-width: ${this.breakpoint}) { ${selector} > .p-datatable-thead > tr > th, ${selector} > .p-datatable-tfoot > tr > td { display: none !important; } ${selector} > .p-datatable-tbody > tr > td { display: flex; width: 100% !important; align-items: center; justify-content: space-between; } ${selector} > .p-datatable-tbody > tr > td:not(:last-child) { border: 0 none; } ${gridLinesSelector} > .p-datatable-tbody > tr > td:last-child { border-top: 0; border-right: 0; border-left: 0; } ${selector} > .p-datatable-tbody > tr > td > .p-column-title { display: block; } } `; this.responsiveStyleElement.innerHTML = innerHTML; } }, destroyResponsiveStyle() { if (this.responsiveStyleElement) { document.head.removeChild(this.responsiveStyleElement); this.responsiveStyleElement = null; } }, destroyStyleElement() { if (this.styleElement) { document.head.removeChild(this.styleElement); this.styleElement = null; } }, recursiveGetChildren(children, results) { if (!results) { results = []; } if (children && children.length) { children.forEach((child) => { if (child.children instanceof Array) { results.concat(this.recursiveGetChildren(child.children, results)); } else if (child.type.name == 'Column') { results.push(child); } }); } return results; }, dataToRender(data) { const _data = data || this.processedData; if (_data && this.paginator) { const first = this.lazy ? 0 : this.d_first; return _data.slice(first, first + this.d_rows); } return _data; }, getVirtualScrollerRef() { return this.$refs.virtualScroller; }, hasSpacerStyle(style) { return ObjectUtils.isNotEmpty(style); } }, computed: { columns() { let children = this.getChildren(); if (!children) { return; } const cols = this.recursiveGetChildren(children, []); if (this.reorderableColumns && this.d_columnOrder) { let orderedColumns = []; for (let columnKey of this.d_columnOrder) { let column = this.findColumnByKey(cols, columnKey); if (column && !this.columnProp(column, 'hidden')) { orderedColumns.push(column); } } return [...orderedColumns, ...cols.filter((item) => orderedColumns.indexOf(item) < 0)]; } return cols; }, headerColumnGroup() { const children = this.getChildren(); if (children) { for (let child of children) { if (child.type.name === 'ColumnGroup' && this.columnProp(child, 'type') === 'header') { return child; } } } return null; }, footerColumnGroup() { const children = this.getChildren(); if (children) { for (let child of children) { if (child.type.name === 'ColumnGroup' && this.columnProp(child, 'type') === 'footer') { return child; } } } return null; }, hasFilters() { return this.filters && Object.keys(this.filters).length > 0 && this.filters.constructor === Object; }, processedData() { let data = this.value || []; if (!this.lazy) { if (data && data.length) { if (this.hasFilters) { data = this.filter(data); } if (this.sorted) { if (this.sortMode === 'single') data = this.sortSingle(data); else if (this.sortMode === 'multiple') data = this.sortMultiple(data); } } } return data; }, totalRecordsLength() { if (this.lazy) { return this.totalRecords; } else { const data = this.processedData; return data ? data.length : 0; } }, empty() { const data = this.processedData; return !data || data.length === 0; }, paginatorTop() { return this.paginator && (this.paginatorPosition !== 'bottom' || this.paginatorPosition === 'both'); }, paginatorBottom() { return this.paginator && (this.paginatorPosition !== 'top' || this.paginatorPosition === 'both'); }, sorted() { return this.d_sortField || (this.d_multiSortMeta && this.d_multiSortMeta.length > 0); }, allRowsSelected() { if (this.selectAll !== null) { return this.selectAll; } else { const val = this.frozenValue ? [...this.frozenValue, ...this.processedData] : this.processedData; return ObjectUtils.isNotEmpty(val) && this.selection && Array.isArray(this.selection) && val.every((v) => this.selection.some((s) => this.equals(s, v))); } }, attributeSelector() { return UniqueComponentId(); }, groupRowSortField() { return this.sortMode === 'single' ? this.sortField : this.d_groupRowsSortMeta ? this.d_groupRowsSortMeta.field : null; }, virtualScrollerDisabled() { return ObjectUtils.isEmpty(this.virtualScrollerOptions) || !this.scrollable; } }, components: { DTPaginator: Paginator, DTTableHeader: TableHeader, DTTableBody: TableBody, DTTableFooter: TableFooter, DTVirtualScroller: VirtualScroller, ArrowDownIcon: ArrowDownIcon, ArrowUpIcon: ArrowUpIcon, SpinnerIcon: SpinnerIcon } }; </script>