@xh/hoist 49.1.0 → 49.2.0
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 +25 -0
- package/LICENSE.md +2 -2
- package/cmp/filter/FilterChooserModel.js +19 -13
- package/cmp/grid/GridModel.js +6 -2
- package/cmp/grid/filter/GridFilterFieldSpec.js +1 -1
- package/cmp/grid/impl/ColumnWidthCalculator.js +2 -0
- package/data/filter/FieldFilter.js +12 -3
- package/desktop/cmp/filter/FilterChooser.js +2 -1
- package/desktop/cmp/grid/find/GridFindField.scss +6 -2
- package/desktop/cmp/panel/impl/PanelHeader.js +4 -2
- package/desktop/cmp/toolbar/Toolbar.scss +5 -0
- package/desktop/cmp/treemap/SplitTreeMapModel.js +3 -0
- package/kit/ag-grid/index.js +1 -1
- package/mobile/cmp/panel/impl/PanelHeader.js +3 -1
- package/package.json +3 -3
- package/svc/GridAutosizeService.js +14 -7
- package/utils/js/Decorators.js +9 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v49.2.0 - 2010-06-14
|
|
4
|
+
|
|
5
|
+
### 🎁 New Features
|
|
6
|
+
|
|
7
|
+
* New `@enumerable` decorator for making class members `enumerable`
|
|
8
|
+
* New `GridAutosizeOption` `renderedRowsOnly` supports more limited autosizing
|
|
9
|
+
for very large grids.
|
|
10
|
+
|
|
11
|
+
### 🐞 Bug Fixes
|
|
12
|
+
|
|
13
|
+
* Fix `FilterChooser` looping between old values if updated too rapidly.
|
|
14
|
+
* Allow user to clear an unsupported `FilterChooser` value.
|
|
15
|
+
* Fix bug where `Panel` would throw when `headerItems = null`
|
|
16
|
+
* Fix column values filtering on `tags` fields if another filter is already present.
|
|
17
|
+
* Fix bug where `SwitchInput` `labelSide` would render inappropriately if within `compact` `toolbar`
|
|
18
|
+
* Fix bug where `SplitTreeMapModel.showSplitter` property wasn't being set in constructor
|
|
19
|
+
|
|
20
|
+
### 📚 Libraries
|
|
21
|
+
|
|
22
|
+
* mobx `6.5 -> 6.6`
|
|
23
|
+
|
|
24
|
+
[Commit Log](https://github.com/xh/hoist-react/compare/v49.1.0...v49.2.0)
|
|
25
|
+
|
|
26
|
+
|
|
3
27
|
## v49.1.0 - 2022-06-03
|
|
4
28
|
|
|
5
29
|
### 🎁 New Features
|
|
@@ -9,6 +33,7 @@
|
|
|
9
33
|
* `FieldFilter` supports `includes` and `excludes` operators for `tags` fields
|
|
10
34
|
|
|
11
35
|
### 🐞 Bug Fixes
|
|
36
|
+
|
|
12
37
|
* Fix regression with `begins`, `ends`, and `not like` filters.
|
|
13
38
|
* Fix `DashCanvas` styling so drag-handles no longer cause horizontal scroll bar to appear
|
|
14
39
|
* Fix bug where `DashCanvas` would not resize appropriately on scrollbar visibility change
|
package/LICENSE.md
CHANGED
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
|
|
176
176
|
END OF TERMS AND CONDITIONS
|
|
177
177
|
|
|
178
|
-
Copyright 2014-
|
|
178
|
+
Copyright 2014-2022 Extremely Heavy Industries, Inc.
|
|
179
179
|
|
|
180
180
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
181
181
|
you may not use this file except in compliance with the License.
|
|
@@ -187,4 +187,4 @@
|
|
|
187
187
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
188
188
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
189
189
|
See the License for the specific language governing permissions and
|
|
190
|
-
limitations under the License.
|
|
190
|
+
limitations under the License.
|
|
@@ -199,7 +199,7 @@ export class FilterChooserModel extends HoistModel {
|
|
|
199
199
|
* they will be displayed as tags and can be removed, but not created using
|
|
200
200
|
* the control.
|
|
201
201
|
*
|
|
202
|
-
* Any other Filter is
|
|
202
|
+
* Any other Filter is unsupported and will cause the control to show a placeholder error.
|
|
203
203
|
*/
|
|
204
204
|
@action
|
|
205
205
|
setValue(value) {
|
|
@@ -208,7 +208,7 @@ export class FilterChooserModel extends HoistModel {
|
|
|
208
208
|
value = parseFilter(value);
|
|
209
209
|
if (this.value?.equals(value)) return;
|
|
210
210
|
|
|
211
|
-
// 1) Ensure
|
|
211
|
+
// 1) Ensure FilterChooser can handle the requested value.
|
|
212
212
|
const isValid = this.validateFilter(value),
|
|
213
213
|
displayFilters = isValid ? this.toDisplayFilters(value) : null;
|
|
214
214
|
|
|
@@ -234,20 +234,26 @@ export class FilterChooserModel extends HoistModel {
|
|
|
234
234
|
|
|
235
235
|
this.selectOptions = !isEmpty(options) ? options : null;
|
|
236
236
|
|
|
237
|
-
//
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
237
|
+
// 4) Do the next steps asynchronously for UI responsiveness and to ensure the component
|
|
238
|
+
// is ready to render the tags correctly (after selectOptions set above).
|
|
239
|
+
wait().thenAction(() => {
|
|
240
|
+
// No-op if we've already re-entered this method by the time this async routine runs.
|
|
241
|
+
if (this.value !== value) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
this.selectValue = sortBy(displayFilters.map(f => JSON.stringify(f)), f => {
|
|
246
|
+
const idx = this.selectValue?.indexOf(f);
|
|
247
|
+
return isFinite(idx) && idx > -1 ? idx : displayFilters.length;
|
|
248
|
+
});
|
|
243
249
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
wait().then(() => {
|
|
250
|
+
// 5) Round-trip value to bound filter
|
|
251
|
+
if (bind) {
|
|
247
252
|
const filter = withFilterByTypes(bind.filter, value, ['FieldFilter', 'CompoundFilter']);
|
|
248
253
|
bind.setFilter(filter);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
254
|
+
}
|
|
255
|
+
}).linkTo(this.filterTask);
|
|
256
|
+
|
|
251
257
|
} catch (e) {
|
|
252
258
|
console.error('Failed to set value on FilterChooserModel', e);
|
|
253
259
|
this.value = null;
|
package/cmp/grid/GridModel.js
CHANGED
|
@@ -390,6 +390,7 @@ export class GridModel extends HoistModel {
|
|
|
390
390
|
{...autosizeOptions},
|
|
391
391
|
{
|
|
392
392
|
mode: GridModel.DEFAULT_AUTOSIZE_MODE,
|
|
393
|
+
renderedRowsOnly: false,
|
|
393
394
|
includeCollapsedChildren: false,
|
|
394
395
|
showMask: false,
|
|
395
396
|
// Larger buffer on mobile (perhaps counterintuitively) to minimize clipping due to
|
|
@@ -1598,9 +1599,12 @@ export class GridModel extends HoistModel {
|
|
|
1598
1599
|
* override this value may specify `Column.autosizeBufferPx`. Default is 5.
|
|
1599
1600
|
* @property {boolean} [showMask] - true to show mask over the grid during the autosize operation.
|
|
1600
1601
|
* Default is true.
|
|
1602
|
+
* @property {boolean} [renderedRowsOnly] - true to limit operation to rendered rows only.
|
|
1603
|
+
* Default is false. Set to true for grids that contain many rows and columns, for which
|
|
1604
|
+
* full autosizing of all data would be too slow.
|
|
1601
1605
|
* @property {boolean} [includeCollapsedChildren] - true to autosize all rows, even when hidden due
|
|
1602
|
-
* to a collapsed ancestor row.
|
|
1603
|
-
* have performance impacts for large tree grids with many cells.
|
|
1606
|
+
* to a collapsed ancestor row. Only has an effect when renderedRowsOnly is false. Default is false.
|
|
1607
|
+
* Note that setting this to true can have performance impacts for large tree grids with many cells.
|
|
1604
1608
|
* @property {function|string|string[]} [columns] - columns ids to autosize, or a function for
|
|
1605
1609
|
* testing if the given column should be autosized. Typically used when calling
|
|
1606
1610
|
* autosizeAsync() manually. To generally exclude a column from autosizing, see the
|
|
@@ -87,7 +87,7 @@ export class GridFilterFieldSpec extends BaseFilterFieldSpec {
|
|
|
87
87
|
let values;
|
|
88
88
|
if (cleanedFilter) {
|
|
89
89
|
values = uniqBy([
|
|
90
|
-
...filteredRecords.map(rec => this.valueFromRecord(rec)),
|
|
90
|
+
...flatten(filteredRecords.map(rec => this.valueFromRecord(rec))),
|
|
91
91
|
...filterValues
|
|
92
92
|
], this.getUniqueValue);
|
|
93
93
|
} else {
|
|
@@ -67,6 +67,8 @@ export class ColumnWidthCalculator {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
calcDataWidth(gridModel, records, column, options) {
|
|
70
|
+
if (isEmpty(records)) return null;
|
|
71
|
+
|
|
70
72
|
try {
|
|
71
73
|
const {store, treeMode} = gridModel;
|
|
72
74
|
if (treeMode && column.isTreeColumn && store.allRootCount !== store.allCount) {
|
|
@@ -8,7 +8,16 @@
|
|
|
8
8
|
import {XH} from '@xh/hoist/core';
|
|
9
9
|
import {parseFieldValue} from '@xh/hoist/data';
|
|
10
10
|
import {throwIf} from '@xh/hoist/utils/js';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
castArray,
|
|
13
|
+
difference,
|
|
14
|
+
escapeRegExp,
|
|
15
|
+
isArray,
|
|
16
|
+
isNil,
|
|
17
|
+
isString,
|
|
18
|
+
isUndefined,
|
|
19
|
+
uniq
|
|
20
|
+
} from 'lodash';
|
|
12
21
|
import {FieldType} from '../Field';
|
|
13
22
|
|
|
14
23
|
import {Filter} from './Filter';
|
|
@@ -45,7 +54,7 @@ export class FieldFilter extends Filter {
|
|
|
45
54
|
* @param {(*|[])} c.value - value(s) to use with operator in filter. When used with operators
|
|
46
55
|
* in the ARRAY_OPERATORS collection, value may be specified as an array. In these cases,
|
|
47
56
|
* the filter will implement an implicit 'OR' for '='/'like'/'begins'/'ends',
|
|
48
|
-
* and an implicit 'AND' for '!='/'not like'.
|
|
57
|
+
* and an implicit 'AND' for '!='/'not like'. Duplicated values are omitted.
|
|
49
58
|
*/
|
|
50
59
|
constructor({field, op, value}) {
|
|
51
60
|
super();
|
|
@@ -61,7 +70,7 @@ export class FieldFilter extends Filter {
|
|
|
61
70
|
|
|
62
71
|
this.field = isString(field) ? field : field.name;
|
|
63
72
|
this.op = op;
|
|
64
|
-
this.value = isArray(value) ?
|
|
73
|
+
this.value = isArray(value) ? uniq(value) : value;
|
|
65
74
|
|
|
66
75
|
Object.freeze(this);
|
|
67
76
|
}
|
|
@@ -32,7 +32,7 @@ export const [FilterChooser, filterChooser] = hoistCmp.withFactory({
|
|
|
32
32
|
{autoFocus, enableClear, leftIcon, maxMenuHeight, menuPlacement, menuWidth} = chooserProps,
|
|
33
33
|
disabled = unsupportedFilter || chooserProps.disabled,
|
|
34
34
|
placeholder = unsupportedFilter ?
|
|
35
|
-
'Unsupported filter
|
|
35
|
+
'Unsupported filter (click to clear)' :
|
|
36
36
|
withDefault(chooserProps.placeholder, 'Filter...');
|
|
37
37
|
|
|
38
38
|
return box({
|
|
@@ -82,6 +82,7 @@ export const [FilterChooser, filterChooser] = hoistCmp.withFactory({
|
|
|
82
82
|
minimal: true,
|
|
83
83
|
onInteraction: (willOpen) => {
|
|
84
84
|
if (!willOpen) model.closeFavoritesMenu();
|
|
85
|
+
if (unsupportedFilter) model.setValue(null);
|
|
85
86
|
}
|
|
86
87
|
})
|
|
87
88
|
});
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
border-radius: 0 3px 3px 0;
|
|
14
14
|
|
|
15
15
|
.xh-button {
|
|
16
|
-
max-height: calc(var(--xh-tbar-compact-item-height-px) * 0.5);
|
|
17
|
-
min-height: calc(var(--xh-tbar-compact-item-height-px) * 0.5);
|
|
16
|
+
max-height: calc(var(--xh-tbar-compact-item-height-px) * 0.5) !important;
|
|
17
|
+
min-height: calc(var(--xh-tbar-compact-item-height-px) * 0.5) !important;
|
|
18
18
|
min-width: var(--xh-tbar-compact-item-height-px);
|
|
19
19
|
max-width: var(--xh-tbar-compact-item-height-px);
|
|
20
20
|
margin: 0 !important;
|
|
@@ -30,4 +30,8 @@
|
|
|
30
30
|
min-width: 30px;
|
|
31
31
|
text-align: right;
|
|
32
32
|
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.xh-toolbar--compact .xh-grid-find-field__controls {
|
|
36
|
+
border: none;
|
|
33
37
|
}
|
|
@@ -9,6 +9,7 @@ import {hoistCmp, useContextModel} from '@xh/hoist/core';
|
|
|
9
9
|
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
10
10
|
import {Icon} from '@xh/hoist/icon';
|
|
11
11
|
import classNames from 'classnames';
|
|
12
|
+
import {isEmpty} from 'lodash';
|
|
12
13
|
import {PanelModel} from '../PanelModel';
|
|
13
14
|
import './PanelHeader.scss';
|
|
14
15
|
|
|
@@ -20,9 +21,10 @@ export const panelHeader = hoistCmp.factory({
|
|
|
20
21
|
render({className, ...props}) {
|
|
21
22
|
const panelModel = useContextModel(PanelModel),
|
|
22
23
|
{collapsed, vertical, side, showHeaderCollapseButton} = panelModel,
|
|
23
|
-
{title, icon, compact
|
|
24
|
+
{title, icon, compact} = props,
|
|
25
|
+
headerItems = props.headerItems ?? [];
|
|
24
26
|
|
|
25
|
-
if (!title && !icon &&
|
|
27
|
+
if (!title && !icon && isEmpty(headerItems) && !showHeaderCollapseButton) return null;
|
|
26
28
|
|
|
27
29
|
const onDoubleClick = () => {
|
|
28
30
|
if (panelModel.collapsible) panelModel.toggleCollapsed();
|
|
@@ -30,6 +30,8 @@ export class SplitTreeMapModel extends HoistModel {
|
|
|
30
30
|
mapFilter;
|
|
31
31
|
/** @member {function} */
|
|
32
32
|
mapTitleFn;
|
|
33
|
+
/** @member {boolean} */
|
|
34
|
+
showSplitter;
|
|
33
35
|
|
|
34
36
|
/** @member {TreeMapModel} */
|
|
35
37
|
@managed primaryMapModel;
|
|
@@ -65,6 +67,7 @@ export class SplitTreeMapModel extends HoistModel {
|
|
|
65
67
|
makeObservable(this);
|
|
66
68
|
this.mapFilter = withDefault(mapFilter, this.defaultMapFilter);
|
|
67
69
|
this.mapTitleFn = mapTitleFn;
|
|
70
|
+
this.showSplitter = showSplitter;
|
|
68
71
|
|
|
69
72
|
throwIf(!['vertical', 'horizontal'].includes(orientation), `Orientation "${orientation}" not recognised.`);
|
|
70
73
|
this.orientation = orientation;
|
package/kit/ag-grid/index.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {div, filler, hbox} from '@xh/hoist/cmp/layout';
|
|
8
8
|
import {hoistCmp} from '@xh/hoist/core';
|
|
9
|
+
import {isEmpty} from 'lodash';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* A standardized header for a Panel component
|
|
@@ -17,7 +18,8 @@ export const panelHeader = hoistCmp.factory({
|
|
|
17
18
|
model: false, memo: false, observer: false,
|
|
18
19
|
|
|
19
20
|
render({className, title, icon, headerItems = []}) {
|
|
20
|
-
|
|
21
|
+
headerItems = headerItems ?? [];
|
|
22
|
+
if (!title && !icon && isEmpty(headerItems)) return null;
|
|
21
23
|
|
|
22
24
|
return hbox({
|
|
23
25
|
className,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "49.
|
|
3
|
+
"version": "49.2.0",
|
|
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",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"inter-ui": "~3.19.3",
|
|
53
53
|
"lodash": "~4.17.21",
|
|
54
54
|
"lodash-inflection": "~1.5.0",
|
|
55
|
-
"mobx": "~6.
|
|
55
|
+
"mobx": "~6.6.0",
|
|
56
56
|
"mobx-react-lite": "~3.4.0",
|
|
57
57
|
"moment": "~2.29.1",
|
|
58
58
|
"numbro": "~2.3.6",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"react-dom": "~17.0.1"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
|
-
"@xh/hoist-dev-utils": "^5.
|
|
84
|
+
"@xh/hoist-dev-utils": "^5.14.0",
|
|
85
85
|
"husky": "~4.3.8",
|
|
86
86
|
"lint-staged": "~10.5.3",
|
|
87
87
|
"react": "~17.0.1",
|
|
@@ -43,9 +43,11 @@ export class GridAutosizeService extends HoistService {
|
|
|
43
43
|
colIds = sortBy(colIds, id => gridModel.columnState.findIndex(col => col.colId === id));
|
|
44
44
|
runInAction(() => {
|
|
45
45
|
// 3) Shrink columns down to their required widths.
|
|
46
|
-
const
|
|
46
|
+
const records = this.gatherRecordsToBeSized(gridModel, options),
|
|
47
|
+
requiredWidths = this.calcRequiredWidths(gridModel, colIds, records, options);
|
|
48
|
+
|
|
47
49
|
gridModel.applyColumnStateChanges(requiredWidths);
|
|
48
|
-
console.debug(
|
|
50
|
+
console.debug(`Column widths autosized via GridAutosizeService (${records.length} records)`, requiredWidths);
|
|
49
51
|
|
|
50
52
|
// 4) Grow columns to fill any remaining space, if enabled.
|
|
51
53
|
const {fillMode} = options;
|
|
@@ -64,12 +66,12 @@ export class GridAutosizeService extends HoistService {
|
|
|
64
66
|
/**
|
|
65
67
|
* @param {GridModel} gridModel
|
|
66
68
|
* @param {string[]} colIds
|
|
69
|
+
* @param {Record[]} records
|
|
67
70
|
* @param {GridAutosizeOptions} options
|
|
68
71
|
* @return {Object[]} - {colId, width} objects to pass to GridModel.applyColumnStateChanges()
|
|
69
72
|
*/
|
|
70
|
-
calcRequiredWidths(gridModel, colIds, options) {
|
|
71
|
-
const
|
|
72
|
-
ret = [];
|
|
73
|
+
calcRequiredWidths(gridModel, colIds, records, options) {
|
|
74
|
+
const ret = [];
|
|
73
75
|
|
|
74
76
|
for (const colId of colIds) {
|
|
75
77
|
const width = this._columnWidthCalculator.calcWidth(gridModel, records, colId, options);
|
|
@@ -86,10 +88,15 @@ export class GridAutosizeService extends HoistService {
|
|
|
86
88
|
*/
|
|
87
89
|
gatherRecordsToBeSized(gridModel, options) {
|
|
88
90
|
let {store, agApi, treeMode, groupBy} = gridModel,
|
|
89
|
-
{includeCollapsedChildren} = options,
|
|
91
|
+
{includeCollapsedChildren, renderedRowsOnly} = options,
|
|
90
92
|
ret = [];
|
|
91
93
|
|
|
92
|
-
if (
|
|
94
|
+
if (renderedRowsOnly) {
|
|
95
|
+
agApi?.getRenderedNodes().forEach(node => {
|
|
96
|
+
const record = store.getById(node.data?.id);
|
|
97
|
+
if (record) ret.push(record);
|
|
98
|
+
});
|
|
99
|
+
} else if (agApi && !includeCollapsedChildren && (treeMode || groupBy)) {
|
|
93
100
|
// In tree/grouped grids, included expanded rows only by default.
|
|
94
101
|
for (let idx = 0; idx < agApi.getDisplayedRowCount(); idx++) {
|
|
95
102
|
const node = agApi.getDisplayedRowAtIndex(idx),
|
package/utils/js/Decorators.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Copyright © 2021 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {debounce, isFunction} from 'lodash';
|
|
8
|
-
import {throwIf, getOrCreate} from './LangUtils';
|
|
8
|
+
import {throwIf, getOrCreate, warnIf} from './LangUtils';
|
|
9
9
|
import {withDebug} from './LogUtils';
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -64,3 +64,11 @@ export function logWithDebug(target, key, descriptor) {
|
|
|
64
64
|
}
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Modify a member so that it is enumerable. Useful for getters, which default to enumerable = false
|
|
70
|
+
*/
|
|
71
|
+
export function enumerable(target, key, descriptor) {
|
|
72
|
+
warnIf(descriptor.enumerable, `Unnecessary use of @enumerable: ${key} is already enumerable.`);
|
|
73
|
+
return {...descriptor, enumerable: true};
|
|
74
|
+
}
|