(core) add + button to the filter bar

Summary:
 - Adds a + button to the filter. Button triggers a menu that allow to
add one of the column that does not already have a filter set.

Caveats:
 - for now menu only allows to choose from visible column.
 - This diff introduces a slight change of behavior of how filter works:
     - Filter used to be automatically removed when user set them to all
inclusive (ie: by clicking the `All` button).
     - With this diff, it is no longer the case.
     - indeed, when filter are added to the filter bar with the `+` btn they are initially in the `all inclusive` state, hence would have been removed with the above mention behaviour.

Test Plan: Added new test to nbrowser/FilterBar

Reviewers: paulfitz

Reviewed By: paulfitz

Differential Revision: https://phab.getgrist.com/D2776
This commit is contained in:
Cyprien P 2021-04-20 17:16:03 +02:00
parent 8a26550312
commit 5479159960
2 changed files with 36 additions and 5 deletions

View File

@ -264,6 +264,7 @@ export function createFilterMenu(openCtl: IOpenController, sectionFilter: Sectio
const labelGetter = tableData.getRowPropFunc(field.displayColModel().colId())!;
const formatter = field.createVisibleColFormatter();
const valueMapFunc = (rowId: number) => formatter.formatAny(labelGetter(rowId));
const activeFilterBar = field.viewSection.peek().activeFilterBar;
const valueCounts: Map<CellValue, {label: string, count: number}> = new Map();
// TODO: as of now, this is not working for non text-or-numeric columns, ie: for Date column it is
@ -282,7 +283,8 @@ export function createFilterMenu(openCtl: IOpenController, sectionFilter: Sectio
onClose: () => openCtl.close(),
doSave: (reset: boolean = false) => {
const spec = columnFilter.makeFilterJson();
field.activeFilter(spec === allInclusive ? '' : spec);
// If filter is moot and filter bar is hidden, let's remove the filter.
field.activeFilter((spec === allInclusive && !activeFilterBar.peek()) ? '' : spec);
if (reset) {
sectionFilter.resetTemporaryRows();
}

View File

@ -1,14 +1,17 @@
import { allInclusive } from "app/client/models/ColumnFilter";
import { ViewFieldRec, ViewSectionRec } from "app/client/models/DocModel";
import { attachColumnFilterMenu } from "app/client/ui/ColumnFilterMenu";
import { cssButton, cssButtonGroup } from "app/client/ui2018/buttons";
import { colors, testId } from "app/client/ui2018/cssVars";
import { icon } from "app/client/ui2018/icons";
import { menu, menuItemAsync } from "app/client/ui2018/menus";
import { dom, IDisposableOwner, IDomArgs, styled } from "grainjs";
export function filterBar(_owner: IDisposableOwner, viewSection: ViewSectionRec) {
return cssFilterBar(
testId('filter-bar'),
dom.forEach(viewSection.filteredFields, (field) => makeFilterField(viewSection, field)),
makePlusButton(viewSection),
cssSpacer(),
dom.maybe(viewSection.filterSpecChanged, () => [
primaryButton(
@ -30,18 +33,38 @@ function makeFilterField(viewSection: ViewSectionRec, field: ViewFieldRec) {
testId('btn'),
cssIcon('FilterSimple'),
cssMenuTextLabel(dom.text(field.label)),
cssBtn.cls('-disabled', field.activeFilter.isSaved),
cssBtn.cls('-saved', field.activeFilter.isSaved),
attachColumnFilterMenu(viewSection, field, {placement: 'bottom-start', attach: 'body'}),
),
deleteButton(
testId('delete'),
cssIcon('CrossSmall'),
cssBtn.cls('-disabled', field.activeFilter.isSaved),
cssBtn.cls('-saved', field.activeFilter.isSaved),
dom.on('click', () => field.activeFilter('')),
)
);
}
function makePlusButton(viewSectionRec: ViewSectionRec) {
return dom.domComputed((use) => {
const fields = use(use(viewSectionRec.viewFields).getObservable());
const anyFilter = fields.find((f) => use(f.isFiltered));
return cssPlusButton(
cssBtn.cls('-saved'),
cssIcon('Plus'),
menu(() => fields.map((f) => (
menuItemAsync(
() => f.activeFilter(allInclusive),
f.label.peek(),
dom.cls('disabled', f.isFiltered)
)
))),
anyFilter ? null : cssPlusLabel('Add Filter'),
testId('add-filter-btn')
);
});
}
const cssFilterBar = styled('div', `
display: flex;
flex-direction: row;
@ -75,13 +98,13 @@ const cssBtn = styled('div', `
.${cssFilterBar.className} > & {
margin: 0 4px;
}
&-disabled {
&-saved {
color: ${colors.light};
--icon-color: ${colors.light};
background-color: ${colors.slate};
border-color: ${colors.slate};
}
&-disabled:hover {
&-saved:hover {
background-color: ${colors.darkGrey};
border-color: ${colors.darkGrey};
}
@ -100,3 +123,9 @@ const cssSpacer = styled('div', `
width: 8px;
flex-shrink: 0;
`);
const cssPlusButton = styled(primaryButton, `
padding: 3px 3px
`);
const cssPlusLabel = styled('span', `
margin: 0 12px 0 4px;
`);