@xh/hoist 72.0.0-SNAPSHOT.1737676613505 → 72.0.0-SNAPSHOT.1737733122023

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
@@ -15,7 +15,8 @@
15
15
  ### 🐞 Bug Fixes
16
16
 
17
17
  * Fixed `ViewManagerModel` unique name validation.
18
- * `GridModel.restoreDefaultsAsync()` now restores default filter rather than simply clearing it.
18
+ * Fixed `GridModel.restoreDefaultsAsync()` to restore any default filter, rather than simply clearing it.
19
+ * Improved suboptimal column state synchronization between `GridModel` and AG Grid.
19
20
 
20
21
  ### ⚙️ Technical
21
22
 
@@ -17,12 +17,11 @@ import {memoryMonitorPanel} from '@xh/hoist/admin/tabs/cluster/instances/memory/
17
17
  import {servicePanel} from '@xh/hoist/admin/tabs/cluster/instances/services/ServicePanel';
18
18
  import {webSocketPanel} from '@xh/hoist/admin/tabs/cluster/instances/websocket/WebSocketPanel';
19
19
  import {badge} from '@xh/hoist/cmp/badge';
20
- import {GridModel, numberCol} from '@xh/hoist/cmp/grid';
20
+ import {GridContextMenuSpec, GridModel, numberCol} from '@xh/hoist/cmp/grid';
21
21
  import {hbox} from '@xh/hoist/cmp/layout';
22
22
  import {getRelativeTimestamp} from '@xh/hoist/cmp/relativetimestamp';
23
23
  import {TabContainerModel, TabModel} from '@xh/hoist/cmp/tab';
24
24
  import {HoistModel, LoadSpec, lookup, managed, PlainObject, XH} from '@xh/hoist/core';
25
- import {RecordActionSpec} from '@xh/hoist/data';
26
25
  import {Icon} from '@xh/hoist/icon';
27
26
  import {makeObservable} from '@xh/hoist/mobx';
28
27
  import {Timer} from '@xh/hoist/utils/async';
@@ -38,15 +37,6 @@ export class InstancesTabModel extends HoistModel {
38
37
  @managed readonly tabContainerModel: TabContainerModel = this.createTabContainerModel();
39
38
  @managed readonly timer: Timer;
40
39
 
41
- shutdownAction: RecordActionSpec = {
42
- icon: Icon.skull(),
43
- text: 'Shutdown Instance',
44
- intent: 'danger',
45
- actionFn: ({record}) => this.shutdownInstanceAsync(record.data),
46
- displayFn: () => ({hidden: AppModel.readonly}),
47
- recordsRequired: 1
48
- };
49
-
50
40
  get instance(): PlainObject {
51
41
  return this.gridModel.selectedRecord?.data;
52
42
  }
@@ -182,10 +172,25 @@ export class InstancesTabModel extends HoistModel {
182
172
  rendererIsComplex: true
183
173
  }
184
174
  ],
185
- contextMenu: [this.shutdownAction, '-', ...GridModel.defaultContextMenu]
175
+ contextMenu: this.createContextMenu()
186
176
  });
187
177
  }
188
178
 
179
+ private createContextMenu(): GridContextMenuSpec {
180
+ return [
181
+ {
182
+ icon: Icon.skull(),
183
+ text: 'Shutdown Instance',
184
+ intent: 'danger',
185
+ actionFn: ({record}) => this.shutdownInstanceAsync(record.data),
186
+ displayFn: () => ({hidden: AppModel.readonly}),
187
+ recordsRequired: 1
188
+ },
189
+ '-',
190
+ ...GridModel.defaultContextMenu
191
+ ];
192
+ }
193
+
189
194
  private createTabContainerModel() {
190
195
  return new TabContainerModel({
191
196
  route: 'default.cluster.instances',
@@ -1,7 +1,6 @@
1
1
  import { GridModel } from '@xh/hoist/cmp/grid';
2
2
  import { TabContainerModel } from '@xh/hoist/cmp/tab';
3
3
  import { HoistModel, LoadSpec, PlainObject } from '@xh/hoist/core';
4
- import { RecordActionSpec } from '@xh/hoist/data';
5
4
  import { Timer } from '@xh/hoist/utils/async';
6
5
  import { ReactNode } from 'react';
7
6
  export declare class InstancesTabModel extends HoistModel {
@@ -12,7 +11,6 @@ export declare class InstancesTabModel extends HoistModel {
12
11
  readonly gridModel: GridModel;
13
12
  readonly tabContainerModel: TabContainerModel;
14
13
  readonly timer: Timer;
15
- shutdownAction: RecordActionSpec;
16
14
  get instance(): PlainObject;
17
15
  get instanceName(): string;
18
16
  get isMultiInstance(): boolean;
@@ -21,6 +19,7 @@ export declare class InstancesTabModel extends HoistModel {
21
19
  constructor();
22
20
  formatInstance(instance: PlainObject): ReactNode;
23
21
  private createGridModel;
22
+ private createContextMenu;
24
23
  private createTabContainerModel;
25
24
  private shutdownInstanceAsync;
26
25
  private get autoRefreshInterval();
@@ -1,6 +1,8 @@
1
1
  /// <reference types="react" />
2
2
  /// <reference types="lodash" />
3
- import { HoistModel, HoistProps, LayoutProps, PlainObject, TestSupportProps } from '@xh/hoist/core';
3
+ import { GridApi } from '@ag-grid-community/core';
4
+ import { ColumnState } from '@xh/hoist/cmp/grid';
5
+ import { HoistModel, HoistProps, LayoutProps, PlainObject, ReactionSpec, TestSupportProps } from '@xh/hoist/core';
4
6
  import { RecordSet } from '@xh/hoist/data/impl/RecordSet';
5
7
  import type { ColDef, ColGroupDef, GetContextMenuItemsParams, GridOptions, GridReadyEvent, ProcessCellForExportParams } from '@xh/hoist/kit/ag-grid';
6
8
  import './Grid.scss';
@@ -53,7 +55,7 @@ export declare class GridLocalModel extends HoistModel {
53
55
  onLinked(): void;
54
56
  private createDefaultAgOptions;
55
57
  getColumnDefs(): Array<ColDef | ColGroupDef>;
56
- getContextMenuItems: (params: GetContextMenuItemsParams) => (string | import("@xh/hoist/kit/ag-grid").MenuItemDef<any, any>)[];
58
+ getContextMenuItems: (params: GetContextMenuItemsParams) => (string | import("@ag-grid-community/core").MenuItemDef<any, any>)[];
57
59
  dataReaction(): {
58
60
  track: () => (boolean | import("@xh/hoist/core").VSide | import("../../data").StoreRecord[] | RecordSet)[];
59
61
  run: () => void;
@@ -63,11 +65,11 @@ export declare class GridLocalModel extends HoistModel {
63
65
  run: () => void;
64
66
  };
65
67
  sortReaction(): {
66
- track: () => (import("@xh/hoist/kit/ag-grid").GridApi<any> | import("@xh/hoist/cmp/grid").GridSorter[])[];
68
+ track: () => (GridApi<any> | import("@xh/hoist/cmp/grid").GridSorter[])[];
67
69
  run: ([agApi, sortBy]: [any, any]) => void;
68
70
  };
69
71
  groupReaction(): {
70
- track: () => (string[] | import("@xh/hoist/kit/ag-grid").GridApi<any>)[];
72
+ track: () => (string[] | GridApi<any>)[];
71
73
  run: ([agApi, groupBy]: [any, any]) => void;
72
74
  };
73
75
  get calculatedRowHeight(): any;
@@ -83,13 +85,10 @@ export declare class GridLocalModel extends HoistModel {
83
85
  get useScrollOptimization(): boolean;
84
86
  applyScrollOptimization(): void;
85
87
  columnsReaction(): {
86
- track: () => (import("@xh/hoist/kit/ag-grid").GridApi<any> | (import("@xh/hoist/cmp/grid").Column | import("@xh/hoist/cmp/grid").ColumnGroup)[])[];
88
+ track: () => (GridApi<any> | (import("@xh/hoist/cmp/grid").Column | import("@xh/hoist/cmp/grid").ColumnGroup)[])[];
87
89
  run: ([api]: [any]) => void;
88
90
  };
89
- columnStateReaction(): {
90
- track: () => (import("@xh/hoist/kit/ag-grid").GridApi<any> | import("@xh/hoist/cmp/grid").ColumnState[])[];
91
- run: ([api, colState]: [any, any]) => void;
92
- };
91
+ columnStateReaction(): ReactionSpec<[GridApi, ColumnState[]]>;
93
92
  sizingModeReaction(): {
94
93
  track: () => import("@xh/hoist/core").SizingMode;
95
94
  run: () => void;
@@ -126,7 +125,7 @@ export declare class GridLocalModel extends HoistModel {
126
125
  readFilterState(): import("@ag-grid-community/core").FilterModel;
127
126
  writeFilterState(filterState: any): void;
128
127
  processCellForClipboard: ({ value, node, column }: ProcessCellForExportParams) => any;
129
- navigateToNextCell: (agParams: any) => import("@xh/hoist/kit/ag-grid").CellPosition;
128
+ navigateToNextCell: (agParams: any) => import("@ag-grid-community/core").CellPosition;
130
129
  onCellMouseDown: (evt: any) => void;
131
130
  onKeyDown: (evt: any) => void;
132
131
  onRowClicked: (evt: any) => void;
@@ -1,4 +1,4 @@
1
- import { CellClickedEvent, CellContextMenuEvent, CellDoubleClickedEvent, RowClickedEvent, RowDoubleClickedEvent } from '@ag-grid-community/core';
1
+ import { CellClickedEvent, CellContextMenuEvent, CellDoubleClickedEvent, ColumnState as AgColumnState, RowClickedEvent, RowDoubleClickedEvent } from '@ag-grid-community/core';
2
2
  import { AgGridModel } from '@xh/hoist/cmp/ag-grid';
3
3
  import { Column, ColumnGroup, ColumnGroupSpec, ColumnSpec, GridAutosizeMode, GridFilterModelConfig, GridGroupSortFn, TreeStyle } from '@xh/hoist/cmp/grid';
4
4
  import { GridFilterModel } from '@xh/hoist/cmp/grid/filter/GridFilterModel';
@@ -430,7 +430,7 @@ export declare class GridModel extends HoistModel {
430
430
  setColumns(colConfigs: Array<ColumnSpec | ColumnGroupSpec>): void;
431
431
  setColumnState(colState: Partial<ColumnState>[]): void;
432
432
  showColChooser(): void;
433
- noteAgColumnStateChanged(agColState: any): void;
433
+ noteAgColumnStateChanged(agColState: AgColumnState[]): void;
434
434
  setExpandState(expandState: any): void;
435
435
  noteAgExpandStateChange(): void;
436
436
  noteAgSelectionStateChanged(): void;
@@ -395,7 +395,7 @@ export class AgGridModel extends HoistModel {
395
395
  ];
396
396
 
397
397
  let {isPivot, columns} = colState;
398
- agApi.setPivotMode(isPivot);
398
+ agApi.setGridOption('pivotMode', isPivot);
399
399
 
400
400
  if (isPivot && columns.some(it => !isNil(it.pivotIndex) && it.pivotIndex >= 0)) {
401
401
  // Exclude the auto group column as this causes issues with ag-grid when in pivot mode
package/cmp/grid/Grid.ts CHANGED
@@ -4,9 +4,10 @@
4
4
  *
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {ColumnState as AgColumnState, GridApi} from '@ag-grid-community/core';
7
8
  import composeRefs from '@seznam/compose-react-refs';
8
9
  import {agGrid, AgGrid} from '@xh/hoist/cmp/ag-grid';
9
- import {getTreeStyleClasses} from '@xh/hoist/cmp/grid';
10
+ import {ColumnState, getTreeStyleClasses} from '@xh/hoist/cmp/grid';
10
11
  import {gridHScrollbar} from '@xh/hoist/cmp/grid/impl/GridHScrollbar';
11
12
  import {getAgGridMenuItems} from '@xh/hoist/cmp/grid/impl/MenuSupport';
12
13
  import {div, fragment, frame, vframe} from '@xh/hoist/cmp/layout';
@@ -17,6 +18,7 @@ import {
17
18
  LayoutProps,
18
19
  lookup,
19
20
  PlainObject,
21
+ ReactionSpec,
20
22
  TestSupportProps,
21
23
  useLocalModel,
22
24
  uses,
@@ -44,7 +46,7 @@ import {wait} from '@xh/hoist/promise';
44
46
  import {consumeEvent, isDisplayed, logWithDebug} from '@xh/hoist/utils/js';
45
47
  import {createObservableRef, getLayoutProps} from '@xh/hoist/utils/react';
46
48
  import classNames from 'classnames';
47
- import {debounce, isEmpty, isEqual, isNil, max, maxBy, merge} from 'lodash';
49
+ import {compact, debounce, isBoolean, isEmpty, isEqual, isNil, max, maxBy, merge} from 'lodash';
48
50
  import './Grid.scss';
49
51
  import {GridModel} from './GridModel';
50
52
  import {columnGroupHeader} from './impl/ColumnGroupHeader';
@@ -463,7 +465,7 @@ export class GridLocalModel extends HoistModel {
463
465
  };
464
466
  }
465
467
 
466
- columnStateReaction() {
468
+ columnStateReaction(): ReactionSpec<[GridApi, ColumnState[]]> {
467
469
  const {model} = this;
468
470
  return {
469
471
  track: () => [model.agApi, model.columnState],
@@ -472,65 +474,57 @@ export class GridLocalModel extends HoistModel {
472
474
 
473
475
  const agColState = api.getColumnState();
474
476
 
475
- // 0) Insert the auto group col state if it exists, since we won't have it in our column state list
477
+ // Insert the auto group col state if it exists, since we won't have it in our column state list
476
478
  const autoColState = agColState.find(c => c.colId === 'ag-Grid-AutoColumn');
477
479
  if (autoColState) {
478
- colState.splice(agColState.indexOf(autoColState), 0, autoColState);
480
+ const {colId, width, hide, pinned} = autoColState;
481
+ colState.splice(agColState.indexOf(autoColState), 0, {
482
+ colId,
483
+ width,
484
+ hidden: hide,
485
+ pinned: isBoolean(pinned) ? (pinned ? 'left' : null) : pinned
486
+ });
479
487
  }
480
488
 
481
- // 1) Columns all in right place -- simply update incorrect props we maintain
482
- if (
483
- isEqual(
484
- colState.map(c => c.colId),
485
- agColState.map(c => c.colId)
486
- )
487
- ) {
488
- let hasChanges = false;
489
- colState.forEach((col, index) => {
490
- const agCol = agColState[index],
491
- id = col.colId;
492
-
493
- if (agCol.width !== col.width) {
494
- api.setColumnWidths([{key: id, newWidth: col.width}]);
489
+ // Determine if column order has changed
490
+ const applyOrder = !isEqual(
491
+ colState.map(c => c.colId),
492
+ agColState.map(c => c.colId)
493
+ );
494
+
495
+ // Build a list of column state changes
496
+ colState = compact(
497
+ colState.map(({colId, width, hidden, pinned}) => {
498
+ const agCol: AgColumnState = agColState.find(c => c.colId === colId) || {
499
+ colId
500
+ },
501
+ ret: any = {colId};
502
+
503
+ let hasChanges = applyOrder;
504
+
505
+ if (agCol.width !== width) {
506
+ ret.width = width;
495
507
  hasChanges = true;
496
508
  }
497
- if (agCol.hide !== col.hidden) {
498
- api.setColumnsVisible([id], !col.hidden);
509
+
510
+ if (agCol.hide !== hidden) {
511
+ ret.hide = hidden;
499
512
  hasChanges = true;
500
513
  }
501
- if (agCol.pinned !== col.pinned) {
502
- api.setColumnsPinned([id], col.pinned);
514
+
515
+ if (agCol.pinned !== pinned) {
516
+ ret.pinned = pinned;
503
517
  hasChanges = true;
504
518
  }
505
- });
506
-
507
- // We need to tell agGrid to refresh its flexed column sizes due to
508
- // a regression introduced in 25.1.0. See #2341
509
- if (hasChanges) {
510
- api.columnModel.refreshFlexedColumns({
511
- updateBodyWidths: true,
512
- fireResizedEvent: true
513
- });
514
- }
515
519
 
516
- return;
517
- }
520
+ return hasChanges ? ret : null;
521
+ })
522
+ );
518
523
 
519
- // 2) Otherwise do an (expensive) full refresh of column state
520
- // Merge our state onto the ag column state to get any state which we do not yet support
521
- colState = colState.map(({colId, width, hidden, pinned}) => {
522
- const agCol = agColState.find(c => c.colId === colId) || {};
523
- return {
524
- colId,
525
- ...agCol,
526
- width,
527
- pinned,
528
- hide: hidden
529
- };
530
- });
524
+ if (isEmpty(colState)) return;
531
525
 
532
526
  this.doWithPreservedState({expansion: false}, () => {
533
- api.applyColumnState({state: colState, applyOrder: true});
527
+ api.applyColumnState({state: colState, applyOrder});
534
528
  });
535
529
  }
536
530
  };
@@ -870,6 +864,7 @@ export class GridLocalModel extends HoistModel {
870
864
  * by conditionally stopping the focus event from propagating.
871
865
  */
872
866
  private static didAddFocusFixListener = false;
867
+
873
868
  static addFocusFixListener() {
874
869
  if (this.didAddFocusFixListener) return;
875
870
  document.addEventListener(
@@ -9,6 +9,7 @@ import {
9
9
  CellContextMenuEvent,
10
10
  CellDoubleClickedEvent,
11
11
  ColumnEvent,
12
+ ColumnState as AgColumnState,
12
13
  RowClickedEvent,
13
14
  RowDoubleClickedEvent
14
15
  } from '@ag-grid-community/core';
@@ -77,6 +78,7 @@ import {
77
78
  first,
78
79
  forEach,
79
80
  isArray,
81
+ isBoolean,
80
82
  isEmpty,
81
83
  isFunction,
82
84
  isNil,
@@ -1073,17 +1075,19 @@ export class GridModel extends HoistModel {
1073
1075
  (this.colChooserModel as any)?.open();
1074
1076
  }
1075
1077
 
1076
- noteAgColumnStateChanged(agColState) {
1077
- const colStateChanges = agColState.map(({colId, width, hide, pinned}) => {
1078
- const col = this.findColumn(this.columns, colId);
1079
- if (!col) return null;
1080
- return {
1081
- colId,
1082
- pinned: pinned ?? null,
1083
- hidden: !!hide,
1084
- width: col.flex ? undefined : width
1085
- };
1086
- });
1078
+ noteAgColumnStateChanged(agColState: AgColumnState[]) {
1079
+ const colStateChanges: Partial<ColumnState>[] = agColState.map(
1080
+ ({colId, width, hide, pinned}) => {
1081
+ const col = this.findColumn(this.columns, colId);
1082
+ if (!col) return null;
1083
+ return {
1084
+ colId,
1085
+ pinned: isBoolean(pinned) ? (pinned ? 'left' : null) : pinned,
1086
+ hidden: !!hide,
1087
+ width: col.flex ? undefined : width
1088
+ };
1089
+ }
1090
+ );
1087
1091
 
1088
1092
  pull(colStateChanges, null);
1089
1093
  this.applyColumnStateChanges(colStateChanges);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "72.0.0-SNAPSHOT.1737676613505",
3
+ "version": "72.0.0-SNAPSHOT.1737733122023",
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",