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, type: Array,
default: [], 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 () { data: function () {
return { return {
@@ -942,6 +949,10 @@ export const CoreFilterCmpt = {
return res; return res;
}, {}); }, {});
}, },
isColumnInPresetScope(columnField) {
if (!this.$props.presetColumns.length) return true;
return this.$props.presetColumns.includes(columnField);
},
afterTablePresetApplied(preset) { afterTablePresetApplied(preset) {
this.selectedFields = [...preset.displayedColumns]; this.selectedFields = [...preset.displayedColumns];
this.appliedPreset = { ...preset }; this.appliedPreset = { ...preset };
@@ -973,6 +984,7 @@ export const CoreFilterCmpt = {
const displayedColumns = this.tabulator.columnManager.columns const displayedColumns = this.tabulator.columnManager.columns
.filter((column) => column.field !== "collapse") .filter((column) => column.field !== "collapse")
.filter((column) => this.isColumnInPresetScope(column.field))
.filter((column) => column.visible) .filter((column) => column.visible)
.map((column) => column.field); .map((column) => column.field);
@@ -1029,12 +1041,13 @@ export const CoreFilterCmpt = {
}); });
const activeSort = this.tabulator.getSorters()[0]; const activeSort = this.tabulator.getSorters()[0];
const sort = activeSort const sort =
? { activeSort && this.isColumnInPresetScope(activeSort.field)
column: activeSort.field, ? {
direction: activeSort.dir, column: activeSort.field,
} direction: activeSort.dir,
: null; }
: null;
if ( if (
sort?.column !== this.appliedPreset.sort?.column || sort?.column !== this.appliedPreset.sort?.column ||
sort?.direction !== this.appliedPreset.sort?.direction sort?.direction !== this.appliedPreset.sort?.direction
@@ -1148,6 +1161,7 @@ export const CoreFilterCmpt = {
:presetsId="$props.presetsId" :presetsId="$props.presetsId"
:tabulator="tabulator" :tabulator="tabulator"
:generalPresets="$props.generalPresets" :generalPresets="$props.generalPresets"
:presetColumns="$props.presetColumns"
ref="tablePresets" ref="tablePresets"
class="card-body collapse" class="card-body collapse"
></table-presets> ></table-presets>
@@ -26,6 +26,9 @@ export default {
presetsId: { type: String }, presetsId: { type: String },
tabulator: { type: Object }, tabulator: { type: Object },
generalPresets: { type: Array, default: [] }, 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"], emits: ["tablePresetApplied"],
data: function () { data: function () {
@@ -162,6 +165,10 @@ export default {
}, },
}, },
methods: { methods: {
isInPresetScope(columnField) {
if (!this.$props.presetColumns.length) return true;
return this.$props.presetColumns.includes(columnField);
},
getPresetIndex() { getPresetIndex() {
return this.presetIndexBeingApplied; return this.presetIndexBeingApplied;
}, },
@@ -227,9 +234,9 @@ export default {
} }
}, },
generateNewPreset() { generateNewPreset() {
const columns = this.tabulator.columnManager.columns.filter( const columns = this.tabulator.columnManager.columns
(column) => column.field !== "collapse", .filter((column) => column.field !== "collapse")
); .filter((column) => this.isInPresetScope(column.field));
const displayedColumns = columns const displayedColumns = columns
.filter((column) => column.visible) .filter((column) => column.visible)
@@ -251,12 +258,13 @@ export default {
); );
const activeSort = this.tabulator.getSorters()[0]; const activeSort = this.tabulator.getSorters()[0];
const sort = activeSort const sort =
? { activeSort && this.isInPresetScope(activeSort.field)
column: activeSort.field, ? {
direction: activeSort.dir, column: activeSort.field,
} direction: activeSort.dir,
: null; }
: null;
this.newPreset = { this.newPreset = {
displayedColumns, displayedColumns,
@@ -325,8 +333,11 @@ export default {
preset.displayedColumns.length; preset.displayedColumns.length;
preset.displayedColumns = preset.displayedColumns.filter( preset.displayedColumns = preset.displayedColumns.filter(
(columnField) => { (columnField) => {
return validColumns.some( return (
(column) => column.field === columnField, this.isInPresetScope(columnField) &&
validColumns.some(
(column) => column.field === columnField,
)
); );
}, },
); );
@@ -365,10 +376,11 @@ export default {
if (preset.sort) { if (preset.sort) {
if ( if (
!this.isInPresetScope(preset.sort.column) ||
!validColumns.some( !validColumns.some(
(column) => (column) =>
column.field === preset.sort.column && column.field === preset.sort.column &&
column.headerSort, column.headerSort !== false,
) )
) { ) {
preset.sort = null; preset.sort = null;
@@ -407,7 +419,10 @@ export default {
window.localStorage["tabulatorPreset-" + this.$props.presetsId] = window.localStorage["tabulatorPreset-" + this.$props.presetsId] =
preset.id ? "custom:" + preset.id : "general:" + preset.name; 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 }); this.$emit("tablePresetApplied", { preset });
@@ -8,27 +8,36 @@ export default class TablePresetsModule extends Module {
super(table); super(table);
} }
applyPreset(preset) { // presetColumns limits the preset to the given column fields. A table with dynamic
let columnsConfig = this.table.options.columns; // columns gives the fields of its constant columns here.
let reorderedColumnsConfig = []; applyPreset(preset, presetColumns = []) {
preset.displayedColumns.forEach((columnField) => { if (presetColumns.length) {
let columnConfig = columnsConfig.find( this.applyColumnsInScope(preset, presetColumns);
(column) => column.field === columnField, } else {
); let columnsConfig = this.table.options.columns;
if (!columnConfig) return; let reorderedColumnsConfig = [];
columnConfig.visible = true; preset.displayedColumns.forEach((columnField) => {
reorderedColumnsConfig.push(columnConfig); let columnConfig = columnsConfig.find(
}); (column) => column.field === columnField,
);
let hiddenColumnsConfig = columnsConfig if (!columnConfig) return;
.filter((column) => !preset.displayedColumns.includes(column.field)) columnConfig.visible = true;
.map((column) => { reorderedColumnsConfig.push(columnConfig);
column.visible = false;
return column;
}); });
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(); this.table.clearHeaderFilter();
if (preset.headerFilters) { 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);
}
} }