@xh/hoist 77.0.0-SNAPSHOT.1760707577281 → 77.0.0-SNAPSHOT.1760709229150
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 +4 -0
- package/appcontainer/ImpersonationBarModel.ts +7 -2
- package/build/types/appcontainer/ImpersonationBarModel.d.ts +2 -0
- package/desktop/appcontainer/AppContainer.ts +15 -10
- package/desktop/appcontainer/ImpersonationBar.ts +16 -6
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
apps to programmatically show dynamic info in a widget header without perturbing its saved state.
|
|
10
10
|
* Enhanced grid column filtering to support sorting the list of available values.
|
|
11
11
|
|
|
12
|
+
### ⚙️ Technical
|
|
13
|
+
|
|
14
|
+
* Autofocus the user input when the impersonation bar is shown.
|
|
15
|
+
|
|
12
16
|
## 76.0.0 - 2025-09-26
|
|
13
17
|
|
|
14
18
|
### 💥 Breaking Changes (upgrade difficulty: 🟠 MEDIUM - AG Grid update, Hoist React upgrade)
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
+
import {HoistInputModel} from '@xh/hoist/cmp/input';
|
|
7
8
|
import {HoistModel, XH} from '@xh/hoist/core';
|
|
8
|
-
import {action,
|
|
9
|
+
import {action, bindable, makeObservable, observable} from '@xh/hoist/mobx';
|
|
9
10
|
import {throwIf} from '@xh/hoist/utils/js';
|
|
11
|
+
import {createRef} from 'react';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* @internal
|
|
@@ -18,6 +20,9 @@ export class ImpersonationBarModel extends HoistModel {
|
|
|
18
20
|
@observable.ref targets: string[] = [];
|
|
19
21
|
@bindable pendingTarget: string = null;
|
|
20
22
|
|
|
23
|
+
// For managed focus of desktop select.
|
|
24
|
+
inputRef = createRef<HoistInputModel>();
|
|
25
|
+
|
|
21
26
|
constructor() {
|
|
22
27
|
super();
|
|
23
28
|
makeObservable(this);
|
|
@@ -58,7 +63,7 @@ export class ImpersonationBarModel extends HoistModel {
|
|
|
58
63
|
@action
|
|
59
64
|
toggleVisibility() {
|
|
60
65
|
if (this.isOpen) {
|
|
61
|
-
this.hide();
|
|
66
|
+
XH.identityService.isImpersonating ? this.inputRef.current?.focus() : this.hide();
|
|
62
67
|
} else {
|
|
63
68
|
this.show();
|
|
64
69
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { HoistInputModel } from '@xh/hoist/cmp/input';
|
|
1
2
|
import { HoistModel } from '@xh/hoist/core';
|
|
2
3
|
/**
|
|
3
4
|
* @internal
|
|
@@ -7,6 +8,7 @@ export declare class ImpersonationBarModel extends HoistModel {
|
|
|
7
8
|
showRequested: boolean;
|
|
8
9
|
targets: string[];
|
|
9
10
|
pendingTarget: string;
|
|
11
|
+
inputRef: import("react").RefObject<HoistInputModel>;
|
|
10
12
|
constructor();
|
|
11
13
|
init(): void;
|
|
12
14
|
get isOpen(): boolean;
|
|
@@ -4,32 +4,34 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
+
import {HotkeyConfig} from '@blueprintjs/core/src/hooks/hotkeys/hotkeyConfig';
|
|
7
8
|
import {AppContainerModel} from '@xh/hoist/appcontainer/AppContainerModel';
|
|
9
|
+
import {errorBoundary} from '@xh/hoist/cmp/error/ErrorBoundary';
|
|
8
10
|
import {fragment, frame, vframe, viewport} from '@xh/hoist/cmp/layout';
|
|
11
|
+
import {mask} from '@xh/hoist/cmp/mask';
|
|
9
12
|
import {createElement, hoistCmp, refreshContextView, uses, XH} from '@xh/hoist/core';
|
|
10
|
-
import {errorBoundary} from '@xh/hoist/cmp/error/ErrorBoundary';
|
|
11
13
|
import {changelogDialog} from '@xh/hoist/desktop/appcontainer/ChangelogDialog';
|
|
12
|
-
import {suspendPanel} from './suspend/SuspendPanel';
|
|
13
14
|
import {dockContainerImpl} from '@xh/hoist/desktop/cmp/dock/impl/DockContainer';
|
|
15
|
+
import {errorMessageImpl} from '@xh/hoist/desktop/cmp/error/impl/ErrorMessage';
|
|
14
16
|
import {colChooserDialog as colChooser} from '@xh/hoist/desktop/cmp/grid/impl/colchooser/ColChooserDialog';
|
|
15
17
|
import {ColChooserModel} from '@xh/hoist/desktop/cmp/grid/impl/colchooser/ColChooserModel';
|
|
16
|
-
import {zoneMapperDialog as zoneMapper} from '@xh/hoist/desktop/cmp/zoneGrid/impl/ZoneMapperDialog';
|
|
17
18
|
import {columnHeaderFilter} from '@xh/hoist/desktop/cmp/grid/impl/filter/ColumnHeaderFilter';
|
|
18
19
|
import {ColumnHeaderFilterModel} from '@xh/hoist/desktop/cmp/grid/impl/filter/ColumnHeaderFilterModel';
|
|
19
20
|
import {gridFilterDialog} from '@xh/hoist/desktop/cmp/grid/impl/filter/GridFilterDialog';
|
|
20
|
-
import {
|
|
21
|
+
import {maskImpl} from '@xh/hoist/desktop/cmp/mask/impl/Mask';
|
|
21
22
|
import {ModalSupportModel} from '@xh/hoist/desktop/cmp/modalsupport';
|
|
22
23
|
import {pinPadImpl} from '@xh/hoist/desktop/cmp/pinpad/impl/PinPad';
|
|
23
24
|
import {storeFilterFieldImpl} from '@xh/hoist/desktop/cmp/store/impl/StoreFilterField';
|
|
24
25
|
import {tabContainerImpl} from '@xh/hoist/desktop/cmp/tab/impl/TabContainer';
|
|
26
|
+
import {zoneMapperDialog as zoneMapper} from '@xh/hoist/desktop/cmp/zoneGrid/impl/ZoneMapperDialog';
|
|
25
27
|
import {useContextMenu, useHotkeys} from '@xh/hoist/desktop/hooks';
|
|
26
28
|
import {installDesktopImpls} from '@xh/hoist/dynamics/desktop';
|
|
27
29
|
import {inspectorPanel} from '@xh/hoist/inspector/InspectorPanel';
|
|
28
30
|
import {blueprintProvider} from '@xh/hoist/kit/blueprint';
|
|
29
|
-
import {
|
|
30
|
-
import {maskImpl} from '@xh/hoist/desktop/cmp/mask/impl/Mask';
|
|
31
|
+
import {consumeEvent} from '@xh/hoist/utils/js';
|
|
31
32
|
import {elementFromContent, useOnMount} from '@xh/hoist/utils/react';
|
|
32
33
|
import {isEmpty} from 'lodash';
|
|
34
|
+
import {ReactElement} from 'react';
|
|
33
35
|
import {aboutDialog} from './AboutDialog';
|
|
34
36
|
import {banner} from './Banner';
|
|
35
37
|
import {exceptionDialog} from './ExceptionDialog';
|
|
@@ -39,9 +41,9 @@ import {lockoutPanel} from './LockoutPanel';
|
|
|
39
41
|
import {loginPanel} from './LoginPanel';
|
|
40
42
|
import {messageSource} from './MessageSource';
|
|
41
43
|
import {optionsDialog} from './OptionsDialog';
|
|
44
|
+
import {suspendPanel} from './suspend/SuspendPanel';
|
|
42
45
|
import {toastSource} from './ToastSource';
|
|
43
46
|
import {versionBar} from './VersionBar';
|
|
44
|
-
import {ReactElement} from 'react';
|
|
45
47
|
|
|
46
48
|
installDesktopImpls({
|
|
47
49
|
tabContainerImpl,
|
|
@@ -183,9 +185,9 @@ const bannerList = hoistCmp.factory<AppContainerModel>({
|
|
|
183
185
|
}
|
|
184
186
|
});
|
|
185
187
|
|
|
186
|
-
function globalHotKeys(model) {
|
|
188
|
+
function globalHotKeys(model: AppContainerModel) {
|
|
187
189
|
const {impersonationBarModel, optionsDialogModel} = model,
|
|
188
|
-
ret = [
|
|
190
|
+
ret: HotkeyConfig[] = [
|
|
189
191
|
{
|
|
190
192
|
global: true,
|
|
191
193
|
combo: 'shift + r',
|
|
@@ -199,7 +201,10 @@ function globalHotKeys(model) {
|
|
|
199
201
|
global: true,
|
|
200
202
|
combo: 'shift + i',
|
|
201
203
|
label: 'Impersonate another user',
|
|
202
|
-
onKeyDown:
|
|
204
|
+
onKeyDown: e => {
|
|
205
|
+
consumeEvent(e); // avoid typing "i" in the impersonation bar select
|
|
206
|
+
impersonationBarModel.toggleVisibility();
|
|
207
|
+
}
|
|
203
208
|
});
|
|
204
209
|
}
|
|
205
210
|
if (optionsDialogModel.hasOptions) {
|
|
@@ -31,9 +31,12 @@ export const impersonationBar = hoistCmp.factory({
|
|
|
31
31
|
|
|
32
32
|
const {targets} = model;
|
|
33
33
|
|
|
34
|
-
let msg = `Logged in as ${authUsername}
|
|
34
|
+
let msg = `Logged in as ${authUsername}`,
|
|
35
|
+
placeholder = 'Select a user...';
|
|
36
|
+
|
|
35
37
|
if (isImpersonating) {
|
|
36
38
|
msg += ` › impersonating ${username}`;
|
|
39
|
+
placeholder = `Impersonating ${username}`;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
return toolbar({
|
|
@@ -53,11 +56,16 @@ export const impersonationBar = hoistCmp.factory({
|
|
|
53
56
|
bind: 'pendingTarget',
|
|
54
57
|
options: targets,
|
|
55
58
|
enableCreate: true,
|
|
56
|
-
|
|
59
|
+
// Autofocus when shown to begin impersonation
|
|
60
|
+
autoFocus: !isImpersonating,
|
|
61
|
+
placeholder,
|
|
57
62
|
createMessageFn: q => `Impersonate new user "${q}"`,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
minWidth: 150,
|
|
64
|
+
maxWidth: 350,
|
|
65
|
+
menuWidth: 350,
|
|
66
|
+
flex: 1,
|
|
67
|
+
onCommit: model.onCommit,
|
|
68
|
+
ref: model.inputRef
|
|
61
69
|
}),
|
|
62
70
|
button({
|
|
63
71
|
text: isImpersonating ? 'Exit Impersonation' : 'Cancel',
|
|
@@ -72,6 +80,7 @@ export const impersonationBar = hoistCmp.factory({
|
|
|
72
80
|
const showUseResponsiblyAlert = () => {
|
|
73
81
|
XH.alert({
|
|
74
82
|
title: 'Important Reminders',
|
|
83
|
+
icon: Icon.warning(),
|
|
75
84
|
message: fragment(
|
|
76
85
|
h3('With great power comes great responsibility.'),
|
|
77
86
|
ul(
|
|
@@ -95,7 +104,8 @@ const showUseResponsiblyAlert = () => {
|
|
|
95
104
|
)
|
|
96
105
|
),
|
|
97
106
|
confirmProps: {
|
|
98
|
-
text: 'I understand and will be careful'
|
|
107
|
+
text: 'I understand and will be careful',
|
|
108
|
+
autoFocus: false
|
|
99
109
|
}
|
|
100
110
|
});
|
|
101
111
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "77.0.0-SNAPSHOT.
|
|
3
|
+
"version": "77.0.0-SNAPSHOT.1760709229150",
|
|
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",
|