@xh/hoist 77.0.0-SNAPSHOT.1760707577281 → 77.0.0-SNAPSHOT.1760710210101

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 CHANGED
@@ -9,6 +9,17 @@
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
+
16
+ ### 📚 Libraries
17
+
18
+ * @auth0/auth0-spa-js `2.4 → 2.7`
19
+ * @azure/msal-browser `4.23 → 4.25`
20
+ * dompurify `3.2 → 3.3`
21
+ * mobx `6.13 → 6.15`
22
+
12
23
  ## 76.0.0 - 2025-09-26
13
24
 
14
25
  ### 💥 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, observable, makeObservable, bindable} from '@xh/hoist/mobx';
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 {mask} from '@xh/hoist/cmp/mask';
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 {errorMessageImpl} from '@xh/hoist/desktop/cmp/error/impl/ErrorMessage';
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: () => impersonationBarModel.toggleVisibility()
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
- placeholder: 'Select User...',
59
+ // Autofocus when shown to begin impersonation
60
+ autoFocus: !isImpersonating,
61
+ placeholder,
57
62
  createMessageFn: q => `Impersonate new user "${q}"`,
58
- width: 250,
59
- menuWidth: 300,
60
- onCommit: model.onCommit
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.1760707577281",
3
+ "version": "77.0.0-SNAPSHOT.1760710210101",
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",
@@ -28,8 +28,8 @@
28
28
  ]
29
29
  },
30
30
  "dependencies": {
31
- "@auth0/auth0-spa-js": "~2.4.1",
32
- "@azure/msal-browser": "~4.23.0",
31
+ "@auth0/auth0-spa-js": "~2.7.0",
32
+ "@azure/msal-browser": "~4.25.0",
33
33
  "@blueprintjs/core": "^5.10.5",
34
34
  "@blueprintjs/datetime": "^5.3.7",
35
35
  "@blueprintjs/datetime2": "^2.3.7",
@@ -48,7 +48,7 @@
48
48
  "codemirror": "~5.65.0",
49
49
  "core-js": "3.x",
50
50
  "debounce-promise": "~3.1.0",
51
- "dompurify": "~3.2.3",
51
+ "dompurify": "~3.3.0",
52
52
  "downloadjs": "~1.4.7",
53
53
  "fast-deep-equal": "~3.1.1",
54
54
  "filesize": "~11.0.2",
@@ -58,7 +58,7 @@
58
58
  "jwt-decode": "~4.0.0",
59
59
  "lodash": "~4.17.21",
60
60
  "lodash-inflection": "~1.5.0",
61
- "mobx": "~6.13.2",
61
+ "mobx": "~6.15.0",
62
62
  "mobx-react-lite": "~4.1.0",
63
63
  "moment": "~2.30.1",
64
64
  "numbro": "~2.5.0",