(core) Allow filtering hidden columns

Summary:
Existing filters are now moved out of fields
and into a new metadata table for filters, and the
client is updated to retrieve/update/save filters from
the new table. This enables storing of filters for
columns that don't have fields (notably, hidden columns).

Test Plan: Browser and server tests.

Reviewers: jarek

Reviewed By: jarek

Differential Revision: https://phab.getgrist.com/D3138
This commit is contained in:
George Gevoian
2021-11-19 12:30:11 -08:00
parent 0d460ac2d4
commit 7fe4423a6f
20 changed files with 431 additions and 165 deletions

View File

@@ -78,7 +78,7 @@ function BaseView(gristDoc, viewSectionModel, options) {
// Create a section filter and a filtered row source that subscribes to its changes.
// `sectionFilter` also provides an `addTemporaryRow()` to allow views to display newly inserted rows,
// and `setFilterOverride()` to allow controlling a filter from a column menu.
this._sectionFilter = SectionFilter.create(this, this.viewSection.viewFields, this.tableModel.tableData);
this._sectionFilter = SectionFilter.create(this, this.viewSection, this.tableModel.tableData);
this._filteredRowSource = rowset.FilteredRowSource.create(this, this._sectionFilter.sectionFilterFunc.get());
this._filteredRowSource.subscribeTo(this._mainRowSource);
this.autoDispose(this._sectionFilter.sectionFilterFunc.addListener(filterFunc => {
@@ -669,10 +669,11 @@ BaseView.prototype.getLastDataRowIndex = function() {
};
/**
* Creates and opens ColumnFilterMenu for a given field, and returns its PopupControl.
* Creates and opens ColumnFilterMenu for a given field/column, and returns its PopupControl.
*/
BaseView.prototype.createFilterMenu = function(openCtl, field, onClose) {
return createFilterMenu(openCtl, this._sectionFilter, field, this._mainRowSource, this.tableModel.tableData, onClose);
BaseView.prototype.createFilterMenu = function(openCtl, filterInfo, onClose) {
return createFilterMenu(openCtl, this._sectionFilter, filterInfo, this._mainRowSource,
this.tableModel.tableData, onClose);
};
/**

View File

@@ -1386,7 +1386,9 @@ GridView.prototype._getColumnMenuOptions = function(copySelection) {
GridView.prototype._columnFilterMenu = function(ctl, field) {
this.ctxMenuHolder.autoDispose(ctl);
return this.createFilterMenu(ctl, field);
const filterInfo = this.viewSection.filters()
.find(({fieldOrColumn}) => fieldOrColumn.origCol().origColRef() === field.column().origColRef());
return this.createFilterMenu(ctl, filterInfo);
};
GridView.prototype.maybeSelectColumn = function (elem, field) {

View File

@@ -650,9 +650,9 @@ export class GristDoc extends DisposableWithEvents {
}
public getCsvLink() {
const filters = this.viewModel.activeSection.peek().filteredFields.get().map(field=> ({
colRef : field.colRef.peek(),
filter : field.activeFilter.peek()
const filters = this.viewModel.activeSection.peek().activeFilters.get().map(filterInfo => ({
colRef : filterInfo.fieldOrColumn.origCol().origColRef(),
filter : filterInfo.filter()
}));
const params = {

View File

@@ -576,29 +576,34 @@ ViewConfigTab.prototype._buildFilterDom = function() {
}
return [
grainjsDom.forEach(section.filteredFields, (field) => {
grainjsDom.forEach(section.activeFilters, (filterInfo) => {
return cssRow(
cssIconWrapper(
cssFilterIcon('FilterSimple', cssNoMarginLeft.cls('')),
attachColumnFilterMenu(section, field, {
attachColumnFilterMenu(section, filterInfo, {
placement: 'bottom-end', attach: 'body',
trigger: ['click', (_el, popupControl) => popupControls.set(field, popupControl)],
trigger: [
'click',
(_el, popupControl) => popupControls.set(filterInfo.fieldOrColumn.origCol(), popupControl)
],
}),
),
cssLabel(grainjsDom.text(field.label)),
cssLabel(grainjsDom.text(filterInfo.fieldOrColumn.label)),
cssIconWrapper(
cssFilterIcon('Remove', dom.on('click', () => field.activeFilter('')),
testId('remove-filter')),
cssFilterIcon('Remove',
dom.on('click', () => section.setFilter(filterInfo.fieldOrColumn.origCol().origColRef(), '')),
testId('remove-filter')
),
),
testId('filter'),
);
}),
cssRow(
grainjsDom.domComputed((use) => {
const fields = use(use(section.viewFields).getObservable());
const filters = use(section.filters);
return cssTextBtn(
cssPlusIcon('Plus'), 'Add Filter',
addFilterMenu(fields, popupControls, {placement: 'bottom-end'}),
addFilterMenu(filters, section, popupControls, {placement: 'bottom-end'}),
testId('add-filter-btn'),
);
}),