adapt filter/tablePresets/tablePresetsModule to account for dynamic tables with a new presetColumns property. by default the preset holds all columns, if a scope is given it only holds those columns, their header filters and their sort; on apply the scope is merged into the current table columns so the dynamic ones keep their place;

This commit is contained in:
Johann Hoffmann
2026-08-26 16:45:06 +02:00
parent 6d9ce91302
commit 943999cef9
3 changed files with 117 additions and 39 deletions
+20 -6
View File
@@ -106,6 +106,13 @@ export const CoreFilterCmpt = {
type: Array,
default: [],
},
// fields of the columns a preset is allowed to handle. Give the constant
// columns here if the table builds dynamic columns at runtime. An empty
// array gives the preset every column of the table.
presetColumns: {
type: Array,
default: [],
},
},
data: function () {
return {
@@ -942,6 +949,10 @@ export const CoreFilterCmpt = {
return res;
}, {});
},
isColumnInPresetScope(columnField) {
if (!this.$props.presetColumns.length) return true;
return this.$props.presetColumns.includes(columnField);
},
afterTablePresetApplied(preset) {
this.selectedFields = [...preset.displayedColumns];
this.appliedPreset = { ...preset };
@@ -973,6 +984,7 @@ export const CoreFilterCmpt = {
const displayedColumns = this.tabulator.columnManager.columns
.filter((column) => column.field !== "collapse")
.filter((column) => this.isColumnInPresetScope(column.field))
.filter((column) => column.visible)
.map((column) => column.field);
@@ -1029,12 +1041,13 @@ export const CoreFilterCmpt = {
});
const activeSort = this.tabulator.getSorters()[0];
const sort = activeSort
? {
column: activeSort.field,
direction: activeSort.dir,
}
: null;
const sort =
activeSort && this.isColumnInPresetScope(activeSort.field)
? {
column: activeSort.field,
direction: activeSort.dir,
}
: null;
if (
sort?.column !== this.appliedPreset.sort?.column ||
sort?.direction !== this.appliedPreset.sort?.direction
@@ -1148,6 +1161,7 @@ export const CoreFilterCmpt = {
:presetsId="$props.presetsId"
:tabulator="tabulator"
:generalPresets="$props.generalPresets"
:presetColumns="$props.presetColumns"
ref="tablePresets"
class="card-body collapse"
></table-presets>
@@ -26,6 +26,9 @@ export default {
presetsId: { type: String },
tabulator: { type: Object },
generalPresets: { type: Array, default: [] },
// fields of the columns the preset is allowed to handle. An empty array
// gives the preset every column of the table.
presetColumns: { type: Array, default: [] },
},
emits: ["tablePresetApplied"],
data: function () {
@@ -162,6 +165,10 @@ export default {
},
},
methods: {
isInPresetScope(columnField) {
if (!this.$props.presetColumns.length) return true;
return this.$props.presetColumns.includes(columnField);
},
getPresetIndex() {
return this.presetIndexBeingApplied;
},
@@ -227,9 +234,9 @@ export default {
}
},
generateNewPreset() {
const columns = this.tabulator.columnManager.columns.filter(
(column) => column.field !== "collapse",
);
const columns = this.tabulator.columnManager.columns
.filter((column) => column.field !== "collapse")
.filter((column) => this.isInPresetScope(column.field));
const displayedColumns = columns
.filter((column) => column.visible)
@@ -251,12 +258,13 @@ export default {
);
const activeSort = this.tabulator.getSorters()[0];
const sort = activeSort
? {
column: activeSort.field,
direction: activeSort.dir,
}
: null;
const sort =
activeSort && this.isInPresetScope(activeSort.field)
? {
column: activeSort.field,
direction: activeSort.dir,
}
: null;
this.newPreset = {
displayedColumns,
@@ -325,8 +333,11 @@ export default {
preset.displayedColumns.length;
preset.displayedColumns = preset.displayedColumns.filter(
(columnField) => {
return validColumns.some(
(column) => column.field === columnField,
return (
this.isInPresetScope(columnField) &&
validColumns.some(
(column) => column.field === columnField,
)
);
},
);
@@ -365,10 +376,11 @@ export default {
if (preset.sort) {
if (
!this.isInPresetScope(preset.sort.column) ||
!validColumns.some(
(column) =>
column.field === preset.sort.column &&
column.headerSort,
column.headerSort !== false,
)
) {
preset.sort = null;
@@ -407,7 +419,10 @@ export default {
window.localStorage["tabulatorPreset-" + this.$props.presetsId] =
preset.id ? "custom:" + preset.id : "general:" + preset.name;
this.$props.tabulator.modules.tablePresets.applyPreset(preset);
this.$props.tabulator.modules.tablePresets.applyPreset(
preset,
this.$props.presetColumns,
);
this.$emit("tablePresetApplied", { preset });
@@ -8,27 +8,36 @@ export default class TablePresetsModule extends Module {
super(table);
}
applyPreset(preset) {
let columnsConfig = this.table.options.columns;
let reorderedColumnsConfig = [];
preset.displayedColumns.forEach((columnField) => {
let columnConfig = columnsConfig.find(
(column) => column.field === columnField,
);
if (!columnConfig) return;
columnConfig.visible = true;
reorderedColumnsConfig.push(columnConfig);
});
let hiddenColumnsConfig = columnsConfig
.filter((column) => !preset.displayedColumns.includes(column.field))
.map((column) => {
column.visible = false;
return column;
// presetColumns limits the preset to the given column fields. A table with dynamic
// columns gives the fields of its constant columns here.
applyPreset(preset, presetColumns = []) {
if (presetColumns.length) {
this.applyColumnsInScope(preset, presetColumns);
} else {
let columnsConfig = this.table.options.columns;
let reorderedColumnsConfig = [];
preset.displayedColumns.forEach((columnField) => {
let columnConfig = columnsConfig.find(
(column) => column.field === columnField,
);
if (!columnConfig) return;
columnConfig.visible = true;
reorderedColumnsConfig.push(columnConfig);
});
this.table.setColumns(
reorderedColumnsConfig.concat(hiddenColumnsConfig),
);
let hiddenColumnsConfig = columnsConfig
.filter(
(column) =>
!preset.displayedColumns.includes(column.field),
)
.map((column) => {
column.visible = false;
return column;
});
this.table.setColumns(
reorderedColumnsConfig.concat(hiddenColumnsConfig),
);
}
this.table.clearHeaderFilter();
if (preset.headerFilters) {
@@ -61,4 +70,44 @@ export default class TablePresetsModule extends Module {
);
}
}
// The preset orders and hides the columns of the scope only. A column outside the
// scope keeps its position, because the table does not show it in every state.
applyColumnsInScope(preset, presetColumns) {
const displayedColumns = preset.displayedColumns ?? [];
// a displayed column takes the rank of the preset. A column that the preset
// hides has no rank and keeps the current order at the end of its block.
const rank = (columnConfig) => {
const index = displayedColumns.indexOf(columnConfig.field);
return index === -1 ? displayedColumns.length : index;
};
let columnsConfig = [];
let scopeBlock = [];
// the sort runs for each block of neighbouring scope columns. This way a scope
// column never moves over a column outside the scope.
const addScopeBlock = () => {
scopeBlock.sort((a, b) => rank(a) - rank(b));
columnsConfig = columnsConfig.concat(scopeBlock);
scopeBlock = [];
};
this.table.getColumnDefinitions().forEach((columnConfig) => {
if (!presetColumns.includes(columnConfig.field)) {
addScopeBlock();
columnsConfig.push(columnConfig);
return;
}
columnConfig.visible = displayedColumns.includes(
columnConfig.field,
);
scopeBlock.push(columnConfig);
});
addScopeBlock();
this.table.setColumns(columnsConfig);
}
}