primevue-mirror/components/datatable/DataTable.vue

2260 lines
84 KiB
Vue
Raw Normal View History

2022-09-06 12:03:37 +00:00
<template>
<div :class="containerClass" data-scrollselectors=".p-datatable-wrapper">
<slot></slot>
<div class="p-datatable-loading-overlay p-component-overlay" v-if="loading">
<slot v-if="$slots.loading" name="loading"></slot>
<i v-else :class="loadingIconClass"></i>
</div>
<div class="p-datatable-header" v-if="$slots.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="p-paginator-top" @page="onPage($event)" :alwaysShow="alwaysShowPaginator">
<template #start v-if="$slots.paginatorstart">
<slot name="paginatorstart"></slot>
</template>
<template #end v-if="$slots.paginatorend">
<slot name="paginatorend"></slot>
</template>
</DTPaginator>
<div class="p-datatable-wrapper" :style="{ maxHeight: virtualScrollerDisabled ? scrollHeight : '' }">
<DTVirtualScroller ref="virtualScroller" v-bind="virtualScrollerOptions" :items="processedData" :columns="columns" :style="{ height: scrollHeight }" :disabled="virtualScrollerDisabled" loaderDisabled :showSpacer="false">
<template #content="slotProps">
<table ref="table" role="table" :class="[tableClass, 'p-datatable-table']" :style="[tableStyle, slotProps.spacerStyle]">
<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"
@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)" />
<DTTableBody ref="frozenBodyRef" v-if="frozenValue" :value="frozenValue" :frozenRow="true" class="p-datatable-frozen-tbody" :columns="slotProps.columns" :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"
@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" :isVirtualScrollerDisabled="true" />
<DTTableBody ref="bodyRef" :value="dataToRender(slotProps.rows)" :class="slotProps.styleClass" :columns="slotProps.columns" :empty="empty" :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"
@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"
:virtualScrollerContentProps="slotProps" :isVirtualScrollerDisabled="virtualScrollerDisabled" />
<DTTableFooter :columnGroup="footerColumnGroup" :columns="slotProps.columns" />
</table>
</template>
</DTVirtualScroller>
</div>
<DTPaginator v-if="paginatorBottom" :rows="d_rows" :first="d_first" :totalRecords="totalRecordsLength" :pageLinkSize="pageLinkSize" :template="paginatorTemplate" :rowsPerPageOptions="rowsPerPageOptions"
:currentPageReportTemplate="currentPageReportTemplate" class="p-paginator-bottom" @page="onPage($event)" :alwaysShow="alwaysShowPaginator">
<template #start v-if="$slots.paginatorstart">
<slot name="paginatorstart"></slot>
</template>
<template #end v-if="$slots.paginatorend">
<slot name="paginatorend"></slot>
</template>
</DTPaginator>
<div class="p-datatable-footer" v-if="$slots.footer">
<slot name="footer"></slot>
</div>
<div ref="resizeHelper" class="p-column-resizer-helper" style="display: none"></div>
<span ref="reorderIndicatorUp" class="pi pi-arrow-down p-datatable-reorder-indicator-up" style="position: absolute; display: none" v-if="reorderableColumns" />
<span ref="reorderIndicatorDown" class="pi pi-arrow-up p-datatable-reorder-indicator-down" style="position: absolute; display: none" v-if="reorderableColumns" />
</div>
</template>
<script>
import {ObjectUtils,DomHandler,UniqueComponentId} from 'primevue/utils';
import {FilterMatchMode,FilterOperator,FilterService} from 'primevue/api';
import Paginator from 'primevue/paginator';
import VirtualScroller from 'primevue/virtualscroller';
import TableHeader from './TableHeader.vue';
import TableBody from './TableBody.vue';
import TableFooter from './TableFooter.vue';
export default {
name: 'DataTable',
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'],
props: {
value: {
type: Array,
default: null
},
dataKey: {
type: [String, Function],
default: null
},
rows: {
type: Number,
default: 0
},
first: {
type: Number,
default: 0
},
totalRecords: {
type: Number,
default: 0
},
paginator: {
type: Boolean,
default: false
},
paginatorPosition: {
type: String,
default: 'bottom'
},
alwaysShowPaginator: {
type: Boolean,
default: true
},
paginatorTemplate: {
type: String,
default: 'FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown'
},
pageLinkSize: {
type: Number,
default: 5
},
rowsPerPageOptions: {
type: Array,
default: null
},
currentPageReportTemplate: {
type: String,
default: '({currentPage} of {totalPages})'
},
lazy: {
type: Boolean,
default: false
},
loading: {
type: Boolean,
default: false
},
loadingIcon: {
type: String,
default: 'pi pi-spinner'
},
sortField: {
type: [String, Function],
default: null
},
sortOrder: {
type: Number,
default: null
},
defaultSortOrder: {
type: Number,
default: 1
},
multiSortMeta: {
type: Array,
default: null
},
sortMode: {
type: String,
default: 'single'
},
removableSort: {
type: Boolean,
default: false
},
filters: {
type: Object,
default: null
},
filterDisplay: {
type: String,
default: null
},
globalFilterFields: {
type: Array,
default: null
},
filterLocale: {
type: String,
default: undefined
},
selection: {
type: [Array,Object],
default: null
},
selectionMode: {
type: String,
default: null
},
compareSelectionBy: {
type: String,
default: 'deepEquals'
},
metaKeySelection: {
type: Boolean,
default: true
},
contextMenu: {
type: Boolean,
default: false
},
contextMenuSelection: {
type: Object,
default: null
},
selectAll: {
type: Boolean,
default: null
},
rowHover: {
type: Boolean,
default: false
},
csvSeparator: {
type: String,
default: ','
},
exportFilename: {
type: String,
default: 'download'
},
exportFunction: {
type: Function,
default: null
},
autoLayout: {
type: Boolean,
default: false
},
resizableColumns: {
type: Boolean,
default: false
},
columnResizeMode: {
type: String,
default: 'fit'
},
reorderableColumns: {
type: Boolean,
default: false
},
expandedRows: {
type: Array,
default: null
},
expandedRowIcon: {
type: String,
default: 'pi-chevron-down'
},
collapsedRowIcon: {
type: String,
default: 'pi-chevron-right'
},
rowGroupMode: {
type: String,
default: null
},
groupRowsBy: {
type: [Array,String],
default: null
},
expandableRowGroups: {
type: Boolean,
default: false
},
expandedRowGroups: {
type: Array,
default: null
},
stateStorage: {
type: String,
default: 'session'
},
stateKey: {
type: String,
default: null
},
editMode: {
type: String,
default: null
},
editingRows: {
type: Array,
default: null
},
rowClass: {
type: null,
default: null
},
rowStyle: {
type: null,
default: null
},
scrollable: {
type: Boolean,
default: false
},
scrollDirection: {
type: String,
default: "vertical"
},
virtualScrollerOptions: {
type: Object,
default: null
},
scrollHeight: {
type: String,
default: null
},
frozenValue: {
type: Array,
default: null
},
responsiveLayout: {
type: String,
default: 'stack'
},
breakpoint: {
type: String,
default: '960px'
},
showGridlines: {
type: Boolean,
default: false
},
stripedRows: {
type: Boolean,
default: false
},
tableStyle: {
type: null,
default: null
},
tableClass: {
type: String,
default: null
}
},
data() {
return {
d_first: this.first,
d_rows: this.rows,
d_sortField: this.sortField,
d_sortOrder: this.sortOrder,
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;
},
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(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.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.hasClass(targetNode, 'p-sortable-column') || DomHandler.hasClass(targetNode, 'p-column-title') || DomHandler.hasClass(targetNode, 'p-column-header-content')
|| DomHandler.hasClass(targetNode, 'p-sortable-column-icon') || DomHandler.hasClass(targetNode.parentElement, 'p-sortable-column-icon')) {
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];
data.sort((data1, data2) => {
let value1 = ObjectUtils.resolveFieldData(data1, this.d_sortField);
let value2 = ObjectUtils.resolveFieldData(data2, this.d_sortField);
let result = null;
if (value1 == null && value2 != null)
result = -1;
else if (value1 != null && value2 == null)
result = 1;
else if (value1 == null && value2 == null)
result = 0;
else if (typeof value1 === 'string' && typeof value2 === 'string')
result = value1.localeCompare(value2, undefined, { numeric: true });
else
result = (value1 < value2) ? -1 : (value1 > value2) ? 1 : 0;
return (this.d_sortOrder * result);
});
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);
let result = null;
if (typeof value1 === 'string' || value1 instanceof String) {
if (value1.localeCompare && (value1 !== value2)) {
return (this.d_multiSortMeta[index].order * value1.localeCompare(value2, undefined, { numeric: true }));
}
}
else {
result = (value1 < value2) ? -1 : 1;
}
if (value1 === value2) {
return (this.d_multiSortMeta.length - 1) > (index) ? (this.multisortField(data1, data2, index + 1)) : 0;
}
return (this.d_multiSortMeta[index].order * result);
},
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];
},
filter(data) {
if (!data) {
return;
}
this.clearEditingMetaData();
let globalFilterFieldsArray;
if (this.filters['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 this.filters) {
if (Object.prototype.hasOwnProperty.call(this.filters, prop) && prop !== 'global') {
localFiltered = true;
let filterField = prop;
let filterMeta = this.filters[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 (this.filters['global'] && !globalMatch && globalFilterFieldsArray) {
for(let j = 0; j < globalFilterFieldsArray.length; j++) {
let globalFilterField = globalFilterFieldsArray[j];
globalMatch = FilterService.filters[this.filters['global'].matchMode || FilterMatchMode.CONTAINS](ObjectUtils.resolveFieldData(data[i], globalFilterField), this.filters['global'].value, this.filterLocale);
if (globalMatch) {
break;
}
}
}
let matches;
if (this.filters['global']) {
matches = localFiltered ? (localFiltered && localMatch && globalMatch) : globalMatch;
}
else {
matches = localFiltered && localMatch;
}
if (matches) {
filteredValue.push(data[i]);
}
}
if (filteredValue.length === this.value.length) {
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;
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;
},
onRowDblClick(e) {
const event = e.originalEvent;
if (DomHandler.isClickable(event.target)) {
return;
}
this.$emit('row-dblclick', e);
},
onRowRightClick(event) {
DomHandler.clearSelection();
event.originalEvent.target.focus();
this.$emit('update:contextMenuSelection', event.data);
this.$emit('row-contextmenu', event);
},
onRowTouchEnd() {
this.rowTouched = true;
},
onRowKeyDown(e) {
const event = e.originalEvent;
const rowData = e.data;
const rowIndex = e.index;
if (this.selectionMode) {
const row = event.target;
switch (event.which) {
//down arrow
case 40:
var nextRow = this.findNextSelectableRow(row);
if (nextRow) {
nextRow.focus();
}
event.preventDefault();
break;
//up arrow
case 38:
var prevRow = this.findPrevSelectableRow(row);
if (prevRow) {
prevRow.focus();
}
event.preventDefault();
break;
//enter
case 13:
this.onRowClick({originalEvent: event, data: rowData, index: rowIndex});
break;
default:
//no op
break;
}
}
},
findNextSelectableRow(row) {
let nextRow = row.nextElementSibling;
if (nextRow) {
if (DomHandler.hasClass(nextRow, 'p-selectable-row'))
return nextRow;
else
return this.findNextSelectableRow(nextRow);
}
else {
return null;
}
},
findPrevSelectableRow(row) {
let prevRow = row.previousElementSibling;
if (prevRow) {
if (DomHandler.hasClass(prevRow, 'p-selectable-row'))
return prevRow;
else
return this.findPrevSelectableRow(prevRow);
}
else {
return null;
}
},
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, 'field')) {
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;
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);
}
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.resizeTableCells(newColumnWidth);
}
this.$emit('column-resize-end', {
element: this.resizeColumnElement,
delta: delta
});
}
this.$refs.resizeHelper.style.display = 'none';
this.resizeColumn = null;
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, '.p-datatable-thead > tr > th');
headers.forEach(header => widths.push(DomHandler.getOuterWidth(header)));
this.destroyStyleElement();
this.createStyleElement();
let innerHTML = '';
widths.forEach((width, index) => {
let colWidth = index === colIndex ? newColumnWidth : (nextColumnWidth && index === colIndex + 1) ? nextColumnWidth : width;
let style = this.scrollable ? `flex: 1 1 ${colWidth}px !important` : `width: ${colWidth}px !important`;
innerHTML += `
.p-datatable[${this.attributeSelector}] .p-datatable-thead > tr > th:nth-child(${index + 1}),
.p-datatable[${this.attributeSelector}] .p-datatable-tbody > tr > td:nth-child(${index + 1}),
.p-datatable[${this.attributeSelector}] .p-datatable-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.hasClass(event.target, 'p-column-resizer'))
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.hasClass(event.target, 'p-datatable-reorderablerow-handle'))
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) {
DomHandler.removeClass(rowElement, 'p-datatable-dragpoint-bottom');
this.droppedRowIndex = index;
if (prevRowElement)
DomHandler.addClass(prevRowElement, 'p-datatable-dragpoint-bottom');
else
DomHandler.addClass(rowElement, 'p-datatable-dragpoint-top');
}
else {
if (prevRowElement)
DomHandler.removeClass(prevRowElement, 'p-datatable-dragpoint-bottom');
else
DomHandler.addClass(rowElement, 'p-datatable-dragpoint-top');
this.droppedRowIndex = index + 1;
DomHandler.addClass(rowElement, 'p-datatable-dragpoint-bottom');
}
event.preventDefault();
}
},
onRowDragLeave(event) {
let rowElement = event.currentTarget;
let prevRowElement = rowElement.previousElementSibling;
if (prevRowElement) {
DomHandler.removeClass(prevRowElement, 'p-datatable-dragpoint-bottom');
}
DomHandler.removeClass(rowElement, 'p-datatable-dragpoint-bottom');
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, dropIndex);
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, '.p-datatable-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 = '';
widths.forEach((width, index) => {
let style = this.scrollable ? `flex: 1 1 ${width}px !important` : `width: ${width}px !important`;
innerHTML += `
.p-datatable[${this.attributeSelector}] .p-datatable-thead > tr > th:nth-child(${index + 1}),
.p-datatable[${this.attributeSelector}] .p-datatable-tbody > tr > td:nth-child(${index + 1}),
.p-datatable[${this.attributeSelector}] .p-datatable-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';
document.head.appendChild(this.styleElement);
},
createResponsiveStyle() {
if (!this.responsiveStyleElement) {
this.responsiveStyleElement = document.createElement('style');
this.responsiveStyleElement.type = 'text/css';
document.head.appendChild(this.responsiveStyleElement);
let innerHTML = `
@media screen and (max-width: ${this.breakpoint}) {
.p-datatable[${this.attributeSelector}] .p-datatable-thead > tr > th,
.p-datatable[${this.attributeSelector}] .p-datatable-tfoot > tr > td {
display: none !important;
}
.p-datatable[${this.attributeSelector}] .p-datatable-tbody > tr > td {
display: flex;
width: 100% !important;
align-items: center;
justify-content: space-between;
}
.p-datatable[${this.attributeSelector}] .p-datatable-tbody > tr > td:not(:last-child) {
border: 0 none;
}
.p-datatable[${this.attributeSelector}].p-datatable-gridlines .p-datatable-tbody > tr > td:last-child {
border-top: 0;
border-right: 0;
border-left: 0;
}
.p-datatable[${this.attributeSelector}] .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;
}
},
computed: {
containerClass() {
return [
'p-datatable p-component', {
'p-datatable-hoverable-rows': (this.rowHover || this.selectionMode),
'p-datatable-auto-layout': this.autoLayout,
'p-datatable-resizable': this.resizableColumns,
'p-datatable-resizable-fit': this.resizableColumns && this.columnResizeMode === 'fit',
'p-datatable-scrollable': this.scrollable,
'p-datatable-scrollable-vertical': this.scrollable && this.scrollDirection === 'vertical',
'p-datatable-scrollable-horizontal': this.scrollable && this.scrollDirection === 'horizontal',
'p-datatable-scrollable-both': this.scrollable && this.scrollDirection === 'both',
'p-datatable-flex-scrollable': (this.scrollable && this.scrollHeight === 'flex'),
'p-datatable-responsive-stack': this.responsiveLayout === 'stack',
'p-datatable-responsive-scroll': this.responsiveLayout === 'scroll',
'p-datatable-striped': this.stripedRows,
'p-datatable-gridlines': this.showGridlines,
'p-datatable-grouped-header': this.headerColumnGroup != null,
'p-datatable-grouped-footer': this.footerColumnGroup != null
}
];
},
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);
},
loadingIconClass() {
return ['p-datatable-loading-icon pi-spin', this.loadingIcon];
},
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
}
}
</script>
<style>
.p-datatable {
position: relative;
}
.p-datatable table {
border-collapse: collapse;
min-width: 100%;
table-layout: fixed;
}
.p-datatable .p-sortable-column {
cursor: pointer;
user-select: none;
}
.p-datatable .p-sortable-column .p-column-title,
.p-datatable .p-sortable-column .p-sortable-column-icon,
.p-datatable .p-sortable-column .p-sortable-column-badge {
vertical-align: middle;
}
.p-datatable .p-sortable-column .p-sortable-column-badge {
display: inline-flex;
align-items: center;
justify-content: center;
}
.p-datatable-responsive-scroll > .p-datatable-wrapper {
overflow-x: auto;
}
.p-datatable-responsive-scroll > .p-datatable-wrapper > table,
.p-datatable-auto-layout > .p-datatable-wrapper > table {
table-layout: auto;
}
.p-datatable-hoverable-rows .p-selectable-row {
cursor: pointer;
}
/* Scrollable */
.p-datatable-scrollable .p-datatable-wrapper {
position: relative;
overflow: auto;
}
.p-datatable-scrollable .p-datatable-thead,
.p-datatable-scrollable .p-datatable-tbody,
.p-datatable-scrollable .p-datatable-tfoot {
display: block;
}
.p-datatable-scrollable .p-datatable-thead > tr,
.p-datatable-scrollable .p-datatable-tbody > tr,
.p-datatable-scrollable .p-datatable-tfoot > tr {
display: flex;
flex-wrap: nowrap;
width: 100%;
}
.p-datatable-scrollable .p-datatable-thead > tr > th,
.p-datatable-scrollable .p-datatable-tbody > tr > td,
.p-datatable-scrollable .p-datatable-tfoot > tr > td {
display: flex;
flex: 1 1 0;
align-items: center;
}
.p-datatable-scrollable .p-datatable-thead {
position: sticky;
top: 0;
z-index: 1;
}
.p-datatable-scrollable .p-datatable-frozen-tbody {
position: sticky;
z-index: 1;
}
.p-datatable-scrollable .p-datatable-tfoot {
position: sticky;
bottom: 0;
z-index: 1;
}
.p-datatable-scrollable .p-frozen-column {
position: sticky;
background: inherit;
}
.p-datatable-scrollable th.p-frozen-column {
z-index: 1;
}
.p-datatable-scrollable-both .p-datatable-thead > tr > th,
.p-datatable-scrollable-both .p-datatable-tbody > tr > td,
.p-datatable-scrollable-both .p-datatable-tfoot > tr > td,
.p-datatable-scrollable-horizontal .p-datatable-thead > tr > th
.p-datatable-scrollable-horizontal .p-datatable-tbody > tr > td,
.p-datatable-scrollable-horizontal .p-datatable-tfoot > tr > td {
flex: 1 0 auto;
}
.p-datatable-flex-scrollable {
display: flex;
flex-direction: column;
height: 100%;
}
.p-datatable-flex-scrollable .p-datatable-wrapper {
display: flex;
flex-direction: column;
flex: 1;
height: 100%;
}
.p-datatable-scrollable .p-rowgroup-header {
position: sticky;
z-index: 1;
}
.p-datatable-scrollable.p-datatable-grouped-header .p-datatable-thead,
.p-datatable-scrollable.p-datatable-grouped-footer .p-datatable-tfoot {
display: table;
border-collapse: collapse;
width: 100%;
table-layout: fixed;
}
.p-datatable-scrollable.p-datatable-grouped-header .p-datatable-thead > tr,
.p-datatable-scrollable.p-datatable-grouped-footer .p-datatable-tfoot > tr {
display: table-row;
}
.p-datatable-scrollable.p-datatable-grouped-header .p-datatable-thead > tr > th,
.p-datatable-scrollable.p-datatable-grouped-footer .p-datatable-tfoot > tr > td {
display: table-cell;
}
.p-datatable-scrollable .p-virtualscroller > .p-datatable-table {
display: inline-block; /* For Safari */
}
/* Resizable */
.p-datatable-resizable > .p-datatable-wrapper {
overflow-x: auto;
}
.p-datatable-resizable .p-datatable-thead > tr > th,
.p-datatable-resizable .p-datatable-tfoot > tr > td,
.p-datatable-resizable .p-datatable-tbody > tr > td {
overflow: hidden;
white-space: nowrap;
}
.p-datatable-resizable .p-resizable-column:not(.p-frozen-column) {
background-clip: padding-box;
position: relative;
}
.p-datatable-resizable-fit .p-resizable-column:last-child .p-column-resizer {
display: none;
}
.p-datatable .p-column-resizer {
display: block;
position: absolute !important;
top: 0;
right: 0;
margin: 0;
width: .5rem;
height: 100%;
padding: 0px;
cursor:col-resize;
border: 1px solid transparent;
}
.p-datatable .p-column-header-content {
display: flex;
align-items: center;
}
.p-datatable .p-column-resizer-helper {
width: 1px;
position: absolute;
z-index: 10;
display: none;
}
.p-datatable .p-row-editor-init,
.p-datatable .p-row-editor-save,
.p-datatable .p-row-editor-cancel {
display: inline-flex;
align-items: center;
justify-content: center;
overflow: hidden;
position: relative;
}
/* Expand */
.p-datatable .p-row-toggler {
display: inline-flex;
align-items: center;
justify-content: center;
overflow: hidden;
position: relative;
}
/* Reorder */
.p-datatable-reorder-indicator-up,
.p-datatable-reorder-indicator-down {
position: absolute;
display: none;
}
.p-reorderable-column,
.p-datatable-reorderablerow-handle {
cursor: move;
}
/* Loader */
.p-datatable .p-datatable-loading-overlay {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
}
/* Filter */
.p-column-filter-row {
display: flex;
align-items: center;
width: 100%;
}
.p-column-filter-menu {
display: inline-flex;
margin-left: auto;
}
.p-column-filter-row .p-column-filter-element {
flex: 1 1 auto;
width: 1%;
}
.p-column-filter-menu-button,
.p-column-filter-clear-button {
display: inline-flex;
justify-content: center;
align-items: center;
cursor: pointer;
text-decoration: none;
overflow: hidden;
position: relative;
}
.p-column-filter-overlay {
position: absolute;
top: 0;
left: 0;
}
.p-column-filter-row-items {
margin: 0;
padding: 0;
list-style: none;
}
.p-column-filter-row-item {
cursor: pointer;
}
.p-column-filter-add-button,
.p-column-filter-remove-button {
justify-content: center;
}
.p-column-filter-add-button .p-button-label,
.p-column-filter-remove-button .p-button-label {
flex-grow: 0;
}
.p-column-filter-buttonbar {
display: flex;
align-items: center;
justify-content: space-between;
}
.p-column-filter-buttonbar .p-button:not(.p-button-icon-only) {
width: auto;
}
/* Responsive */
.p-datatable .p-datatable-tbody > tr > td > .p-column-title {
display: none;
}
/* VirtualScroller */
.p-datatable .p-virtualscroller-loading {
transform: none !important;
min-height: 0;
position: sticky;
top: 0;
left: 0;
}
</style>