@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 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, loadModel} = model,
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().linkTo(loadModel)
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 {selection} = this.gridModel;
39
- if (!selection.length) return;
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: selection.map(it => it.data.name)}
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 width = ev.columnApi.getColumnState().find(it => it.colId === ev.column.colId)?.width;
716
- this.model.noteColumnManuallySized(ev.column.colId, width);
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
  }
@@ -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 ?? null;
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. If an
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. When this flag is
110
- * true, the children property on raw data objects will be used to load child records.
111
- * (default true).
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.children) {
859
- this.createRecords(raw.children, rec, recordMap);
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
  };
@@ -54,6 +54,10 @@
54
54
  }
55
55
 
56
56
  &__field {
57
+ overflow: hidden;
58
+ text-overflow: ellipsis;
59
+ white-space: nowrap;
60
+
57
61
  .prefix {
58
62
  color: var(--xh-text-color-muted);
59
63
  }
@@ -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 {hbox, div} from '@xh/hoist/cmp/layout';
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
- hbox({
30
- className: `xh-custom-filter-tab__row__top`,
29
+ vbox({
30
+ className: `xh-custom-filter-tab__row__body`,
31
31
  items: [
32
- select({
33
- bind: 'op',
34
- enableFilter: false,
35
- hideSelectedOptionCheck: true,
36
- options,
37
- optionRenderer: (opt) => operatorRenderer({opt})
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
- button({
40
- icon: Icon.delete(),
41
- intent: 'danger',
42
- onClick: () => model.removeRow()
43
+ div({
44
+ omit: hideInput,
45
+ className: `xh-custom-filter-tab__row__bottom`,
46
+ item: inputField()
43
47
  })
44
48
  ]
45
49
  }),
46
- hbox({
47
- omit: hideInput,
48
- className: `xh-custom-filter-tab__row__bottom`,
49
- item: inputField()
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: 200,
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
- &__top {
24
- display: flex;
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: 200px;
34
+ width: 100%;
46
35
  }
47
36
  }
37
+
38
+ &__right {
39
+ padding-left: 2px;
40
+ }
48
41
  }
49
42
 
50
43
  &__implicit_join_message {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "45.0.1",
3
+ "version": "45.0.2",
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",