@xh/hoist 80.0.0-SNAPSHOT.1768415875152 → 80.0.0-SNAPSHOT.1768600489622

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.
@@ -83,7 +83,7 @@ export declare class GridLocalModel extends HoistModel {
83
83
  get useScrollOptimization(): boolean;
84
84
  applyScrollOptimization(): void;
85
85
  columnsReaction(): {
86
- track: () => ((import("@xh/hoist/cmp/grid").Column | import("@xh/hoist/cmp/grid").ColumnGroup)[] | GridApi<any>)[];
86
+ track: () => (import("@xh/hoist/cmp/grid").ColumnOrGroup[] | GridApi<any>)[];
87
87
  run: ([api]: [any]) => void;
88
88
  };
89
89
  columnStateReaction(): ReactionSpec<[GridApi, ColumnState[]]>;
@@ -1,7 +1,7 @@
1
1
  import { AgGridModel } from '@xh/hoist/cmp/ag-grid';
2
- import { Column, ColumnGroup, ColumnGroupSpec, ColumnSpec, GridAutosizeMode, GridFilterModelConfig, GridGroupSortFn, TreeStyle } from '@xh/hoist/cmp/grid';
2
+ import { Column, ColumnGroup, ColumnOrGroup, ColumnOrGroupSpec, ColumnSpec, GridAutosizeMode, GridFilterModelConfig, GridGroupSortFn, TreeStyle } from '@xh/hoist/cmp/grid';
3
3
  import { GridFilterModel } from '@xh/hoist/cmp/grid/filter/GridFilterModel';
4
- import { Awaitable, HoistModel, HSide, PlainObject, SizingMode, Some, TaskObserver, Thunkable, VSide } from '@xh/hoist/core';
4
+ import { Awaitable, HoistModel, HSide, LoadSpec, PlainObject, SizingMode, Some, TaskObserver, Thunkable, VSide } from '@xh/hoist/core';
5
5
  import { Store, StoreConfig, StoreRecord, StoreRecordId, StoreRecordOrId, StoreSelectionConfig, StoreSelectionModel, StoreTransaction } from '@xh/hoist/data';
6
6
  import { AgColumnState, CellClickedEvent, CellContextMenuEvent, CellDoubleClickedEvent, CellEditingStartedEvent, CellEditingStoppedEvent, RowClickedEvent, RowDoubleClickedEvent } from '@xh/hoist/kit/ag-grid';
7
7
  import { ExportOptions } from '@xh/hoist/svc/GridExportService';
@@ -12,7 +12,7 @@ import { GridSorter, GridSorterLike } from './GridSorter';
12
12
  import { ColChooserConfig, ColumnState, GridModelPersistOptions, GroupRowRenderer, RowClassFn, RowClassRuleFn } from './Types';
13
13
  export interface GridConfig {
14
14
  /** Columns for this grid. */
15
- columns?: Array<ColumnSpec | ColumnGroupSpec>;
15
+ columns?: ColumnOrGroupSpec[];
16
16
  /** Column configs to be set on all columns. Merges deeply. */
17
17
  colDefaults?: Partial<ColumnSpec>;
18
18
  /**
@@ -280,7 +280,7 @@ export declare class GridModel extends HoistModel {
280
280
  filterModel: GridFilterModel;
281
281
  agGridModel: AgGridModel;
282
282
  viewRef: RefObject<HTMLDivElement>;
283
- columns: Array<ColumnGroup | Column>;
283
+ columns: ColumnOrGroup[];
284
284
  columnState: ColumnState[];
285
285
  expandState: any;
286
286
  sortBy: GridSorter[];
@@ -471,14 +471,14 @@ export declare class GridModel extends HoistModel {
471
471
  * This method is a no-op if provided any sorters without a corresponding column.
472
472
  */
473
473
  setSortBy(sorters: Some<GridSorterLike>): void;
474
- doLoadAsync(loadSpec: any): Promise<any>;
474
+ doLoadAsync(loadSpec: LoadSpec): Promise<any>;
475
475
  /** Load the underlying store. */
476
476
  loadData(rawData: any[], rawSummaryData?: Some<PlainObject>): void;
477
477
  /** Update the underlying store. */
478
478
  updateData(rawData: PlainObject[] | StoreTransaction): import("@xh/hoist/data").StoreChangeLog;
479
479
  /** Clear the underlying store, removing all rows. */
480
480
  clear(): void;
481
- setColumns(colConfigs: Array<ColumnSpec | ColumnGroupSpec>): void;
481
+ setColumns(colConfigs: ColumnOrGroupSpec[]): void;
482
482
  setColumnState(colState: ColumnState[]): void;
483
483
  showColChooser(): void;
484
484
  noteAgColumnStateChanged(agColState: AgColumnState[]): void;
@@ -530,9 +530,9 @@ export declare class GridModel extends HoistModel {
530
530
  */
531
531
  getColumnPinned(colId: string): HSide;
532
532
  /** Return matching leaf-level Column object from the provided collection. */
533
- findColumn(cols: Array<Column | ColumnGroup>, colId: string): Column;
533
+ findColumn(cols: ColumnOrGroup[], colId: string): Column;
534
534
  /** Return matching ColumnGroup from the provided collection. */
535
- findColumnGroup(cols: Array<Column | ColumnGroup>, groupId: string): ColumnGroup;
535
+ findColumnGroup(cols: ColumnOrGroup[], groupId: string): ColumnGroup;
536
536
  /**
537
537
  * Return the current state object representation for the given colId.
538
538
  *
@@ -601,8 +601,6 @@ export declare class GridModel extends HoistModel {
601
601
  private collectIds;
602
602
  private formatValuesForExport;
603
603
  private parseAndSetColumnsAndStore;
604
- private validateStoreConfig;
605
- private validateColConfigs;
606
604
  private validateColumns;
607
605
  private cleanColumnState;
608
606
  private enhanceColConfigsFromStore;
@@ -1,11 +1,11 @@
1
1
  import { GridFilterFieldSpecConfig } from '@xh/hoist/cmp/grid/filter/GridFilterFieldSpec';
2
2
  import { HSide, PersistOptions, Some } from '@xh/hoist/core';
3
- import { Store, StoreRecord, View } from '@xh/hoist/data';
4
- import { ReactElement, ReactNode } from 'react';
5
- import { Column } from './columns/Column';
6
- import { ColumnGroup } from './columns/ColumnGroup';
7
- import { GridModel } from './GridModel';
8
- import type { CellClassParams, HeaderClassParams, HeaderValueGetterParams, ICellRendererParams, IRowNode, ITooltipParams, RowClassParams, ValueSetterParams, CustomCellEditorProps } from '@xh/hoist/kit/ag-grid';
3
+ import { FilterBindTarget, FilterValueSource, Store, StoreRecord } from '@xh/hoist/data';
4
+ import type { CellClassParams, CustomCellEditorProps, HeaderClassParams, HeaderValueGetterParams, ICellRendererParams, IRowNode, ITooltipParams, RowClassParams, ValueSetterParams } from '@xh/hoist/kit/ag-grid';
5
+ import type { ReactElement, ReactNode } from 'react';
6
+ import type { Column, ColumnSpec } from './columns/Column';
7
+ import type { ColumnGroup, ColumnGroupSpec } from './columns/ColumnGroup';
8
+ import type { GridModel } from './GridModel';
9
9
  export interface ColumnState {
10
10
  colId: string;
11
11
  width: number;
@@ -57,10 +57,11 @@ export interface GridModelPersistOptions extends PersistOptions {
57
57
  }
58
58
  export interface GridFilterModelConfig {
59
59
  /**
60
- * Store / Cube View to be filtered as column filters are applied. Defaulted to the
61
- * gridModel's store.
60
+ * Target (typically a {@link Store} or Cube {@link View}) to be filtered as column filters
61
+ * are applied and used as a source for unique values displayed in the filtering UI when
62
+ * applicable. Defaulted to the gridModel's store.
62
63
  */
63
- bind?: Store | View;
64
+ bind?: GridFilterBindTarget;
64
65
  /**
65
66
  * True to update filters immediately after each change made in the column-based filter UI.
66
67
  * Defaults to False.
@@ -68,13 +69,19 @@ export interface GridFilterModelConfig {
68
69
  commitOnChange?: boolean;
69
70
  /**
70
71
  * Specifies the fields this model supports for filtering. Should be configs for
71
- * {@link GridFilterFieldSpec}, string names to match with Fields in bound Store/View, or omitted
72
- * entirely to indicate that all fields should be filter-enabled.
72
+ * {@link GridFilterFieldSpec}, string names to match with Fields in bound Store/View, or
73
+ * omitted entirely to indicate that all fields should be filter-enabled.
73
74
  */
74
75
  fieldSpecs?: Array<string | GridFilterFieldSpecConfig>;
75
76
  /** Default properties to be assigned to all fieldSpecs created by this model. */
76
77
  fieldSpecDefaults?: Omit<GridFilterFieldSpecConfig, 'field'>;
77
78
  }
79
+ /**
80
+ * {@link GridFilterModel} currently accepts a single `bind` target that also provides available
81
+ * values. Note that both `Store` and `View` satisfy this intersection.
82
+ */
83
+ export interface GridFilterBindTarget extends FilterBindTarget, FilterValueSource {
84
+ }
78
85
  /**
79
86
  * Renderer for a group row
80
87
  * @param context - The group renderer params from ag-Grid
@@ -102,6 +109,9 @@ export interface ColChooserConfig {
102
109
  /** Chooser height for popover and dialog. Desktop only. */
103
110
  height?: string | number;
104
111
  }
112
+ export type ColumnOrGroup = Column | ColumnGroup;
113
+ export type ColumnOrGroupSpec = ColumnSpec | ColumnGroupSpec;
114
+ export declare function isColumnSpec(spec: ColumnOrGroupSpec): spec is ColumnSpec;
105
115
  /**
106
116
  * Sort comparator function for a grid column. Note that this comparator will also be called if
107
117
  * agGrid-provided column filtering is enabled: it is used to sort values shown for set filter
@@ -191,7 +201,7 @@ export type ColumnTooltipFn<T = any> = (value: T, cellContext: CellContext & {
191
201
  * @returns CSS class(es) to use.
192
202
  */
193
203
  export type ColumnHeaderClassFn = (context: {
194
- column: Column | ColumnGroup;
204
+ column: ColumnOrGroup;
195
205
  gridModel: GridModel;
196
206
  agParams: HeaderClassParams;
197
207
  }) => Some<string>;
@@ -2,7 +2,7 @@ import { HAlign, PlainObject, Some, Thunkable } from '@xh/hoist/core';
2
2
  import type { ColGroupDef } from '@xh/hoist/kit/ag-grid';
3
3
  import { ReactNode } from 'react';
4
4
  import { GridModel } from '../GridModel';
5
- import { ColumnHeaderClassFn, ColumnHeaderNameFn } from '../Types';
5
+ import { ColumnHeaderClassFn, ColumnHeaderNameFn, ColumnOrGroup } from '../Types';
6
6
  import { Column, ColumnSpec } from './Column';
7
7
  export interface ColumnGroupSpec {
8
8
  /** Column or ColumnGroup configs for children of this group.*/
@@ -35,7 +35,7 @@ export interface ColumnGroupSpec {
35
35
  * Provided to GridModels as plain configuration objects.
36
36
  */
37
37
  export declare class ColumnGroup {
38
- readonly children: Array<ColumnGroup | Column>;
38
+ readonly children: ColumnOrGroup[];
39
39
  readonly gridModel: GridModel;
40
40
  readonly groupId: string;
41
41
  readonly headerName: ReactNode | ColumnHeaderNameFn;
@@ -60,7 +60,7 @@ export declare class ColumnGroup {
60
60
  *
61
61
  * @internal
62
62
  */
63
- constructor(config: ColumnGroupSpec, gridModel: GridModel, children: Array<Column | ColumnGroup>);
63
+ constructor(config: ColumnGroupSpec, gridModel: GridModel, children: ColumnOrGroup[]);
64
64
  getAgSpec(): ColGroupDef;
65
65
  getLeafColumns(): Column[];
66
66
  }
@@ -1,6 +1,6 @@
1
- import { GridFilterFieldSpec, GridFilterModelConfig } from '@xh/hoist/cmp/grid';
1
+ import { GridFilterBindTarget, GridFilterFieldSpec, GridFilterModelConfig } from '@xh/hoist/cmp/grid';
2
2
  import { HoistModel } from '@xh/hoist/core';
3
- import { CompoundFilter, FieldFilter, Filter, FilterLike, Store, View } from '@xh/hoist/data';
3
+ import { CompoundFilter, FieldFilter, Filter, FilterLike } from '@xh/hoist/data';
4
4
  import { GridModel } from '../GridModel';
5
5
  /**
6
6
  * Model for managing a Grid's column filters.
@@ -9,7 +9,7 @@ import { GridModel } from '../GridModel';
9
9
  export declare class GridFilterModel extends HoistModel {
10
10
  xhImpl: boolean;
11
11
  gridModel: GridModel;
12
- bind: Store | View;
12
+ bind: GridFilterBindTarget;
13
13
  commitOnChange: boolean;
14
14
  fieldSpecs: GridFilterFieldSpec[];
15
15
  get filter(): Filter;
@@ -1,5 +1,5 @@
1
- import { Column, ColumnGroup, ColumnRenderer, GroupRowRenderer } from '@xh/hoist/cmp/grid';
2
- import { HeaderClassParams } from '@xh/hoist/kit/ag-grid';
1
+ import { ColumnOrGroup, ColumnRenderer, GroupRowRenderer } from '@xh/hoist/cmp/grid';
2
+ import type { HeaderClassParams } from '@xh/hoist/kit/ag-grid';
3
3
  /** @internal */
4
4
  export declare function managedRenderer<T extends ColumnRenderer | GroupRowRenderer>(fn: T, identifier: string): T;
5
5
  /**
@@ -8,4 +8,4 @@ export declare function managedRenderer<T extends ColumnRenderer | GroupRowRende
8
8
  *
9
9
  * @internal
10
10
  */
11
- export declare function getAgHeaderClassFn(column: Column | ColumnGroup): (params: HeaderClassParams) => string[];
11
+ export declare function getAgHeaderClassFn(column: ColumnOrGroup): (params: HeaderClassParams) => string[];
@@ -75,7 +75,7 @@ export declare class TabContainerModel extends HoistModel {
75
75
  protected lastActiveTabId: string;
76
76
  /**
77
77
  * @param config - TabContainer configuration.
78
- * @param [depth] - Depth in hierarchy of nested TabContainerModels. Not for application use.
78
+ * @param depth - Depth in hierarchy of nested TabContainerModels. Not for application use.
79
79
  */
80
80
  constructor({ tabs, defaultTabId, route, track, renderMode, refreshMode, persistWith, emptyText, xhImpl, switcher }: TabContainerConfig, depth?: number);
81
81
  /** Set/replace all tabs within the container. */
@@ -80,3 +80,5 @@ export type FieldType = (typeof FieldType)[keyof typeof FieldType];
80
80
  * @returns fieldName transformed into user-facing / longer name for display.
81
81
  */
82
82
  export declare function genDisplayName(fieldName: string): string;
83
+ /** Convenience function to return the name of a field from one of several common inputs. */
84
+ export declare function getFieldName(field: string | Field | FieldSpec): string;
@@ -1,14 +1,14 @@
1
1
  import { HoistBase, PlainObject, Some } from '@xh/hoist/core';
2
- import { CubeField, CubeFieldSpec } from './CubeField';
3
- import { ViewRowData } from './ViewRowData';
4
- import { QueryConfig } from './Query';
5
- import { View } from './View';
6
2
  import { Store, StoreRecordIdSpec, StoreTransaction } from '../Store';
7
3
  import { StoreRecord } from '../StoreRecord';
4
+ import { BucketSpec } from './BucketSpec';
5
+ import { CubeField, CubeFieldSpec } from './CubeField';
6
+ import { QueryConfig } from './Query';
8
7
  import { AggregateRow } from './row/AggregateRow';
9
- import { BucketRow } from './row/BucketRow';
10
8
  import { BaseRow } from './row/BaseRow';
11
- import { BucketSpec } from './BucketSpec';
9
+ import { BucketRow } from './row/BucketRow';
10
+ import { View } from './View';
11
+ import { ViewRowData } from './ViewRowData';
12
12
  export interface CubeConfig {
13
13
  fields: CubeField[] | CubeFieldSpec[];
14
14
  /** Default configs applied to all `CubeField`s constructed internally by this Cube. */
@@ -91,6 +91,7 @@ export declare class Cube extends HoistBase {
91
91
  get empty(): boolean;
92
92
  /** Count of currently connected, auto-updating Views. */
93
93
  get connectedViewCount(): number;
94
+ getField(name: string): CubeField;
94
95
  /**
95
96
  * Query the cube.
96
97
  *
@@ -10,10 +10,13 @@ import {
10
10
  ColumnCellClassRuleFn,
11
11
  ColumnGroup,
12
12
  ColumnGroupSpec,
13
+ ColumnOrGroup,
14
+ ColumnOrGroupSpec,
13
15
  ColumnSpec,
14
16
  GridAutosizeMode,
15
17
  GridFilterModelConfig,
16
18
  GridGroupSortFn,
19
+ isColumnSpec,
17
20
  TreeStyle
18
21
  } from '@xh/hoist/cmp/grid';
19
22
  import {GridFilterModel} from '@xh/hoist/cmp/grid/filter/GridFilterModel';
@@ -22,6 +25,7 @@ import {
22
25
  Awaitable,
23
26
  HoistModel,
24
27
  HSide,
28
+ LoadSpec,
25
29
  managed,
26
30
  PlainObject,
27
31
  SizingMode,
@@ -32,7 +36,9 @@ import {
32
36
  XH
33
37
  } from '@xh/hoist/core';
34
38
  import {
39
+ Field,
35
40
  FieldSpec,
41
+ getFieldName,
36
42
  Store,
37
43
  StoreConfig,
38
44
  StoreRecord,
@@ -87,7 +93,6 @@ import {
87
93
  isEmpty,
88
94
  isFunction,
89
95
  isNil,
90
- isPlainObject,
91
96
  isString,
92
97
  isUndefined,
93
98
  keysIn,
@@ -117,7 +122,7 @@ import {
117
122
 
118
123
  export interface GridConfig {
119
124
  /** Columns for this grid. */
120
- columns?: Array<ColumnSpec | ColumnGroupSpec>;
125
+ columns?: ColumnOrGroupSpec[];
121
126
 
122
127
  /** Column configs to be set on all columns. Merges deeply. */
123
128
  colDefaults?: Partial<ColumnSpec>;
@@ -454,7 +459,7 @@ export class GridModel extends HoistModel {
454
459
  //------------------------
455
460
  // Observable API
456
461
  //------------------------
457
- @observable.ref columns: Array<ColumnGroup | Column> = [];
462
+ @observable.ref columns: ColumnOrGroup[] = [];
458
463
  @observable.ref columnState: ColumnState[] = [];
459
464
  @observable.ref expandState: any = {};
460
465
  @observable.ref sortBy: GridSorter[] = [];
@@ -1146,7 +1151,7 @@ export class GridModel extends HoistModel {
1146
1151
  this.sortBy = newSorters;
1147
1152
  }
1148
1153
 
1149
- override async doLoadAsync(loadSpec) {
1154
+ override async doLoadAsync(loadSpec: LoadSpec) {
1150
1155
  // Delegate to any store that has load support
1151
1156
  return (this.store as any).loadSupport?.loadAsync(loadSpec);
1152
1157
  }
@@ -1167,8 +1172,7 @@ export class GridModel extends HoistModel {
1167
1172
  }
1168
1173
 
1169
1174
  @action
1170
- setColumns(colConfigs: Array<ColumnSpec | ColumnGroupSpec>) {
1171
- this.validateColConfigs(colConfigs);
1175
+ setColumns(colConfigs: ColumnOrGroupSpec[]) {
1172
1176
  colConfigs = this.enhanceColConfigsFromStore(colConfigs);
1173
1177
 
1174
1178
  const columns = compact(colConfigs.map(c => this.buildColumn(c)));
@@ -1362,7 +1366,7 @@ export class GridModel extends HoistModel {
1362
1366
  }
1363
1367
 
1364
1368
  /** Return matching leaf-level Column object from the provided collection. */
1365
- findColumn(cols: Array<Column | ColumnGroup>, colId: string): Column {
1369
+ findColumn(cols: ColumnOrGroup[], colId: string): Column {
1366
1370
  for (let col of cols) {
1367
1371
  if (col instanceof ColumnGroup) {
1368
1372
  const ret = this.findColumn(col.children, colId);
@@ -1375,7 +1379,7 @@ export class GridModel extends HoistModel {
1375
1379
  }
1376
1380
 
1377
1381
  /** Return matching ColumnGroup from the provided collection. */
1378
- findColumnGroup(cols: Array<Column | ColumnGroup>, groupId: string): ColumnGroup {
1382
+ findColumnGroup(cols: ColumnOrGroup[], groupId: string): ColumnGroup {
1379
1383
  for (let col of cols) {
1380
1384
  if (col instanceof ColumnGroup) {
1381
1385
  if (col.groupId === groupId) return col;
@@ -1595,7 +1599,7 @@ export class GridModel extends HoistModel {
1595
1599
  //-----------------------
1596
1600
  // Implementation
1597
1601
  //-----------------------
1598
- private buildColumn(config: ColumnGroupSpec | ColumnSpec, borderedGroup?: ColumnGroupSpec) {
1602
+ private buildColumn(config: ColumnOrGroupSpec, borderedGroup?: ColumnGroupSpec): ColumnOrGroup {
1599
1603
  // Merge leaf config with defaults.
1600
1604
  // Ensure *any* tooltip setting on column itself always wins.
1601
1605
  if (this.colDefaults && !this.isGroupSpec(config)) {
@@ -1609,12 +1613,8 @@ export class GridModel extends HoistModel {
1609
1613
 
1610
1614
  if (this.isGroupSpec(config)) {
1611
1615
  if (config.borders !== false) borderedGroup = config;
1612
- const children = compact(
1613
- config.children.map(c => this.buildColumn(c, borderedGroup))
1614
- ) as Array<ColumnGroup | Column>;
1615
- return !isEmpty(children)
1616
- ? new ColumnGroup(config as ColumnGroupSpec, this, children)
1617
- : null;
1616
+ const children = compact(config.children.map(c => this.buildColumn(c, borderedGroup)));
1617
+ return !isEmpty(children) ? new ColumnGroup(config, this, children) : null;
1618
1618
  }
1619
1619
 
1620
1620
  if (borderedGroup) {
@@ -1649,19 +1649,23 @@ export class GridModel extends HoistModel {
1649
1649
  }
1650
1650
  }
1651
1651
 
1652
- private gatherLeaves(columns, leaves = []) {
1652
+ private gatherLeaves(columns: ColumnOrGroup[], leaves: Column[] = []): Column[] {
1653
1653
  columns.forEach(col => {
1654
- if (col.groupId) this.gatherLeaves(col.children, leaves);
1655
- if (col.colId) leaves.push(col);
1654
+ if (col instanceof ColumnGroup) {
1655
+ this.gatherLeaves(col.children, leaves);
1656
+ } else {
1657
+ leaves.push(col);
1658
+ }
1656
1659
  });
1657
1660
 
1658
1661
  return leaves;
1659
1662
  }
1660
1663
 
1661
- private collectIds(cols, ids = []) {
1664
+ private collectIds(cols: ColumnOrGroup[], ids: string[] = []) {
1662
1665
  cols.forEach(col => {
1663
- if (col.colId) ids.push(col.colId);
1664
- if (col.groupId) {
1666
+ if (col instanceof Column) {
1667
+ ids.push(col.colId);
1668
+ } else {
1665
1669
  ids.push(col.groupId);
1666
1670
  this.collectIds(col.children, ids);
1667
1671
  }
@@ -1686,47 +1690,31 @@ export class GridModel extends HoistModel {
1686
1690
  // so it can be better re-used across Hoist APIs such as `Filter` and `FormModel`. However for
1687
1691
  // convenience, a `GridModel.store` config can also be very minimal (or non-existent), and
1688
1692
  // in this case GridModel should work out the required Store fields from column definitions.
1689
- private parseAndSetColumnsAndStore(colConfigs, store = {}) {
1690
- // 1) Validate configs.
1691
- this.validateStoreConfig(store);
1692
- this.validateColConfigs(colConfigs);
1693
-
1694
- // 2) Enhance colConfigs with field-level metadata provided by store, if any.
1695
- colConfigs = this.enhanceColConfigsFromStore(colConfigs, store);
1693
+ private parseAndSetColumnsAndStore(
1694
+ colConfigs: ColumnOrGroupSpec[],
1695
+ storeOrConfig: Store | StoreConfig = {}
1696
+ ) {
1697
+ // Enhance colConfigs with field-level metadata provided by store, if any.
1698
+ colConfigs = this.enhanceColConfigsFromStore(colConfigs, storeOrConfig);
1696
1699
 
1697
- // 3) Create and set columns with (possibly) enhanced configs.
1700
+ // Create and set columns with (possibly) enhanced configs.
1698
1701
  this.setColumns(colConfigs);
1699
1702
 
1700
- let newStore: Store;
1701
- // 4) Create store if needed
1702
- if (isPlainObject(store)) {
1703
- store = this.enhanceStoreConfigFromColumns(store);
1704
- newStore = new Store({loadTreeData: this.treeMode, ...store});
1705
- newStore.xhImpl = this.xhImpl;
1706
- this.markManaged(newStore);
1703
+ // Set or create Store as needed.
1704
+ let store: Store;
1705
+ if (storeOrConfig instanceof Store) {
1706
+ store = storeOrConfig;
1707
1707
  } else {
1708
- newStore = store as Store;
1708
+ storeOrConfig = this.enhanceStoreConfigFromColumns(storeOrConfig);
1709
+ store = new Store({loadTreeData: this.treeMode, ...storeOrConfig});
1710
+ store.xhImpl = this.xhImpl;
1711
+ this.markManaged(store);
1709
1712
  }
1710
1713
 
1711
- this.store = newStore;
1712
- }
1713
-
1714
- private validateStoreConfig(store) {
1715
- throwIf(
1716
- !(store instanceof Store || isPlainObject(store)),
1717
- 'GridModel.store config must be either an instance of a Store or a config to create one.'
1718
- );
1719
- }
1720
-
1721
- private validateColConfigs(colConfigs) {
1722
- throwIf(!isArray(colConfigs), 'GridModel.columns config must be an array.');
1723
- throwIf(
1724
- colConfigs.some(c => !isPlainObject(c)),
1725
- 'GridModel.columns config only accepts plain objects for Column or ColumnGroup configs.'
1726
- );
1714
+ this.store = store;
1727
1715
  }
1728
1716
 
1729
- private validateColumns(cols) {
1717
+ private validateColumns(cols: ColumnOrGroup[]) {
1730
1718
  if (isEmpty(cols)) return;
1731
1719
 
1732
1720
  const ids = this.collectIds(cols);
@@ -1782,16 +1770,30 @@ export class GridModel extends HoistModel {
1782
1770
 
1783
1771
  // Selectively enhance raw column configs with field-level metadata from store.fields and/or
1784
1772
  // field config partials provided by the column configs themselves.
1785
- private enhanceColConfigsFromStore(colConfigs, storeOrConfig?) {
1773
+ private enhanceColConfigsFromStore(
1774
+ colConfigs: ColumnOrGroupSpec[],
1775
+ storeOrConfig?: Store | StoreConfig
1776
+ ): ColumnOrGroupSpec[] {
1786
1777
  const store = storeOrConfig || this.store,
1787
1778
  storeFields = store?.fields,
1788
- fieldsByName = {};
1779
+ fieldsByName: Record<string, Field | FieldSpec> = {};
1789
1780
 
1790
1781
  // Extract field definitions in all supported forms: pull Field instances/configs from
1791
- // storeFields first, then fill in with any col-level `field` config objects.
1792
- storeFields?.forEach(sf => (fieldsByName[sf.name] = sf));
1782
+ // storeFields first...
1783
+ storeFields?.forEach(sf => {
1784
+ if (sf && !isString(sf)) {
1785
+ fieldsByName[sf.name] = sf;
1786
+ }
1787
+ });
1788
+
1789
+ // Then fill in with any col-level `field` config objects.
1793
1790
  colConfigs.forEach(cc => {
1794
- if (isPlainObject(cc.field) && !fieldsByName[cc.field.name]) {
1791
+ if (
1792
+ isColumnSpec(cc) &&
1793
+ cc.field &&
1794
+ !isString(cc.field) &&
1795
+ !fieldsByName[cc.field.name]
1796
+ ) {
1795
1797
  fieldsByName[cc.field.name] = cc.field;
1796
1798
  }
1797
1799
  });
@@ -1800,16 +1802,17 @@ export class GridModel extends HoistModel {
1800
1802
 
1801
1803
  const numTypes = ['int', 'number'],
1802
1804
  dateTypes = ['date', 'localDate'];
1805
+
1803
1806
  return colConfigs.map(col => {
1804
1807
  // Recurse into children for column groups
1805
- if (col.children) {
1808
+ if (!isColumnSpec(col)) {
1806
1809
  return {
1807
1810
  ...col,
1808
1811
  children: this.enhanceColConfigsFromStore(col.children, storeOrConfig)
1809
1812
  };
1810
1813
  }
1811
1814
 
1812
- const colFieldName = isPlainObject(col.field) ? col.field.name : col.field,
1815
+ const colFieldName = getFieldName(col.field),
1813
1816
  field = fieldsByName[colFieldName];
1814
1817
 
1815
1818
  if (!field) return col;
@@ -1837,9 +1840,9 @@ export class GridModel extends HoistModel {
1837
1840
  // Ensure store config has a complete set of fields for all configured columns. Note this
1838
1841
  // requires columns to have been constructed and set, and will only work with a raw store
1839
1842
  // config object, not an instance.
1840
- private enhanceStoreConfigFromColumns(storeConfig) {
1843
+ private enhanceStoreConfigFromColumns(storeConfig: StoreConfig) {
1841
1844
  const fields = storeConfig.fields ?? [],
1842
- storeFieldNames = fields.map(it => it.name ?? it),
1845
+ storeFieldNames = fields.map(it => getFieldName(it)),
1843
1846
  leafColsByFieldName = this.leafColsByFieldName();
1844
1847
 
1845
1848
  const newFields: FieldSpec[] = [];
@@ -1886,31 +1889,33 @@ export class GridModel extends HoistModel {
1886
1889
  return sizingMode;
1887
1890
  }
1888
1891
 
1889
- private parseSelModel(selModel): StoreSelectionModel {
1890
- const {store} = this;
1891
- selModel = withDefault(selModel, XH.isMobileApp ? 'disabled' : 'single');
1892
-
1892
+ private parseSelModel(selModel: GridConfig['selModel']): StoreSelectionModel {
1893
+ // Return actual instance directly.
1893
1894
  if (selModel instanceof StoreSelectionModel) {
1894
1895
  return selModel;
1895
1896
  }
1896
1897
 
1897
- if (isPlainObject(selModel)) {
1898
- return this.markManaged(new StoreSelectionModel({...selModel, store, xhImpl: true}));
1898
+ // Default unspecified based on platform, treat explicit null as disabled.
1899
+ if (selModel === undefined) {
1900
+ selModel = XH.isMobileApp ? 'disabled' : 'single';
1901
+ } else if (selModel === null) {
1902
+ selModel = 'disabled';
1899
1903
  }
1900
1904
 
1901
- // Assume its just the mode...
1902
- let mode: any = 'single';
1905
+ // Strings specify the mode.
1903
1906
  if (isString(selModel)) {
1904
- mode = selModel;
1905
- } else if (selModel === null) {
1906
- mode = 'disabled';
1907
+ selModel = {mode: selModel};
1907
1908
  }
1908
- return this.markManaged(new StoreSelectionModel({mode, store, xhImpl: true}));
1909
+
1910
+ return this.markManaged(
1911
+ new StoreSelectionModel({...selModel, store: this.store, xhImpl: true})
1912
+ );
1909
1913
  }
1910
1914
 
1911
- private parseFilterModel(filterModel) {
1915
+ private parseFilterModel(filterModel: GridConfig['filterModel']) {
1912
1916
  if (XH.isMobileApp || !filterModel) return null;
1913
- filterModel = isPlainObject(filterModel) ? filterModel : {};
1917
+
1918
+ filterModel = filterModel === true ? {} : filterModel;
1914
1919
  return new GridFilterModel({bind: this.store, ...filterModel}, this);
1915
1920
  }
1916
1921
 
@@ -1921,17 +1926,15 @@ export class GridModel extends HoistModel {
1921
1926
  };
1922
1927
  }
1923
1928
 
1924
- private parseChooserModel(chooserModel): HoistModel {
1925
- const modelClass = XH.isMobileApp ? MobileColChooserModel : DesktopColChooserModel;
1929
+ private parseChooserModel(chooserModel: GridConfig['colChooserModel']): HoistModel {
1930
+ if (!chooserModel) return null;
1926
1931
 
1927
- if (isPlainObject(chooserModel)) {
1928
- return this.markManaged(new modelClass({...chooserModel, gridModel: this}));
1929
- }
1930
-
1931
- return chooserModel ? this.markManaged(new modelClass({gridModel: this})) : null;
1932
+ const modelClass = XH.isMobileApp ? MobileColChooserModel : DesktopColChooserModel;
1933
+ chooserModel = chooserModel === true ? {} : chooserModel;
1934
+ return this.markManaged(new modelClass({...chooserModel, gridModel: this}));
1932
1935
  }
1933
1936
 
1934
- private isGroupSpec(col: ColumnGroupSpec | ColumnSpec): col is ColumnGroupSpec {
1937
+ private isGroupSpec(col: ColumnOrGroupSpec): col is ColumnGroupSpec {
1935
1938
  return 'children' in col;
1936
1939
  }
1937
1940