@xh/hoist 77.0.0-SNAPSHOT.1760663247565 → 77.0.0-SNAPSHOT.1760707577281
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -0
- package/build/types/desktop/cmp/grid/impl/filter/headerfilter/HeaderFilterModel.d.ts +4 -2
- package/build/types/desktop/cmp/grid/impl/filter/headerfilter/values/ValuesTabModel.d.ts +10 -3
- package/desktop/cmp/grid/impl/filter/headerfilter/HeaderFilter.ts +0 -1
- package/desktop/cmp/grid/impl/filter/headerfilter/HeaderFilterModel.ts +12 -4
- package/desktop/cmp/grid/impl/filter/headerfilter/values/ValuesTab.scss +12 -4
- package/desktop/cmp/grid/impl/filter/headerfilter/values/ValuesTab.ts +8 -2
- package/desktop/cmp/grid/impl/filter/headerfilter/values/ValuesTabModel.ts +42 -10
- package/package.json +1 -1
- package/styles/vars.scss +2 -2
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Added a public `@bindable titleDetails` config to `DashViewModel` to support displaying additional
|
|
8
8
|
information in the title bar of dashboard widgets. The new property is not persisted, allowing
|
|
9
9
|
apps to programmatically show dynamic info in a widget header without perturbing its saved state.
|
|
10
|
+
* Enhanced grid column filtering to support sorting the list of available values.
|
|
10
11
|
|
|
11
12
|
## 76.0.0 - 2025-09-26
|
|
12
13
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { Column, GridFilterFieldSpec, GridFilterModel, GridModel } from '@xh/hoist/cmp/grid';
|
|
1
2
|
import { TabContainerModel } from '@xh/hoist/cmp/tab';
|
|
2
3
|
import { HoistModel } from '@xh/hoist/core';
|
|
3
4
|
import { CompoundFilter, FieldFilter, FieldType, Filter, FilterLike, Store } from '@xh/hoist/data';
|
|
4
|
-
import {
|
|
5
|
+
import { ColumnHeaderFilterModel } from '../ColumnHeaderFilterModel';
|
|
5
6
|
import { CustomTabModel } from './custom/CustomTabModel';
|
|
6
7
|
import { ValuesTabModel } from './values/ValuesTabModel';
|
|
7
|
-
import { ColumnHeaderFilterModel } from '../ColumnHeaderFilterModel';
|
|
8
8
|
export declare class HeaderFilterModel extends HoistModel {
|
|
9
9
|
xhImpl: boolean;
|
|
10
10
|
fieldSpec: GridFilterFieldSpec;
|
|
@@ -14,7 +14,9 @@ export declare class HeaderFilterModel extends HoistModel {
|
|
|
14
14
|
customTabModel: CustomTabModel;
|
|
15
15
|
get filterModel(): GridFilterModel;
|
|
16
16
|
get field(): string;
|
|
17
|
+
get gridModel(): GridModel;
|
|
17
18
|
get store(): Store;
|
|
19
|
+
get column(): Column;
|
|
18
20
|
get fieldType(): FieldType;
|
|
19
21
|
get currentGridFilter(): Filter;
|
|
20
22
|
get columnFilters(): FieldFilter[];
|
|
@@ -5,12 +5,16 @@ import { HeaderFilterModel } from '../HeaderFilterModel';
|
|
|
5
5
|
export declare class ValuesTabModel extends HoistModel {
|
|
6
6
|
xhImpl: boolean;
|
|
7
7
|
headerFilterModel: HeaderFilterModel;
|
|
8
|
-
/** Checkbox grid to display enumerated set of values */
|
|
8
|
+
/** Checkbox grid to display enumerated set of values. */
|
|
9
9
|
gridModel: GridModel;
|
|
10
|
-
/** List of currently checked values
|
|
10
|
+
/** List of currently checked values. */
|
|
11
11
|
pendingValues: any[];
|
|
12
|
-
/** Bound search term for `StoreFilterField
|
|
12
|
+
/** Bound search term for `StoreFilterField`. */
|
|
13
13
|
filterText: string;
|
|
14
|
+
/**
|
|
15
|
+
* Merge current filter with pendingValues on commit.
|
|
16
|
+
* Used when commitOnChange is false.
|
|
17
|
+
*/
|
|
14
18
|
combineCurrentFilters: boolean;
|
|
15
19
|
/** FieldFilter output by this model. */
|
|
16
20
|
get filter(): FieldFilterSpec;
|
|
@@ -22,15 +26,18 @@ export declare class ValuesTabModel extends HoistModel {
|
|
|
22
26
|
get values(): any[];
|
|
23
27
|
get valueCount(): number;
|
|
24
28
|
get hasHiddenValues(): boolean;
|
|
29
|
+
get sortIcon(): any;
|
|
25
30
|
constructor(headerFilterModel: HeaderFilterModel);
|
|
26
31
|
syncWithFilter(): void;
|
|
27
32
|
reset(): void;
|
|
28
33
|
setRecsChecked(isChecked: boolean, values: any[]): void;
|
|
34
|
+
toggleSort(): void;
|
|
29
35
|
toggleAllRecsChecked(): void;
|
|
30
36
|
private onFilterTextChange;
|
|
31
37
|
private onCombineCurrentFiltersToggle;
|
|
32
38
|
private getFilter;
|
|
33
39
|
private doSyncWithFilter;
|
|
34
40
|
private syncGrid;
|
|
41
|
+
private initGridSortBy;
|
|
35
42
|
private createGridModel;
|
|
36
43
|
}
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import {Column, GridFilterFieldSpec, GridFilterModel, GridModel} from '@xh/hoist/cmp/grid';
|
|
8
9
|
import {TabContainerModel} from '@xh/hoist/cmp/tab';
|
|
9
|
-
import {HoistModel,
|
|
10
|
+
import {HoistModel, lookup, managed} from '@xh/hoist/core';
|
|
10
11
|
import {
|
|
11
12
|
CompoundFilter,
|
|
12
13
|
FieldFilter,
|
|
@@ -19,12 +20,11 @@ import {
|
|
|
19
20
|
import {action, computed} from '@xh/hoist/mobx';
|
|
20
21
|
import {wait} from '@xh/hoist/promise';
|
|
21
22
|
import {isEmpty} from 'lodash';
|
|
22
|
-
import {
|
|
23
|
+
import {ColumnHeaderFilterModel} from '../ColumnHeaderFilterModel';
|
|
23
24
|
import {customTab} from './custom/CustomTab';
|
|
24
25
|
import {CustomTabModel} from './custom/CustomTabModel';
|
|
25
26
|
import {valuesTab} from './values/ValuesTab';
|
|
26
27
|
import {ValuesTabModel} from './values/ValuesTabModel';
|
|
27
|
-
import {ColumnHeaderFilterModel} from '../ColumnHeaderFilterModel';
|
|
28
28
|
|
|
29
29
|
export class HeaderFilterModel extends HoistModel {
|
|
30
30
|
override xhImpl = true;
|
|
@@ -46,8 +46,16 @@ export class HeaderFilterModel extends HoistModel {
|
|
|
46
46
|
return this.fieldSpec.field;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
get gridModel(): GridModel {
|
|
50
|
+
return this.filterModel.gridModel;
|
|
51
|
+
}
|
|
52
|
+
|
|
49
53
|
get store(): Store {
|
|
50
|
-
return this.
|
|
54
|
+
return this.gridModel.store;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get column(): Column {
|
|
58
|
+
return this.parent.column;
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
get fieldType(): FieldType {
|
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
.xh-values-filter-tab {
|
|
2
|
-
|
|
3
|
-
padding: 5px 7px;
|
|
2
|
+
&__filter-controls {
|
|
4
3
|
border-bottom: 1px solid var(--xh-grid-header-border-color);
|
|
4
|
+
padding: 5px 7px;
|
|
5
5
|
row-gap: 5px;
|
|
6
|
+
|
|
6
7
|
.bp5-control-indicator {
|
|
7
8
|
font-size: 1em;
|
|
8
9
|
}
|
|
10
|
+
|
|
9
11
|
label {
|
|
10
12
|
font-size: var(--xh-grid-compact-header-font-size-px);
|
|
11
13
|
color: var(--xh-grid-header-text-color);
|
|
12
14
|
cursor: pointer;
|
|
13
15
|
}
|
|
16
|
+
|
|
17
|
+
&__sort-icon {
|
|
18
|
+
border-left: var(--xh-menu-border);
|
|
19
|
+
padding-left: var(--xh-pad-half-px);
|
|
20
|
+
color: var(--xh-grid-header-text-color);
|
|
21
|
+
}
|
|
14
22
|
}
|
|
15
23
|
|
|
16
24
|
&__hidden-values-message {
|
|
17
|
-
display: flex;
|
|
18
|
-
padding: var(--xh-pad-half-px);
|
|
19
25
|
background-color: var(--xh-bg-alt);
|
|
20
26
|
border-top: var(--xh-border-solid);
|
|
21
27
|
color: var(--xh-text-color-muted);
|
|
28
|
+
display: flex;
|
|
29
|
+
padding: var(--xh-pad-half-px);
|
|
22
30
|
|
|
23
31
|
.xh-icon {
|
|
24
32
|
margin-right: var(--xh-pad-half-px);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {isEmpty} from 'lodash';
|
|
8
8
|
import {grid} from '@xh/hoist/cmp/grid';
|
|
9
|
-
import {div, hframe, placeholder, label, vbox, vframe} from '@xh/hoist/cmp/layout';
|
|
9
|
+
import {div, hframe, placeholder, label, vbox, vframe, filler} from '@xh/hoist/cmp/layout';
|
|
10
10
|
import {storeFilterField} from '@xh/hoist/cmp/store';
|
|
11
11
|
import {XH, hoistCmp, uses} from '@xh/hoist/core';
|
|
12
12
|
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
@@ -60,7 +60,7 @@ const storeFilterSelect = hoistCmp.factory<ValuesTabModel>(({model}) => {
|
|
|
60
60
|
addToFilterId = XH.genId();
|
|
61
61
|
|
|
62
62
|
return vbox({
|
|
63
|
-
className: '
|
|
63
|
+
className: 'xh-values-filter-tab__filter-controls',
|
|
64
64
|
items: [
|
|
65
65
|
hframe(
|
|
66
66
|
checkbox({
|
|
@@ -73,6 +73,12 @@ const storeFilterSelect = hoistCmp.factory<ValuesTabModel>(({model}) => {
|
|
|
73
73
|
label({
|
|
74
74
|
htmlFor: selectAllId,
|
|
75
75
|
item: `(Select All${filterText ? ' Search Results' : ''})`
|
|
76
|
+
}),
|
|
77
|
+
filler(),
|
|
78
|
+
div({
|
|
79
|
+
className: 'xh-values-filter-tab__filter-controls__sort-icon',
|
|
80
|
+
item: model.sortIcon,
|
|
81
|
+
onClick: () => model.toggleSort()
|
|
76
82
|
})
|
|
77
83
|
),
|
|
78
84
|
hframe({
|
|
@@ -7,26 +7,27 @@
|
|
|
7
7
|
import {GridFilterModel, GridModel} from '@xh/hoist/cmp/grid';
|
|
8
8
|
import {HoistModel, managed} from '@xh/hoist/core';
|
|
9
9
|
import {FieldFilterSpec} from '@xh/hoist/data';
|
|
10
|
-
import {HeaderFilterModel} from '../HeaderFilterModel';
|
|
11
10
|
import {checkbox} from '@xh/hoist/desktop/cmp/input';
|
|
11
|
+
import {Icon} from '@xh/hoist/icon';
|
|
12
12
|
import {action, bindable, computed, makeObservable, observable} from '@xh/hoist/mobx';
|
|
13
13
|
import {castArray, difference, flatten, isEmpty, map, partition, uniq, without} from 'lodash';
|
|
14
|
+
import {HeaderFilterModel} from '../HeaderFilterModel';
|
|
14
15
|
|
|
15
16
|
export class ValuesTabModel extends HoistModel {
|
|
16
17
|
override xhImpl = true;
|
|
17
18
|
|
|
18
19
|
headerFilterModel: HeaderFilterModel;
|
|
19
20
|
|
|
20
|
-
/** Checkbox grid to display enumerated set of values */
|
|
21
|
-
@managed
|
|
21
|
+
/** Checkbox grid to display enumerated set of values. */
|
|
22
|
+
@managed gridModel: GridModel;
|
|
22
23
|
|
|
23
|
-
/** List of currently checked values
|
|
24
|
+
/** List of currently checked values. */
|
|
24
25
|
@observable.ref pendingValues: any[] = [];
|
|
25
26
|
|
|
26
|
-
/** Bound search term for `StoreFilterField
|
|
27
|
+
/** Bound search term for `StoreFilterField`. */
|
|
27
28
|
@bindable filterText: string = null;
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
/**
|
|
30
31
|
* Merge current filter with pendingValues on commit.
|
|
31
32
|
* Used when commitOnChange is false.
|
|
32
33
|
*/
|
|
@@ -80,12 +81,26 @@ export class ValuesTabModel extends HoistModel {
|
|
|
80
81
|
return this.values.length < this.valueCount;
|
|
81
82
|
}
|
|
82
83
|
|
|
84
|
+
get sortIcon() {
|
|
85
|
+
const {sort, abs} = this.gridModel.sortBy[0];
|
|
86
|
+
if (sort === 'asc') {
|
|
87
|
+
if (abs) return Icon.sortAbsAsc();
|
|
88
|
+
return Icon.sortAsc();
|
|
89
|
+
}
|
|
90
|
+
if (sort === 'desc') {
|
|
91
|
+
if (abs) return Icon.sortAbsDesc();
|
|
92
|
+
return Icon.sortDesc();
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
83
97
|
constructor(headerFilterModel: HeaderFilterModel) {
|
|
84
98
|
super();
|
|
85
99
|
makeObservable(this);
|
|
86
100
|
|
|
87
101
|
this.headerFilterModel = headerFilterModel;
|
|
88
102
|
this.gridModel = this.createGridModel();
|
|
103
|
+
this.initGridSortBy();
|
|
89
104
|
|
|
90
105
|
this.addReaction(
|
|
91
106
|
{
|
|
@@ -125,6 +140,13 @@ export class ValuesTabModel extends HoistModel {
|
|
|
125
140
|
: without(this.pendingValues, ...values);
|
|
126
141
|
}
|
|
127
142
|
|
|
143
|
+
@action
|
|
144
|
+
toggleSort() {
|
|
145
|
+
const {colId, sort, abs} = this.gridModel.sortBy.find(it => it.colId === 'value'),
|
|
146
|
+
newSort = sort === 'asc' ? 'desc' : 'asc';
|
|
147
|
+
this.gridModel.setSortBy({colId, sort: newSort, abs});
|
|
148
|
+
}
|
|
149
|
+
|
|
128
150
|
toggleAllRecsChecked() {
|
|
129
151
|
const setAllToChecked = !this.allVisibleRecsChecked,
|
|
130
152
|
values = this.gridModel.store.records.map(it => it.get('value'));
|
|
@@ -244,13 +266,22 @@ export class ValuesTabModel extends HoistModel {
|
|
|
244
266
|
this.gridModel.loadData(data);
|
|
245
267
|
}
|
|
246
268
|
|
|
269
|
+
private initGridSortBy() {
|
|
270
|
+
const {gridModel: srcGridModel, column} = this.headerFilterModel,
|
|
271
|
+
srcColGridSorter = srcGridModel.sortBy.find(it => it.colId === column.colId);
|
|
272
|
+
|
|
273
|
+
this.gridModel.setSortBy({
|
|
274
|
+
colId: 'value',
|
|
275
|
+
sort: srcColGridSorter?.sort ?? 'asc',
|
|
276
|
+
abs: srcColGridSorter?.abs ?? false
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
247
280
|
private createGridModel() {
|
|
248
281
|
const {BLANK_PLACEHOLDER} = GridFilterModel,
|
|
249
282
|
{headerFilterModel, fieldSpec} = this,
|
|
250
|
-
{fieldType} = headerFilterModel,
|
|
251
|
-
renderer =
|
|
252
|
-
fieldSpec.renderer ??
|
|
253
|
-
(fieldType !== 'tags' ? this.headerFilterModel.parent.column.renderer : null);
|
|
283
|
+
{fieldType, column} = headerFilterModel,
|
|
284
|
+
renderer = fieldSpec.renderer ?? (fieldType !== 'tags' ? column.renderer : null);
|
|
254
285
|
|
|
255
286
|
return new GridModel({
|
|
256
287
|
store: {
|
|
@@ -301,6 +332,7 @@ export class ValuesTabModel extends HoistModel {
|
|
|
301
332
|
{
|
|
302
333
|
field: 'value',
|
|
303
334
|
align: 'left',
|
|
335
|
+
tooltip: true,
|
|
304
336
|
comparator: (v1, v2, sortDir, abs, {defaultComparator}) => {
|
|
305
337
|
const mul = sortDir === 'desc' ? -1 : 1;
|
|
306
338
|
if (v1 === BLANK_PLACEHOLDER) return 1 * mul;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "77.0.0-SNAPSHOT.
|
|
3
|
+
"version": "77.0.0-SNAPSHOT.1760707577281",
|
|
4
4
|
"description": "Hoist add-on for building and deploying React Applications.",
|
|
5
5
|
"repository": "github:xh/hoist-react",
|
|
6
6
|
"homepage": "https://xh.io",
|
package/styles/vars.scss
CHANGED
|
@@ -522,8 +522,8 @@ body {
|
|
|
522
522
|
--xh-zone-grid-label-color: var(--zone-grid-label-color, inherit);
|
|
523
523
|
|
|
524
524
|
// Grid column-header-based filter popover (desktop only)
|
|
525
|
-
--xh-grid-filter-popover-height-px: var(--grid-filter-popover-height-px,
|
|
526
|
-
--xh-grid-filter-popover-width-px: var(--grid-filter-popover-width-px,
|
|
525
|
+
--xh-grid-filter-popover-height-px: var(--grid-filter-popover-height-px, 400px);
|
|
526
|
+
--xh-grid-filter-popover-width-px: var(--grid-filter-popover-width-px, 280px);
|
|
527
527
|
|
|
528
528
|
// Dark Grid
|
|
529
529
|
&.xh-dark {
|