Skip to content

Windows Excel like behavior for grid column filtering #3894

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
May 13, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f4063c8
ready for review
oodarluz Jan 9, 2025
82e2ff9
fixes pre PR
oodarluz Jan 9, 2025
f1d2990
trimming code
oodarluz Jan 9, 2025
4c916e9
done
oodarluz Jan 9, 2025
18d9f6e
done
oodarluz Jan 9, 2025
1432952
post CR with colin state change
oodarluz Jan 9, 2025
16d74c3
comment layout correction
oodarluz Jan 10, 2025
06796cc
post review changes set commit on change to false add change log
oodarluz Jan 17, 2025
be269c3
Merge confilct in change log
oodarluz Jan 17, 2025
f3b08e6
merge conflict and catchup
oodarluz Jan 24, 2025
8a3d862
Merge branch 'develop' into grid-column-filter
amcclain Feb 11, 2025
ad0bad9
Merge branch 'refs/heads/develop' into grid-column-filter
cnrudd May 5, 2025
3ef05a7
Merge branch 'refs/heads/develop' into grid-column-filter
cnrudd May 6, 2025
03371a9
Merge branch 'refs/heads/develop' into grid-column-filter
cnrudd May 6, 2025
8d173f4
Add missing model prop to storeFilterField
cnrudd May 6, 2025
42286e2
revert prior commit
cnrudd May 6, 2025
f8f1e3c
clarify commment
cnrudd May 6, 2025
ea25832
Bugfix: ensure only the value field is filtered. Don't want 'al' to …
cnrudd May 7, 2025
3dde7f6
More rigorously follow Excel on Windows behaviour when filterModel.co…
cnrudd May 7, 2025
fadc84e
Hide combineCurrentFilters checkbox if there are no current filters
cnrudd May 7, 2025
63bf4c0
Merge branch 'refs/heads/develop' into grid-column-filter
cnrudd May 12, 2025
7c24203
update changelog entry to add grid column header UX changes.
cnrudd May 12, 2025
13cbdb2
Merge branch 'refs/heads/develop' into grid-column-filter
cnrudd May 12, 2025
9d6b014
Grid column filter values tab: Make select and add checkbox labels cl…
cnrudd May 12, 2025
d2bef43
Grid column filter values tab: Space Bar toggles checkbox in grid row.
cnrudd May 13, 2025
5a44a4d
Merge branch 'refs/heads/develop' into grid-column-filter
cnrudd May 13, 2025
b72436e
CR with Lee.
cnrudd May 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions desktop/cmp/grid/impl/filter/headerfilter/values/ValuesTab.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
.xh-values-filter-tab {
.store-filter-header {
padding: 5px 7px;
border-bottom: 1px solid var(--xh-grid-header-border-color);
row-gap: 5px;
.bp5-control-indicator {
font-size: 1em;
}
span {
font-size: var(--xh-grid-compact-header-font-size-px);
color: var(--xh-grid-header-text-color);
}
}

&__hidden-values-message {
display: flex;
padding: var(--xh-pad-half-px);
Expand Down
31 changes: 29 additions & 2 deletions desktop/cmp/grid/impl/filter/headerfilter/values/ValuesTab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
* Copyright © 2025 Extremely Heavy Industries Inc.
*/
import {grid} from '@xh/hoist/cmp/grid';
import {div, placeholder, vframe} from '@xh/hoist/cmp/layout';
import {div, hframe, placeholder, span, vbox, vframe} from '@xh/hoist/cmp/layout';
import {storeFilterField} from '@xh/hoist/cmp/store';
import {hoistCmp, uses} from '@xh/hoist/core';
import {button} from '@xh/hoist/desktop/cmp/button';
import {checkbox} from '@xh/hoist/desktop/cmp/input';
import {panel} from '@xh/hoist/desktop/cmp/panel';
import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
import {Icon} from '@xh/hoist/icon';
Expand Down Expand Up @@ -47,7 +48,33 @@ const tbar = hoistCmp.factory(() => {
const body = hoistCmp.factory<ValuesTabModel>(({model}) => {
const {isCustomFilter} = model.headerFilterModel;
if (isCustomFilter) return customFilterPlaceholder();
return vframe(grid(), hiddenValuesMessage());
return vframe(storeFilterSelect(), grid(), hiddenValuesMessage());
});

const storeFilterSelect = hoistCmp.factory<ValuesTabModel>(({model}) => {
const {gridModel, allVisibleRecsChecked, filterText, headerFilterModel} = model,
{store} = gridModel;
return vbox({
className: 'store-filter-header',
items: [
hframe(
checkbox({
disabled: store.empty,
displayUnsetState: true,
value: allVisibleRecsChecked,
onChange: () => model.toggleAllRecsChecked()
}),
span(`(Select All${filterText ? ' Search Results' : ''})`)
),
hframe({
omit: !filterText || store.empty || headerFilterModel.commitOnChange,
items: [
checkbox({bind: 'combineCurrentFilters'}),
span(`Add current selection to filter`)
]
})
]
});
});

const customFilterPlaceholder = hoistCmp.factory<ValuesTabModel>(({model}) => {
Expand Down
50 changes: 35 additions & 15 deletions desktop/cmp/grid/impl/filter/headerfilter/values/ValuesTabModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {FieldFilterSpec} from '@xh/hoist/data';
import {HeaderFilterModel} from '../HeaderFilterModel';
import {checkbox} from '@xh/hoist/desktop/cmp/input';
import {action, bindable, computed, makeObservable, observable} from '@xh/hoist/mobx';
import {castArray, difference, isEmpty, partition, uniq, without} from 'lodash';
import {castArray, difference, flatten, isEmpty, map, partition, uniq, without} from 'lodash';

export class ValuesTabModel extends HoistModel {
override xhImpl = true;
Expand All @@ -26,6 +26,10 @@ export class ValuesTabModel extends HoistModel {
/** Bound search term for `StoreFilterField` */
@bindable filterText: string = null;

/** Available only when commit on change is false merge
* current filter with pendingValues on commit*/
@bindable combineCurrentFilters: boolean = false;

/** FieldFilter output by this model. */
@computed.struct
get filter(): FieldFilterSpec {
Expand Down Expand Up @@ -81,11 +85,18 @@ export class ValuesTabModel extends HoistModel {
this.headerFilterModel = headerFilterModel;
this.gridModel = this.createGridModel();

this.addReaction({
track: () => this.pendingValues,
run: () => this.syncGrid(),
fireImmediately: true
});
this.addReaction(
{
track: () => this.pendingValues,
run: () => this.syncGrid(),
fireImmediately: true
},
{
track: () => [this.filterText, this.combineCurrentFilters],
run: () => this.setPendingValues(),
debounce: 300
}
);
}

syncWithFilter() {
Expand Down Expand Up @@ -115,6 +126,23 @@ export class ValuesTabModel extends HoistModel {
//-------------------
// Implementation
//-------------------
@action
setPendingValues() {
if (!this.filterText) {
this.doSyncWithFilter();
this.syncGrid();
return;
}

const {records} = this.gridModel.store,
currentFilterValues = flatten(map(this.columnFilters, 'value')),
values = map(records, it => it.get('value'));

this.pendingValues = uniq(
this.combineCurrentFilters ? [...currentFilterValues, ...values] : values
);
}

private getFilter() {
const {gridFilterModel, pendingValues, values, valueCount, field} = this,
included = pendingValues.map(it => gridFilterModel.fromDisplayValue(it)),
Expand Down Expand Up @@ -217,17 +245,10 @@ export class ValuesTabModel extends HoistModel {
onRowClicked: ({data: record}) => {
this.setRecsChecked(!record.get('isChecked'), record.get('value'));
},
hideHeaders: true,
columns: [
{
field: 'isChecked',
headerName: ({gridModel}) => {
return checkbox({
disabled: gridModel.store.empty,
displayUnsetState: true,
value: this.allVisibleRecsChecked,
onChange: () => this.toggleAllRecsChecked()
});
},
width: 28,
autosizable: false,
pinned: true,
Expand All @@ -245,7 +266,6 @@ export class ValuesTabModel extends HoistModel {
},
{
field: 'value',
displayName: '(Select All)',
align: 'left',
comparator: (v1, v2, sortDir, abs, {defaultComparator}) => {
const mul = sortDir === 'desc' ? -1 : 1;
Expand Down
Loading