@xh/hoist 59.3.2 → 59.5.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.
- package/CHANGELOG.md +39 -3
- package/admin/differ/DifferModel.ts +5 -7
- package/appcontainer/AppContainerModel.ts +8 -10
- package/appcontainer/AppStateModel.ts +3 -2
- package/appcontainer/PageStateModel.ts +1 -2
- package/appcontainer/SizingModeModel.ts +2 -2
- package/cmp/ag-grid/AgGrid.ts +4 -3
- package/cmp/ag-grid/AgGridModel.ts +19 -14
- package/cmp/chart/Chart.ts +4 -3
- package/cmp/dataview/DataViewModel.ts +2 -2
- package/cmp/filter/FilterChooserModel.ts +5 -5
- package/cmp/grid/Grid.ts +9 -7
- package/cmp/grid/GridContextMenu.ts +2 -2
- package/cmp/grid/GridModel.ts +13 -21
- package/cmp/grid/Types.ts +6 -5
- package/cmp/grid/columns/Column.ts +12 -12
- package/cmp/grid/columns/ColumnGroup.ts +17 -6
- package/cmp/grid/helpers/GridCountLabel.ts +5 -4
- package/cmp/grid/impl/ColumnWidthCalculator.ts +3 -3
- package/cmp/grid/impl/GridPersistenceModel.ts +11 -4
- package/cmp/grid/impl/Utils.ts +1 -1
- package/cmp/grid/renderers/MultiFieldRenderer.ts +28 -22
- package/cmp/grouping/GroupingChooserModel.ts +2 -2
- package/cmp/relativetimestamp/RelativeTimestamp.ts +5 -2
- package/cmp/tab/TabContainerModel.ts +2 -2
- package/cmp/zoneGrid/ZoneGridModel.ts +1 -1
- package/cmp/zoneGrid/impl/ZoneGridPersistenceModel.ts +10 -4
- package/core/HoistBase.ts +48 -8
- package/core/HoistBaseDecorators.ts +11 -6
- package/core/HoistComponent.ts +1 -3
- package/core/elem.ts +2 -2
- package/core/exception/ExceptionHandler.ts +4 -4
- package/core/impl/InstallServices.ts +2 -8
- package/core/load/LoadSupport.ts +10 -11
- package/core/model/HoistModel.ts +1 -1
- package/data/Store.ts +1 -1
- package/data/UrlStore.ts +3 -3
- package/data/cube/aggregate/UniqueAggregator.ts +3 -5
- package/data/filter/CompoundFilter.ts +5 -3
- package/data/filter/FieldFilter.ts +4 -3
- package/data/filter/Filter.ts +2 -3
- package/data/filter/FunctionFilter.ts +2 -1
- package/data/impl/RecordSet.ts +5 -5
- package/desktop/appcontainer/ToastSource.ts +1 -1
- package/desktop/cmp/button/ColChooserButton.ts +7 -5
- package/desktop/cmp/button/ZoneMapperButton.ts +7 -5
- package/desktop/cmp/dash/canvas/DashCanvasModel.ts +2 -2
- package/desktop/cmp/dash/container/DashContainerModel.ts +2 -2
- package/desktop/cmp/dock/DockViewModel.ts +21 -11
- package/desktop/cmp/dock/impl/DockView.ts +4 -2
- package/desktop/cmp/form/FormField.ts +2 -2
- package/desktop/cmp/grid/editors/BooleanEditor.ts +5 -1
- package/desktop/cmp/input/DateInput.ts +1 -3
- package/desktop/cmp/panel/Panel.ts +4 -2
- package/desktop/cmp/panel/PanelModel.ts +5 -5
- package/desktop/cmp/rest/Actions.ts +15 -9
- package/desktop/cmp/treemap/TreeMap.ts +7 -10
- package/inspector/instances/InstancesModel.ts +6 -6
- package/mobile/cmp/button/ColAutosizeButton.ts +4 -3
- package/mobile/cmp/button/ColChooserButton.ts +4 -3
- package/mobile/cmp/button/ExpandCollapseButton.ts +4 -3
- package/mobile/cmp/button/ZoneMapperButton.ts +4 -3
- package/mobile/cmp/input/DateInput.ts +1 -1
- package/mobile/cmp/input/Select.ts +3 -3
- package/mobile/cmp/panel/Panel.ts +4 -2
- package/package.json +2 -2
- package/svc/AutoRefreshService.ts +3 -3
- package/svc/ChangelogService.ts +3 -3
- package/svc/EnvironmentService.ts +1 -1
- package/svc/FetchService.ts +5 -5
- package/svc/GridAutosizeService.ts +4 -7
- package/svc/GridExportService.ts +3 -8
- package/svc/IdentityService.ts +1 -1
- package/svc/TrackService.ts +9 -15
- package/svc/WebSocketService.ts +14 -15
- package/utils/async/AsyncUtils.ts +3 -2
- package/utils/async/Timer.ts +5 -4
- package/utils/datetime/LocalDate.ts +13 -13
- package/utils/js/BrowserUtils.ts +8 -8
- package/utils/js/Decorators.ts +18 -3
- package/utils/js/LangUtils.ts +10 -9
- package/utils/js/LogUtils.ts +66 -26
- package/utils/react/LayoutPropUtils.ts +3 -3
|
@@ -4,17 +4,17 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2023 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {HAlign, PlainObject, Some, Thunkable} from '@xh/hoist/core';
|
|
7
|
+
import {HAlign, PlainObject, Some, Thunkable, XH} from '@xh/hoist/core';
|
|
8
8
|
import {genDisplayName} from '@xh/hoist/data';
|
|
9
|
+
|
|
10
|
+
import type {ColGroupDef} from '@xh/hoist/kit/ag-grid';
|
|
9
11
|
import {throwIf, withDefault} from '@xh/hoist/utils/js';
|
|
10
|
-
import {clone, isEmpty, isFunction, isString} from 'lodash';
|
|
12
|
+
import {clone, isEmpty, isFunction, isString, keysIn} from 'lodash';
|
|
11
13
|
import {ReactNode} from 'react';
|
|
12
14
|
import {GridModel} from '../GridModel';
|
|
13
15
|
import {ColumnHeaderClassFn, ColumnHeaderNameFn} from '../Types';
|
|
14
16
|
import {Column, ColumnSpec, getAgHeaderClassFn} from './Column';
|
|
15
17
|
|
|
16
|
-
import type {ColGroupDef} from '@xh/hoist/kit/ag-grid';
|
|
17
|
-
|
|
18
18
|
export interface ColumnGroupSpec {
|
|
19
19
|
/** Column or ColumnGroup configs for children of this group.*/
|
|
20
20
|
children: Array<ColumnGroupSpec | ColumnSpec>;
|
|
@@ -39,6 +39,8 @@ export interface ColumnGroupSpec {
|
|
|
39
39
|
|
|
40
40
|
/** True to skip this column when adding to grid. */
|
|
41
41
|
omit?: Thunkable<boolean>;
|
|
42
|
+
|
|
43
|
+
appData?: PlainObject;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
/**
|
|
@@ -62,6 +64,8 @@ export class ColumnGroup {
|
|
|
62
64
|
*/
|
|
63
65
|
agOptions?: PlainObject;
|
|
64
66
|
|
|
67
|
+
appData: PlainObject;
|
|
68
|
+
|
|
65
69
|
/**
|
|
66
70
|
* Not for application use. ColumnGroups are created internally by Hoist.
|
|
67
71
|
*
|
|
@@ -83,6 +87,7 @@ export class ColumnGroup {
|
|
|
83
87
|
headerAlign,
|
|
84
88
|
agOptions,
|
|
85
89
|
borders,
|
|
90
|
+
appData,
|
|
86
91
|
...rest
|
|
87
92
|
} = config;
|
|
88
93
|
|
|
@@ -92,8 +97,6 @@ export class ColumnGroup {
|
|
|
92
97
|
'Must specify groupId or a string headerName for a ColumnGroup'
|
|
93
98
|
);
|
|
94
99
|
|
|
95
|
-
Object.assign(this, rest);
|
|
96
|
-
|
|
97
100
|
this.groupId = withDefault(groupId, headerName) as string;
|
|
98
101
|
this.headerName = withDefault(headerName, genDisplayName(this.groupId));
|
|
99
102
|
this.headerClass = headerClass;
|
|
@@ -102,6 +105,14 @@ export class ColumnGroup {
|
|
|
102
105
|
this.children = children;
|
|
103
106
|
this.gridModel = gridModel;
|
|
104
107
|
this.agOptions = agOptions ? clone(agOptions) : {};
|
|
108
|
+
this.appData = appData ? clone(appData) : {};
|
|
109
|
+
|
|
110
|
+
if (!isEmpty(rest)) {
|
|
111
|
+
const keys = keysIn(rest);
|
|
112
|
+
throw XH.exception(
|
|
113
|
+
`Column group '${this.groupId}' configured with unsupported key(s) '${keys}'. Custom config data must be nested within the 'appData' property.`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
105
116
|
}
|
|
106
117
|
|
|
107
118
|
getAgSpec(): ColGroupDef {
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2023 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {GridModel} from '../GridModel';
|
|
8
7
|
import {box} from '@xh/hoist/cmp/layout';
|
|
9
8
|
import {BoxProps, hoistCmp, HoistProps, useContextModel} from '@xh/hoist/core';
|
|
10
9
|
import {fmtNumber} from '@xh/hoist/format';
|
|
11
|
-
import {pluralize, singularize, withDefault} from '@xh/hoist/utils/js';
|
|
10
|
+
import {logError, pluralize, singularize, withDefault} from '@xh/hoist/utils/js';
|
|
11
|
+
import {GridModel} from '../GridModel';
|
|
12
12
|
|
|
13
13
|
export interface GridCountLabelProps extends HoistProps, BoxProps {
|
|
14
14
|
/** GridModel to which this component should bind. */
|
|
@@ -49,8 +49,9 @@ export const [GridCountLabel, gridCountLabel] = hoistCmp.withFactory<GridCountLa
|
|
|
49
49
|
gridModel = withDefault(gridModel, useContextModel(GridModel));
|
|
50
50
|
|
|
51
51
|
if (!gridModel) {
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
logError(
|
|
53
|
+
`GridModel not found - provide via 'gridModel' prop or context.`,
|
|
54
|
+
GridCountLabel
|
|
54
55
|
);
|
|
55
56
|
return '';
|
|
56
57
|
}
|
|
@@ -9,7 +9,7 @@ import {GridAutosizeOptions} from '@xh/hoist/cmp/grid/GridAutosizeOptions';
|
|
|
9
9
|
import {XH} from '@xh/hoist/core';
|
|
10
10
|
import {CompoundFilter, FieldFilter, Filter, StoreRecord} from '@xh/hoist/data';
|
|
11
11
|
import {forEachAsync} from '@xh/hoist/utils/async';
|
|
12
|
-
import {stripTags} from '@xh/hoist/utils/js';
|
|
12
|
+
import {logWarn, stripTags} from '@xh/hoist/utils/js';
|
|
13
13
|
import {
|
|
14
14
|
forOwn,
|
|
15
15
|
groupBy,
|
|
@@ -74,7 +74,7 @@ export class ColumnWidthCalculator {
|
|
|
74
74
|
try {
|
|
75
75
|
return this.getHeaderWidth(gridModel, column, autosizeIncludeHeaderIcons, bufferPx);
|
|
76
76
|
} catch (e) {
|
|
77
|
-
|
|
77
|
+
logWarn([`Error calculating max header width for colId '${column.colId}'.`, e], this);
|
|
78
78
|
} finally {
|
|
79
79
|
this.resetHeaderClassNames();
|
|
80
80
|
}
|
|
@@ -104,7 +104,7 @@ export class ColumnWidthCalculator {
|
|
|
104
104
|
return await this.calcLevelWidthAsync(gridModel, records, column, options);
|
|
105
105
|
}
|
|
106
106
|
} catch (e) {
|
|
107
|
-
|
|
107
|
+
logWarn([`Error calculating max data width for colId '${column.colId}'.`, e], this);
|
|
108
108
|
} finally {
|
|
109
109
|
this.resetClassNames();
|
|
110
110
|
}
|
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2023 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import {GridSorterLike} from '@xh/hoist/cmp/grid';
|
|
8
|
+
import {HoistModel, managed, PersistenceProvider, Some, XH} from '@xh/hoist/core';
|
|
8
9
|
import {action, makeObservable, observable} from '@xh/hoist/mobx';
|
|
9
10
|
import {isUndefined} from 'lodash';
|
|
10
11
|
import {GridModel} from '../GridModel';
|
|
11
|
-
import {GridModelPersistOptions} from '../Types';
|
|
12
|
+
import {ColumnState, GridModelPersistOptions} from '../Types';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Model to manage persisting state from GridModel.
|
|
@@ -22,7 +23,13 @@ export class GridPersistenceModel extends HoistModel {
|
|
|
22
23
|
gridModel: GridModel;
|
|
23
24
|
|
|
24
25
|
@observable.ref
|
|
25
|
-
state:
|
|
26
|
+
state: {
|
|
27
|
+
columns?: Partial<ColumnState>[];
|
|
28
|
+
sortBy?: Some<GridSorterLike>;
|
|
29
|
+
groupBy?: Some<string>;
|
|
30
|
+
version?: number;
|
|
31
|
+
autosize?: any;
|
|
32
|
+
};
|
|
26
33
|
|
|
27
34
|
@managed
|
|
28
35
|
provider: PersistenceProvider;
|
|
@@ -51,7 +58,7 @@ export class GridPersistenceModel extends HoistModel {
|
|
|
51
58
|
run: state => this.provider.write(state)
|
|
52
59
|
});
|
|
53
60
|
} catch (e) {
|
|
54
|
-
|
|
61
|
+
this.logError(e);
|
|
55
62
|
this.state = {version: this.VERSION};
|
|
56
63
|
}
|
|
57
64
|
|
package/cmp/grid/impl/Utils.ts
CHANGED
|
@@ -19,7 +19,7 @@ export function managedRenderer<T extends ColumnRenderer | GroupRowRenderer>(
|
|
|
19
19
|
try {
|
|
20
20
|
return fn.apply(null, arguments);
|
|
21
21
|
} catch (e) {
|
|
22
|
-
console.warn(`Renderer for '${identifier}' has thrown an error
|
|
22
|
+
console.warn(`Renderer for '${identifier}' has thrown an error`, e);
|
|
23
23
|
return '#ERROR';
|
|
24
24
|
}
|
|
25
25
|
} as unknown as T;
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {ColumnRenderer} from '@xh/hoist/cmp/grid';
|
|
8
8
|
import {div, span} from '@xh/hoist/cmp/layout';
|
|
9
|
-
import {throwIf, warnIf} from '@xh/hoist/utils/js';
|
|
10
|
-
import {isString, partition} from 'lodash';
|
|
9
|
+
import {throwIf, warnIf, intersperse} from '@xh/hoist/utils/js';
|
|
10
|
+
import {isNil, isString, partition, pull} from 'lodash';
|
|
11
11
|
import {ReactNode} from 'react';
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -30,24 +30,24 @@ export function multiFieldRenderer(value, context): ReactNode {
|
|
|
30
30
|
);
|
|
31
31
|
|
|
32
32
|
const {mainRenderer, delimiter, subFields = []} = multiFieldConfig,
|
|
33
|
-
[topFields, bottomFields] = partition(subFields, it => it.position === 'top')
|
|
34
|
-
topRowItems = [],
|
|
35
|
-
bottomRowItems = [];
|
|
33
|
+
[topFields, bottomFields] = partition(subFields, it => it.position === 'top');
|
|
36
34
|
|
|
37
|
-
// Render main field to top row
|
|
38
|
-
topRowItems
|
|
35
|
+
// Render main field and subfields to top row
|
|
36
|
+
let topRowItems: ReactNode[] = [
|
|
37
|
+
renderMainField(value, mainRenderer, context),
|
|
38
|
+
...topFields.map(it => renderSubField(it, context))
|
|
39
|
+
];
|
|
40
|
+
pull(topRowItems, null);
|
|
39
41
|
|
|
40
|
-
// Render
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
topRowItems.push(renderSubField(it, context));
|
|
44
|
-
});
|
|
42
|
+
// Render subfield to bottom row
|
|
43
|
+
let bottomRowItems: ReactNode[] = bottomFields.map(it => renderSubField(it, context));
|
|
44
|
+
pull(bottomRowItems, null);
|
|
45
45
|
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
bottomRowItems
|
|
50
|
-
}
|
|
46
|
+
// Insert delimiter if applicable
|
|
47
|
+
if (delimiter) {
|
|
48
|
+
topRowItems = intersperse(topRowItems, renderDelimiter(delimiter));
|
|
49
|
+
bottomRowItems = intersperse(bottomRowItems, renderDelimiter(delimiter));
|
|
50
|
+
}
|
|
51
51
|
|
|
52
52
|
return div({
|
|
53
53
|
className: 'xh-multifield-renderer',
|
|
@@ -107,14 +107,20 @@ function renderSubField({colId, label}, context) {
|
|
|
107
107
|
|
|
108
108
|
if (label && !isString(label)) label = headerName;
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
const renderedVal = renderValue(value, renderer, column, context),
|
|
111
|
+
renderedValIsEmpty = renderedVal === '' || isNil(renderedVal);
|
|
112
|
+
|
|
113
|
+
return renderedValIsEmpty
|
|
114
|
+
? null
|
|
115
|
+
: div({
|
|
116
|
+
className: 'xh-multifield-renderer-field',
|
|
117
|
+
items: [label ? `${label}: ` : null, renderedVal]
|
|
118
|
+
});
|
|
114
119
|
}
|
|
115
120
|
|
|
116
121
|
function renderValue(value, renderer, column, context) {
|
|
117
|
-
|
|
122
|
+
const ret = renderer ? renderer(value, {...context, column}) : value;
|
|
123
|
+
return isNil(ret) ? null : ret;
|
|
118
124
|
}
|
|
119
125
|
|
|
120
126
|
function renderDelimiter(delimiter) {
|
|
@@ -170,7 +170,7 @@ export class GroupingChooserModel extends HoistModel {
|
|
|
170
170
|
run: state => this.provider.write(state)
|
|
171
171
|
});
|
|
172
172
|
} catch (e) {
|
|
173
|
-
|
|
173
|
+
this.logError(e);
|
|
174
174
|
XH.safeDestroy(this.provider);
|
|
175
175
|
this.provider = null;
|
|
176
176
|
}
|
|
@@ -190,7 +190,7 @@ export class GroupingChooserModel extends HoistModel {
|
|
|
190
190
|
@action
|
|
191
191
|
setValue(value: string[]) {
|
|
192
192
|
if (!this.validateValue(value)) {
|
|
193
|
-
|
|
193
|
+
this.logWarn('Attempted to set invalid value', value);
|
|
194
194
|
return;
|
|
195
195
|
}
|
|
196
196
|
this.value = value;
|
|
@@ -20,7 +20,7 @@ import {fmtCompactDate, fmtDateTime} from '@xh/hoist/format';
|
|
|
20
20
|
import {action, computed, makeObservable, observable} from '@xh/hoist/mobx';
|
|
21
21
|
import {Timer} from '@xh/hoist/utils/async';
|
|
22
22
|
import {DAYS, HOURS, LocalDate, SECONDS} from '@xh/hoist/utils/datetime';
|
|
23
|
-
import {withDefault} from '@xh/hoist/utils/js';
|
|
23
|
+
import {logWarn, withDefault} from '@xh/hoist/utils/js';
|
|
24
24
|
|
|
25
25
|
interface RelativeTimestampProps extends HoistProps, BoxProps {
|
|
26
26
|
/**
|
|
@@ -231,7 +231,10 @@ function doFormat(timestamp: Date | number, opts: RelativeTimestampOptions): str
|
|
|
231
231
|
|
|
232
232
|
// 1) Degenerate cases
|
|
233
233
|
if (isFuture && !allowFuture) {
|
|
234
|
-
|
|
234
|
+
logWarn(
|
|
235
|
+
`Unexpected future date provided for timestamp: ${elapsed}ms in the future.`,
|
|
236
|
+
RelativeTimestamp
|
|
237
|
+
);
|
|
235
238
|
return '[????]';
|
|
236
239
|
}
|
|
237
240
|
|
|
@@ -144,7 +144,7 @@ export class TabContainerModel extends HoistModel {
|
|
|
144
144
|
|
|
145
145
|
if (route) {
|
|
146
146
|
if (XH.isMobileApp) {
|
|
147
|
-
|
|
147
|
+
this.logWarn('TabContainer routing is not supported for mobile applications.');
|
|
148
148
|
return;
|
|
149
149
|
}
|
|
150
150
|
|
|
@@ -384,7 +384,7 @@ export class TabContainerModel extends HoistModel {
|
|
|
384
384
|
this.provider = PersistenceProvider.create({path: 'tabContainer', ...persistWith});
|
|
385
385
|
state = this.provider.read() || null;
|
|
386
386
|
} catch (e) {
|
|
387
|
-
|
|
387
|
+
this.logError(e);
|
|
388
388
|
XH.safeDestroy(this.provider);
|
|
389
389
|
this.provider = null;
|
|
390
390
|
}
|
|
@@ -620,7 +620,7 @@ export class ZoneGridModel extends HoistModel {
|
|
|
620
620
|
ret[zone] = this.parseZoneMapping(zone, rawMapping);
|
|
621
621
|
} catch (e) {
|
|
622
622
|
if (strict) throw e;
|
|
623
|
-
|
|
623
|
+
this.logWarn(e.message);
|
|
624
624
|
ret[zone] = this._defaultState.mappings[zone];
|
|
625
625
|
}
|
|
626
626
|
});
|
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2023 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import {GridSorterLike} from '@xh/hoist/cmp/grid';
|
|
8
|
+
import {HoistModel, managed, PersistenceProvider, Some} from '@xh/hoist/core';
|
|
8
9
|
import {action, makeObservable, observable} from '@xh/hoist/mobx';
|
|
9
10
|
import {isUndefined} from 'lodash';
|
|
10
11
|
import {ZoneGridModel} from '../ZoneGridModel';
|
|
11
|
-
import {ZoneGridModelPersistOptions} from '../Types';
|
|
12
|
+
import {Zone, ZoneGridModelPersistOptions, ZoneMapping} from '../Types';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Model to manage persisting state from ZoneGridModel.
|
|
@@ -22,7 +23,12 @@ export class ZoneGridPersistenceModel extends HoistModel {
|
|
|
22
23
|
zoneGridModel: ZoneGridModel;
|
|
23
24
|
|
|
24
25
|
@observable.ref
|
|
25
|
-
state:
|
|
26
|
+
state: {
|
|
27
|
+
sortBy?: GridSorterLike;
|
|
28
|
+
groupBy?: Some<string>;
|
|
29
|
+
version?: number;
|
|
30
|
+
mappings?: Record<Zone, Some<string | ZoneMapping>>;
|
|
31
|
+
};
|
|
26
32
|
|
|
27
33
|
@managed
|
|
28
34
|
provider: PersistenceProvider;
|
|
@@ -51,7 +57,7 @@ export class ZoneGridPersistenceModel extends HoistModel {
|
|
|
51
57
|
run: state => this.provider.write(state)
|
|
52
58
|
});
|
|
53
59
|
} catch (e) {
|
|
54
|
-
|
|
60
|
+
this.logError(e);
|
|
55
61
|
this.state = {version: this.VERSION};
|
|
56
62
|
}
|
|
57
63
|
|
package/core/HoistBase.ts
CHANGED
|
@@ -4,8 +4,17 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2023 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {XH, PersistenceProvider, PersistOptions, DebounceSpec} from './';
|
|
8
|
-
import {
|
|
7
|
+
import {XH, PersistenceProvider, PersistOptions, DebounceSpec, Some} from './';
|
|
8
|
+
import {
|
|
9
|
+
throwIf,
|
|
10
|
+
getOrCreate,
|
|
11
|
+
logInfo,
|
|
12
|
+
logDebug,
|
|
13
|
+
logError,
|
|
14
|
+
logWarn,
|
|
15
|
+
withDebug,
|
|
16
|
+
withInfo
|
|
17
|
+
} from '@xh/hoist/utils/js';
|
|
9
18
|
import {
|
|
10
19
|
cloneDeep,
|
|
11
20
|
debounce as lodashDebounce,
|
|
@@ -26,7 +35,7 @@ import {
|
|
|
26
35
|
when as mobxWhen
|
|
27
36
|
} from '@xh/hoist/mobx';
|
|
28
37
|
import {IAutorunOptions, IReactionOptions} from 'mobx/dist/api/autorun';
|
|
29
|
-
import {IReactionDisposer} from 'mobx/dist/internal';
|
|
38
|
+
import {IReactionDisposer, IEqualsComparer} from 'mobx/dist/internal';
|
|
30
39
|
|
|
31
40
|
export interface HoistBaseClass {
|
|
32
41
|
new (...args: any[]): HoistBase;
|
|
@@ -69,6 +78,33 @@ export abstract class HoistBase {
|
|
|
69
78
|
/** Default persistence options for this object. */
|
|
70
79
|
persistWith: PersistOptions = null;
|
|
71
80
|
|
|
81
|
+
//--------------------------------------------------
|
|
82
|
+
// Logging Delegates
|
|
83
|
+
//--------------------------------------------------
|
|
84
|
+
logInfo(...messages: unknown[]) {
|
|
85
|
+
logInfo(messages, this);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
logWarn(...messages: unknown[]) {
|
|
89
|
+
logWarn(messages, this);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
logError(...messages: unknown[]) {
|
|
93
|
+
logError(messages, this);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
logDebug(...messages: unknown[]) {
|
|
97
|
+
logDebug(messages, this);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
withInfo<T>(messages: Some<unknown>, fn: () => T): T {
|
|
101
|
+
return withInfo<T>(messages, fn, this);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
withDebug<T>(messages: Some<unknown>, fn: () => T): T {
|
|
105
|
+
return withDebug<T>(messages, fn, this);
|
|
106
|
+
}
|
|
107
|
+
|
|
72
108
|
/**
|
|
73
109
|
* Add and start one or more managed reactions.
|
|
74
110
|
*
|
|
@@ -104,12 +140,12 @@ export abstract class HoistBase {
|
|
|
104
140
|
addReaction(...specs: ReactionSpec<any>[]): IReactionDisposer | IReactionDisposer[] {
|
|
105
141
|
const disposers = specs.map(s => {
|
|
106
142
|
if (!s) return null;
|
|
107
|
-
let {track, when, run, debounce, ...
|
|
143
|
+
let {track, when, run, debounce, ...rest} = s;
|
|
108
144
|
throwIf(
|
|
109
145
|
(track && when) || (!track && !when),
|
|
110
146
|
"Must specify either 'track' or 'when' in addReaction."
|
|
111
147
|
);
|
|
112
|
-
opts = parseReactionOptions(
|
|
148
|
+
const opts = parseReactionOptions(rest);
|
|
113
149
|
run = bindAndDebounce(this, run, debounce);
|
|
114
150
|
|
|
115
151
|
const disposer = track ? mobxReaction(track, run, opts) : mobxWhen(when, run, opts);
|
|
@@ -231,9 +267,10 @@ export abstract class HoistBase {
|
|
|
231
267
|
run: data => provider.write(data)
|
|
232
268
|
});
|
|
233
269
|
} catch (e) {
|
|
234
|
-
|
|
270
|
+
this.logError(
|
|
235
271
|
`Failed to configure Persistence for '${property}'. Be sure to fully specify ` +
|
|
236
|
-
`'persistWith' on this object or in the method call
|
|
272
|
+
`'persistWith' on this object or in the method call`,
|
|
273
|
+
e
|
|
237
274
|
);
|
|
238
275
|
}
|
|
239
276
|
}
|
|
@@ -257,7 +294,7 @@ export abstract class HoistBase {
|
|
|
257
294
|
/**
|
|
258
295
|
* Object containing options accepted by MobX 'reaction' API as well as arguments below.
|
|
259
296
|
*/
|
|
260
|
-
export interface ReactionSpec<T = any> extends IReactionOptions<T, any> {
|
|
297
|
+
export interface ReactionSpec<T = any> extends Omit<IReactionOptions<T, any>, 'equals'> {
|
|
261
298
|
/**
|
|
262
299
|
* Function returning data to observe - first arg to the underlying reaction() call.
|
|
263
300
|
* Specify this or `when`.
|
|
@@ -275,6 +312,9 @@ export interface ReactionSpec<T = any> extends IReactionOptions<T, any> {
|
|
|
275
312
|
|
|
276
313
|
/** Specify to debounce run function */
|
|
277
314
|
debounce?: DebounceSpec;
|
|
315
|
+
|
|
316
|
+
/** Specify a default from {@link comparer} or a custom comparer function. */
|
|
317
|
+
equals?: keyof typeof comparer | IEqualsComparer<T>;
|
|
278
318
|
}
|
|
279
319
|
|
|
280
320
|
/**
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {cloneDeep, isUndefined} from 'lodash';
|
|
8
8
|
import {wait} from '../promise';
|
|
9
|
-
import {throwIf} from '../utils/js';
|
|
9
|
+
import {logError, throwIf} from '../utils/js';
|
|
10
10
|
import {HoistBaseClass, PersistenceProvider, PersistOptions} from './';
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -66,9 +66,10 @@ function createPersistDescriptor(
|
|
|
66
66
|
'@persist decorator should be applied to an instance of HoistBase'
|
|
67
67
|
);
|
|
68
68
|
if (descriptor.get || descriptor.set) {
|
|
69
|
-
|
|
69
|
+
logError(
|
|
70
70
|
`Error defining ${property} : @persist or @persistWith should be defined closest ` +
|
|
71
|
-
`to property, and after mobx annotation e.g. '@bindable @persist ${property}'
|
|
71
|
+
`to property, and after mobx annotation e.g. '@bindable @persist ${property}'`,
|
|
72
|
+
target
|
|
72
73
|
);
|
|
73
74
|
return descriptor;
|
|
74
75
|
}
|
|
@@ -89,9 +90,13 @@ function createPersistDescriptor(
|
|
|
89
90
|
});
|
|
90
91
|
});
|
|
91
92
|
} catch (e) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
`'
|
|
93
|
+
logError(
|
|
94
|
+
[
|
|
95
|
+
`Failed to configure Persistence for '${property}'. Be sure to fully specify ` +
|
|
96
|
+
`'persistWith' on this object or annotation`,
|
|
97
|
+
e
|
|
98
|
+
],
|
|
99
|
+
target
|
|
95
100
|
);
|
|
96
101
|
}
|
|
97
102
|
|
package/core/HoistComponent.ts
CHANGED
|
@@ -282,9 +282,7 @@ function wrapWithModel(render: RenderFn, cfg: Config): RenderFn {
|
|
|
282
282
|
if (!model && !spec.optional && spec instanceof UsesSpec) {
|
|
283
283
|
console.error(`
|
|
284
284
|
Failed to find model with selector '${formatSelector(spec.selector)}' for
|
|
285
|
-
component '${
|
|
286
|
-
cfg.displayName
|
|
287
|
-
}'. Ensure the proper model is available via context, or
|
|
285
|
+
component '${cfg.displayName}'. Ensure the proper model is available via context, or
|
|
288
286
|
specify explicitly using the 'model' prop.
|
|
289
287
|
`);
|
|
290
288
|
return cmpErrDisplay({...getLayoutProps(props), item: 'No model found'});
|
package/core/elem.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
ReactElement,
|
|
15
15
|
ReactNode
|
|
16
16
|
} from 'react';
|
|
17
|
-
import {
|
|
17
|
+
import {Some, Thunkable} from './types/Types';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Alternative format for specifying React Elements in render functions. This type is designed to
|
|
@@ -39,7 +39,7 @@ import {PlainObject, Some, Thunkable} from './types/Types';
|
|
|
39
39
|
* with this API. The '$' will be stripped from the prop name before passing it along to the
|
|
40
40
|
* underlying component.
|
|
41
41
|
*/
|
|
42
|
-
export type ElementSpec<P
|
|
42
|
+
export type ElementSpec<P> = P & {
|
|
43
43
|
//---------------------------------------------
|
|
44
44
|
// Enhanced attributes to support element factory
|
|
45
45
|
//---------------------------------------------
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {Exception} from './Exception';
|
|
8
8
|
import {fragment, span} from '@xh/hoist/cmp/layout';
|
|
9
|
-
import {stripTags} from '@xh/hoist/utils/js';
|
|
9
|
+
import {logError, logWarn, stripTags} from '@xh/hoist/utils/js';
|
|
10
10
|
import {Icon} from '@xh/hoist/icon';
|
|
11
11
|
import {forOwn, has, isArray, isNil, isObject, omitBy, pick, set} from 'lodash';
|
|
12
12
|
import {HoistException, PlainObject, XH} from '../';
|
|
@@ -189,7 +189,7 @@ export class ExceptionHandler {
|
|
|
189
189
|
username = XH.getUsername();
|
|
190
190
|
|
|
191
191
|
if (!username) {
|
|
192
|
-
|
|
192
|
+
logWarn('Error report cannot be submitted to UI server - user unknown', this);
|
|
193
193
|
return false;
|
|
194
194
|
}
|
|
195
195
|
|
|
@@ -206,7 +206,7 @@ export class ExceptionHandler {
|
|
|
206
206
|
});
|
|
207
207
|
return true;
|
|
208
208
|
} catch (e) {
|
|
209
|
-
|
|
209
|
+
logError(['Exception while submitting error report to UI server', e], this);
|
|
210
210
|
return false;
|
|
211
211
|
}
|
|
212
212
|
}
|
|
@@ -261,7 +261,7 @@ export class ExceptionHandler {
|
|
|
261
261
|
return stripTags(JSON.stringify(ret, null, 4));
|
|
262
262
|
} catch (e) {
|
|
263
263
|
const message = 'Failed to serialize error';
|
|
264
|
-
|
|
264
|
+
logError([message, exception, e], this);
|
|
265
265
|
return JSON.stringify({message}, null, 4);
|
|
266
266
|
}
|
|
267
267
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {HoistService, HoistServiceClass, Some, XH} from '@xh/hoist/core';
|
|
8
8
|
import {instanceManager} from '@xh/hoist/core/impl/InstanceManager';
|
|
9
|
-
import {throwIf
|
|
9
|
+
import {throwIf} from '@xh/hoist/utils/js';
|
|
10
10
|
import {camelCase, castArray} from 'lodash';
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -50,13 +50,7 @@ export async function installServicesAsync(serviceClasses: Some<HoistServiceClas
|
|
|
50
50
|
|
|
51
51
|
async function initServicesInternalAsync(svcs: HoistService[]) {
|
|
52
52
|
const promises = svcs.map(it => {
|
|
53
|
-
return withDebug(
|
|
54
|
-
`Initializing ${it.constructor.name}`,
|
|
55
|
-
() => {
|
|
56
|
-
return it.initAsync();
|
|
57
|
-
},
|
|
58
|
-
'XH'
|
|
59
|
-
);
|
|
53
|
+
return it.withDebug(`Initializing`, () => it.initAsync());
|
|
60
54
|
});
|
|
61
55
|
|
|
62
56
|
const results: any[] = await Promise.allSettled(promises),
|
package/core/load/LoadSupport.ts
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2023 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {HoistBase, managed, RefreshContextModel, TaskObserver} from '../';
|
|
7
|
+
import {HoistBase, managed, PlainObject, RefreshContextModel, TaskObserver} from '../';
|
|
8
8
|
import {LoadSpec, Loadable} from './';
|
|
9
9
|
import {makeObservable, observable, runInAction} from '@xh/hoist/mobx';
|
|
10
|
-
import {throwIf} from '@xh/hoist/utils/js';
|
|
11
|
-
import {isPlainObject} from 'lodash';
|
|
10
|
+
import {logDebug, logError, throwIf} from '@xh/hoist/utils/js';
|
|
11
|
+
import {isPlainObject, pull} from 'lodash';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Provides support for objects that participate in Hoist's loading/refresh lifecycle.
|
|
@@ -55,11 +55,11 @@ export class LoadSupport extends HoistBase implements Loadable {
|
|
|
55
55
|
return this.doLoadAsync(newSpec);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
async refreshAsync(meta?:
|
|
58
|
+
async refreshAsync(meta?: PlainObject) {
|
|
59
59
|
return this.loadAsync({meta, isRefresh: true});
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
async autoRefreshAsync(meta?:
|
|
62
|
+
async autoRefreshAsync(meta?: PlainObject) {
|
|
63
63
|
return this.loadAsync({meta, isAutoRefresh: true});
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -98,18 +98,17 @@ export class LoadSupport extends HoistBase implements Loadable {
|
|
|
98
98
|
if (target instanceof RefreshContextModel) return;
|
|
99
99
|
|
|
100
100
|
const elapsed = this.lastLoadCompleted.getTime() - this.lastLoadRequested.getTime(),
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}${elapsed}ms`;
|
|
101
|
+
status = exception ? 'failed' : null,
|
|
102
|
+
msg = pull([loadSpec.typeDisplay, status, `${elapsed}ms`, exception], null);
|
|
104
103
|
|
|
105
104
|
if (exception) {
|
|
106
105
|
if (exception.isRoutine) {
|
|
107
|
-
|
|
106
|
+
logDebug(msg, target);
|
|
108
107
|
} else {
|
|
109
|
-
|
|
108
|
+
logError(msg, target);
|
|
110
109
|
}
|
|
111
110
|
} else {
|
|
112
|
-
|
|
111
|
+
logDebug(msg, target);
|
|
113
112
|
}
|
|
114
113
|
});
|
|
115
114
|
}
|
package/core/model/HoistModel.ts
CHANGED