Fixed #1843 - Add virtualScroller feature to DataTable

pull/1846/head
mertsincan 2021-12-05 20:18:38 +03:00
parent 330a1119c3
commit fa578b9673
4 changed files with 205 additions and 56 deletions

View File

@ -1,5 +1,6 @@
import { VNode } from 'vue'; import { VNode } from 'vue';
import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers';
import { VirtualScrollerLoaderOptions } from '../virtualscroller';
type ColumnFieldType = string | ((item: any) => string) | undefined; type ColumnFieldType = string | ((item: any) => string) | undefined;
@ -22,6 +23,37 @@ export interface ColumnFilterMatchModeOptions {
[key: string]: string; [key: string]: string;
} }
export interface ColumnLoadingOptions extends VirtualScrollerLoaderOptions {
/**
* Cell index
*/
cellIndex: number;
/**
* Whether the cell is first.
*/
cellFirst: boolean;
/**
* Whether the cell is last.
*/
cellLast: boolean;
/**
* Whether the cell is even.
*/
cellEven: boolean;
/**
* Whether the item is odd.
*/
cellOdd: boolean;
/**
* Column instance
*/
column: Column;
/**
* Column field
*/
field: string;
}
export interface ColumnProps { export interface ColumnProps {
/** /**
* Identifier of a column if field property is not defined. * Identifier of a column if field property is not defined.
@ -375,6 +407,37 @@ export interface ColumnSlots {
*/ */
filterCallback: () => void; filterCallback: () => void;
}) => VNode[]; }) => VNode[];
/**
* Custom loading template.
* @param {Object} scope - loading slot's params.
*/
loading: (scope: {
/**
* Row data.
*/
data: any;
/**
* Column node.
*/
column: Column;
/**
* Column field.
*/
field: string;
/**
* Row index.
*/
index: number;
/**
* Whether the row is frozen.
*/
frozenRow: boolean;
/**
* Loading options.
* @see ColumnLoadingOptions
*/
loadingOptions: ColumnLoadingOptions;
}) => VNode[];
} }
export declare type ColumnEmits = { export declare type ColumnEmits = {

View File

@ -1,5 +1,8 @@
<template> <template>
<td :style="containerStyle" :class="containerClass" @click="onClick" @keydown="onKeyDown" role="cell"> <td v-if="loading" :style="containerStyle" :class="containerClass">
<component :is="column.children.loading" :data="rowData" :column="column" :field="field" :index="rowIndex" :frozenRow="frozenRow" :loadingOptions="loadingOptions" />
</td>
<td v-else :style="containerStyle" :class="containerClass" @click="onClick" @keydown="onKeyDown" role="cell">
<span v-if="responsiveLayout === 'stack'" class="p-column-title">{{columnProp('header')}}</span> <span v-if="responsiveLayout === 'stack'" class="p-column-title">{{columnProp('header')}}</span>
<component :is="column.children.body" :data="rowData" :column="column" :field="field" :index="rowIndex" :frozenRow="frozenRow" v-if="column.children && column.children.body && !d_editing" /> <component :is="column.children.body" :data="rowData" :column="column" :field="field" :index="rowIndex" :frozenRow="frozenRow" v-if="column.children && column.children.body && !d_editing" />
<component :is="column.children.editor" :data="editingRowData" :column="column" :field="field" :index="rowIndex" :frozenRow="frozenRow" v-else-if="column.children && column.children.editor && d_editing" /> <component :is="column.children.editor" :data="editingRowData" :column="column" :field="field" :index="rowIndex" :frozenRow="frozenRow" v-else-if="column.children && column.children.editor && d_editing" />
@ -86,6 +89,10 @@ export default {
responsiveLayout: { responsiveLayout: {
type: String, type: String,
default: 'stack' default: 'stack'
},
virtualScrollerContentProps: {
type: Object,
default: null
} }
}, },
documentEditListener: null, documentEditListener: null,
@ -339,6 +346,9 @@ export default {
this.styleObject.left = left + 'px'; this.styleObject.left = left + 'px';
} }
} }
},
getVirtualScrollerProp(option) {
return this.virtualScrollerContentProps ? this.virtualScrollerContentProps[option] : null;
} }
}, },
computed: { computed: {
@ -361,6 +371,21 @@ export default {
let columnStyle = this.columnProp('style'); let columnStyle = this.columnProp('style');
return this.columnProp('frozen') ? [columnStyle, bodyStyle, this.styleObject]: [columnStyle, bodyStyle]; return this.columnProp('frozen') ? [columnStyle, bodyStyle, this.styleObject]: [columnStyle, bodyStyle];
},
loading() {
return this.getVirtualScrollerProp('loading');
},
loadingOptions() {
const getLoaderOptions = this.getVirtualScrollerProp('getLoaderOptions');
return getLoaderOptions && getLoaderOptions(this.rowIndex, {
cellIndex: this.index,
cellFirst: this.index === 0,
cellLast: this.index === (this.getVirtualScrollerProp('columns').length - 1),
cellEven: this.index % 2 === 0,
cellOdd: this.index % 2 !== 0,
column: this.column,
field: this.field
});
} }
}, },
components: { components: {

View File

@ -16,15 +16,17 @@
<slot name="paginatorend"></slot> <slot name="paginatorend"></slot>
</template> </template>
</DTPaginator> </DTPaginator>
<div class="p-datatable-wrapper" :style="{maxHeight: scrollHeight}"> <div class="p-datatable-wrapper" :style="{ maxHeight: virtualScrollerDisabled ? scrollHeight : '' }">
<table ref="table" role="table" :class="[tableClass, 'p-datatable-table']" :style="tableStyle"> <DTVirtualScroller v-bind="virtualScrollerOptions" :items="processedData" :columns="columns" :style="{ height: scrollHeight }" :disabled="virtualScrollerDisabled" loaderDisabled :showSpacer="false">
<DTTableHeader :columnGroup="headerColumnGroup" :columns="columns" :rowGroupMode="rowGroupMode" <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" :resizableColumns="resizableColumns" :allRowsSelected="allRowsSelected" :empty="empty" :groupRowsBy="groupRowsBy" :groupRowSortField="groupRowSortField" :resizableColumns="resizableColumns" :allRowsSelected="allRowsSelected" :empty="empty"
:sortMode="sortMode" :sortField="d_sortField" :sortOrder="d_sortOrder" :multiSortMeta="d_multiSortMeta" :filters="d_filters" :filtersStore="filters" :filterDisplay="filterDisplay" :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-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-dragstart="onColumnHeaderDragStart($event)" @column-dragover="onColumnHeaderDragOver($event)" @column-dragleave="onColumnHeaderDragLeave($event)" @column-drop="onColumnHeaderDrop($event)"
@column-resizestart="onColumnResizeStart($event)" @checkbox-change="toggleRowsWithCheckbox($event)" /> @column-resizestart="onColumnResizeStart($event)" @checkbox-change="toggleRowsWithCheckbox($event)" />
<DTTableBody v-if="frozenValue" :value="frozenValue" :frozenRow="true" class="p-datatable-frozen-tbody" :columns="columns" :dataKey="dataKey" :selection="selection" :selectionKeys="d_selectionKeys" :selectionMode="selectionMode" :contextMenu="contextMenu" :contextMenuSelection="contextMenuSelection" <DTTableBody 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" :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" :expandedRowIcon="expandedRowIcon" :collapsedRowIcon="collapsedRowIcon" :expandedRows="expandedRows" :expandedRowKeys="d_expandedRowKeys" :expandedRowGroups="expandedRowGroups"
:editingRows="editingRows" :editingRowKeys="d_editingRowKeys" :templates="$slots" :loading="loading" :responsiveLayout="responsiveLayout" :editingRows="editingRows" :editingRowKeys="d_editingRowKeys" :templates="$slots" :loading="loading" :responsiveLayout="responsiveLayout"
@ -33,8 +35,8 @@
@row-toggle="toggleRow($event)" @radio-change="toggleRowWithRadio($event)" @checkbox-change="toggleRowWithCheckbox($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)" @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)" @row-edit-init="onRowEditInit($event)" @row-edit-save="onRowEditSave($event)" @row-edit-cancel="onRowEditCancel($event)"
:editingMeta="d_editingMeta" @editing-meta-change="onEditingMetaChange" /> :editingMeta="d_editingMeta" @editing-meta-change="onEditingMetaChange" :isVirtualScrollerDisabled="true" />
<DTTableBody :value="dataToRender" :columns="columns" :empty="empty" :dataKey="dataKey" :selection="selection" :selectionKeys="d_selectionKeys" :selectionMode="selectionMode" :contextMenu="contextMenu" :contextMenuSelection="contextMenuSelection" <DTTableBody :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" :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" :expandedRowIcon="expandedRowIcon" :collapsedRowIcon="collapsedRowIcon" :expandedRows="expandedRows" :expandedRowKeys="d_expandedRowKeys" :expandedRowGroups="expandedRowGroups"
:editingRows="editingRows" :editingRowKeys="d_editingRowKeys" :templates="$slots" :loading="loading" :responsiveLayout="responsiveLayout" :editingRows="editingRows" :editingRowKeys="d_editingRowKeys" :templates="$slots" :loading="loading" :responsiveLayout="responsiveLayout"
@ -43,9 +45,12 @@
@row-toggle="toggleRow($event)" @radio-change="toggleRowWithRadio($event)" @checkbox-change="toggleRowWithCheckbox($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)" @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)" @row-edit-init="onRowEditInit($event)" @row-edit-save="onRowEditSave($event)" @row-edit-cancel="onRowEditCancel($event)"
:editingMeta="d_editingMeta" @editing-meta-change="onEditingMetaChange" /> :editingMeta="d_editingMeta" @editing-meta-change="onEditingMetaChange"
<DTTableFooter :columnGroup="footerColumnGroup" :columns="columns" /> :virtualScrollerContentProps="slotProps" :isVirtualScrollerDisabled="virtualScrollerDisabled" />
<DTTableFooter :columnGroup="footerColumnGroup" :columns="slotProps.columns" />
</table> </table>
</template>
</DTVirtualScroller>
</div> </div>
<DTPaginator v-if="paginatorBottom" :rows="d_rows" :first="lazy ? 0 : d_first" :totalRecords="totalRecordsLength" :pageLinkSize="pageLinkSize" :template="paginatorTemplate" :rowsPerPageOptions="rowsPerPageOptions" <DTPaginator v-if="paginatorBottom" :rows="d_rows" :first="lazy ? 0 : d_first" :totalRecords="totalRecordsLength" :pageLinkSize="pageLinkSize" :template="paginatorTemplate" :rowsPerPageOptions="rowsPerPageOptions"
:currentPageReportTemplate="currentPageReportTemplate" class="p-paginator-bottom" @page="onPage($event)" :alwaysShow="alwaysShowPaginator"> :currentPageReportTemplate="currentPageReportTemplate" class="p-paginator-bottom" @page="onPage($event)" :alwaysShow="alwaysShowPaginator">
@ -69,6 +74,7 @@
import {ObjectUtils,DomHandler,UniqueComponentId} from 'primevue/utils'; import {ObjectUtils,DomHandler,UniqueComponentId} from 'primevue/utils';
import {FilterMatchMode,FilterOperator,FilterService} from 'primevue/api'; import {FilterMatchMode,FilterOperator,FilterService} from 'primevue/api';
import Paginator from 'primevue/paginator'; import Paginator from 'primevue/paginator';
import VirtualScroller from 'primevue/virtualscroller';
import TableHeader from './TableHeader.vue'; import TableHeader from './TableHeader.vue';
import TableBody from './TableBody.vue'; import TableBody from './TableBody.vue';
import TableFooter from './TableFooter.vue'; import TableFooter from './TableFooter.vue';
@ -297,6 +303,10 @@ export default {
type: String, type: String,
default: "vertical" default: "vertical"
}, },
virtualScrollerOptions: {
type: Object,
default: null
},
scrollHeight: { scrollHeight: {
type: String, type: String,
default: null default: null
@ -1766,6 +1776,16 @@ export default {
} }
return results; 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;
}
}, },
computed: { computed: {
containerClass() { containerClass() {
@ -1859,17 +1879,6 @@ export default {
return data; return data;
}, },
dataToRender() {
const data = this.processedData;
if (data && this.paginator) {
const first = this.lazy ? 0 : this.d_first;
return data.slice(first, first + this.d_rows);
}
else {
return data;
}
},
totalRecordsLength() { totalRecordsLength() {
if (this.lazy) { if (this.lazy) {
return this.totalRecords; return this.totalRecords;
@ -1905,6 +1914,9 @@ export default {
}, },
groupRowSortField() { groupRowSortField() {
return this.sortMode === 'single' ? this.sortField : (this.d_groupRowsSortMeta ? this.d_groupRowsSortMeta.field : null); return this.sortMode === 'single' ? this.sortField : (this.d_groupRowsSortMeta ? this.d_groupRowsSortMeta.field : null);
},
virtualScrollerDisabled() {
return ObjectUtils.isEmpty(this.virtualScrollerOptions) || !this.scrollable;
} }
}, },
components: { components: {
@ -1912,6 +1924,7 @@ export default {
'DTTableHeader': TableHeader, 'DTTableHeader': TableHeader,
'DTTableBody': TableBody, 'DTTableBody': TableBody,
'DTTableFooter': TableFooter, 'DTTableFooter': TableFooter,
'DTVirtualScroller': VirtualScroller
} }
} }
</script> </script>
@ -2205,4 +2218,13 @@ export default {
.p-datatable .p-datatable-tbody > tr > td > .p-column-title { .p-datatable .p-datatable-tbody > tr > td > .p-column-title {
display: none; display: none;
} }
/* VirtualScroller */
.p-datatable .p-virtualscroller-loading {
transform: none !important;
min-height: 0;
position: sticky;
top: 0;
left: 0;
}
</style> </style>

View File

@ -1,37 +1,37 @@
<template> <template>
<tbody class="p-datatable-tbody" role="rowgroup"> <tbody :ref="bodyRef" class="p-datatable-tbody" role="rowgroup" :style="bodyStyle">
<template v-if="!empty"> <template v-if="!empty">
<template v-for="(rowData, index) of value" :key="getRowKey(rowData, index) + '_subheader'"> <template v-for="(rowData, index) of value" :key="getRowKey(rowData, getRowIndex(index)) + '_subheader'">
<tr class="p-rowgroup-header" :style="rowGroupHeaderStyle" v-if="templates['groupheader'] && rowGroupMode === 'subheader' && shouldRenderRowGroupHeader(value, rowData, index)" role="row"> <tr class="p-rowgroup-header" :style="rowGroupHeaderStyle" v-if="templates['groupheader'] && rowGroupMode === 'subheader' && shouldRenderRowGroupHeader(value, rowData, getRowIndex(index))" role="row">
<td :colspan="columnsLength - 1"> <td :colspan="columnsLength - 1">
<button class="p-row-toggler p-link" @click="onRowGroupToggle($event, rowData)" v-if="expandableRowGroups" type="button"> <button class="p-row-toggler p-link" @click="onRowGroupToggle($event, rowData)" v-if="expandableRowGroups" type="button">
<span :class="rowGroupTogglerIcon(rowData)"></span> <span :class="rowGroupTogglerIcon(rowData)"></span>
</button> </button>
<component :is="templates['groupheader']" :data="rowData" :index="index" /> <component :is="templates['groupheader']" :data="rowData" :index="getRowIndex(index)" />
</td> </td>
</tr> </tr>
<tr :class="getRowClass(rowData)" :style="rowStyle" :key="getRowKey(rowData, index)" <tr :class="getRowClass(rowData)" :style="rowStyle" :key="getRowKey(rowData, getRowIndex(index))"
v-if="expandableRowGroups ? isRowGroupExpanded(rowData): true" v-if="expandableRowGroups ? isRowGroupExpanded(rowData): true"
@click="onRowClick($event, rowData, index)" @dblclick="onRowDblClick($event, rowData, index)" @contextmenu="onRowRightClick($event, rowData, index)" @touchend="onRowTouchEnd($event)" @keydown="onRowKeyDown($event, rowData, index)" :tabindex="selectionMode || contextMenu ? '0' : null" @click="onRowClick($event, rowData, getRowIndex(index))" @dblclick="onRowDblClick($event, rowData, getRowIndex(index))" @contextmenu="onRowRightClick($event, rowData, getRowIndex(index))" @touchend="onRowTouchEnd($event)" @keydown="onRowKeyDown($event, rowData, getRowIndex(index))" :tabindex="selectionMode || contextMenu ? '0' : null"
@mousedown="onRowMouseDown($event)" @dragstart="onRowDragStart($event, index)" @dragover="onRowDragOver($event,index)" @dragleave="onRowDragLeave($event)" @dragend="onRowDragEnd($event)" @drop="onRowDrop($event)" role="row"> @mousedown="onRowMouseDown($event)" @dragstart="onRowDragStart($event, getRowIndex(index))" @dragover="onRowDragOver($event, getRowIndex(index))" @dragleave="onRowDragLeave($event)" @dragend="onRowDragEnd($event)" @drop="onRowDrop($event)" role="row">
<template v-for="(col,i) of columns" :key="columnProp(col,'columnKey')||columnProp(col,'field')||i"> <template v-for="(col,i) of columns" :key="columnProp(col,'columnKey')||columnProp(col,'field')||i">
<DTBodyCell v-if="shouldRenderBodyCell(value, col, index)" :rowData="rowData" :column="col" :rowIndex="index" :index="i" :selected="isSelected(rowData)" <DTBodyCell v-if="shouldRenderBodyCell(value, col, getRowIndex(index))" :rowData="rowData" :column="col" :rowIndex="getRowIndex(index)" :index="i" :selected="isSelected(rowData)"
:rowTogglerIcon="columnProp(col,'expander') ? rowTogglerIcon(rowData): null" :frozenRow="frozenRow" :rowTogglerIcon="columnProp(col,'expander') ? rowTogglerIcon(rowData): null" :frozenRow="frozenRow"
:rowspan="rowGroupMode === 'rowspan' ? calculateRowGroupSize(value, col, index) : null" :rowspan="rowGroupMode === 'rowspan' ? calculateRowGroupSize(value, col, getRowIndex(index)) : null"
:editMode="editMode" :editing="editMode === 'row' && isRowEditing(rowData)" :responsiveLayout="responsiveLayout" :editMode="editMode" :editing="editMode === 'row' && isRowEditing(rowData)" :responsiveLayout="responsiveLayout"
@radio-change="onRadioChange($event)" @checkbox-change="onCheckboxChange($event)" @row-toggle="onRowToggle($event)" @radio-change="onRadioChange($event)" @checkbox-change="onCheckboxChange($event)" @row-toggle="onRowToggle($event)"
@cell-edit-init="onCellEditInit($event)" @cell-edit-complete="onCellEditComplete($event)" @cell-edit-cancel="onCellEditCancel($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)" @row-edit-init="onRowEditInit($event)" @row-edit-save="onRowEditSave($event)" @row-edit-cancel="onRowEditCancel($event)"
:editingMeta="editingMeta" @editing-meta-change="onEditingMetaChange"/> :editingMeta="editingMeta" @editing-meta-change="onEditingMetaChange" :virtualScrollerContentProps="virtualScrollerContentProps"/>
</template> </template>
</tr> </tr>
<tr class="p-datatable-row-expansion" v-if="templates['expansion'] && expandedRows && isRowExpanded(rowData)" :key="getRowKey(rowData, index) + '_expansion'" role="row"> <tr class="p-datatable-row-expansion" v-if="templates['expansion'] && expandedRows && isRowExpanded(rowData)" :key="getRowKey(rowData, getRowIndex(index)) + '_expansion'" role="row">
<td :colspan="columnsLength"> <td :colspan="columnsLength">
<component :is="templates['expansion']" :data="rowData" :index="index" /> <component :is="templates['expansion']" :data="rowData" :index="getRowIndex(index)" />
</td> </td>
</tr> </tr>
<tr class="p-rowgroup-footer" v-if="templates['groupfooter'] && rowGroupMode === 'subheader' && shouldRenderRowGroupFooter(value, rowData, index)" :key="getRowKey(rowData, index) + '_subfooter'" role="row"> <tr class="p-rowgroup-footer" v-if="templates['groupfooter'] && rowGroupMode === 'subheader' && shouldRenderRowGroupFooter(value, rowData, getRowIndex(index))" :key="getRowKey(rowData, getRowIndex(index)) + '_subfooter'" role="row">
<component :is="templates['groupfooter']" :data="rowData" :index="index" /> <component :is="templates['groupfooter']" :data="rowData" :index="getRowIndex(index)" />
</tr> </tr>
</template> </template>
</template> </template>
@ -170,6 +170,21 @@ export default {
responsiveLayout: { responsiveLayout: {
type: String, type: String,
default: 'stack' default: 'stack'
},
virtualScrollerContentProps: {
type: Object,
default: null
},
isVirtualScrollerDisabled: {
type: Boolean,
default: false
}
},
watch: {
virtualScrollerContentProps(newValue, oldValue) {
if (!this.isVirtualScrollerDisabled && this.getVirtualScrollerProp('vertical') && this.getVirtualScrollerProp('itemSize', oldValue) !== this.getVirtualScrollerProp('itemSize', newValue)) {
this.updateVirtualScrollerPosition();
}
} }
}, },
mounted() { mounted() {
@ -180,6 +195,10 @@ export default {
if (this.scrollable && this.rowGroupMode === 'subheader') { if (this.scrollable && this.rowGroupMode === 'subheader') {
this.updateFrozenRowGroupHeaderStickyPosition(); this.updateFrozenRowGroupHeaderStickyPosition();
} }
if (!this.isVirtualScrollerDisabled && this.getVirtualScrollerProp('vertical')) {
this.updateVirtualScrollerPosition();
}
}, },
updated() { updated() {
if (this.frozenRow) { if (this.frozenRow) {
@ -213,6 +232,10 @@ export default {
getRowKey(rowData, index) { getRowKey(rowData, index) {
return this.dataKey ? ObjectUtils.resolveFieldData(rowData, this.dataKey): index; return this.dataKey ? ObjectUtils.resolveFieldData(rowData, this.dataKey): index;
}, },
getRowIndex(index) {
const getItemOptions = this.getVirtualScrollerProp('getItemOptions');
return getItemOptions ? getItemOptions(index).index : index;
},
getRowClass(rowData) { getRowClass(rowData) {
let rowStyleClass = []; let rowStyleClass = [];
if (this.selectionMode) { if (this.selectionMode) {
@ -465,6 +488,19 @@ export default {
updateFrozenRowGroupHeaderStickyPosition() { updateFrozenRowGroupHeaderStickyPosition() {
let tableHeaderHeight = DomHandler.getOuterHeight(this.$el.previousElementSibling); let tableHeaderHeight = DomHandler.getOuterHeight(this.$el.previousElementSibling);
this.rowGroupHeaderStyleObject.top = tableHeaderHeight + 'px' this.rowGroupHeaderStyleObject.top = tableHeaderHeight + 'px'
},
updateVirtualScrollerPosition() {
const tableHeaderHeight = DomHandler.getOuterHeight(this.$el.previousElementSibling);
this.$el.style.top = (this.$el.style.top || 0) + tableHeaderHeight + 'px';
},
getVirtualScrollerProp(option, options) {
options = options || this.virtualScrollerContentProps;
return options ? options[option] : null;
},
bodyRef(el) {
// For VirtualScroller
const contentRef = this.getVirtualScrollerProp('contentRef');
contentRef && contentRef(el);
} }
}, },
computed: { computed: {
@ -477,6 +513,9 @@ export default {
} }
return null; return null;
},
bodyStyle() {
return this.getVirtualScrollerProp('contentStyle');
} }
}, },
components: { components: {