@xh/hoist 73.0.0-SNAPSHOT.1745689122610 → 73.0.0-SNAPSHOT.1745973083869

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 (39) hide show
  1. package/admin/AdminUtils.ts +5 -0
  2. package/admin/AppModel.ts +17 -5
  3. package/admin/tabs/activity/tracking/ActivityTracking.scss +18 -0
  4. package/admin/tabs/activity/tracking/ActivityTrackingModel.ts +296 -199
  5. package/admin/tabs/activity/tracking/ActivityTrackingPanel.ts +81 -51
  6. package/admin/tabs/activity/tracking/chart/AggChartModel.ts +218 -0
  7. package/admin/tabs/activity/tracking/chart/AggChartPanel.ts +61 -0
  8. package/admin/tabs/activity/tracking/datafields/DataFieldsEditor.ts +147 -0
  9. package/admin/tabs/activity/tracking/datafields/DataFieldsEditorModel.ts +133 -0
  10. package/admin/tabs/activity/tracking/detail/ActivityDetailModel.ts +114 -59
  11. package/admin/tabs/activity/tracking/detail/ActivityDetailView.ts +61 -30
  12. package/admin/tabs/cluster/instances/memory/MemoryMonitorModel.ts +1 -2
  13. package/build/types/admin/AdminUtils.d.ts +2 -0
  14. package/build/types/admin/AppModel.d.ts +4 -1
  15. package/build/types/admin/tabs/activity/tracking/ActivityTrackingModel.d.ts +30 -26
  16. package/build/types/admin/tabs/activity/tracking/chart/AggChartModel.d.ts +33 -0
  17. package/build/types/admin/tabs/activity/tracking/chart/AggChartPanel.d.ts +2 -0
  18. package/build/types/admin/tabs/activity/tracking/datafields/DataFieldsEditor.d.ts +2 -0
  19. package/build/types/admin/tabs/activity/tracking/datafields/DataFieldsEditorModel.d.ts +46 -0
  20. package/build/types/admin/tabs/activity/tracking/detail/ActivityDetailModel.d.ts +13 -1
  21. package/build/types/cmp/form/FormModel.d.ts +15 -27
  22. package/build/types/cmp/form/field/SubformsFieldModel.d.ts +20 -18
  23. package/build/types/core/HoistBase.d.ts +2 -2
  24. package/build/types/data/cube/CubeField.d.ts +4 -5
  25. package/build/types/desktop/cmp/appOption/AutoRefreshAppOption.d.ts +3 -3
  26. package/build/types/desktop/cmp/appOption/ThemeAppOption.d.ts +3 -3
  27. package/cmp/error/ErrorBoundaryModel.ts +1 -1
  28. package/cmp/form/FormModel.ts +18 -28
  29. package/cmp/form/field/SubformsFieldModel.ts +28 -22
  30. package/cmp/grid/impl/GridHScrollbar.ts +1 -2
  31. package/core/HoistBase.ts +12 -12
  32. package/data/cube/CubeField.ts +17 -18
  33. package/package.json +1 -1
  34. package/promise/Promise.ts +8 -6
  35. package/tsconfig.tsbuildinfo +1 -1
  36. package/admin/tabs/activity/tracking/charts/ChartsModel.ts +0 -218
  37. package/admin/tabs/activity/tracking/charts/ChartsPanel.ts +0 -76
  38. package/build/types/admin/tabs/activity/tracking/charts/ChartsModel.d.ts +0 -34
  39. package/build/types/admin/tabs/activity/tracking/charts/ChartsPanel.d.ts +0 -2
@@ -0,0 +1,46 @@
1
+ import { ActivityTrackingModel } from '@xh/hoist/admin/tabs/activity/tracking/ActivityTrackingModel';
2
+ import { FormModel, SubformsFieldModel } from '@xh/hoist/cmp/form';
3
+ import { HoistModel } from '@xh/hoist/core';
4
+ import { AggregatorToken, FieldType } from '@xh/hoist/data';
5
+ /**
6
+ * Slimmed down {@link CubeFieldSpec} for persisted specs of fields to be extracted from the `data`
7
+ * block of loaded track statements and promoted to top-level columns in the grids. These are the
8
+ * entities (stored on parent `ActivityTrackingModel`) that are edited by the this component.
9
+ */
10
+ export interface ActivityTrackingDataFieldSpec {
11
+ /**
12
+ * Path to field data within the `data` block of each track log entry. Can be dot-delimited for
13
+ * nested data (e.g. `timings.preAuth`). See {@link ActivityTrackingModel.processRawTrackLog}.
14
+ */
15
+ path: string;
16
+ /**
17
+ * Normalized name for the field for use in Cube/Grid - adds `df_` prefix to avoid conflicts
18
+ * and strips out dot-delimiters. See {@link ActivityTrackingModel.setDataFields}.
19
+ */
20
+ name: string;
21
+ displayName?: string;
22
+ type?: FieldType;
23
+ isDimension?: boolean;
24
+ aggregator?: AggregatorToken;
25
+ }
26
+ export declare class DataFieldsEditorModel extends HoistModel {
27
+ showEditor: boolean;
28
+ formModel: FormModel;
29
+ private parentModel;
30
+ aggTokens: AggregatorToken[];
31
+ get dataFields(): SubformsFieldModel;
32
+ get appliedDataFieldCount(): number;
33
+ get hasAppliedDataFields(): boolean;
34
+ constructor(parentModel: ActivityTrackingModel);
35
+ show(): void;
36
+ applyAndClose(): void;
37
+ close(): void;
38
+ addField(): void;
39
+ cloneField(formModel: FormModel): void;
40
+ private syncFromParent;
41
+ /**
42
+ * Normalize field specs and set onto parent.
43
+ * Note, will de-dupe fields by name w/o any alert to user.
44
+ */
45
+ private syncToParent;
46
+ }
@@ -6,11 +6,23 @@ export declare class ActivityDetailModel extends HoistModel {
6
6
  activityTrackingModel: ActivityTrackingModel;
7
7
  gridModel: GridModel;
8
8
  formModel: FormModel;
9
- formattedData: any;
9
+ /**
10
+ * Optional dot-delimited path(s) to filter the displayed `data` payload down to a particular
11
+ * node or nodes, for easier browsing of records with a large data payload. Multiple paths
12
+ * can be separated with `|`.
13
+ */
14
+ formattedDataFilterPath: string;
15
+ /** Stringified, pretty-printed, optionally path-filtered `data` payload. */
16
+ formattedData: string;
10
17
  get hasSelection(): boolean;
11
18
  constructor();
12
19
  onLinked(): void;
13
20
  private showActivityEntriesAsync;
14
21
  private getAllLeafRows;
22
+ /** Extract data from a (detail) grid record and flush it into our form for display. */
15
23
  private showEntryDetail;
24
+ private updateFormattedData;
25
+ private createAndSetCoreModels;
26
+ private createGridModel;
27
+ private createSingleEntryFormModel;
16
28
  }
@@ -3,9 +3,7 @@ import { ValidationState } from '@xh/hoist/data';
3
3
  import { BaseFieldConfig, BaseFieldModel } from './field/BaseFieldModel';
4
4
  import { SubformsFieldConfig, SubformsFieldModel } from './field/SubformsFieldModel';
5
5
  export interface FormConfig {
6
- /**
7
- * FieldModels, or configurations to create them, for all data fields managed by this FormModel.
8
- */
6
+ /** FieldModels, or configs to create them, for all data fields managed by the model. */
9
7
  fields?: Array<BaseFieldModel | BaseFieldConfig | SubformsFieldConfig | SubformsFieldModel>;
10
8
  /** Map of initial values for fields in this model. */
11
9
  initialValues?: PlainObject;
@@ -15,27 +13,27 @@ export interface FormConfig {
15
13
  xhImpl?: boolean;
16
14
  }
17
15
  /**
18
- * FormModel is the main entry point for Form specification. This Model's `fields` collection holds
19
- * multiple FieldModel instances, which in turn hold the state of user edited data and the
20
- * validation rules around editing that data.
16
+ * FormModel is the main entry point for Form specification. This Model's `fields` object references
17
+ * multiple {@link FieldModel} instances, keyed by name, which in turn hold the state of user-
18
+ * edited data and the validation rules around editing that data.
21
19
  *
22
20
  * A complete representation of all fields and data within a Form can be produced via this model's
23
- * `getData()` method, making it easy to harvest all values for e.g. submission to a server.
21
+ * {@link getData} method, making it easy to harvest all values for e.g. submission to a server.
24
22
  *
25
- * Individual field values are also available as observables via this model's `values` proxy. An
26
- * application model can setup a reaction to track changes to any value and execute app-specific
23
+ * Individual field values are also available as observables via this model's {@link values} proxy.
24
+ * An application model can setup a reaction to track changes to any value and execute app-specific
27
25
  * logic such as disabling one field based on the state of another, or setting up cascading options.
28
26
  *
29
27
  * This Model provides an overall validation state, determined by the current validation state of
30
28
  * its fields as per their configured rules and constraints.
31
29
  *
32
- * FormModels can be nested via SubformsFieldModels, a specialized type of FieldModel that itself
33
- * manages a collection of child FormModels. This allows use cases where Forms support editing of
30
+ * FormModels can be nested via {@link SubformsFieldModel}s, a specialized type of FieldModel that
31
+ * itself manages a collection of child FormModels. This enables forms to support editing of
34
32
  * dynamic collections of complex objects with their own internal validation rules (e.g. a FormModel
35
33
  * representing a market order might have multiple nested FormModels to represent execution splits,
36
34
  * where each split has its own internal fields for broker, quantity, and time).
37
35
  *
38
- * @see FieldModel for details on state and validation maintained at the individual field level.
36
+ * See {@link FieldModel} for details on state/validation maintained at the individual field level.
39
37
  */
40
38
  export declare class FormModel extends HoistModel {
41
39
  /** Container object for FieldModel instances, keyed by field name.*/
@@ -87,7 +85,6 @@ export declare class FormModel extends HoistModel {
87
85
  init(initialValues?: PlainObject): void;
88
86
  /**
89
87
  * Set the value of one or more fields on this form.
90
- *
91
88
  * @param values - map of field name to value.
92
89
  */
93
90
  setValues(values: PlainObject): void;
@@ -95,16 +92,12 @@ export declare class FormModel extends HoistModel {
95
92
  get isDirty(): boolean;
96
93
  /**
97
94
  * The Field that is currently focused on this form.
98
- *
99
- * @see FieldModel.focus() for important information on this method
100
- * and its limitations.
95
+ * See {@link FieldModel.focus} for important information on this method and its limitations.
101
96
  */
102
97
  get focusedField(): BaseFieldModel;
103
98
  /**
104
99
  * Focus a field on this form.
105
- *
106
- * @see FieldModel.focus() for important information on this method
107
- * and its limitations.
100
+ * See {@link FieldModel.focus} for important information on this method and its limitations.
108
101
  */
109
102
  focusField(name: string): void;
110
103
  get validationState(): ValidationState;
@@ -114,17 +107,12 @@ export declare class FormModel extends HoistModel {
114
107
  get isValid(): boolean;
115
108
  /** List of all validation errors for this form. */
116
109
  get allErrors(): string[];
117
- /**
118
- * Recompute all validations and return true if the form is valid.
119
- *
120
- * @param opts - set 'display' to true to trigger the display of
121
- * validation errors (if any) by bound FormField components after validation
122
- * is complete.
123
- */
110
+ /** Recompute all validations and return true if the form is valid. */
124
111
  validateAsync(opts?: {
112
+ /** True to trigger display of validation errors (if any) by bound `FormField`s after validation is complete. */
125
113
  display?: boolean;
126
114
  }): Promise<boolean>;
127
- /** Trigger the display of validation errors (if any) by bound FormField components. */
115
+ /** Trigger the display of validation errors (if any) by bound `FormField`s. */
128
116
  displayValidation(): void;
129
117
  private createValuesProxy;
130
118
  }
@@ -4,32 +4,37 @@ import { FormModel } from '../FormModel';
4
4
  import { BaseFieldModel, BaseFieldConfig } from './BaseFieldModel';
5
5
  import { FormConfig } from '../FormModel';
6
6
  export interface SubformsFieldConfig extends BaseFieldConfig {
7
- /** Config for FormModel representing a subform. */
7
+ /**
8
+ * Config for a {@link FormModel} to be auto-created to manage and validate the data for each
9
+ *
10
+ */
8
11
  subforms: FormConfig;
9
12
  /**
10
- * Initial value of this field. If a function, will be
11
- * executed dynamically when form is initialized to provide value.
13
+ * Initial value of this field. If a function, will be executed dynamically when form is
14
+ * initialized to provide value.
12
15
  */
13
16
  initialValue?: any[];
14
17
  }
15
18
  /**
16
- * A data field in a form whose value is a collection of FormModels (subforms).
19
+ * A data field in a form whose value is a collection of nested objects - all of the same shape, but
20
+ * with arbitrary internal complexity. A dedicated {@link FormModel} is auto-created to manage and
21
+ * validate each object independently.
17
22
  *
18
- * Applications should initialize this field with an array of objects. These values will be
19
- * loaded into an array of managed FormModels which will form the value of this field.
23
+ * Applications should initialize this field with an array of objects. These values will be loaded
24
+ * into an array of managed FormModels which will form the value of this field.
20
25
  *
21
26
  * Applications should *not* modify the value property directly, unless they wish to reinitialize
22
- * all existing form contents to new values. Use the methods add() or remove() to
23
- * adjust the contents of the collection while preserving existing form state.
27
+ * all existing form contents to new values. Call {@link add} or {@link remove} on one of these
28
+ * fields to adjust the contents of its collection while preserving existing state.
24
29
  *
25
- * Validation rules for the entire collection may be specified as for any field, but
26
- * validations on the subforms will also bubble up to this field, affecting its overall
27
- * validation state.
30
+ * Validation rules for the entire collection may be specified as for any field, but validations on
31
+ * the subforms will also bubble up to this field, affecting its overall validation state.
28
32
  */
29
33
  export declare class SubformsFieldModel extends BaseFieldModel {
34
+ /** (Sub)FormModels created by this model, tracked to support cleanup. */
30
35
  private createdModels;
31
36
  private formConfig;
32
- private origInitialValues;
37
+ private readonly origInitialValues;
33
38
  constructor({ subforms, initialValue, ...rest }: SubformsFieldConfig);
34
39
  get hasFocus(): boolean;
35
40
  focus(): void;
@@ -49,14 +54,11 @@ export declare class SubformsFieldModel extends BaseFieldModel {
49
54
  display?: boolean;
50
55
  }): Promise<boolean>;
51
56
  protected deriveValidationState(): ValidationState;
52
- /**
53
- * Add a new record (subform) to this field.
54
- *
55
- * @param initialValues - object containing initial values for new record.
56
- * @param index - index in collection where subform should be inserted.
57
- */
57
+ /** Add a new object (subform) to this field's collection. */
58
58
  add(opts?: {
59
+ /** Initial values for the new object/subform. */
59
60
  initialValues?: PlainObject;
61
+ /** Index within the collection where the new subform should be inserted. */
60
62
  index?: number;
61
63
  }): void;
62
64
  remove(formModel: FormModel): void;
@@ -1,7 +1,7 @@
1
- import { PersistOptions, DebounceSpec, Some } from './';
2
1
  import { comparer } from '@xh/hoist/mobx';
3
2
  import { IAutorunOptions, IReactionOptions } from 'mobx/dist/api/autorun';
4
- import { IReactionDisposer, IEqualsComparer } from 'mobx/dist/internal';
3
+ import { IEqualsComparer, IReactionDisposer } from 'mobx/dist/internal';
4
+ import { DebounceSpec, PersistOptions, Some } from './';
5
5
  export interface HoistBaseClass {
6
6
  new (...args: any[]): HoistBase;
7
7
  isHoistBase: boolean;
@@ -3,11 +3,8 @@ import { Aggregator, AverageAggregator, AverageStrictAggregator, ChildCountAggre
3
3
  export interface CubeFieldSpec extends FieldSpec {
4
4
  /** True to allow this field to be used for grouping.*/
5
5
  isDimension?: boolean;
6
- /**
7
- * Instance of a Hoist Cube Aggregator (from the aggregate package), or string alias for the
8
- * same (e.g. 'MAX').
9
- */
10
- aggregator?: Aggregator | 'AVG' | 'AVG_STRICT' | 'CHILD_COUNT' | 'LEAF_COUNT' | 'MAX' | 'MIN' | 'NULL' | 'SINGLE' | 'SUM' | 'SUM_STRICT' | 'UNIQUE';
6
+ /** Instance of a Hoist Cube {@link Aggregator} or string token alias for one. */
7
+ aggregator?: Aggregator | AggregatorToken;
11
8
  /** Function to determine if aggregation should be performed at a given level of a query result. */
12
9
  canAggregateFn?: CanAggregateFn;
13
10
  /** True if any further groupings below this dimension would be derivative (have only one member). */
@@ -19,6 +16,8 @@ export interface CubeFieldSpec extends FieldSpec {
19
16
  */
20
17
  parentDimension?: string;
21
18
  }
19
+ /** Convenient (and serializable) alias for one of Hoist's Cube {@link Aggregator} classes. */
20
+ export type AggregatorToken = 'AVG' | 'AVG_STRICT' | 'CHILD_COUNT' | 'LEAF_COUNT' | 'MAX' | 'MIN' | 'NULL' | 'SINGLE' | 'SUM' | 'SUM_STRICT' | 'UNIQUE';
22
21
  /**
23
22
  * @param dimension - dimension of aggregation
24
23
  * @param value - value of record on dimension
@@ -86,7 +86,7 @@ export declare const autoRefreshAppOption: ({ formFieldProps, inputProps }?: Aut
86
86
  suppressContentEditableWarning?: boolean;
87
87
  suppressHydrationWarning?: boolean;
88
88
  accessKey?: string;
89
- autoCapitalize?: "off" | "none" | "on" | "sentences" | "words" | "characters" | (string & {});
89
+ autoCapitalize?: "none" | "off" | "on" | "sentences" | "words" | "characters" | (string & {});
90
90
  autoFocus?: boolean;
91
91
  contentEditable?: "inherit" | (boolean | "true" | "false") | "plaintext-only";
92
92
  dir?: string;
@@ -121,7 +121,7 @@ export declare const autoRefreshAppOption: ({ formFieldProps, inputProps }?: Aut
121
121
  results?: number;
122
122
  security?: string;
123
123
  unselectable?: "off" | "on";
124
- inputMode?: "search" | "text" | "none" | "tel" | "url" | "email" | "numeric" | "decimal";
124
+ inputMode?: "none" | "search" | "text" | "tel" | "url" | "email" | "numeric" | "decimal";
125
125
  is?: string;
126
126
  "aria-activedescendant"?: string;
127
127
  "aria-atomic"?: boolean | "true" | "false";
@@ -140,7 +140,7 @@ export declare const autoRefreshAppOption: ({ formFieldProps, inputProps }?: Aut
140
140
  "aria-description"?: string;
141
141
  "aria-details"?: string;
142
142
  "aria-disabled"?: boolean | "true" | "false";
143
- "aria-dropeffect"?: "link" | "none" | "copy" | "execute" | "move" | "popup";
143
+ "aria-dropeffect"?: "none" | "link" | "copy" | "execute" | "move" | "popup";
144
144
  "aria-errormessage"?: string;
145
145
  "aria-expanded"?: boolean | "true" | "false";
146
146
  "aria-flowto"?: string;
@@ -84,7 +84,7 @@ export declare const themeAppOption: ({ formFieldProps, inputProps }?: ThemeAppO
84
84
  suppressContentEditableWarning?: boolean;
85
85
  suppressHydrationWarning?: boolean;
86
86
  accessKey?: string;
87
- autoCapitalize?: "off" | "none" | "on" | "sentences" | "words" | "characters" | (string & {});
87
+ autoCapitalize?: "none" | "off" | "on" | "sentences" | "words" | "characters" | (string & {});
88
88
  autoFocus?: boolean;
89
89
  contentEditable?: "inherit" | (boolean | "true" | "false") | "plaintext-only";
90
90
  dir?: string;
@@ -119,7 +119,7 @@ export declare const themeAppOption: ({ formFieldProps, inputProps }?: ThemeAppO
119
119
  results?: number;
120
120
  security?: string;
121
121
  unselectable?: "off" | "on";
122
- inputMode?: "search" | "text" | "none" | "tel" | "url" | "email" | "numeric" | "decimal";
122
+ inputMode?: "none" | "search" | "text" | "tel" | "url" | "email" | "numeric" | "decimal";
123
123
  is?: string;
124
124
  "aria-activedescendant"?: string;
125
125
  "aria-atomic"?: boolean | "true" | "false";
@@ -138,7 +138,7 @@ export declare const themeAppOption: ({ formFieldProps, inputProps }?: ThemeAppO
138
138
  "aria-description"?: string;
139
139
  "aria-details"?: string;
140
140
  "aria-disabled"?: boolean | "true" | "false";
141
- "aria-dropeffect"?: "link" | "none" | "copy" | "execute" | "move" | "popup";
141
+ "aria-dropeffect"?: "none" | "link" | "copy" | "execute" | "move" | "popup";
142
142
  "aria-errormessage"?: string;
143
143
  "aria-expanded"?: boolean | "true" | "false";
144
144
  "aria-flowto"?: string;
@@ -5,8 +5,8 @@
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
7
  import {ExceptionHandlerOptions, HoistModel, XH} from '@xh/hoist/core';
8
+ import {action, makeObservable, observable} from '@xh/hoist/mobx';
8
9
  import {isFunction} from 'lodash';
9
- import {action, makeObservable, observable} from 'mobx';
10
10
  import {ReactNode} from 'react';
11
11
 
12
12
  export interface ErrorBoundaryConfig {
@@ -14,9 +14,7 @@ import {FieldModel} from './field/FieldModel';
14
14
  import {SubformsFieldConfig, SubformsFieldModel} from './field/SubformsFieldModel';
15
15
 
16
16
  export interface FormConfig {
17
- /**
18
- * FieldModels, or configurations to create them, for all data fields managed by this FormModel.
19
- */
17
+ /** FieldModels, or configs to create them, for all data fields managed by the model. */
20
18
  fields?: Array<BaseFieldModel | BaseFieldConfig | SubformsFieldConfig | SubformsFieldModel>;
21
19
 
22
20
  /** Map of initial values for fields in this model. */
@@ -30,27 +28,27 @@ export interface FormConfig {
30
28
  }
31
29
 
32
30
  /**
33
- * FormModel is the main entry point for Form specification. This Model's `fields` collection holds
34
- * multiple FieldModel instances, which in turn hold the state of user edited data and the
35
- * validation rules around editing that data.
31
+ * FormModel is the main entry point for Form specification. This Model's `fields` object references
32
+ * multiple {@link FieldModel} instances, keyed by name, which in turn hold the state of user-
33
+ * edited data and the validation rules around editing that data.
36
34
  *
37
35
  * A complete representation of all fields and data within a Form can be produced via this model's
38
- * `getData()` method, making it easy to harvest all values for e.g. submission to a server.
36
+ * {@link getData} method, making it easy to harvest all values for e.g. submission to a server.
39
37
  *
40
- * Individual field values are also available as observables via this model's `values` proxy. An
41
- * application model can setup a reaction to track changes to any value and execute app-specific
38
+ * Individual field values are also available as observables via this model's {@link values} proxy.
39
+ * An application model can setup a reaction to track changes to any value and execute app-specific
42
40
  * logic such as disabling one field based on the state of another, or setting up cascading options.
43
41
  *
44
42
  * This Model provides an overall validation state, determined by the current validation state of
45
43
  * its fields as per their configured rules and constraints.
46
44
  *
47
- * FormModels can be nested via SubformsFieldModels, a specialized type of FieldModel that itself
48
- * manages a collection of child FormModels. This allows use cases where Forms support editing of
45
+ * FormModels can be nested via {@link SubformsFieldModel}s, a specialized type of FieldModel that
46
+ * itself manages a collection of child FormModels. This enables forms to support editing of
49
47
  * dynamic collections of complex objects with their own internal validation rules (e.g. a FormModel
50
48
  * representing a market order might have multiple nested FormModels to represent execution splits,
51
49
  * where each split has its own internal fields for broker, quantity, and time).
52
50
  *
53
- * @see FieldModel for details on state and validation maintained at the individual field level.
51
+ * See {@link FieldModel} for details on state/validation maintained at the individual field level.
54
52
  */
55
53
  export class FormModel extends HoistModel {
56
54
  /** Container object for FieldModel instances, keyed by field name.*/
@@ -164,7 +162,6 @@ export class FormModel extends HoistModel {
164
162
 
165
163
  /**
166
164
  * Set the value of one or more fields on this form.
167
- *
168
165
  * @param values - map of field name to value.
169
166
  */
170
167
  @action
@@ -184,9 +181,7 @@ export class FormModel extends HoistModel {
184
181
  //-----------------------------------
185
182
  /**
186
183
  * The Field that is currently focused on this form.
187
- *
188
- * @see FieldModel.focus() for important information on this method
189
- * and its limitations.
184
+ * See {@link FieldModel.focus} for important information on this method and its limitations.
190
185
  */
191
186
  @computed
192
187
  get focusedField(): BaseFieldModel {
@@ -195,9 +190,7 @@ export class FormModel extends HoistModel {
195
190
 
196
191
  /**
197
192
  * Focus a field on this form.
198
- *
199
- * @see FieldModel.focus() for important information on this method
200
- * and its limitations.
193
+ * See {@link FieldModel.focus} for important information on this method and its limitations.
201
194
  */
202
195
  focusField(name: string) {
203
196
  this.getField(name)?.focus();
@@ -230,21 +223,18 @@ export class FormModel extends HoistModel {
230
223
  return flatMap(this.fields, s => s.allErrors);
231
224
  }
232
225
 
233
- /**
234
- * Recompute all validations and return true if the form is valid.
235
- *
236
- * @param opts - set 'display' to true to trigger the display of
237
- * validation errors (if any) by bound FormField components after validation
238
- * is complete.
239
- */
240
- async validateAsync(opts?: {display?: boolean}): Promise<boolean> {
226
+ /** Recompute all validations and return true if the form is valid. */
227
+ async validateAsync(opts?: {
228
+ /** True to trigger display of validation errors (if any) by bound `FormField`s after validation is complete. */
229
+ display?: boolean;
230
+ }): Promise<boolean> {
241
231
  const {display = true} = opts ?? {},
242
232
  promises = map(this.fields, m => m.validateAsync({display}));
243
233
  await Promise.all(promises);
244
234
  return this.isValid;
245
235
  }
246
236
 
247
- /** Trigger the display of validation errors (if any) by bound FormField components. */
237
+ /** Trigger the display of validation errors (if any) by bound `FormField`s. */
248
238
  displayValidation() {
249
239
  forOwn(this.fields, m => m.displayValidation());
250
240
  }
@@ -15,36 +15,40 @@ import {BaseFieldModel, BaseFieldConfig} from './BaseFieldModel';
15
15
  import {FormConfig} from '../FormModel';
16
16
 
17
17
  export interface SubformsFieldConfig extends BaseFieldConfig {
18
- /** Config for FormModel representing a subform. */
18
+ /**
19
+ * Config for a {@link FormModel} to be auto-created to manage and validate the data for each
20
+ *
21
+ */
19
22
  subforms: FormConfig;
20
23
 
21
24
  /**
22
- * Initial value of this field. If a function, will be
23
- * executed dynamically when form is initialized to provide value.
25
+ * Initial value of this field. If a function, will be executed dynamically when form is
26
+ * initialized to provide value.
24
27
  */
25
28
  initialValue?: any[];
26
29
  }
27
30
 
28
31
  /**
29
- * A data field in a form whose value is a collection of FormModels (subforms).
32
+ * A data field in a form whose value is a collection of nested objects - all of the same shape, but
33
+ * with arbitrary internal complexity. A dedicated {@link FormModel} is auto-created to manage and
34
+ * validate each object independently.
30
35
  *
31
- * Applications should initialize this field with an array of objects. These values will be
32
- * loaded into an array of managed FormModels which will form the value of this field.
36
+ * Applications should initialize this field with an array of objects. These values will be loaded
37
+ * into an array of managed FormModels which will form the value of this field.
33
38
  *
34
39
  * Applications should *not* modify the value property directly, unless they wish to reinitialize
35
- * all existing form contents to new values. Use the methods add() or remove() to
36
- * adjust the contents of the collection while preserving existing form state.
40
+ * all existing form contents to new values. Call {@link add} or {@link remove} on one of these
41
+ * fields to adjust the contents of its collection while preserving existing state.
37
42
  *
38
- * Validation rules for the entire collection may be specified as for any field, but
39
- * validations on the subforms will also bubble up to this field, affecting its overall
40
- * validation state.
43
+ * Validation rules for the entire collection may be specified as for any field, but validations on
44
+ * the subforms will also bubble up to this field, affecting its overall validation state.
41
45
  */
42
46
  export class SubformsFieldModel extends BaseFieldModel {
43
- // (Sub)FormModels created by this model, tracked to support cleanup.
44
- @managed
45
- private createdModels: FormModel[] = [];
47
+ /** (Sub)FormModels created by this model, tracked to support cleanup. */
48
+ @managed private createdModels: FormModel[] = [];
49
+
46
50
  private formConfig: FormConfig = null;
47
- private origInitialValues: any[];
51
+ private readonly origInitialValues: any[];
48
52
 
49
53
  constructor({subforms, initialValue = [], ...rest}: SubformsFieldConfig) {
50
54
  super(rest);
@@ -161,14 +165,16 @@ export class SubformsFieldModel extends BaseFieldModel {
161
165
  //-----------------------------
162
166
  // Collection management
163
167
  //-----------------------------
164
- /**
165
- * Add a new record (subform) to this field.
166
- *
167
- * @param initialValues - object containing initial values for new record.
168
- * @param index - index in collection where subform should be inserted.
169
- */
168
+ /** Add a new object (subform) to this field's collection. */
170
169
  @action
171
- add(opts: {initialValues?: PlainObject; index?: number} = {}) {
170
+ add(
171
+ opts: {
172
+ /** Initial values for the new object/subform. */
173
+ initialValues?: PlainObject;
174
+ /** Index within the collection where the new subform should be inserted. */
175
+ index?: number;
176
+ } = {}
177
+ ) {
172
178
  const {initialValues = {}, index = this.value.length} = opts,
173
179
  newSubforms = this.parseValue([initialValues]),
174
180
  newValue = clone(this.value);
@@ -7,10 +7,9 @@
7
7
  import {GridLocalModel, GridModel} from '@xh/hoist/cmp/grid';
8
8
  import {div} from '@xh/hoist/cmp/layout';
9
9
  import {hoistCmp, HoistModel, HoistProps, useLocalModel} from '@xh/hoist/core';
10
- import {makeObservable} from '@xh/hoist/mobx';
10
+ import {action, makeObservable, observable} from '@xh/hoist/mobx';
11
11
  import {observeResize} from '@xh/hoist/utils/js';
12
12
  import {sumBy} from 'lodash';
13
- import {action, observable} from 'mobx';
14
13
  import {createRef, RefObject} from 'react';
15
14
 
16
15
  /**
package/core/HoistBase.ts CHANGED
@@ -4,15 +4,21 @@
4
4
  *
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
- import {runInAction} from 'mobx';
8
- import {XH, PersistenceProvider, PersistOptions, DebounceSpec, Some, PersistableState} from './';
9
7
  import {
10
- throwIf,
8
+ action,
9
+ autorun as mobxAutorun,
10
+ comparer,
11
+ reaction as mobxReaction,
12
+ runInAction,
13
+ when as mobxWhen
14
+ } from '@xh/hoist/mobx';
15
+ import {
11
16
  getOrCreate,
12
- logInfo,
13
17
  logDebug,
14
18
  logError,
19
+ logInfo,
15
20
  logWarn,
21
+ throwIf,
16
22
  withDebug,
17
23
  withInfo
18
24
  } from '@xh/hoist/utils/js';
@@ -25,15 +31,9 @@ import {
25
31
  isString,
26
32
  upperFirst
27
33
  } from 'lodash';
28
- import {
29
- action,
30
- comparer,
31
- autorun as mobxAutorun,
32
- reaction as mobxReaction,
33
- when as mobxWhen
34
- } from '@xh/hoist/mobx';
35
34
  import {IAutorunOptions, IReactionOptions} from 'mobx/dist/api/autorun';
36
- import {IReactionDisposer, IEqualsComparer} from 'mobx/dist/internal';
35
+ import {IEqualsComparer, IReactionDisposer} from 'mobx/dist/internal';
36
+ import {DebounceSpec, PersistableState, PersistenceProvider, PersistOptions, Some, XH} from './';
37
37
 
38
38
  export interface HoistBaseClass {
39
39
  new (...args: any[]): HoistBase;
@@ -28,23 +28,8 @@ export interface CubeFieldSpec extends FieldSpec {
28
28
  /** True to allow this field to be used for grouping.*/
29
29
  isDimension?: boolean;
30
30
 
31
- /**
32
- * Instance of a Hoist Cube Aggregator (from the aggregate package), or string alias for the
33
- * same (e.g. 'MAX').
34
- */
35
- aggregator?:
36
- | Aggregator
37
- | 'AVG'
38
- | 'AVG_STRICT'
39
- | 'CHILD_COUNT'
40
- | 'LEAF_COUNT'
41
- | 'MAX'
42
- | 'MIN'
43
- | 'NULL'
44
- | 'SINGLE'
45
- | 'SUM'
46
- | 'SUM_STRICT'
47
- | 'UNIQUE';
31
+ /** Instance of a Hoist Cube {@link Aggregator} or string token alias for one. */
32
+ aggregator?: Aggregator | AggregatorToken;
48
33
 
49
34
  /** Function to determine if aggregation should be performed at a given level of a query result. */
50
35
  canAggregateFn?: CanAggregateFn;
@@ -60,6 +45,20 @@ export interface CubeFieldSpec extends FieldSpec {
60
45
  parentDimension?: string;
61
46
  }
62
47
 
48
+ /** Convenient (and serializable) alias for one of Hoist's Cube {@link Aggregator} classes. */
49
+ export type AggregatorToken =
50
+ | 'AVG'
51
+ | 'AVG_STRICT'
52
+ | 'CHILD_COUNT'
53
+ | 'LEAF_COUNT'
54
+ | 'MAX'
55
+ | 'MIN'
56
+ | 'NULL'
57
+ | 'SINGLE'
58
+ | 'SUM'
59
+ | 'SUM_STRICT'
60
+ | 'UNIQUE';
61
+
63
62
  /**
64
63
  * @param dimension - dimension of aggregation
65
64
  * @param value - value of record on dimension
@@ -114,7 +113,7 @@ export class CubeField extends Field {
114
113
  //------------------------
115
114
  // Implementation
116
115
  //------------------------
117
- private parseAggregator(val: any): Aggregator {
116
+ private parseAggregator(val: Aggregator | AggregatorToken): Aggregator {
118
117
  if (isString(val)) {
119
118
  switch (val) {
120
119
  case 'AVG':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "73.0.0-SNAPSHOT.1745689122610",
3
+ "version": "73.0.0-SNAPSHOT.1745973083869",
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",