@xh/hoist 66.0.2 → 66.1.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 +23 -0
- package/admin/regroup/RegroupDialog.ts +2 -2
- package/admin/regroup/RegroupDialogModel.ts +9 -9
- package/admin/tabs/userData/roles/RoleModel.ts +25 -15
- package/admin/tabs/userData/roles/RolePanel.ts +3 -1
- package/admin/tabs/userData/roles/recategorize/RecategorizeDialog.ts +59 -0
- package/admin/tabs/userData/roles/recategorize/RecategorizeDialogModel.ts +83 -0
- package/appcontainer/RouterModel.ts +2 -2
- package/build/types/admin/regroup/RegroupDialogModel.d.ts +1 -1
- package/build/types/admin/tabs/userData/roles/RoleModel.d.ts +3 -0
- package/build/types/admin/tabs/userData/roles/recategorize/RecategorizeDialog.d.ts +2 -0
- package/build/types/admin/tabs/userData/roles/recategorize/RecategorizeDialogModel.d.ts +30 -0
- package/build/types/cmp/grid/Grid.d.ts +8 -0
- package/build/types/cmp/markdown/Markdown.d.ts +7 -0
- package/build/types/data/cube/row/LeafRow.d.ts +11 -2
- package/build/types/security/authzero/AuthZeroClient.d.ts +7 -0
- package/build/types/security/msal/MsalClient.d.ts +7 -0
- package/build/types/utils/js/LangUtils.d.ts +13 -0
- package/cmp/badge/Badge.ts +2 -3
- package/cmp/chart/Chart.ts +4 -4
- package/cmp/chart/ChartModel.ts +3 -2
- package/cmp/chart/impl/copyToClipboard.ts +1 -2
- package/cmp/dataview/DataView.ts +3 -2
- package/cmp/grid/Grid.ts +29 -1
- package/cmp/grid/GridSorter.ts +2 -1
- package/cmp/layout/Box.ts +2 -3
- package/cmp/markdown/Markdown.ts +11 -2
- package/data/cube/row/LeafRow.ts +15 -2
- package/data/impl/RecordSet.ts +7 -7
- package/desktop/cmp/input/Select.ts +3 -12
- package/desktop/cmp/rest/impl/RestFormModel.ts +3 -3
- package/desktop/cmp/treemap/TreeMap.ts +4 -3
- package/mobile/cmp/input/Select.ts +3 -3
- package/mobile/cmp/navigator/NavigatorModel.ts +4 -4
- package/package.json +1 -1
- package/security/authzero/AuthZeroClient.ts +26 -17
- package/security/msal/MsalClient.ts +30 -19
- package/tsconfig.tsbuildinfo +1 -1
- package/utils/js/LangUtils.ts +24 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 66.1.0 - 2024-07-31
|
|
4
|
+
|
|
5
|
+
### 🎁 New Features
|
|
6
|
+
|
|
7
|
+
* Enhanced `markdown` component to support the underlying `components` prop from `react-markdown`.
|
|
8
|
+
Use this prop to customize markdown rendering.
|
|
9
|
+
* New `mergeDeep` method provided in `@xh/hoist/utils/js` as an alternative to `lodash.merge`,
|
|
10
|
+
without lodash's surprising deep-merging of array-based properties.
|
|
11
|
+
* Enhanced Roles Admin UI to support bulk category reassignment.
|
|
12
|
+
|
|
13
|
+
### 🐞 Bug Fixes
|
|
14
|
+
|
|
15
|
+
* Fixed `Record.descendants` and `Record.allDescendants` getters that were incorrectly returning the
|
|
16
|
+
parent record itself. Now only the descendants are returned, as expected.
|
|
17
|
+
* Fixed `Grid` regression where pinned columns were automatically un-pinned when the viewport became
|
|
18
|
+
too small to accommodate them.
|
|
19
|
+
* Fixed bug where `Grid` context-menus would lose focus when rendered inside `Overlay` components.
|
|
20
|
+
|
|
21
|
+
### ⚙️ Technical
|
|
22
|
+
|
|
23
|
+
* Enhanced beta `MsalClient` and `AuthZeroClient` OAuth implementations to support passing
|
|
24
|
+
app-specific configs directly into the constructors of their underlying client implementation.
|
|
25
|
+
|
|
3
26
|
## 66.0.2 - 2024-07-17
|
|
4
27
|
|
|
5
28
|
### 🐞 Bug Fixes
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
import {filler} from '@xh/hoist/cmp/layout';
|
|
8
8
|
import {hoistCmp, uses} from '@xh/hoist/core';
|
|
9
9
|
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
10
|
+
import {select} from '@xh/hoist/desktop/cmp/input';
|
|
10
11
|
import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
|
|
11
12
|
import {Icon} from '@xh/hoist/icon';
|
|
12
13
|
import {dialog, dialogBody} from '@xh/hoist/kit/blueprint';
|
|
13
|
-
import {select} from '@xh/hoist/desktop/cmp/input';
|
|
14
14
|
|
|
15
15
|
import {RegroupDialogModel} from './RegroupDialogModel';
|
|
16
16
|
|
|
@@ -23,7 +23,7 @@ export const regroupDialog = hoistCmp.factory({
|
|
|
23
23
|
|
|
24
24
|
return dialog({
|
|
25
25
|
title: 'Change Group',
|
|
26
|
-
icon: Icon.
|
|
26
|
+
icon: Icon.folder(),
|
|
27
27
|
style: {width: 300},
|
|
28
28
|
isOpen: true,
|
|
29
29
|
isCloseButtonShown: false,
|
|
@@ -4,38 +4,38 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2024 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {uniq} from 'lodash';
|
|
8
7
|
import {HoistModel, XH} from '@xh/hoist/core';
|
|
9
|
-
import {action, bindable, observable, makeObservable} from '@xh/hoist/mobx';
|
|
10
8
|
import {Icon} from '@xh/hoist/icon/Icon';
|
|
9
|
+
import {action, bindable, makeObservable, observable} from '@xh/hoist/mobx';
|
|
10
|
+
import {uniq} from 'lodash';
|
|
11
11
|
|
|
12
12
|
export class RegroupDialogModel extends HoistModel {
|
|
13
|
-
|
|
13
|
+
private parent;
|
|
14
14
|
|
|
15
15
|
@bindable groupName = null;
|
|
16
16
|
@observable isOpen = false;
|
|
17
17
|
|
|
18
18
|
regroupAction = {
|
|
19
19
|
text: 'Change Group',
|
|
20
|
-
icon: Icon.
|
|
20
|
+
icon: Icon.folder(),
|
|
21
21
|
recordsRequired: true,
|
|
22
22
|
actionFn: () => this.open(),
|
|
23
|
-
displayFn: () => ({hidden: this.
|
|
23
|
+
displayFn: () => ({hidden: this.parent.gridModel.readonly})
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
get options() {
|
|
27
|
-
return uniq(this.
|
|
27
|
+
return uniq(this.parent.gridModel.store.allRecords.map(it => it.data.groupName)).sort();
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
constructor(parent) {
|
|
31
31
|
super();
|
|
32
32
|
makeObservable(this);
|
|
33
|
-
this.
|
|
33
|
+
this.parent = parent;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
async saveAsync() {
|
|
37
|
-
const {
|
|
38
|
-
{selectedRecords, store} =
|
|
37
|
+
const {parent, groupName} = this,
|
|
38
|
+
{selectedRecords, store} = parent.gridModel,
|
|
39
39
|
ids = selectedRecords.map(it => it.id),
|
|
40
40
|
resp = await store.bulkUpdateRecordsAsync(ids, {groupName}),
|
|
41
41
|
failuresPresent = resp.fail > 0,
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2024 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
+
import {RecategorizeDialogModel} from '@xh/hoist/admin/tabs/userData/roles/recategorize/RecategorizeDialogModel';
|
|
7
8
|
import {FilterChooserModel} from '@xh/hoist/cmp/filter';
|
|
8
9
|
import {GridModel, tagsRenderer, TreeStyle} from '@xh/hoist/cmp/grid';
|
|
9
10
|
import * as Col from '@xh/hoist/cmp/grid/columns';
|
|
@@ -34,6 +35,7 @@ export class RoleModel extends HoistModel {
|
|
|
34
35
|
@managed gridModel: GridModel;
|
|
35
36
|
@managed filterChooserModel: FilterChooserModel;
|
|
36
37
|
@managed readonly roleEditorModel = new RoleEditorModel(this);
|
|
38
|
+
@managed recategorizeDialogModel = new RecategorizeDialogModel(this);
|
|
37
39
|
|
|
38
40
|
@observable.ref allRoles: HoistRole[] = [];
|
|
39
41
|
@observable.ref moduleConfig: RoleModuleConfig;
|
|
@@ -75,7 +77,9 @@ export class RoleModel extends HoistModel {
|
|
|
75
77
|
const {data} = await XH.fetchJson({url: 'roleAdmin/list', loadSpec});
|
|
76
78
|
if (loadSpec.isStale) return;
|
|
77
79
|
|
|
78
|
-
runInAction(() =>
|
|
80
|
+
runInAction(() => {
|
|
81
|
+
this.allRoles = this.processRolesFromServer(data);
|
|
82
|
+
});
|
|
79
83
|
this.displayRoles();
|
|
80
84
|
await this.gridModel.preSelectFirstAsync();
|
|
81
85
|
} catch (e) {
|
|
@@ -158,7 +162,7 @@ export class RoleModel extends HoistModel {
|
|
|
158
162
|
disabled: !record || record.data.isGroupRow
|
|
159
163
|
}),
|
|
160
164
|
actionFn: ({record}) => this.editAsync(record.data as HoistRole),
|
|
161
|
-
recordsRequired:
|
|
165
|
+
recordsRequired: 1
|
|
162
166
|
};
|
|
163
167
|
}
|
|
164
168
|
|
|
@@ -170,7 +174,7 @@ export class RoleModel extends HoistModel {
|
|
|
170
174
|
disabled: !record || record.data.isGroupRow
|
|
171
175
|
}),
|
|
172
176
|
actionFn: ({record}) => this.createAsync(record.data as HoistRole),
|
|
173
|
-
recordsRequired:
|
|
177
|
+
recordsRequired: 1
|
|
174
178
|
};
|
|
175
179
|
}
|
|
176
180
|
|
|
@@ -186,7 +190,7 @@ export class RoleModel extends HoistModel {
|
|
|
186
190
|
this.deleteAsync(record.data as HoistRole)
|
|
187
191
|
.catchDefault()
|
|
188
192
|
.linkTo(this.loadModel),
|
|
189
|
-
recordsRequired:
|
|
193
|
+
recordsRequired: 1
|
|
190
194
|
};
|
|
191
195
|
}
|
|
192
196
|
|
|
@@ -269,6 +273,7 @@ export class RoleModel extends HoistModel {
|
|
|
269
273
|
treeMode: true,
|
|
270
274
|
treeStyle: TreeStyle.HIGHLIGHTS_AND_BORDERS,
|
|
271
275
|
autosizeOptions: {mode: 'managed'},
|
|
276
|
+
selModel: 'multiple',
|
|
272
277
|
emptyText: 'No roles found.',
|
|
273
278
|
colChooserModel: true,
|
|
274
279
|
sortBy: 'name',
|
|
@@ -334,17 +339,7 @@ export class RoleModel extends HoistModel {
|
|
|
334
339
|
{field: {name: 'lastUpdatedBy', type: 'string'}, hidden: true},
|
|
335
340
|
{field: {name: 'notes', type: 'string'}, filterable: false, flex: 1}
|
|
336
341
|
],
|
|
337
|
-
contextMenu: this.
|
|
338
|
-
? [this.groupByAction(), ...GridModel.defaultContextMenu]
|
|
339
|
-
: [
|
|
340
|
-
this.addAction(),
|
|
341
|
-
this.editAction(),
|
|
342
|
-
this.cloneAction(),
|
|
343
|
-
this.deleteAction(),
|
|
344
|
-
'-',
|
|
345
|
-
this.groupByAction(),
|
|
346
|
-
...GridModel.defaultContextMenu
|
|
347
|
-
],
|
|
342
|
+
contextMenu: () => this.getContextMenuItems(),
|
|
348
343
|
onRowDoubleClicked: ({data: record}) => {
|
|
349
344
|
if (record && !record.data.isGroupRow) {
|
|
350
345
|
this.editAsync(record.data as HoistRole);
|
|
@@ -353,6 +348,21 @@ export class RoleModel extends HoistModel {
|
|
|
353
348
|
});
|
|
354
349
|
}
|
|
355
350
|
|
|
351
|
+
private getContextMenuItems() {
|
|
352
|
+
return this.readonly
|
|
353
|
+
? [this.groupByAction(), ...GridModel.defaultContextMenu]
|
|
354
|
+
: [
|
|
355
|
+
this.addAction(),
|
|
356
|
+
this.editAction(),
|
|
357
|
+
this.cloneAction(),
|
|
358
|
+
this.deleteAction(),
|
|
359
|
+
this.recategorizeDialogModel.recategorizeAction(),
|
|
360
|
+
'-',
|
|
361
|
+
this.groupByAction(),
|
|
362
|
+
...GridModel.defaultContextMenu
|
|
363
|
+
];
|
|
364
|
+
}
|
|
365
|
+
|
|
356
366
|
private createFilterChooserModel(): FilterChooserModel {
|
|
357
367
|
const config = this.moduleConfig;
|
|
358
368
|
return new FilterChooserModel({
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2024 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
+
import {recategorizeDialog} from '@xh/hoist/admin/tabs/userData/roles/recategorize/RecategorizeDialog';
|
|
7
8
|
import {grid} from '@xh/hoist/cmp/grid';
|
|
8
9
|
import {fragment, hframe, vframe} from '@xh/hoist/cmp/layout';
|
|
9
10
|
import {creates, hoistCmp} from '@xh/hoist/core';
|
|
@@ -48,7 +49,8 @@ export const rolePanel = hoistCmp.factory({
|
|
|
48
49
|
],
|
|
49
50
|
item: hframe(vframe(grid(), roleGraph()), detailsPanel())
|
|
50
51
|
}),
|
|
51
|
-
roleEditor()
|
|
52
|
+
roleEditor(),
|
|
53
|
+
recategorizeDialog()
|
|
52
54
|
);
|
|
53
55
|
}
|
|
54
56
|
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file belongs to Hoist, an application development toolkit
|
|
3
|
+
* developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
|
|
4
|
+
*
|
|
5
|
+
* Copyright © 2024 Extremely Heavy Industries Inc.
|
|
6
|
+
*/
|
|
7
|
+
import {RecategorizeDialogModel} from '@xh/hoist/admin/tabs/userData/roles/recategorize/RecategorizeDialogModel';
|
|
8
|
+
import {filler} from '@xh/hoist/cmp/layout';
|
|
9
|
+
import {hoistCmp, uses} from '@xh/hoist/core';
|
|
10
|
+
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
11
|
+
import {select} from '@xh/hoist/desktop/cmp/input';
|
|
12
|
+
import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
|
|
13
|
+
import {Icon} from '@xh/hoist/icon';
|
|
14
|
+
import {dialog, dialogBody} from '@xh/hoist/kit/blueprint';
|
|
15
|
+
|
|
16
|
+
export const recategorizeDialog = hoistCmp.factory({
|
|
17
|
+
model: uses(RecategorizeDialogModel),
|
|
18
|
+
|
|
19
|
+
render({model}) {
|
|
20
|
+
const {isOpen} = model;
|
|
21
|
+
if (!isOpen) return null;
|
|
22
|
+
|
|
23
|
+
return dialog({
|
|
24
|
+
title: `Change Category (${model.selectedRecords.length} roles)`,
|
|
25
|
+
icon: Icon.folder(),
|
|
26
|
+
style: {width: 300},
|
|
27
|
+
isOpen: true,
|
|
28
|
+
isCloseButtonShown: false,
|
|
29
|
+
items: [
|
|
30
|
+
dialogBody(
|
|
31
|
+
select({
|
|
32
|
+
bind: 'categoryName',
|
|
33
|
+
enableCreate: true,
|
|
34
|
+
options: model.options,
|
|
35
|
+
width: 260
|
|
36
|
+
})
|
|
37
|
+
),
|
|
38
|
+
tbar()
|
|
39
|
+
]
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const tbar = hoistCmp.factory<RecategorizeDialogModel>(({model}) => {
|
|
45
|
+
return toolbar(
|
|
46
|
+
filler(),
|
|
47
|
+
button({
|
|
48
|
+
text: 'Cancel',
|
|
49
|
+
onClick: () => model.close()
|
|
50
|
+
}),
|
|
51
|
+
button({
|
|
52
|
+
text: 'Save',
|
|
53
|
+
icon: Icon.check(),
|
|
54
|
+
intent: 'success',
|
|
55
|
+
disabled: model.categoryName == null,
|
|
56
|
+
onClick: () => model.saveAsync()
|
|
57
|
+
})
|
|
58
|
+
);
|
|
59
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file belongs to Hoist, an application development toolkit
|
|
3
|
+
* developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
|
|
4
|
+
*
|
|
5
|
+
* Copyright © 2024 Extremely Heavy Industries Inc.
|
|
6
|
+
*/
|
|
7
|
+
import {RoleModel} from '@xh/hoist/admin/tabs/userData/roles/RoleModel';
|
|
8
|
+
import {HoistModel, XH} from '@xh/hoist/core';
|
|
9
|
+
import {StoreRecord} from '@xh/hoist/data';
|
|
10
|
+
import {Icon} from '@xh/hoist/icon/Icon';
|
|
11
|
+
import {action, bindable, makeObservable, observable} from '@xh/hoist/mobx';
|
|
12
|
+
import {compact, every, filter, map, uniq} from 'lodash';
|
|
13
|
+
|
|
14
|
+
export class RecategorizeDialogModel extends HoistModel {
|
|
15
|
+
private parent: RoleModel;
|
|
16
|
+
selectedRecords: StoreRecord[];
|
|
17
|
+
|
|
18
|
+
@bindable categoryName = null;
|
|
19
|
+
@observable isOpen = false;
|
|
20
|
+
|
|
21
|
+
recategorizeAction() {
|
|
22
|
+
return {
|
|
23
|
+
text: 'Change Category',
|
|
24
|
+
icon: Icon.folder(),
|
|
25
|
+
recordsRequired: true,
|
|
26
|
+
actionFn: ({selectedRecords}) => this.open(selectedRecords),
|
|
27
|
+
displayFn: ({selectedRecords}) => {
|
|
28
|
+
return {
|
|
29
|
+
hidden: this.parent.readonly,
|
|
30
|
+
disabled: every(selectedRecords, it => it.data?.isGroupRow)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get options() {
|
|
37
|
+
return [
|
|
38
|
+
...compact(uniq(this.parent.allRoles.map(it => it.category))).sort(),
|
|
39
|
+
{value: '_CLEAR_ROLES_', label: '[Clear Existing Category]'}
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
constructor(parent) {
|
|
44
|
+
super();
|
|
45
|
+
makeObservable(this);
|
|
46
|
+
this.parent = parent;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async saveAsync() {
|
|
50
|
+
if (this.parent.readonly) return;
|
|
51
|
+
const roleSpec = filter(
|
|
52
|
+
this.selectedRecords.map(it => it.data),
|
|
53
|
+
it => !it.isGroupRow
|
|
54
|
+
);
|
|
55
|
+
const roles: string[] = map(roleSpec, it => it.name);
|
|
56
|
+
await XH.fetchService
|
|
57
|
+
.postJson({
|
|
58
|
+
url: 'roleAdmin/bulkCategoryUpdate',
|
|
59
|
+
body: {
|
|
60
|
+
roles,
|
|
61
|
+
category: this.categoryName === '_CLEAR_ROLES_' ? null : this.categoryName
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
.catchDefault();
|
|
65
|
+
await this.parent.refreshAsync();
|
|
66
|
+
this.close();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//-----------------
|
|
70
|
+
// Actions
|
|
71
|
+
//-----------------
|
|
72
|
+
@action
|
|
73
|
+
close() {
|
|
74
|
+
this.categoryName = null;
|
|
75
|
+
this.isOpen = false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@action
|
|
79
|
+
open(selectedRecords) {
|
|
80
|
+
this.selectedRecords = selectedRecords;
|
|
81
|
+
this.isOpen = true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {HoistModel} from '../core';
|
|
8
8
|
import {action, observable, makeObservable} from '@xh/hoist/mobx';
|
|
9
|
-
import {
|
|
9
|
+
import {mergeDeep} from '@xh/hoist/utils/js';
|
|
10
10
|
import {isOmitted} from '@xh/hoist/utils/impl';
|
|
11
11
|
import {createRouter, Router, State} from 'router5';
|
|
12
12
|
import browserPlugin from 'router5-plugin-browser';
|
|
@@ -51,7 +51,7 @@ export class RouterModel extends HoistModel {
|
|
|
51
51
|
*/
|
|
52
52
|
appendRoute(routeName: string, newParams: object = {}) {
|
|
53
53
|
const {name, params} = this.currentState;
|
|
54
|
-
return this.router.navigate(`${name}.${routeName}`,
|
|
54
|
+
return this.router.navigate(`${name}.${routeName}`, mergeDeep({}, params, newParams));
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RecategorizeDialogModel } from '@xh/hoist/admin/tabs/userData/roles/recategorize/RecategorizeDialogModel';
|
|
1
2
|
import { FilterChooserModel } from '@xh/hoist/cmp/filter';
|
|
2
3
|
import { GridModel } from '@xh/hoist/cmp/grid';
|
|
3
4
|
import { HoistModel, LoadSpec } from '@xh/hoist/core';
|
|
@@ -15,6 +16,7 @@ export declare class RoleModel extends HoistModel {
|
|
|
15
16
|
gridModel: GridModel;
|
|
16
17
|
filterChooserModel: FilterChooserModel;
|
|
17
18
|
readonly roleEditorModel: RoleEditorModel;
|
|
19
|
+
recategorizeDialogModel: RecategorizeDialogModel;
|
|
18
20
|
allRoles: HoistRole[];
|
|
19
21
|
moduleConfig: RoleModuleConfig;
|
|
20
22
|
showInGroups: boolean;
|
|
@@ -37,5 +39,6 @@ export declare class RoleModel extends HoistModel {
|
|
|
37
39
|
private processRolesFromServer;
|
|
38
40
|
private processRolesForTreeGrid;
|
|
39
41
|
private createGridModel;
|
|
42
|
+
private getContextMenuItems;
|
|
40
43
|
private createFilterChooserModel;
|
|
41
44
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { HoistModel } from '@xh/hoist/core';
|
|
2
|
+
import { StoreRecord } from '@xh/hoist/data';
|
|
3
|
+
export declare class RecategorizeDialogModel extends HoistModel {
|
|
4
|
+
private parent;
|
|
5
|
+
selectedRecords: StoreRecord[];
|
|
6
|
+
categoryName: any;
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
recategorizeAction(): {
|
|
9
|
+
text: string;
|
|
10
|
+
icon: any;
|
|
11
|
+
recordsRequired: boolean;
|
|
12
|
+
actionFn: ({ selectedRecords }: {
|
|
13
|
+
selectedRecords: any;
|
|
14
|
+
}) => void;
|
|
15
|
+
displayFn: ({ selectedRecords }: {
|
|
16
|
+
selectedRecords: any;
|
|
17
|
+
}) => {
|
|
18
|
+
hidden: boolean;
|
|
19
|
+
disabled: boolean;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
get options(): (string | {
|
|
23
|
+
value: string;
|
|
24
|
+
label: string;
|
|
25
|
+
})[];
|
|
26
|
+
constructor(parent: any);
|
|
27
|
+
saveAsync(): Promise<void>;
|
|
28
|
+
close(): void;
|
|
29
|
+
open(selectedRecords: any): void;
|
|
30
|
+
}
|
|
@@ -49,6 +49,7 @@ export declare class GridLocalModel extends HoistModel {
|
|
|
49
49
|
/** @returns true if any root-level records have children */
|
|
50
50
|
get isHierarchical(): boolean;
|
|
51
51
|
get emptyText(): import("react").ReactNode;
|
|
52
|
+
constructor();
|
|
52
53
|
onLinked(): void;
|
|
53
54
|
private createDefaultAgOptions;
|
|
54
55
|
getColumnDefs(): Array<ColDef | ColGroupDef>;
|
|
@@ -130,4 +131,11 @@ export declare class GridLocalModel extends HoistModel {
|
|
|
130
131
|
onKeyDown: (evt: any) => void;
|
|
131
132
|
onRowClicked: (evt: any) => void;
|
|
132
133
|
onRowDoubleClicked: (evt: any) => void;
|
|
134
|
+
/**
|
|
135
|
+
* When a `Grid` context menu is open at the same time as a BP `Overlay2` with `enforceFocus`,
|
|
136
|
+
* the context menu will lose focus, causing menu items not to highlight on hover. Prevent this
|
|
137
|
+
* by conditionally stopping the focus event from propagating.
|
|
138
|
+
*/
|
|
139
|
+
private static didAddFocusFixListener;
|
|
140
|
+
static addFocusFixListener(): void;
|
|
133
141
|
}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { HoistProps } from '@xh/hoist/core';
|
|
3
|
+
import { Components } from 'react-markdown';
|
|
3
4
|
interface MarkdownProps extends HoistProps {
|
|
4
5
|
/** Markdown formatted string to render. */
|
|
5
6
|
content: string;
|
|
7
|
+
/**
|
|
8
|
+
* Map of html tag to tag or functional component to control rendering of standard html
|
|
9
|
+
* elements. See https://www.npmjs.com/package/react-markdown/v/8.0.6#appendix-b-components
|
|
10
|
+
* for details.
|
|
11
|
+
*/
|
|
12
|
+
components?: Components;
|
|
6
13
|
/** True (default) to render new lines with <br/> tags. */
|
|
7
14
|
lineBreaks?: boolean;
|
|
8
15
|
}
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { PlainObject } from '@xh/hoist/core';
|
|
2
|
-
import { StoreRecord } from '
|
|
2
|
+
import { StoreRecord, StoreRecordId } from '@xh/hoist/data';
|
|
3
3
|
import { View } from '../View';
|
|
4
4
|
import { BaseRow } from './BaseRow';
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Represents a leaf row returned by a {@link View} or call to {@link Cube.executeQuery}.
|
|
7
|
+
*
|
|
8
|
+
* These rows are 1-1 with the source records loaded into the Cube's internal store - i.e. they are
|
|
9
|
+
* not computed aggregates - although the data they contain is a shallow copy of the original and
|
|
10
|
+
* limited to the fields requested by the View / Query that produced them.
|
|
7
11
|
*/
|
|
8
12
|
export declare class LeafRow extends BaseRow {
|
|
13
|
+
/**
|
|
14
|
+
* Id of the StoreRecord within the Cube that was used to construct this leaf row.
|
|
15
|
+
* Useful if you need to update this leaf's data via {@link Cube.updateDataAsync}.
|
|
16
|
+
*/
|
|
17
|
+
readonly cubeRecordId: StoreRecordId;
|
|
9
18
|
get isLeaf(): boolean;
|
|
10
19
|
constructor(view: View, id: string, rawRecord: StoreRecord);
|
|
11
20
|
applyLeafDataUpdate(newRec: StoreRecord, updatedRowDatas: Set<PlainObject>): void;
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
import { Auth0ClientOptions } from '@auth0/auth0-spa-js';
|
|
1
2
|
import { Token, TokenMap } from '@xh/hoist/security/Token';
|
|
2
3
|
import { BaseOAuthClient, BaseOAuthClientConfig } from '../BaseOAuthClient';
|
|
3
4
|
export interface AuthZeroClientConfig extends BaseOAuthClientConfig<AuthZeroTokenSpec> {
|
|
4
5
|
/** Domain of your app registered with Auth0 */
|
|
5
6
|
domain: string;
|
|
7
|
+
/**
|
|
8
|
+
* Additional options for the Auth0Client ctor. Will be deep merged with defaults, with options
|
|
9
|
+
* supplied here taking precedence. Use with care, as overriding defaults may have unintended
|
|
10
|
+
* consequences or fail to work with Hoist's expected usage of the client library.
|
|
11
|
+
*/
|
|
12
|
+
authZeroClientOptions?: Partial<Auth0ClientOptions>;
|
|
6
13
|
}
|
|
7
14
|
export interface AuthZeroTokenSpec {
|
|
8
15
|
/** Scopes for the desired access token.*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as msal from '@azure/msal-browser';
|
|
1
2
|
import { LogLevel } from '@azure/msal-browser';
|
|
2
3
|
import { Token, TokenMap } from '@xh/hoist/security/Token';
|
|
3
4
|
import { BaseOAuthClient, BaseOAuthClientConfig } from '../BaseOAuthClient';
|
|
@@ -36,6 +37,12 @@ export interface MsalClientConfig extends BaseOAuthClientConfig<MsalTokenSpec> {
|
|
|
36
37
|
initRefreshTokenExpirationOffsetSecs?: number;
|
|
37
38
|
/** The log level of MSAL. Default is LogLevel.Warning. */
|
|
38
39
|
msalLogLevel?: LogLevel;
|
|
40
|
+
/**
|
|
41
|
+
* Additional options for the MSAL client ctor. Will be deep merged with defaults, with options
|
|
42
|
+
* supplied here taking precedence. Use with care, as overriding defaults may have unintended
|
|
43
|
+
* consequences or fail to work with Hoist's expected usage of the client library.
|
|
44
|
+
*/
|
|
45
|
+
msalClientOptions?: Partial<msal.Configuration>;
|
|
39
46
|
}
|
|
40
47
|
export interface MsalTokenSpec {
|
|
41
48
|
/** Scopes for the desired access token. */
|
|
@@ -121,3 +121,16 @@ export declare function intersperse<T>(arr: T[], separator: T): T[];
|
|
|
121
121
|
* Return value passed or the result of executing it, if it is a function.
|
|
122
122
|
*/
|
|
123
123
|
export declare function executeIfFunction<T>(v: Thunkable<T>): T;
|
|
124
|
+
/**
|
|
125
|
+
* Merge objects deeply.
|
|
126
|
+
*
|
|
127
|
+
* Use this for merging properties from various sources into a target object.
|
|
128
|
+
* The target value will be mutated and returned.
|
|
129
|
+
*
|
|
130
|
+
* Note that this method has the same semantics as Lodash merge, with the important exception
|
|
131
|
+
* that properties containing arrays will *not* be merged deeply.
|
|
132
|
+
*/
|
|
133
|
+
export declare function mergeDeep<T, S>(object: T, source: S): T & S;
|
|
134
|
+
export declare function mergeDeep<T, S1, S2>(object: T, source1: S1, source2: S2): T & S1 & S2;
|
|
135
|
+
export declare function mergeDeep<T, S1, S2, S3>(object: T, source1: S1, source2: S2, source3: S3): T & S1 & S2 & S3;
|
|
136
|
+
export declare function mergeDeep<T, S>(target: T, ...sources: S[]): T & S;
|
package/cmp/badge/Badge.ts
CHANGED
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {div} from '@xh/hoist/cmp/layout';
|
|
8
8
|
import {BoxProps, hoistCmp, HoistProps, Intent} from '@xh/hoist/core';
|
|
9
|
-
import {TEST_ID} from '@xh/hoist/utils/js';
|
|
9
|
+
import {TEST_ID, mergeDeep} from '@xh/hoist/utils/js';
|
|
10
10
|
import {splitLayoutProps} from '@xh/hoist/utils/react';
|
|
11
11
|
import classNames from 'classnames';
|
|
12
|
-
import {merge} from 'lodash';
|
|
13
12
|
import './Badge.scss';
|
|
14
13
|
|
|
15
14
|
export interface BadgeProps extends HoistProps, BoxProps {
|
|
@@ -42,7 +41,7 @@ export const [Badge, badge] = hoistCmp.withFactory<BadgeProps>({
|
|
|
42
41
|
classes.push('xh-badge--compact');
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
const divProps =
|
|
44
|
+
const divProps = mergeDeep(
|
|
46
45
|
{className: classNames(className, classes)},
|
|
47
46
|
{style: layoutProps},
|
|
48
47
|
{[TEST_ID]: testId},
|
package/cmp/chart/Chart.ts
CHANGED
|
@@ -22,14 +22,14 @@ import {useContextMenu} from '@xh/hoist/dynamics/desktop';
|
|
|
22
22
|
import {Icon} from '@xh/hoist/icon';
|
|
23
23
|
import {Highcharts} from '@xh/hoist/kit/highcharts';
|
|
24
24
|
import {runInAction} from '@xh/hoist/mobx';
|
|
25
|
-
import {logError} from '@xh/hoist/utils/js';
|
|
25
|
+
import {logError, mergeDeep} from '@xh/hoist/utils/js';
|
|
26
26
|
import {
|
|
27
27
|
createObservableRef,
|
|
28
28
|
getLayoutProps,
|
|
29
29
|
useOnResize,
|
|
30
30
|
useOnVisibleChange
|
|
31
31
|
} from '@xh/hoist/utils/react';
|
|
32
|
-
import {assign, castArray, cloneDeep, forOwn, isEqual, isPlainObject,
|
|
32
|
+
import {assign, castArray, cloneDeep, forOwn, isEqual, isPlainObject, omit} from 'lodash';
|
|
33
33
|
import {placeholder} from '../layout';
|
|
34
34
|
import './Chart.scss';
|
|
35
35
|
import {ChartModel} from './ChartModel';
|
|
@@ -252,7 +252,7 @@ class ChartLocalModel extends HoistModel {
|
|
|
252
252
|
defaultConf = this.getDefaultConfig();
|
|
253
253
|
|
|
254
254
|
this.mergeAxisConfigs(themeConf, propsConf);
|
|
255
|
-
return
|
|
255
|
+
return mergeDeep(defaultConf, themeConf, propsConf);
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
getDefaultConfig() {
|
|
@@ -336,7 +336,7 @@ class ChartLocalModel extends HoistModel {
|
|
|
336
336
|
arr = castArray(conf[axis] || {}),
|
|
337
337
|
defaultAxisConfig = this.getDefaultAxisConfig(axis);
|
|
338
338
|
|
|
339
|
-
conf[axis] = arr.map(it =>
|
|
339
|
+
conf[axis] = arr.map(it => mergeDeep({}, defaultAxisConfig, theme[axis], it));
|
|
340
340
|
theme[axis] = null;
|
|
341
341
|
});
|
|
342
342
|
}
|
package/cmp/chart/ChartModel.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {HoistModel, PlainObject, Some} from '@xh/hoist/core';
|
|
8
8
|
import {action, makeObservable, observable} from '@xh/hoist/mobx';
|
|
9
|
-
import {castArray, cloneDeep
|
|
9
|
+
import {castArray, cloneDeep} from 'lodash';
|
|
10
|
+
import {mergeDeep} from '@xh/hoist/utils/js';
|
|
10
11
|
|
|
11
12
|
interface ChartConfig {
|
|
12
13
|
/** The initial highchartsConfig for this chart. */
|
|
@@ -81,7 +82,7 @@ export class ChartModel extends HoistModel {
|
|
|
81
82
|
*/
|
|
82
83
|
@action
|
|
83
84
|
updateHighchartsConfig(update: any) {
|
|
84
|
-
this.highchartsConfig =
|
|
85
|
+
this.highchartsConfig = mergeDeep(cloneDeep(this.highchartsConfig), update);
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
/** @param series - one or more data series to be charted. */
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* Copyright © 2024 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {XH} from '@xh/hoist/core';
|
|
8
|
-
import {merge} from 'lodash';
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* Copy the chart in it's current state to the clipboard.
|
|
@@ -45,7 +44,7 @@ export function installCopyToClipboard(Highcharts) {
|
|
|
45
44
|
async function convertChartToPngAsync(chart) {
|
|
46
45
|
const svg = await new Promise((resolve, reject) =>
|
|
47
46
|
chart.getSVGForLocalExport(
|
|
48
|
-
|
|
47
|
+
chart.options.exporting,
|
|
49
48
|
{},
|
|
50
49
|
() => reject('Cannot fallback to export server'),
|
|
51
50
|
svg => resolve(svg)
|