@xh/hoist 56.1.0 → 56.3.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +74 -30
  2. package/admin/tabs/server/memory/MemoryMonitorModel.ts +3 -2
  3. package/admin/tabs/server/websocket/WebSocketModel.ts +1 -1
  4. package/appcontainer/AppContainerModel.ts +3 -1
  5. package/appcontainer/BannerModel.ts +24 -14
  6. package/appcontainer/BannerSourceModel.ts +19 -24
  7. package/cmp/ag-grid/AgGrid.ts +20 -7
  8. package/cmp/filter/FilterChooserModel.ts +2 -2
  9. package/cmp/form/FormModel.ts +1 -1
  10. package/cmp/grid/Grid.ts +68 -25
  11. package/core/HoistComponent.ts +8 -9
  12. package/core/HoistProps.ts +4 -1
  13. package/core/XH.ts +5 -5
  14. package/core/types/Interfaces.ts +11 -2
  15. package/data/RecordAction.ts +1 -1
  16. package/data/Store.ts +1 -1
  17. package/data/StoreRecord.ts +2 -2
  18. package/data/cube/Cube.ts +3 -3
  19. package/data/cube/CubeField.ts +2 -5
  20. package/data/cube/View.ts +1 -1
  21. package/data/validation/Rule.ts +3 -3
  22. package/desktop/appcontainer/Banner.scss +0 -2
  23. package/desktop/appcontainer/Banner.ts +2 -3
  24. package/desktop/cmp/dash/DashViewModel.ts +2 -1
  25. package/desktop/cmp/dash/canvas/DashCanvasModel.ts +1 -1
  26. package/desktop/cmp/dash/container/DashContainerModel.ts +10 -3
  27. package/desktop/cmp/error/ErrorMessage.ts +2 -2
  28. package/desktop/cmp/grid/impl/filter/ColumnHeaderFilter.scss +2 -2
  29. package/desktop/cmp/input/CodeInput.ts +2 -2
  30. package/desktop/cmp/treemap/TreeMapModel.ts +1 -1
  31. package/docs/upgrade-to-typescript.md +6 -1
  32. package/kit/ag-grid/index.ts +1 -0
  33. package/mobile/appcontainer/Banner.scss +1 -1
  34. package/mobile/appcontainer/Banner.ts +2 -3
  35. package/mobile/appcontainer/ExceptionDialogDetails.ts +3 -3
  36. package/mobile/cmp/error/ErrorMessage.ts +2 -2
  37. package/mobile/cmp/input/DateInput.ts +2 -2
  38. package/mobile/cmp/popover/Popover.ts +10 -2
  39. package/package.json +8 -8
  40. package/styles/vars.scss +4 -7
  41. package/svc/AlertBannerService.ts +2 -0
package/CHANGELOG.md CHANGED
@@ -1,12 +1,60 @@
1
1
  # Changelog
2
2
 
3
- ## 56.1.0 - 2023-04-14
4
- * Add support for new memory management diagnostics provided by hoist-core
5
- (requires hoist-core 16.1.0 for full operation).
3
+ ## v56.3.0 - 2023-05-08
4
+
5
+ ### 🎁 New Features
6
+
7
+ * Added support for new `sortOrder` argument to `XH.showBanner()`. A default sort order is applied
8
+ if unspecified, ensuring banners do not unexpectedly change order when refreshed.
9
+
10
+ ### ⚙️ Typescript API Adjustments
11
+
12
+ * Improved the recommendation for the app `declare` statement within
13
+ our [TypeScript migration docs](https://github.com/xh/hoist-react/blob/develop/docs/upgrade-to-typescript.md#bootstrapts--service-declarations).
14
+ * See this [Toolbox commit](https://github.com/xh/toolbox/commit/8df642cf) for a small,
15
+ recommended app-level change to improve autocompletion and usage checks within IntelliJ.
16
+ * Added generic support to `XH.message()` and `XH.prompt()` signatures with return type
17
+ of `Promise<T | boolean>`.
18
+ * Moved declaration of optional `children` prop to base `HoistProps` interface - required for TSX
19
+ support.
20
+
21
+ ### ✨ Styles
22
+
23
+ * Removed `--xh-banner-height` CSS var.
24
+ * Desktop banners are implemented via `Toolbar`, which correctly sets a min height.
25
+ * Mobile banners now specify `min-height: 40px` via the `.xh-banner` class.
26
+ * This change allows banners containing custom components to grow to fit their contents without
27
+ requiring app-level CSS overrides.
28
+ * Added new `--xh-grid-filter-popover-[height|width]-px` CSS variables to support easier custom
29
+ sizing for grid column header filter popovers.
30
+
31
+ ### ⚙️ Technical
32
+
33
+ * Updated internal config defaults to support latest AG Grid v29.3.4+ with use of
34
+ AG `suppressBrowserResizeObserver` config. Applications are encouraged to update to the latest AG
35
+ Grid dependencies to take advantage of ongoing performance updates.
36
+
37
+ ## v56.2.0 - 2023-04-28
38
+
39
+ ### 🎁 New Features
40
+
41
+ * Added `DashContainerModel.margin` config to customize the width of the resize splitters
42
+ between widgets.
43
+
44
+ ### ⚙️ Technical
45
+
46
+ * Improve scrolling performance for `Grid` and `DataView` via internal configuration updates.
47
+
48
+ ## v56.1.0 - 2023-04-14
49
+
50
+ ### 🎁 New Features
51
+
52
+ * Display improved memory management diagnostics within Admin console Memory Monitor.
53
+ * New metrics require optional-but-recommended update to `hoist-core >= v16.1.0`.
6
54
 
7
55
  ### 🐞 Bug Fixes
8
- * Fixes bug with display/reporting of exceptions during app initialization sequence.
9
56
 
57
+ * Fixes bug with display/reporting of exceptions during app initialization sequence.
10
58
 
11
59
  ## v56.0.0 - 2023-03-29
12
60
 
@@ -28,10 +76,12 @@
28
76
  * Add a dependency on `@ag-grid-community/styles` to import new dedicated styles package.
29
77
  * Imports of AG Grid CSS files within your app's `Bootstrap.ts` file will also need to be
30
78
  updated to import styles from their new location. The recommended imports are now:
79
+
31
80
  ```typescript
32
81
  import '@ag-grid-community/styles/ag-grid.css';
33
82
  import '@ag-grid-community/styles/ag-theme-balham.css';
34
83
  ```
84
+
35
85
  * New `xhActivityTrackingConfig` soft-configuration entry places new limits on the size of
36
86
  any `data` objects passed to `XH.track()` calls.
37
87
  * Any track requests with data objects exceeding this length will be persisted, but without the
@@ -65,20 +115,14 @@ import '@ag-grid-community/styles/ag-theme-balham.css';
65
115
 
66
116
  ## v55.4.0 - 2023-03-23
67
117
 
68
- ### 🐞 Bug Fixes
69
- * Addresses `AgGrid` v28 regression whereby changing column visibility via state throws an
70
- exception and doesn't
71
- render the grid when column groups are set via the `groupId` property.
72
-
73
118
  ### 💥 Breaking Changes
74
- * Hoist now requires `AgGrid` v29.1.0 or higher - update your `AgGrid` dependency in your app's
75
- `package.json` file. See the [ag-Grid Changelog](https://www.ag-grid.com/changelog) for details.
76
- * `AgGrid` stylesheets are now imported from the new `@ag-grid-community/styles` module. Update
77
- your app's `Bootstrap.ts` file to import `ag-grid.css` and `ag-theme-balham.css` from this
78
- module, and include it as a dependency in your app's `package.json` file.
79
119
 
80
- ### ⚙️ Technical
81
- * AgGrid `28.1.0 -> 29.1.0`
120
+ * Requires AG Grid v29.0.0 or higher - see release notes for v56.0.0 above.
121
+
122
+ ### 🐞 Bug Fixes
123
+
124
+ * Addresses `AgGrid` v28 regression whereby changing column visibility via state breaks grid
125
+ rendering when column groups are set via the `groupId` property.
82
126
 
83
127
  ## v55.3.2 - 2023-03-22
84
128
 
@@ -1019,7 +1063,7 @@ to use TypeScript for its own app-level code.
1019
1063
  with the select library component and touch devices.
1020
1064
  * Ensure `Column.autosizeBufferPx` is respected if provided.
1021
1065
 
1022
- ### ✨ Style
1066
+ ### ✨ Styles
1023
1067
 
1024
1068
  * New `--xh-menu-item` CSS vars added, with tweaks to default desktop menu styling.
1025
1069
  * Highlight background color added to mobile menu items while pressed.
@@ -1143,7 +1187,7 @@ to use TypeScript for its own app-level code.
1143
1187
  * Triggering inline editing of text or select editor cells by typing characters will no longer lose
1144
1188
  the first character pressed.
1145
1189
 
1146
- ### ✨ Style
1190
+ ### ✨ Styles
1147
1191
 
1148
1192
  * New `TreeStyle.COLORS` and `TreeStyle.COLORS_AND_BORDERS` tree grid styles have been added. Use
1149
1193
  the `--xh-grid-tree-group-color-level-*` CSS vars to customize colors as needed.
@@ -1185,7 +1229,7 @@ to use TypeScript for its own app-level code.
1185
1229
  `agOptions`.
1186
1230
  * Fixes an issue on iOS where `NumberInput` would incorrectly bring up a text keyboard.
1187
1231
 
1188
- ### ✨ Style
1232
+ ### ✨ Styles
1189
1233
 
1190
1234
  * Reduced default Grid header and group row heights to minimize their use of vertical space,
1191
1235
  especially at larger sizing modes. As before, apps can override via the `AgGrid.HEADER_HEIGHTS`
@@ -1210,7 +1254,7 @@ to use TypeScript for its own app-level code.
1210
1254
  * The in-app changelog will no longer prompt the user with the "What's New" button if category-based
1211
1255
  filtering results in a version without any release notes.
1212
1256
 
1213
- ### ✨ Style
1257
+ ### ✨ Styles
1214
1258
 
1215
1259
  * New CSS vars added to support easier customization of desktop Tab font/size/color. Tabs now
1216
1260
  respect standard `--xh-font-size` by default.
@@ -1232,7 +1276,7 @@ to use TypeScript for its own app-level code.
1232
1276
  * Mobile `Select` input now supports async `queryFn` prop for parity with desktop.
1233
1277
  * `TreeMapModel` now supports new `maxLabels` config for improved performance.
1234
1278
 
1235
- ### ✨ Style
1279
+ ### ✨ Styles
1236
1280
 
1237
1281
  * Hoist's default font is now [Inter](https://rsms.me/inter/), shipped and bundled via the
1238
1282
  `inter-ui` npm package. Inter is a modern, open-source font that leverages optical sizing to
@@ -1293,7 +1337,7 @@ to use TypeScript for its own app-level code.
1293
1337
 
1294
1338
  * Fixed an issue preventing `FormField` labels from rendering if `fieldDefaults` was undefined.
1295
1339
 
1296
- ### ✨ Style
1340
+ ### ✨ Styles
1297
1341
 
1298
1342
  * New `Badge.compact` prop sets size to half that of parent element when true (default false). The
1299
1343
  `position` prop has been removed in favor of customizing placement of the component.
@@ -1347,7 +1391,7 @@ to use TypeScript for its own app-level code.
1347
1391
  and
1348
1392
  `selectedIds`, respectively, in `StoreSelectionModel`
1349
1393
 
1350
- ### ✨ Style
1394
+ ### ✨ Styles
1351
1395
 
1352
1396
  * Higher contrast on grid context menus for improved legibility.
1353
1397
 
@@ -1415,7 +1459,7 @@ to use TypeScript for its own app-level code.
1415
1459
  custom handling in a raw `AgGrid` component, see the example here:
1416
1460
  https://www.ag-grid.com/javascript-grid/row-selection/#example-selection-with-keyboard-arrow-keys
1417
1461
 
1418
- ### ✨ Style
1462
+ ### ✨ Styles
1419
1463
 
1420
1464
  * The red and green color values applied in dark mode have been lightened for improved legibility.
1421
1465
  * The default `colorSpec` config for number formatters has changed to use new dedicated CSS classes
@@ -1471,7 +1515,7 @@ to use TypeScript for its own app-level code.
1471
1515
  * `withShortDebug` has been deprecated. Use `withDebug` instead, which has the identical behavior.
1472
1516
  This API simplification mirrors a recent change to `hoist-core`.
1473
1517
 
1474
- ### ✨ Style
1518
+ ### ✨ Styles
1475
1519
 
1476
1520
  * If the first child of a `Placeholder` component is a Hoist icon, it will not automatically be
1477
1521
  styled to 4x size with reduced opacity. (See new Toolbox example under the "Other" tab.)
@@ -1640,7 +1684,7 @@ your dev-utils dependency for your project to build.
1640
1684
  * Improvements to exception serialization, especially for any raw javascript `Error` thrown by
1641
1685
  client-side code.
1642
1686
 
1643
- ### ✨ Style
1687
+ ### ✨ Styles
1644
1688
 
1645
1689
  * Buttons nested inline within desktop input components (e.g. clear buttons) tweaked to avoid
1646
1690
  odd-looking background highlight on hover.
@@ -1993,7 +2037,7 @@ decorators, in favor of a simpler inheritance-based approach to defining models
1993
2037
  * Fix issue where grid row striping inadvertently disabled by default for non-tree grids.
1994
2038
  * Fix issue where grid empty text cleared on autosize.
1995
2039
 
1996
- ### ✨ Style
2040
+ ### ✨ Styles
1997
2041
 
1998
2042
  * Default `Chart` themes reworked in both light and dark modes to better match overall Hoist theme.
1999
2043
 
@@ -2302,7 +2346,7 @@ below regarding related updates to `GridModel.columns` config processing.
2302
2346
  * `StoreFilterField.filterOptions` has been removed. Set `filterIncludesChildren` directly on
2303
2347
  the store instead.
2304
2348
 
2305
- ### ✨ Style
2349
+ ### ✨ Styles
2306
2350
 
2307
2351
  * CSS variables for "intents" - most commonly used on buttons - have been reworked to use HSL color
2308
2352
  values and support several standard variations of lightness and transparency.
@@ -3651,7 +3695,7 @@ leverage the context for model support discussed above.
3651
3695
  * When checking for a possible expired session within `XH.handleException()`, prompt for app login
3652
3696
  only for Ajax requests made to relative URLs (not e.g. remote APIs accessed via CORS). #1189
3653
3697
 
3654
- ### ✨ Style
3698
+ ### ✨ Styles
3655
3699
 
3656
3700
  * Panel splitter collapse button more visible in dark theme. CSS vars to customize further fixed.
3657
3701
  * The mobile app menu button has been moved to the right side of the top appBar, consistent with its
@@ -3908,7 +3952,7 @@ leverage the context for model support discussed above.
3908
3952
  * FetchService's fetch methods no longer support `acceptJson` parameter. Instead, pass an {"Accept":
3909
3953
  "application/json"} header using the `headers` parameter.
3910
3954
 
3911
- ### ✨ Style
3955
+ ### ✨ Styles
3912
3956
 
3913
3957
  * Black point + grid colors adjusted in dark theme to better blend with overall blue-gray tint.
3914
3958
  * Mobile styles have been adjusted to increase the default font size and grid row height, in
@@ -87,7 +87,8 @@ export class MemoryMonitorModel extends HoistModel {
87
87
  floor: 0,
88
88
  top: '30%',
89
89
  height: '70%',
90
- title: {text: 'Heap (mb)'}
90
+ title: {text: 'Heap (mb)'},
91
+ offset: 0
91
92
  }
92
93
  ],
93
94
  tooltip: {outside: true, shared: true}
@@ -185,7 +186,7 @@ export class MemoryMonitorModel extends HoistModel {
185
186
  async dumpHeapAsync() {
186
187
  try {
187
188
  const appEnv = XH.getEnv('appEnvironment').toLowerCase(),
188
- filename = await XH.prompt({
189
+ filename = await XH.prompt<string>({
189
190
  title: 'Dump Heap',
190
191
  icon: Icon.fileArchive(),
191
192
  message: `Specify a filename for the heap dump (to be saved in ${this.heapDumpDir})`,
@@ -93,7 +93,7 @@ export class WebSocketModel extends HoistModel {
93
93
  const {selectedRecords} = this.gridModel;
94
94
  if (isEmpty(selectedRecords)) return;
95
95
 
96
- const message = await XH.prompt({
96
+ const message = await XH.prompt<string>({
97
97
  title: 'Force suspend',
98
98
  icon: Icon.stopCircle(),
99
99
  confirmProps: {text: 'Force Suspend', icon: Icon.stopCircle(), intent: 'danger'},
@@ -19,6 +19,7 @@ import {SizingModeModel} from './SizingModeModel';
19
19
  import {ViewportSizeModel} from './ViewportSizeModel';
20
20
  import {ThemeModel} from './ThemeModel';
21
21
  import {ToastSourceModel} from './ToastSourceModel';
22
+ import {BannerModel} from './BannerModel';
22
23
 
23
24
  /**
24
25
  * Root object for Framework GUI State.
@@ -87,10 +88,11 @@ export class AppContainerModel extends HoistModel {
87
88
  buttonText = mobile ? version : `Update to ${version}`;
88
89
 
89
90
  XH.showBanner({
90
- category: 'app-update',
91
+ category: 'xhAppUpdate',
91
92
  message,
92
93
  icon: Icon.rocket({size: 'lg'}),
93
94
  intent: 'warning',
95
+ sortOrder: BannerModel.BANNER_SORTS.APP_UPDATE,
94
96
  enableClose: false,
95
97
  actionButtonProps: {
96
98
  icon: Icon.refresh(),
@@ -18,36 +18,46 @@ export class BannerModel extends HoistModel {
18
18
  icon;
19
19
  message;
20
20
  intent;
21
+ sortOrder;
21
22
  className;
22
23
  enableClose;
23
24
  onClose;
24
25
  onClick;
25
26
  actionButtonProps;
26
- props;
27
27
 
28
- constructor({
29
- category = 'default',
30
- icon,
31
- message,
32
- intent = 'primary',
33
- className,
34
- enableClose = true,
35
- onClose,
36
- onClick,
37
- actionButtonProps,
38
- ...props
39
- }: BannerSpec) {
28
+ /**
29
+ * Sort order for Hoist-provided banners.
30
+ */
31
+ static BANNER_SORTS = {
32
+ APP_UPDATE: -2,
33
+ ADMIN_ALERT: -1
34
+ };
35
+
36
+ constructor(spec: BannerSpec) {
40
37
  super();
41
38
 
39
+ const {
40
+ category = 'default',
41
+ icon,
42
+ message,
43
+ intent = 'primary',
44
+ sortOrder,
45
+ className,
46
+ enableClose = true,
47
+ onClose,
48
+ onClick,
49
+ actionButtonProps
50
+ } = spec;
51
+
42
52
  this.category = category;
43
53
  this.icon = icon;
44
54
  this.message = message;
45
55
  this.intent = intent;
56
+ this.sortOrder = sortOrder;
46
57
  this.className = className;
47
58
  this.enableClose = enableClose;
48
59
  this.onClose = onClose;
49
60
  this.onClick = onClick;
50
61
  this.actionButtonProps = actionButtonProps;
51
- this.props = props;
52
62
  }
53
63
  }
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import {XH, HoistModel, managed, BannerSpec} from '@xh/hoist/core';
8
8
  import {action, observable, makeObservable} from '@xh/hoist/mobx';
9
- import {find, reject} from 'lodash';
9
+ import {find, reject, sortBy, without, last} from 'lodash';
10
10
 
11
11
  import {BannerModel} from './BannerModel';
12
12
 
@@ -22,17 +22,29 @@ export class BannerSourceModel extends HoistModel {
22
22
  @observable.ref
23
23
  bannerModels: BannerModel[] = [];
24
24
 
25
- MAX_BANNERS = 4;
26
-
27
25
  constructor() {
28
26
  super();
29
27
  makeObservable(this);
30
28
  }
31
29
 
32
30
  @action
33
- show(config: BannerSpec): BannerModel {
34
- const ret = new BannerModel(config);
35
- this.addModel(ret);
31
+ show(spec: BannerSpec): BannerModel {
32
+ let {bannerModels} = this,
33
+ ret = new BannerModel(spec);
34
+
35
+ // Removes banner from new banner's category if it exists.
36
+ const existing = find(bannerModels, {category: ret.category});
37
+ if (existing) {
38
+ bannerModels = without(bannerModels, existing);
39
+ XH.safeDestroy(existing);
40
+ }
41
+
42
+ // Place in requested pos, existing pos, or last
43
+ const maxSortOrder = last(bannerModels)?.sortOrder ?? 0;
44
+ ret.sortOrder = spec.sortOrder ?? existing?.sortOrder ?? maxSortOrder + 1;
45
+ bannerModels = sortBy([...bannerModels, ret], 'sortOrder');
46
+
47
+ this.bannerModels = bannerModels;
36
48
  return ret;
37
49
  }
38
50
 
@@ -43,24 +55,7 @@ export class BannerSourceModel extends HoistModel {
43
55
  this.bannerModels = reject(this.bannerModels, {category});
44
56
  }
45
57
 
46
- //-----------------------------------
47
- // Implementation
48
- //------------------------------------
49
- @action
50
- addModel(model: BannerModel) {
51
- // Remove existing banner for category
52
- this.hide(model.category);
53
-
54
- // Add new banner, removing old banners if limit exceeded
55
- const models = [...this.bannerModels, model];
56
- while (models.length > this.MAX_BANNERS) {
57
- const bannerModel = models.shift();
58
- XH.safeDestroy(bannerModel);
59
- }
60
- this.bannerModels = models;
61
- }
62
-
63
- getBanner(category: string): BannerModel {
58
+ private getBanner(category: string): BannerModel {
64
59
  return find(this.bannerModels, {category});
65
60
  }
66
61
  }
@@ -35,13 +35,18 @@ export interface AgGridProps extends HoistProps<AgGridModel>, GridOptions, Layou
35
35
  * via the `model` prop to control additional Hoist customizations.
36
36
  *
37
37
  * This component complements and contrasts with the primary Hoist `Grid` class, which provides a
38
- * significantly more managed and opinionated wrapper around ag-Grid and a number of Hoist-specific
38
+ * significantly more managed and opinionated use of ag-Grid and a number of Hoist-specific
39
39
  * extensions and customizations. That fully managed component is expected to cover the majority of
40
40
  * use cases within Hoist apps and is recommended as the primary grid class within the toolkit.
41
41
  *
42
42
  * This wrapper is provided for advanced usages of grid that wish to leverage features of the
43
43
  * underlying component not yet supported by the Hoist layer - most notably pivoting - where the
44
44
  * managed option would conflict with or complicate access to those features.
45
+ *
46
+ * Note that this component uses the ag-Grid `getRowHeight` prop to provide the grid with row
47
+ * heights. As of 4/2023, this may cause scrolling to be slow in large data sets, and
48
+ * applications may wish to set this prop to `null` and use either a fixed `rowWidth` property, or
49
+ * an explicit per-row setting instead. See GridModel for a more efficient, data aware approach.
45
50
  */
46
51
  export const [AgGrid, agGrid] = hoistCmp.withFactory<AgGridProps>({
47
52
  displayName: 'AgGrid',
@@ -79,6 +84,7 @@ export const [AgGrid, agGrid] = hoistCmp.withFactory<AgGridProps>({
79
84
  item: createElement(AgGridReact, {
80
85
  // Default some ag-grid props, but allow overriding.
81
86
  getRowHeight: impl.getRowHeight,
87
+ suppressBrowserResizeObserver: true,
82
88
  // Pass others on directly.
83
89
  ...agGridProps,
84
90
 
@@ -125,8 +131,10 @@ class AgGridLocalModel extends HoistModel {
125
131
  @lookup(AgGridModel) model: AgGridModel;
126
132
 
127
133
  get headerHeight() {
128
- const {hideHeaders, sizingMode} = this.model;
129
- return hideHeaders ? 0 : (AgGrid as any).getHeaderHeightForSizingMode(sizingMode);
134
+ const {hideHeaders, sizingMode} = this.model,
135
+ AgGridCmp = AgGrid as any;
136
+
137
+ return hideHeaders ? 0 : AgGridCmp.getHeaderHeightForSizingMode(sizingMode);
130
138
  }
131
139
 
132
140
  override onLinked() {
@@ -141,15 +149,20 @@ class AgGridLocalModel extends HoistModel {
141
149
  }
142
150
  }
143
151
 
152
+ getRowHeight = ({node}) => {
153
+ const {sizingMode} = this.model,
154
+ {groupDisplayType} = this.componentProps,
155
+ AgGridCmp = AgGrid as any;
156
+ return node.group && groupDisplayType === 'groupRows'
157
+ ? AgGridCmp.getGroupRowHeightForSizingMode(sizingMode)
158
+ : AgGridCmp.getRowHeightForSizingMode(sizingMode);
159
+ };
160
+
144
161
  noteGridReady = agParams => {
145
162
  this.model.handleGridReady(agParams);
146
163
  this.componentProps.onGridReady?.(agParams);
147
164
  };
148
165
 
149
- getRowHeight = () => {
150
- return (AgGrid as any).getRowHeightForSizingMode(this.model.sizingMode);
151
- };
152
-
153
166
  override destroy() {
154
167
  this.model?.handleGridUnmount();
155
168
  super.destroy();
@@ -60,7 +60,7 @@ export interface FilterChooserConfig {
60
60
  fieldSpecs?: Array<FilterChooserFieldSpecConfig | string>;
61
61
 
62
62
  /** Default properties to be assigned to all FilterChooserFieldSpecs created by this model. */
63
- fieldSpecDefaults?: FilterChooserFieldSpecConfig;
63
+ fieldSpecDefaults?: Partial<FilterChooserFieldSpecConfig>;
64
64
 
65
65
  /**
66
66
  * Store or cube View that should actually be filtered as this model's value changes.
@@ -460,7 +460,7 @@ export class FilterChooserModel extends HoistModel {
460
460
  //--------------------------------
461
461
  parseFieldSpecs(
462
462
  specs: Array<FilterChooserFieldSpecConfig | string>,
463
- fieldSpecDefaults: FilterChooserFieldSpecConfig
463
+ fieldSpecDefaults: Partial<FilterChooserFieldSpecConfig>
464
464
  ): Array<FilterChooserFieldSpec> {
465
465
  const {valueSource} = this;
466
466
 
@@ -79,7 +79,7 @@ export class FormModel extends HoistModel {
79
79
  *
80
80
  * See {@link getData} instead if you need to get or react to the values of *any/all* fields.
81
81
  */
82
- get values(): Record<string, any> {
82
+ get values(): PlainObject {
83
83
  return this.valuesProxy;
84
84
  }
85
85
 
package/cmp/grid/Grid.ts CHANGED
@@ -8,7 +8,6 @@ import composeRefs from '@seznam/compose-react-refs';
8
8
  import {agGrid, AgGrid} from '@xh/hoist/cmp/ag-grid';
9
9
  import {getTreeStyleClasses} from '@xh/hoist/cmp/grid';
10
10
  import {getAgGridMenuItems} from '@xh/hoist/cmp/grid/impl/MenuSupport';
11
- import {Column} from './columns/Column';
12
11
  import {div, fragment, frame} from '@xh/hoist/cmp/layout';
13
12
  import {
14
13
  hoistCmp,
@@ -17,7 +16,6 @@ import {
17
16
  LayoutProps,
18
17
  lookup,
19
18
  PlainObject,
20
- SizingMode,
21
19
  useLocalModel,
22
20
  uses,
23
21
  XH
@@ -137,23 +135,9 @@ class GridLocalModel extends HoistModel {
137
135
  private model: GridModel;
138
136
  agOptions: GridOptions;
139
137
  viewRef = createRef<HTMLElement>();
140
- private fixedRowHeight: number;
141
138
  private rowKeyNavSupport: RowKeyNavSupport;
142
139
  private prevRs: RecordSet;
143
140
 
144
- getRowHeight(node) {
145
- const {model, agOptions} = this,
146
- {sizingMode, groupRowHeight} = model,
147
- {groupDisplayType} = agOptions;
148
-
149
- if (node?.group) {
150
- return groupRowHeight ?? groupDisplayType === 'groupRows'
151
- ? (AgGrid as any).getGroupRowHeightForSizingMode(sizingMode)
152
- : (AgGrid as any).getRowHeightForSizingMode(sizingMode);
153
- }
154
- return this.fixedRowHeight;
155
- }
156
-
157
141
  /** @returns true if any root-level records have children */
158
142
  @computed
159
143
  get isHierarchical(): boolean {
@@ -220,7 +204,7 @@ class GridLocalModel extends HoistModel {
220
204
  suppressRowClickSelection: !selModel.isEnabled,
221
205
  isRowSelectable: () => selModel.isEnabled,
222
206
  tooltipShowDelay: 0,
223
- getRowHeight: ({node}) => this.getRowHeight(node),
207
+ getRowHeight: this.defaultGetRowHeight,
224
208
  getRowClass: ({data}) => (model.rowClassFn ? model.rowClassFn(data) : null),
225
209
  rowClassRules: model.rowClassRules,
226
210
  noRowsOverlayComponent: observer(() => div(this.emptyText)),
@@ -367,20 +351,78 @@ class GridLocalModel extends HoistModel {
367
351
  };
368
352
  }
369
353
 
354
+ //----------------------
355
+ // Row Height Management
356
+ //----------------------
357
+ @computed
358
+ get calculatedRowHeight() {
359
+ const {model} = this,
360
+ AgGridCmp = AgGrid as any;
361
+ return max([
362
+ AgGridCmp.getRowHeightForSizingMode(model.sizingMode),
363
+ maxBy(model.getVisibleLeafColumns(), 'rowHeight')?.rowHeight
364
+ ]);
365
+ }
366
+
367
+ @computed
368
+ get calculatedGroupRowHeight() {
369
+ const {sizingMode, groupRowHeight} = this.model,
370
+ {groupDisplayType} = this.agOptions,
371
+ AgGridCmp = AgGrid as any;
372
+ return groupRowHeight ?? groupDisplayType === 'groupRows'
373
+ ? AgGridCmp.getGroupRowHeightForSizingMode(sizingMode)
374
+ : AgGridCmp.getRowHeightForSizingMode(sizingMode);
375
+ }
376
+
377
+ defaultGetRowHeight = ({node}) => {
378
+ return node.group ? this.calculatedGroupRowHeight : this.calculatedRowHeight;
379
+ };
380
+
370
381
  rowHeightReaction() {
371
- const {model} = this;
372
382
  return {
373
- track: () => [model.getVisibleLeafColumns(), model.sizingMode],
374
- run: ([visibleCols, sizingMode]: [Column[], SizingMode]) => {
375
- this.fixedRowHeight = max([
376
- (AgGrid as any).getRowHeightForSizingMode(sizingMode),
377
- maxBy(visibleCols, 'rowHeight')?.rowHeight
378
- ]);
383
+ track: () => [
384
+ this.useScrollOptimization,
385
+ this.calculatedRowHeight,
386
+ this.calculatedGroupRowHeight
387
+ ],
388
+ run: () => {
389
+ const {agApi} = this.model;
390
+ if (!agApi) return;
391
+ agApi.resetRowHeights();
392
+ this.applyScrollOptimization();
379
393
  },
380
- fireImmediately: true
394
+ debounce: 1
381
395
  };
382
396
  }
383
397
 
398
+ @computed
399
+ get useScrollOptimization() {
400
+ // When true, we preemptively evaluate and assign functional row heights after data loading.
401
+ // This improves slow scrolling but means function not guaranteed to be re-called
402
+ // when node is rendered in viewport.
403
+ const {model, agOptions} = this;
404
+ return (
405
+ agOptions.getRowHeight &&
406
+ !agOptions.rowHeight &&
407
+ !model.getVisibleLeafColumns().some(c => c.autoHeight) &&
408
+ model.experimental.useScrollOptimization !== false
409
+ );
410
+ }
411
+
412
+ applyScrollOptimization() {
413
+ if (!this.useScrollOptimization) return;
414
+ const {agApi, agColumnApi} = this.model,
415
+ {getRowHeight} = this.agOptions,
416
+ params = {api: agApi, columnApi: agColumnApi, context: null} as any;
417
+
418
+ agApi.forEachNode(node => {
419
+ params.node = node;
420
+ params.data = node.data;
421
+ node.setRowHeight(getRowHeight(params));
422
+ });
423
+ agApi.onRowHeightChanged();
424
+ }
425
+
384
426
  columnsReaction() {
385
427
  const {model} = this;
386
428
  return {
@@ -627,6 +669,7 @@ class GridLocalModel extends HoistModel {
627
669
  model.noteAgExpandStateChange();
628
670
 
629
671
  this.prevRs = newRs;
672
+ this.applyScrollOptimization();
630
673
  }
631
674
 
632
675
  syncSelection() {
@@ -43,8 +43,10 @@ import {
43
43
  } from 'react';
44
44
 
45
45
  /**
46
- * Configuration for creating a Component. May be specified either as a render function,
47
- * or an object containing a render function and associated metadata.
46
+ * Type representing props passed to a HoistComponent's render function.
47
+ *
48
+ * This type removes from its base type several props that are used by HoistComponent itself and
49
+ * not provided to the render function.
48
50
  */
49
51
  export type RenderPropsOf<P extends HoistProps> = P & {
50
52
  /** Pre-processed by HoistComponent internals into a mounted model. Never passed to render. */
@@ -52,15 +54,12 @@ export type RenderPropsOf<P extends HoistProps> = P & {
52
54
 
53
55
  /** Pre-processed by HoistComponent internals and attached to model. Never passed to render. */
54
56
  modelRef: never;
55
-
56
- /**
57
- * React Children. Populated on props by React internally, before rendering. Applications
58
- * will typically provide children to a component via JSX or the `item(s)` property passed to
59
- * an element factory.
60
- */
61
- children?: ReactNode;
62
57
  };
63
58
 
59
+ /**
60
+ * Configuration for creating a Component. May be specified either as a render function,
61
+ * or an object containing a render function and associated metadata.
62
+ */
64
63
  export type ComponentConfig<P extends HoistProps> =
65
64
  | ((props: RenderPropsOf<P>, ref?: ForwardedRef<any>) => ReactNode)
66
65
  | {