visua-vue/src/components/table/VDataTable.vue
2025-07-29 06:16:44 +02:00

426 lines
12 KiB
Vue

<script setup lang="ts">
import DataTable from 'primevue/datatable';
import type { DataTableProps, DataTableSlots } from 'primevue/datatable';
import { useId, ref, watch, computed } from 'vue';
export interface IVDataTable extends Partial<Omit<DataTableProps, 'pt' | 'dt' | 'ptOptions' | 'unstyled'>>{
id?: string
title?: string
}
const props = withDefaults(defineProps<IVDataTable>(), {
id: () => useId(),
title: '',
selection: undefined,
paginator: false,
rowsPerPageOptions: undefined,
rows: 0,
value: undefined,
dataKey: undefined,
showGridlines: false,
stripedRows: false,
first: 0,
totalRecords: 0,
pageLinkSize: 5,
paginatorPosition: 'bottom',
paginatorTemplate: undefined,
alwaysShowPaginator:true,
currentPageReportTemplate: '({currentPage} de {totalPages})',
lazy: false,
loading: false,
sortField: undefined,
sortOrder: undefined,
nullSortOrder: 1,
defaultSortOrder: 1,
sortMode: 'single',
removableSort: false,
filters: undefined,
filterDisplay: undefined,
globalFilterFields: undefined,
filterLocale: undefined,
selectionMode: undefined,
compareSelectionBy: 'deepEquals',
metaKeySelection: false,
contextMenu: false,
contextMenuSelection: undefined,
selectAll: undefined,
rowHover: false,
csvSeparator: ',',
exportFilename: 'download',
exportFunction: undefined,
resizableColumns: false,
columnResizeMode: 'fit',
reorderableColumns: false,
expandedRows: undefined,
expandedRowIcon: undefined,
collapsedRowIcon: undefined,
rowGroupMode: undefined,
groupRowsBy: undefined,
expandableRowGroups: false,
expandedRowGroups: undefined,
stateStorage: 'session',
stateKey: undefined,
editMode: undefined,
editingRows: undefined,
rowClass: undefined,
rowStyle: undefined,
scrollable: false,
scrollHeight: undefined,
virtualScrollerOptions: undefined,
frozenValue: undefined,
breakpoint: '960px',
showHeaders: true,
highlightOnSelect: false,
size: undefined,
tableClass: undefined,
tableProps: undefined,
tableStyle: undefined,
filterButtonProps: undefined,
editButtonProps: undefined,
multiSortMeta: undefined,
})
type VDataTableSlots = DataTableSlots & {
default?: (props: Record<string, unknown>) => unknown
};
const slots = defineSlots<VDataTableSlots>();
const dataTableSlotKeys = [
'header',
'footer',
'empty',
'groupheader',
'groupfooter',
'loading',
'expansion',
'loadingicon',
'rowreorderindicatorupicon',
'rowreorderindicatordownicon',
'rowgrouptogglericon',
'paginatorcontainer',
'paginatorstart',
'paginatorend',
'paginatorfirstpagelinkicon',
'paginatorprevpagelinkicon',
'paginatornextpagelinkicon',
'paginatorlastpagelinkicon',
'paginatorrowsperpagedropdownicon',
'paginatorjumptopagedropdownicon'
] as const;
const availableSlots = computed(() =>
dataTableSlotKeys.filter((key) => !!slots[key]).map((key) => [key, slots[key]])
);
const dataTableRef = ref();
defineExpose({
exportCSV: () => dataTableRef.value.exportCSV()
});
const emit = defineEmits([
'update:selection',
'update:rows',
'update:sortField',
'update:sortOrder',
'update:multiSortMeta',
'update:contextMenuSelection',
'update:expandedRows',
'update:expandedRowGroups',
'update:filters',
'update:editingRows',
'update:first',
'page',
'sort',
'row-select',
'row-unselect',
'filter',
'value-change',
'row-click',
'row-dbclick',
'row-contextmenu',
'row-select-all',
'row-unselect-all',
'select-all-change',
'column-resize-end',
'column-reorder',
'row-reorder',
'row-expand',
'row-collapse',
'rowgroup-expand',
'rowgroup-collapse',
'cell-edit-init',
'cell-edit-complete',
'cell-edit-cancel',
'row-edit-init',
'row-edit-save',
'row-edit-cancel',
'state-restore',
'state-save',
]);
const localSelection = ref(props.selection);
watch(() => props.selection, (newVal) => {
if(localSelection.value !== newVal) {
localSelection.value = newVal;
}
});
watch(localSelection, (newVal) => {
if(props.selection !== newVal){
emit('update:selection', newVal);
}
});
const localRows = ref(props.rows);
watch(() => props.rows, (newVal) => {
if(localRows.value !== newVal){
localRows.value = newVal;
}
})
watch(localRows, (newVal) => {
if(props.rows !== newVal){
emit('update:rows', newVal);
}
});
const localFirst = ref(props.first);
watch(() => props.first, (newVal) => {
if(localFirst.value !== newVal){
localFirst.value = newVal;
}
})
watch(localFirst, (newVal) => {
if(props.first !== newVal){
emit('update:first', newVal);
}
});
const localSortField = ref(props.sortField);
watch(() => props.sortField, (newVal) => {
if(localSortField.value !== newVal){
localSortField.value = newVal;
}
})
watch(localSortField, (newVal) => {
if(props.sortField !== newVal){
emit('update:sortField', newVal);
}
});
const localSortOrder = ref(props.sortOrder);
watch(() => props.sortOrder, (newVal) => {
if(localSortOrder.value !== newVal){
localSortOrder.value = newVal;
}
})
watch(localSortOrder, (newVal) => {
if(props.sortOrder !== newVal){
emit('update:sortOrder', newVal);
}
});
const localMultiSortMeta = ref(props.multiSortMeta);
watch(() => props.multiSortMeta, (newVal) => {
if(localMultiSortMeta.value !== newVal){
localMultiSortMeta.value = newVal;
}
})
watch(localMultiSortMeta, (newVal) => {
if(props.multiSortMeta !== newVal){
emit('update:multiSortMeta', newVal);
}
});
const localContextMenuSelection = ref(props.contextMenuSelection);
watch(() => props.contextMenuSelection, (newVal) => {
if(localContextMenuSelection.value !== newVal){
localContextMenuSelection.value = newVal;
}
})
watch(localContextMenuSelection, (newVal) => {
if(props.contextMenuSelection){
emit('update:contextMenuSelection', newVal);
}
});
const localExpandedRows = ref(props.expandedRows);
watch(() => props.expandedRows, (newVal) => {
if(localExpandedRows.value !== newVal){
localExpandedRows.value = newVal;
}
})
watch(localExpandedRows, (newVal) => {
if(props.expandedRows !== newVal){
emit('update:expandedRows', newVal);
}
});
const localExpandedRowGroups = ref(props.expandedRowGroups);
watch(() => props.expandedRowGroups, (newVal) => {
if(localExpandedRowGroups.value !== newVal){
localExpandedRowGroups.value = newVal;
}
})
watch(localExpandedRowGroups, (newVal) => {
if(props.expandedRowGroups !== newVal){
emit('update:expandedRowGroups', newVal);
}
});
const localFilters = ref(props.filters);
watch(() => props.filters, (newVal) => {
if(localFilters.value !== newVal){
localFilters.value = newVal;
}
})
watch(localFilters, (newVal) => {
if(props.filters !== newVal){
emit('update:filters', newVal);
}
});
const localEditingRows = ref(props.editingRows);
watch(() => props.editingRows, (newVal) => {
if(localEditingRows.value = newVal){
localEditingRows.value = newVal;
}
})
watch(localEditingRows, (newVal) => {
if(props.editingRows !== newVal){
emit('update:editingRows', newVal);
}
});
</script>
<template>
<DataTable
:id="`datatable-${props.id}`"
role="table"
ref="dataTableRef"
:value="props.value"
:paginator="props.paginator"
:rowsPerPageOptions="props.rowsPerPageOptions"
:data-key="props.dataKey"
:show-gridlines="props.showGridlines"
:striped-rows="props.stripedRows"
:total-records="props.totalRecords"
:page-link-size="props.pageLinkSize"
:paginator-position="props.paginatorPosition"
:paginator-template="props.paginatorTemplate"
:always-show-paginator="props.alwaysShowPaginator"
:current-page-report-template="props.currentPageReportTemplate"
:lazy="props.lazy"
:loading="props.loading"
:nullSortOrder="props.nullSortOrder"
:defaultSortOrder="props.defaultSortOrder"
:sortMode="props.sortMode"
:removableSort="props.removableSort"
:filterDisplay="props.filterDisplay"
:globalFilterFields="props.globalFilterFields"
:filterLocale="props.filterLocale"
:selectionMode="props.selectionMode"
:compareSelectionBy="props.compareSelectionBy"
:metaKeySelection="props.metaKeySelection"
:contextMenu="props.contextMenu"
:selectAll="props.selectAll"
:rowHover="props.rowHover"
:csvSeparator="props.csvSeparator"
:exportFilename="props.exportFilename"
:exportFunction="props.exportFunction"
:resizableColumns="props.resizableColumns"
:columnResizeMode="props.columnResizeMode"
:reorderableColumns="props.reorderableColumns"
:expandedRowIcon="props.expandedRowIcon"
:collapsedRowIcon="props.collapsedRowIcon"
:rowGroupMode="props.rowGroupMode"
:groupRowsBy="props.groupRowsBy"
:expandableRowGroups="props.expandableRowGroups"
:stateStorage="props.stateStorage"
:stateKey="props.stateKey"
:editMode="props.editMode"
:rowClass="props.rowClass"
:rowStyle="props.rowStyle"
:scrollable="props.scrollable"
:scrollHeight="props.scrollHeight"
:virtualScrollerOptions="props.virtualScrollerOptions"
:frozenValue="props.frozenValue"
:breakpoint="props.breakpoint"
:showHeaders="props.showHeaders"
:highlightOnSelect="props.highlightOnSelect"
:size="props.size"
:tableClass="props.tableClass"
:tableProps="props.tableProps"
:tableStyle="props.tableStyle"
:filterButtonProps="props.filterButtonProps"
:editButtonProps="props.editButtonProps"
style="width: 100%; height: fit-content;"
v-model:selection="localSelection"
v-model:rows="localRows"
v-model:first="localFirst"
v-model:sortField="localSortField"
v-model:sortOrder="localSortOrder"
v-model:filters="localFilters"
v-model:contextMenuSelection="localContextMenuSelection"
v-model:expandedRows="localExpandedRows"
v-model:expandedRowGroups="localExpandedRowGroups"
v-model:editingRows="localEditingRows"
v-model:multi-sort-meta="localMultiSortMeta"
@update:selection="emit('update:selection', $event)"
@update:context-menu-selection="emit('update:contextMenuSelection', $event)"
@update:editing-rows="emit('update:editingRows', $event)"
@update:expanded-row-groups="emit('update:expandedRowGroups', $event)"
@update:filters="emit('update:filters', $event)"
@update:first="emit('update:first', $event)"
@update:multi-sort-meta="emit('update:multiSortMeta', $event)"
@update:rows="emit('update:rows', $event)"
@update:sort-field="emit('update:sortField', $event)"
@update:sort-order="emit('update:sortOrder', $event)"
@page="emit('page', $event)"
@sort="emit('sort', $event)"
@rowSelect="emit('row-select', $event)"
@rowUnselect="emit('row-unselect', $event)"
@filter="emit('filter', $event)"
@valueChange="emit('value-change', $event)"
@rowClick="emit('row-click', $event)"
@rowCollapse="emit('row-collapse', $event)"
@rowContextmenu="emit('row-contextmenu', $event)"
@rowDblclick="emit('row-dbclick', $event)"
@rowEditCancel="emit('row-edit-cancel', $event)"
@rowEditInit="emit('row-edit-init', $event)"
@rowEditSave="emit('row-edit-save', $event)"
@rowExpand="emit('row-expand', $event)"
@rowReorder="emit('row-reorder', $event)"
@rowSelectAll="emit('row-select-all', $event)"
@rowUnselectAll="emit('row-unselect-all', $event)"
@rowgroupCollapse="emit('rowgroup-collapse', $event)"
@rowgroupExpand="emit('rowgroup-expand', $event)"
@columnReorder="emit('column-reorder', $event)"
@columnResizeEnd="emit('column-resize-end', $event)"
@cellEditCancel="emit('cell-edit-cancel', $event)"
@cellEditComplete="emit('cell-edit-complete', $event)"
@cellEditInit="emit('cell-edit-init', $event)"
@stateRestore="emit('state-restore', $event)"
@stateSave="emit('state-save', $event)"
@selectAllChange="emit('select-all-change', $event)"
class="p-datatable"
>
<template
v-for="([name]) in availableSlots"
:key="name"
v-slot:[name]="slotProps"
>
<slot :name="name" v-bind="slotProps" />
</template>
<slot></slot>
</DataTable>
</template>
<style lang="css" scoped>
.p-datatable{
box-sizing: border-box;
}
</style>