diff --git a/app/client/ui/ColumnFilterMenu.ts b/app/client/ui/ColumnFilterMenu.ts index eb5aea6f..41ef7bba 100644 --- a/app/client/ui/ColumnFilterMenu.ts +++ b/app/client/ui/ColumnFilterMenu.ts @@ -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 = 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(); } diff --git a/app/client/ui/FilterBar.ts b/app/client/ui/FilterBar.ts index 2737a5cf..9e56dc83 100644 --- a/app/client/ui/FilterBar.ts +++ b/app/client/ui/FilterBar.ts @@ -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; +`);