@xh/hoist 45.0.1 → 45.0.2
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 +12 -0
- package/admin/tabs/general/alertBanner/AlertBannerModel.js +22 -14
- package/admin/tabs/general/alertBanner/AlertBannerPanel.js +2 -2
- package/admin/tabs/server/services/ServiceModel.js +4 -4
- package/cmp/grid/Grid.js +3 -2
- package/cmp/grid/GridModel.js +5 -0
- package/cmp/grid/columns/Column.js +4 -3
- package/data/Store.js +12 -7
- package/desktop/cmp/filter/FilterChooser.js +5 -1
- package/desktop/cmp/filter/FilterChooser.scss +4 -0
- package/desktop/cmp/grid/impl/filter/custom/CustomRow.js +25 -18
- package/desktop/cmp/grid/impl/filter/custom/CustomTab.scss +9 -16
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v45.0.2 - 2022-01-13
|
|
4
|
+
|
|
5
|
+
### 🎁 New Features
|
|
6
|
+
* `FilterChooser` has new `menuWidth` prop, allowing you to specify as width for the dropdown
|
|
7
|
+
menu that is different from the control.
|
|
8
|
+
|
|
9
|
+
### 🐞 Bug Fixes
|
|
10
|
+
* Fixed cache clearing method on Admin Console's Server > Services tab.
|
|
11
|
+
* Several fixes to behavior of `GridAutosizeMode.MANAGED`
|
|
12
|
+
|
|
13
|
+
[Commit Log](https://github.com/xh/hoist-react/compare/v45.0.1...v45.0.2)
|
|
14
|
+
|
|
3
15
|
## v45.0.1 - 2022-01-07
|
|
4
16
|
|
|
5
17
|
### 🐞 Bug Fixes
|
|
@@ -100,6 +100,28 @@ export class AlertBannerModel extends HoistModel {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
async saveAsync() {
|
|
103
|
+
return this
|
|
104
|
+
.saveInternalAsync()
|
|
105
|
+
.linkTo(this.loadModel)
|
|
106
|
+
.catchDefault();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
resetForm() {
|
|
110
|
+
this.formModel.reset();
|
|
111
|
+
this.refreshAsync();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
//----------------
|
|
115
|
+
// Implementation
|
|
116
|
+
//----------------
|
|
117
|
+
@action
|
|
118
|
+
syncPreview() {
|
|
119
|
+
const conf = XH.alertBannerService.genBannerConfig(this.formModel.getData());
|
|
120
|
+
this.bannerModel = new BannerModel(conf);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@action
|
|
124
|
+
async saveInternalAsync() {
|
|
103
125
|
const {formModel, savedValue} = this,
|
|
104
126
|
{
|
|
105
127
|
active,
|
|
@@ -182,18 +204,4 @@ export class AlertBannerModel extends HoistModel {
|
|
|
182
204
|
await XH.alertBannerService.checkForBannerAsync();
|
|
183
205
|
await this.refreshAsync();
|
|
184
206
|
}
|
|
185
|
-
|
|
186
|
-
resetForm() {
|
|
187
|
-
this.formModel.reset();
|
|
188
|
-
this.refreshAsync();
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
//----------------
|
|
192
|
-
// Implementation
|
|
193
|
-
//----------------
|
|
194
|
-
@action
|
|
195
|
-
syncPreview() {
|
|
196
|
-
const conf = XH.alertBannerService.genBannerConfig(this.formModel.getData());
|
|
197
|
-
this.bannerModel = new BannerModel(conf);
|
|
198
|
-
}
|
|
199
207
|
}
|
|
@@ -42,7 +42,7 @@ export const alertBannerPanel = hoistCmp.factory({
|
|
|
42
42
|
|
|
43
43
|
const formPanel = hoistCmp.factory(
|
|
44
44
|
({model}) => {
|
|
45
|
-
const {formModel
|
|
45
|
+
const {formModel} = model,
|
|
46
46
|
{isDirty, isValid} = formModel;
|
|
47
47
|
|
|
48
48
|
return panel({
|
|
@@ -162,7 +162,7 @@ const formPanel = hoistCmp.factory(
|
|
|
162
162
|
icon: Icon.check(),
|
|
163
163
|
intent: 'success',
|
|
164
164
|
disabled: !isValid || !isDirty,
|
|
165
|
-
onClick: () => model.saveAsync()
|
|
165
|
+
onClick: () => model.saveAsync()
|
|
166
166
|
})
|
|
167
167
|
]
|
|
168
168
|
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import {GridModel} from '@xh/hoist/cmp/grid';
|
|
8
8
|
import {HoistModel, managed, XH} from '@xh/hoist/core';
|
|
9
9
|
import {UrlStore} from '@xh/hoist/data';
|
|
10
|
-
import {lowerFirst} from 'lodash';
|
|
10
|
+
import {isEmpty, lowerFirst} from 'lodash';
|
|
11
11
|
|
|
12
12
|
export class ServiceModel extends HoistModel {
|
|
13
13
|
|
|
@@ -35,13 +35,13 @@ export class ServiceModel extends HoistModel {
|
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
async clearCachesAsync() {
|
|
38
|
-
const {
|
|
39
|
-
if (
|
|
38
|
+
const {selectedRecords} = this.gridModel;
|
|
39
|
+
if (isEmpty(selectedRecords)) return;
|
|
40
40
|
|
|
41
41
|
try {
|
|
42
42
|
await XH.fetchJson({
|
|
43
43
|
url: 'serviceAdmin/clearCaches',
|
|
44
|
-
params: {names:
|
|
44
|
+
params: {names: selectedRecords.map(it => it.data.name)}
|
|
45
45
|
});
|
|
46
46
|
await this.refreshAsync();
|
|
47
47
|
XH.successToast('Service caches cleared.');
|
package/cmp/grid/Grid.js
CHANGED
|
@@ -712,8 +712,9 @@ class GridLocalModel extends HoistModel {
|
|
|
712
712
|
onColumnResized = (ev) => {
|
|
713
713
|
if (!isDisplayed(this.viewRef.current) || !ev.finished) return;
|
|
714
714
|
if (ev.source === 'uiColumnDragged') {
|
|
715
|
-
const
|
|
716
|
-
|
|
715
|
+
const colId = ev.columns[0].colId,
|
|
716
|
+
width = ev.columnApi.getColumnState().find(it => it.colId === colId)?.width;
|
|
717
|
+
this.model.noteColumnManuallySized(colId, width);
|
|
717
718
|
} else if (ev.source === 'autosizeColumns') {
|
|
718
719
|
this.model.noteAgColumnStateChanged(ev.columnApi.getColumnState());
|
|
719
720
|
}
|
package/cmp/grid/GridModel.js
CHANGED
|
@@ -455,6 +455,11 @@ export class GridModel extends HoistModel {
|
|
|
455
455
|
|
|
456
456
|
this.filterModel?.clear();
|
|
457
457
|
this.persistenceModel?.clear();
|
|
458
|
+
|
|
459
|
+
if (this.autosizeOptions.mode === GridAutosizeMode.MANAGED) {
|
|
460
|
+
await this.autosizeAsync();
|
|
461
|
+
}
|
|
462
|
+
|
|
458
463
|
return true;
|
|
459
464
|
}
|
|
460
465
|
|
|
@@ -20,9 +20,10 @@ import {
|
|
|
20
20
|
isNil,
|
|
21
21
|
isNumber,
|
|
22
22
|
isPlainObject,
|
|
23
|
-
isString
|
|
23
|
+
isString,
|
|
24
|
+
toString
|
|
24
25
|
} from 'lodash';
|
|
25
|
-
import {forwardRef, useImperativeHandle, useState, createElement} from 'react';
|
|
26
|
+
import {forwardRef, useImperativeHandle, useState, createElement, isValidElement} from 'react';
|
|
26
27
|
import classNames from 'classnames';
|
|
27
28
|
import {GridSorter} from '../impl/GridSorter';
|
|
28
29
|
import {ExcelFormat} from './ExcelFormat';
|
|
@@ -651,7 +652,7 @@ export class Column {
|
|
|
651
652
|
tooltipSpec(val, {record, column: this, gridModel, agParams}) :
|
|
652
653
|
val;
|
|
653
654
|
|
|
654
|
-
return ret
|
|
655
|
+
return isValidElement(ret) ? ret : toString(ret);
|
|
655
656
|
});
|
|
656
657
|
}
|
|
657
658
|
|
package/data/Store.js
CHANGED
|
@@ -49,6 +49,9 @@ export class Store extends HoistBase {
|
|
|
49
49
|
/** @member {boolean} */
|
|
50
50
|
loadTreeData;
|
|
51
51
|
|
|
52
|
+
/** @member {string} */
|
|
53
|
+
loadTreeDataFrom;
|
|
54
|
+
|
|
52
55
|
/** @member {boolean} */
|
|
53
56
|
loadRootAsSummary;
|
|
54
57
|
|
|
@@ -102,13 +105,13 @@ export class Store extends HoistBase {
|
|
|
102
105
|
* @param {function} [c.processRawData] - function to run on each individual data object
|
|
103
106
|
* presented to loadData() prior to creating a StoreRecord from that object. This function
|
|
104
107
|
* must return an object, cloning the original object if edits are necessary.
|
|
105
|
-
* @param {(Filter|*|*[])} [c.filter] - one or more filters or configs to create one.
|
|
108
|
+
* @param {(Filter|*|*[])} [c.filter] - one or more filters or configs to create one. If an
|
|
106
109
|
* array, a single 'AND' filter will be created.
|
|
107
110
|
* @param {boolean} [c.filterIncludesChildren] - true if all children of a passing record should
|
|
108
111
|
* also be considered passing (default false).
|
|
109
|
-
* @param {boolean} [c.loadTreeData] - true to load hierarchical/tree data
|
|
110
|
-
*
|
|
111
|
-
* (
|
|
112
|
+
* @param {boolean} [c.loadTreeData] - true (default) to load hierarchical/tree data, if any.
|
|
113
|
+
* @param {string} [c.loadTreeDataFrom] - the property on each raw data object that holds its
|
|
114
|
+
* (raw) child objects, if any. Default 'children', no effect if `loadTreeData: false`.
|
|
112
115
|
* @param {boolean} [c.loadRootAsSummary] - true to treat the root node in hierarchical data as
|
|
113
116
|
* the summary record (default false).
|
|
114
117
|
* @param {boolean} [c.freezeData] - true to freeze the internal data object of the record.
|
|
@@ -135,6 +138,7 @@ export class Store extends HoistBase {
|
|
|
135
138
|
filter = null,
|
|
136
139
|
filterIncludesChildren = false,
|
|
137
140
|
loadTreeData = true,
|
|
141
|
+
loadTreeDataFrom = 'children',
|
|
138
142
|
loadRootAsSummary = false,
|
|
139
143
|
freezeData = true,
|
|
140
144
|
idEncodesTreePath = false,
|
|
@@ -151,6 +155,7 @@ export class Store extends HoistBase {
|
|
|
151
155
|
this.filter = parseFilter(filter);
|
|
152
156
|
this.filterIncludesChildren = filterIncludesChildren;
|
|
153
157
|
this.loadTreeData = loadTreeData;
|
|
158
|
+
this.loadTreeDataFrom = loadTreeDataFrom;
|
|
154
159
|
this.loadRootAsSummary = loadRootAsSummary;
|
|
155
160
|
this.freezeData = freezeData;
|
|
156
161
|
this.idEncodesTreePath = idEncodesTreePath;
|
|
@@ -843,7 +848,7 @@ export class Store extends HoistBase {
|
|
|
843
848
|
}
|
|
844
849
|
|
|
845
850
|
createRecords(rawData, parent, recordMap = new Map()) {
|
|
846
|
-
const {loadTreeData} = this;
|
|
851
|
+
const {loadTreeData, loadTreeDataFrom} = this;
|
|
847
852
|
rawData.forEach(raw => {
|
|
848
853
|
const rec = this.createRecord(raw, parent),
|
|
849
854
|
{id} = rec;
|
|
@@ -855,8 +860,8 @@ export class Store extends HoistBase {
|
|
|
855
860
|
|
|
856
861
|
recordMap.set(id, rec);
|
|
857
862
|
|
|
858
|
-
if (loadTreeData && raw
|
|
859
|
-
this.createRecords(raw
|
|
863
|
+
if (loadTreeData && raw[loadTreeDataFrom]) {
|
|
864
|
+
this.createRecords(raw[loadTreeDataFrom], rec, recordMap);
|
|
860
865
|
}
|
|
861
866
|
});
|
|
862
867
|
return recordMap;
|
|
@@ -29,7 +29,7 @@ export const [FilterChooser, filterChooser] = hoistCmp.withFactory({
|
|
|
29
29
|
render({model, className, ...props}, ref) {
|
|
30
30
|
const [layoutProps, chooserProps] = splitLayoutProps(props),
|
|
31
31
|
{inputRef, suggestFieldsWhenEmpty, selectOptions, unsupportedFilter, favoritesIsOpen} = model,
|
|
32
|
-
{autoFocus, enableClear, leftIcon, maxMenuHeight, menuPlacement} = chooserProps,
|
|
32
|
+
{autoFocus, enableClear, leftIcon, maxMenuHeight, menuPlacement, menuWidth} = chooserProps,
|
|
33
33
|
disabled = unsupportedFilter || chooserProps.disabled,
|
|
34
34
|
placeholder = unsupportedFilter ?
|
|
35
35
|
'Unsupported filter' : // Todo: How to message this better?
|
|
@@ -49,6 +49,7 @@ export const [FilterChooser, filterChooser] = hoistCmp.withFactory({
|
|
|
49
49
|
autoFocus,
|
|
50
50
|
disabled,
|
|
51
51
|
menuPlacement,
|
|
52
|
+
menuWidth,
|
|
52
53
|
placeholder,
|
|
53
54
|
leftIcon: withDefault(leftIcon, Icon.filter()),
|
|
54
55
|
enableClear: withDefault(enableClear, true),
|
|
@@ -106,6 +107,9 @@ FilterChooser.propTypes = {
|
|
|
106
107
|
/** Placement of the dropdown menu relative to the input control. */
|
|
107
108
|
menuPlacement: PT.oneOf(['auto', 'top', 'bottom']),
|
|
108
109
|
|
|
110
|
+
/** Width in pixels for the dropdown menu - if unspecified, defaults to control width. */
|
|
111
|
+
menuWidth: PT.number,
|
|
112
|
+
|
|
109
113
|
/** Text to display when control is empty. */
|
|
110
114
|
placeholder: PT.string
|
|
111
115
|
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Copyright © 2021 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {hoistCmp, uses} from '@xh/hoist/core';
|
|
8
|
-
import {
|
|
8
|
+
import {vbox, div} from '@xh/hoist/cmp/layout';
|
|
9
9
|
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
10
10
|
import {dateInput, numberInput, select, textInput} from '@xh/hoist/desktop/cmp/input';
|
|
11
11
|
import {Icon} from '@xh/hoist/icon';
|
|
@@ -26,27 +26,34 @@ export const customRow = hoistCmp.factory({
|
|
|
26
26
|
return div({
|
|
27
27
|
className: `xh-custom-filter-tab__row xh-custom-filter-tab__row--${kebabCase(op)}`,
|
|
28
28
|
items: [
|
|
29
|
-
|
|
30
|
-
className: `xh-custom-filter-
|
|
29
|
+
vbox({
|
|
30
|
+
className: `xh-custom-filter-tab__row__body`,
|
|
31
31
|
items: [
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
div({
|
|
33
|
+
className: `xh-custom-filter-tab__row__top`,
|
|
34
|
+
item: select({
|
|
35
|
+
bind: 'op',
|
|
36
|
+
enableFilter: false,
|
|
37
|
+
hideSelectedOptionCheck: true,
|
|
38
|
+
width: '100%',
|
|
39
|
+
options,
|
|
40
|
+
optionRenderer: (opt) => operatorRenderer({opt})
|
|
41
|
+
})
|
|
38
42
|
}),
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
div({
|
|
44
|
+
omit: hideInput,
|
|
45
|
+
className: `xh-custom-filter-tab__row__bottom`,
|
|
46
|
+
item: inputField()
|
|
43
47
|
})
|
|
44
48
|
]
|
|
45
49
|
}),
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
div({
|
|
51
|
+
className: `xh-custom-filter-tab__row__right`,
|
|
52
|
+
item: button({
|
|
53
|
+
icon: Icon.delete(),
|
|
54
|
+
intent: 'danger',
|
|
55
|
+
onClick: () => model.removeRow()
|
|
56
|
+
})
|
|
50
57
|
})
|
|
51
58
|
]
|
|
52
59
|
});
|
|
@@ -62,7 +69,7 @@ const inputField = hoistCmp.factory(
|
|
|
62
69
|
props = {
|
|
63
70
|
bind: 'inputVal',
|
|
64
71
|
enableClear: true,
|
|
65
|
-
width:
|
|
72
|
+
width: '100%',
|
|
66
73
|
autoFocus: true,
|
|
67
74
|
commitOnChange,
|
|
68
75
|
...fieldSpec.inputProps
|
|
@@ -17,34 +17,27 @@
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
&__row {
|
|
20
|
+
display: flex;
|
|
20
21
|
padding: var(--xh-pad-half-px);
|
|
21
22
|
border-bottom: 1px solid var(--xh-grid-border-color);
|
|
22
23
|
|
|
23
|
-
&
|
|
24
|
-
|
|
25
|
-
align-items: center;
|
|
26
|
-
|
|
27
|
-
.xh-select {
|
|
28
|
-
flex: none;
|
|
29
|
-
width: 200px !important;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.xh-button {
|
|
33
|
-
padding: 0 !important;
|
|
34
|
-
min-width: 30px !important;
|
|
35
|
-
margin-left: 2px;
|
|
36
|
-
min-height: 24px !important;
|
|
37
|
-
}
|
|
24
|
+
&__body {
|
|
25
|
+
flex: 1;
|
|
38
26
|
}
|
|
39
27
|
|
|
40
28
|
&__bottom {
|
|
41
29
|
margin-top: var(--xh-pad-half-px);
|
|
42
30
|
|
|
43
31
|
.bp3-popover-wrapper,
|
|
32
|
+
.bp3-popover-target,
|
|
44
33
|
.bp3-input-group {
|
|
45
|
-
width:
|
|
34
|
+
width: 100%;
|
|
46
35
|
}
|
|
47
36
|
}
|
|
37
|
+
|
|
38
|
+
&__right {
|
|
39
|
+
padding-left: 2px;
|
|
40
|
+
}
|
|
48
41
|
}
|
|
49
42
|
|
|
50
43
|
&__implicit_join_message {
|