@xh/hoist 74.0.0 → 74.1.1

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 (33) hide show
  1. package/CHANGELOG.md +36 -4
  2. package/build/types/cmp/grouping/GroupingChooserModel.d.ts +8 -4
  3. package/build/types/cmp/viewmanager/ViewInfo.d.ts +7 -3
  4. package/build/types/core/types/Interfaces.d.ts +1 -1
  5. package/build/types/desktop/cmp/grouping/GroupingChooser.d.ts +17 -6
  6. package/build/types/desktop/cmp/panel/Panel.d.ts +1 -1
  7. package/build/types/desktop/cmp/viewmanager/ViewManager.d.ts +26 -10
  8. package/build/types/mobile/cmp/grouping/GroupingChooser.d.ts +2 -6
  9. package/build/types/utils/impl/MenuItems.d.ts +13 -0
  10. package/build/types/utils/impl/index.d.ts +1 -0
  11. package/cmp/grouping/GroupingChooserModel.ts +25 -12
  12. package/cmp/viewmanager/ViewInfo.ts +7 -3
  13. package/core/HoistAppModel.ts +1 -0
  14. package/core/exception/ExceptionHandler.ts +1 -1
  15. package/core/types/Interfaces.ts +2 -2
  16. package/desktop/cmp/button/AppMenuButton.ts +3 -46
  17. package/desktop/cmp/grouping/GroupingChooser.scss +39 -35
  18. package/desktop/cmp/grouping/GroupingChooser.ts +157 -89
  19. package/desktop/cmp/panel/Panel.ts +1 -1
  20. package/desktop/cmp/viewmanager/ViewManager.ts +58 -16
  21. package/desktop/cmp/viewmanager/ViewMenu.ts +9 -2
  22. package/mobile/appcontainer/FeedbackDialog.ts +4 -0
  23. package/mobile/appcontainer/OptionsDialog.ts +3 -1
  24. package/mobile/cmp/grid/impl/ColChooser.ts +3 -2
  25. package/mobile/cmp/grouping/GroupingChooser.scss +41 -20
  26. package/mobile/cmp/grouping/GroupingChooser.ts +60 -89
  27. package/mobile/cmp/panel/DialogPanel.scss +5 -0
  28. package/package.json +1 -1
  29. package/svc/TrackService.ts +4 -3
  30. package/tsconfig.tsbuildinfo +1 -1
  31. package/utils/impl/MenuItems.ts +57 -0
  32. package/utils/impl/index.ts +1 -0
  33. package/utils/js/LangUtils.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -1,22 +1,54 @@
1
1
  # Changelog
2
2
 
3
+ ## v74.1.1 - 2025-07-02
4
+
5
+ ### 🎁 New Features
6
+
7
+ * Further refinements to the `GroupingChooser` desktop UI.
8
+ * Added new props `favoritesSide` and `favoritesTitle`.
9
+ * Deprecated `popoverTitle` prop - use `editorTitle` instead.
10
+ * Moved "Save as Favorite" button to a new compact toolbar within the popover.
11
+
12
+ ### 🐞 Bug Fixes
13
+
14
+ * Fixed a bug where `TrackService` was not properly verifying that tracked `data` was below the
15
+ configured `maxDataLength` limit.
16
+
17
+ ## v74.1.0 - 2025-06-30
18
+
19
+ ### 🎁 New Features
20
+
21
+ * Updated the `GroupingChooser` UI to use a single popover for both updating the value and
22
+ selecting/managing favorite groupings (if enabled).
23
+ * Adjusted `GroupingChooserModel` API and some CSS class names and testIds of `GroupingChooser`
24
+ internals, although those changes are very unlikely to require app-level adjustments.
25
+ * Adjusted/removed (rarely used) desktop and mobile `GroupingChooser` props related to popover
26
+ sizing and titling.
27
+ * Updated the mobile UI to use a full-screen dialog, similar to `ColumnChooser`.
28
+ * Added props to `ViewManager` to customize icons used for different types of views, and modified
29
+ default icons for Global and Shared views.
30
+ * Added `ViewManager.extraMenuItems` prop to allow insertion of custom, app-specific items into the
31
+ component's standard menu.
32
+
3
33
  ## v74.0.0 - 2025-06-11
4
34
 
5
35
  ### 💥 Breaking Changes (upgrade difficulty: 🟢 LOW - minor changes to ViewManagerModel, ChartModel)
6
36
 
7
37
  * Removed `ViewManagerModel.settleTime`. Now set via individual `PersistOptions.settleTime` instead.
8
- * ️Removed `ChartModel.showContextMenu`. Use a setting of `false` for the new `ChartModel.contextMenu`
9
- property instead.
38
+ * ️Removed `ChartModel.showContextMenu`. Use a setting of `false` for the new
39
+ `ChartModel.contextMenu` property instead.
10
40
 
11
41
  ### 🎁 New Features
42
+
12
43
  * Added `ViewManagerModel.preserveUnsavedChanges` flag to opt-out of that behaviour.
13
44
  * Added `PersistOptions.settleTime` to configure time to wait for state to settle before persisting.
14
45
  * Support for grid column level `onCellClicked` events.
15
46
  * General improvements to `MenuItem` api
16
- * New `MenuContext` object now sent as 2nd arg to `actionFn` and `prepareFn`.
17
- * New `ChartModel.contextMenu` property provides a fully customizable context menu for charts.
47
+ * New `MenuContext` object now sent as 2nd arg to `actionFn` and `prepareFn`.
48
+ * New `ChartModel.contextMenu` property provides a fully customizable context menu for charts.
18
49
 
19
50
  ### 🐞 Bug Fixes
51
+
20
52
  * Improved `ViewManagerModel.settleTime` by delegating to individual `PersistenceProviders`.
21
53
  * Fixed bug where grid column state could become unintentionally dirty when columns were hidden.
22
54
  * Improved `WebsocketService` heartbeat detection to auto-reconnect when the socket reports as open
@@ -8,7 +8,10 @@ export interface GroupingChooserConfig {
8
8
  dimensions?: (DimensionSpec | string)[];
9
9
  /** Initial value as an array of dimension names, or a function to produce such an array. */
10
10
  initialValue?: string[] | (() => string[]);
11
- /** Initial favorites as an array of dim name arrays, or a function to produce such an array. */
11
+ /**
12
+ * Initial favorites as an array of dim name arrays, or a function to produce such an array.
13
+ * Ignored if `persistWith.persistFavorites: false`.
14
+ */
12
15
  initialFavorites?: string[][] | (() => string[][]);
13
16
  /** Options governing persistence. */
14
17
  persistWith?: GroupingChooserPersistOptions;
@@ -47,7 +50,6 @@ export declare class GroupingChooserModel extends HoistModel {
47
50
  persistFavorites: boolean;
48
51
  pendingValue: string[];
49
52
  editorIsOpen: boolean;
50
- favoritesIsOpen: boolean;
51
53
  popoverRef: import("react").RefObject<HTMLElement> & import("react").RefCallback<HTMLElement>;
52
54
  private dimensions;
53
55
  private dimensionNames;
@@ -55,12 +57,12 @@ export declare class GroupingChooserModel extends HoistModel {
55
57
  get dimensionSpecs(): DimensionSpec[];
56
58
  get isValid(): boolean;
57
59
  get isAddEnabled(): boolean;
60
+ get isAddFavoriteEnabled(): boolean;
58
61
  constructor({ dimensions, initialValue, initialFavorites, persistWith, allowEmpty, maxDepth, commitOnChange }: GroupingChooserConfig);
59
62
  setDimensions(dimensions: Array<DimensionSpec | string>): void;
60
63
  setValue(value: string[]): void;
61
64
  toggleEditor(): void;
62
- toggleFavoritesMenu(): void;
63
- closePopover(): void;
65
+ closeEditor(): void;
64
66
  addPendingDim(dimName: string): void;
65
67
  replacePendingDimAtIdx(dimName: string, idx: number): void;
66
68
  removePendingDimAtIdx(idx: number): void;
@@ -74,8 +76,10 @@ export declare class GroupingChooserModel extends HoistModel {
74
76
  value: string[];
75
77
  label: string;
76
78
  }[];
79
+ get hasFavorites(): boolean;
77
80
  setFavorites(favorites: string[][]): void;
78
81
  addFavorite(value: string[]): void;
82
+ addPendingAsFavorite(): void;
79
83
  removeFavorite(value: string[]): void;
80
84
  isFavorite(value: string[]): boolean;
81
85
  private initPersist;
@@ -15,15 +15,19 @@ export declare class ViewInfo {
15
15
  readonly description: string;
16
16
  /** User owning this view. Null if the view is global.*/
17
17
  readonly owner: string;
18
- /** Is the owner making this view accessible to others? Always true for global views. */
18
+ /**
19
+ * True if the owner (which can be the current user) has made this view accessible to all other
20
+ * users. Note always `false` for global views, to better distinguish the two sharing models.
21
+ */
19
22
  readonly isShared: boolean;
20
23
  /** True if this view is global and visible to all users. */
21
24
  readonly isGlobal: boolean;
22
25
  /** Optional group name used for bucketing this view in display. */
23
26
  readonly group: string;
24
27
  /**
25
- * Should this view be pinned by users by default?
26
- * This value is intended to be used for global views only.
28
+ * True if this view should be pinned by default to all users' menus, where it will appear
29
+ * unless the user has explicitly unpinned it. Only applicable for global views, can be enabled
30
+ * by view managers to promote especially important global views and ensure users see them.
27
31
  */
28
32
  readonly isDefaultPinned: boolean;
29
33
  /**
@@ -177,7 +177,7 @@ export interface TrackOptions {
177
177
  /** Correlation ID to save along with track log. */
178
178
  correlationId?: string;
179
179
  /** App-supplied data to save along with track log.*/
180
- data?: PlainObject | PlainObject[];
180
+ data?: PlainObject | Array<unknown>;
181
181
  /**
182
182
  * Set true to log on the server all primitive values in the 'data' property.
183
183
  * May also be specified as list of specific property keys that should be logged.
@@ -1,23 +1,34 @@
1
1
  import { GroupingChooserModel } from '@xh/hoist/cmp/grouping';
2
+ import { Side } from '@xh/hoist/core';
2
3
  import { ButtonProps } from '@xh/hoist/desktop/cmp/button';
3
4
  import '@xh/hoist/desktop/register';
4
- import { ReactElement } from 'react';
5
5
  import './GroupingChooser.scss';
6
+ import { ReactNode } from 'react';
6
7
  export interface GroupingChooserProps extends ButtonProps<GroupingChooserModel> {
8
+ /** Title for value-editing portion of popover, or null to suppress. */
9
+ editorTitle?: ReactNode;
7
10
  /** Text to represent empty state (i.e. value = null or []) */
8
11
  emptyText?: string;
12
+ /**
13
+ * Side of the popover, relative to the value-editing controls, on which the Favorites list
14
+ * should be rendered, if enabled.
15
+ */
16
+ favoritesSide?: Side;
17
+ /** Title for favorites-list portion of popover, or null to suppress. */
18
+ favoritesTitle?: ReactNode;
9
19
  /** Min height in pixels of the popover menu itself. */
10
20
  popoverMinHeight?: number;
11
21
  /** Position of popover relative to target button. */
12
22
  popoverPosition?: 'bottom' | 'top';
13
- /** Title for popover (default "GROUP BY") or null to suppress. */
14
- popoverTitle?: string;
15
- /** Width in pixels of the popover menu itself. */
23
+ /** @deprecated - use `editorTitle` instead */
24
+ popoverTitle?: ReactNode;
25
+ /**
26
+ * Width in pixels of the popover menu itself.
27
+ * If unspecified, will default based on favorites enabled status + side.
28
+ */
16
29
  popoverWidth?: number;
17
30
  /** True (default) to style target button as an input field - blends better in toolbars. */
18
31
  styleButtonAsInput?: boolean;
19
- /** Icon clicked to launch favorites menu. Defaults to Icon.favorite() */
20
- favoritesIcon?: ReactElement;
21
32
  }
22
33
  /**
23
34
  * Control for selecting a list of dimensions for grouping APIs, with built-in support for
@@ -44,7 +44,7 @@ export interface PanelProps extends HoistProps<PanelModel>, Omit<BoxProps, 'titl
44
44
  */
45
45
  tbar?: Some<ReactNode>;
46
46
  /**
47
- * A toolbar to be docked at the top of the panel.
47
+ * A toolbar to be docked at the bottom of the panel.
48
48
  * If specified as an array, items will be passed as children to a Toolbar component.
49
49
  */
50
50
  bbar?: Some<ReactNode>;
@@ -1,26 +1,42 @@
1
- import { HoistProps } from '@xh/hoist/core';
1
+ import { HoistProps, MenuItemLike } from '@xh/hoist/core';
2
2
  import { ViewManagerModel } from '@xh/hoist/cmp/viewmanager';
3
3
  import { ButtonProps } from '@xh/hoist/desktop/cmp/button';
4
+ import { ReactElement } from 'react';
4
5
  import './ViewManager.scss';
5
- /**
6
- * Visibility options for save/revert button.
7
- *
8
- * 'never' to hide button.
9
- * 'whenDirty' to only show when persistence state is dirty and button is therefore enabled.
10
- * 'always' will always show button.
11
- */
12
- export type ViewManagerStateButtonMode = 'whenDirty' | 'always' | 'never';
13
6
  export interface ViewManagerProps extends HoistProps<ViewManagerModel> {
14
7
  menuButtonProps?: Partial<ButtonProps>;
15
8
  saveButtonProps?: Partial<ButtonProps>;
16
9
  revertButtonProps?: Partial<ButtonProps>;
10
+ /** Button icon when on the default (in-code state) view. Default `Icon.bookmark`. */
11
+ defaultViewIcon?: ReactElement;
12
+ /** Button icon when the selected view is owned by the current user. Default `Icon.bookmark`. */
13
+ ownedViewIcon?: ReactElement;
14
+ /** Button icon when the selected view is shared by another user. Default `Icon.users`. */
15
+ sharedViewIcon?: ReactElement;
16
+ /** Button icon when the selected view is globally shared. Default `Icon.globe`. */
17
+ globalViewIcon?: ReactElement;
17
18
  /** Default 'whenDirty' */
18
19
  showSaveButton?: ViewManagerStateButtonMode;
19
20
  /** Default 'never' */
20
21
  showRevertButton?: ViewManagerStateButtonMode;
21
- /** Side the save and revert buttons should appear on (default 'right') */
22
+ /** Side relative to the menu on which save/revert buttons should render. Default 'right'. */
22
23
  buttonSide?: 'left' | 'right';
24
+ /**
25
+ * Array of extra menu items. Can contain:
26
+ * + `MenuItems` or configs to create them.
27
+ * + `MenuDividers` or the special string token '-'.
28
+ * + React Elements or strings, which will be interpreted as the `text` property for a MenuItem.
29
+ */
30
+ extraMenuItems?: MenuItemLike[];
23
31
  }
32
+ /**
33
+ * Visibility options for save/revert buttons inlined next to the ViewManager menu:
34
+ * 'never' to always hide - user must save/revert via menu.
35
+ * 'whenDirty' (default) to show only when view state is dirty and the button is enabled.
36
+ * 'always' to always show, including when view not dirty and the button is disabled.
37
+ * Useful to avoid jumpiness in toolbar layouts.
38
+ */
39
+ export type ViewManagerStateButtonMode = 'whenDirty' | 'always' | 'never';
24
40
  /**
25
41
  * Desktop ViewManager component - a button-based menu for saving and swapping between named
26
42
  * bundles of persisted component state (e.g. grid views, dashboards, and similar).
@@ -3,14 +3,10 @@ import { ButtonProps } from '@xh/hoist/mobile/cmp/button';
3
3
  import '@xh/hoist/mobile/register';
4
4
  import './GroupingChooser.scss';
5
5
  export interface GroupingChooserProps extends ButtonProps<GroupingChooserModel> {
6
+ /** Custom title for editor dialog, or null to suppress. */
7
+ dialogTitle?: string;
6
8
  /** Text to represent empty state (i.e. value = null or [])*/
7
9
  emptyText?: string;
8
- /** Title for popover (default "GROUP BY") or null to suppress. */
9
- popoverTitle?: string;
10
- /** Min height in pixels of the popover inner content (excl. header & toolbar). */
11
- popoverMinHeight?: number;
12
- /** Width in pixels of the popover menu itself. */
13
- popoverWidth?: number;
14
10
  }
15
11
  /**
16
12
  * Control for selecting a list of dimensions for grouping APIs.
@@ -0,0 +1,13 @@
1
+ import { MenuItemLike } from '@xh/hoist/core';
2
+ import { ReactNode } from 'react';
3
+ /**
4
+ * Parse MenuItem configs into Blueprint MenuItems.
5
+ *
6
+ * Note this is currently used in a few limited places and is not generally applied to all menu-
7
+ * like components in Hoist. In particular, it is not used by the `menu` component re-exported from
8
+ * Blueprint. See https://github.com/xh/hoist-react/issues/2400 covering TBD work to more fully
9
+ * standardize a Hoist menu component that might then incorporate this processing.
10
+ *
11
+ * @internal
12
+ */
13
+ export declare function parseMenuItems(items: MenuItemLike[]): ReactNode[];
@@ -2,3 +2,4 @@ export * from './Separators';
2
2
  export * from './TimeZone';
3
3
  export * from './Equals';
4
4
  export * from './IsOmitted';
5
+ export * from './MenuItems';
@@ -23,7 +23,10 @@ export interface GroupingChooserConfig {
23
23
  /** Initial value as an array of dimension names, or a function to produce such an array. */
24
24
  initialValue?: string[] | (() => string[]);
25
25
 
26
- /** Initial favorites as an array of dim name arrays, or a function to produce such an array. */
26
+ /**
27
+ * Initial favorites as an array of dim name arrays, or a function to produce such an array.
28
+ * Ignored if `persistWith.persistFavorites: false`.
29
+ */
27
30
  initialFavorites?: string[][] | (() => string[][]);
28
31
 
29
32
  /** Options governing persistence. */
@@ -74,7 +77,6 @@ export class GroupingChooserModel extends HoistModel {
74
77
  // Implementation fields for Control
75
78
  @observable.ref pendingValue: string[] = [];
76
79
  @observable editorIsOpen: boolean = false;
77
- @observable favoritesIsOpen: boolean = false;
78
80
  popoverRef = createObservableRef<HTMLElement>();
79
81
 
80
82
  // Internal state
@@ -105,6 +107,15 @@ export class GroupingChooserModel extends HoistModel {
105
107
  return !atMaxDepth && !isEmpty(availableDims);
106
108
  }
107
109
 
110
+ @computed
111
+ get isAddFavoriteEnabled(): boolean {
112
+ return (
113
+ this.persistFavorites &&
114
+ !isEmpty(this.pendingValue) &&
115
+ !this.isFavorite(this.pendingValue)
116
+ );
117
+ }
118
+
108
119
  constructor({
109
120
  dimensions,
110
121
  initialValue = [],
@@ -169,19 +180,11 @@ export class GroupingChooserModel extends HoistModel {
169
180
  toggleEditor() {
170
181
  this.pendingValue = this.value;
171
182
  this.editorIsOpen = !this.editorIsOpen;
172
- this.favoritesIsOpen = false;
173
- }
174
-
175
- @action
176
- toggleFavoritesMenu() {
177
- this.favoritesIsOpen = !this.favoritesIsOpen;
178
- this.editorIsOpen = false;
179
183
  }
180
184
 
181
185
  @action
182
- closePopover() {
186
+ closeEditor() {
183
187
  this.editorIsOpen = false;
184
- this.favoritesIsOpen = false;
185
188
  }
186
189
 
187
190
  //-------------------------
@@ -224,7 +227,7 @@ export class GroupingChooserModel extends HoistModel {
224
227
  if (!isEqual(value, pendingValue) && this.validateValue(pendingValue)) {
225
228
  this.setValue(pendingValue);
226
229
  }
227
- this.closePopover();
230
+ this.closeEditor();
228
231
  }
229
232
 
230
233
  validateValue(value: string[]) {
@@ -263,6 +266,11 @@ export class GroupingChooserModel extends HoistModel {
263
266
  );
264
267
  }
265
268
 
269
+ @computed
270
+ get hasFavorites() {
271
+ return !isEmpty(this.favorites);
272
+ }
273
+
266
274
  @action
267
275
  setFavorites(favorites: string[][]) {
268
276
  this.favorites = favorites.filter(v => this.validateValue(v));
@@ -274,6 +282,11 @@ export class GroupingChooserModel extends HoistModel {
274
282
  this.favorites = [...this.favorites, value];
275
283
  }
276
284
 
285
+ @action
286
+ addPendingAsFavorite() {
287
+ this.addFavorite(this.pendingValue);
288
+ }
289
+
277
290
  @action
278
291
  removeFavorite(value: string[]) {
279
292
  this.favorites = this.favorites.filter(v => !isEqual(v, value));
@@ -29,7 +29,10 @@ export class ViewInfo {
29
29
  /** User owning this view. Null if the view is global.*/
30
30
  readonly owner: string;
31
31
 
32
- /** Is the owner making this view accessible to others? Always true for global views. */
32
+ /**
33
+ * True if the owner (which can be the current user) has made this view accessible to all other
34
+ * users. Note always `false` for global views, to better distinguish the two sharing models.
35
+ */
33
36
  readonly isShared: boolean;
34
37
 
35
38
  /** True if this view is global and visible to all users. */
@@ -39,8 +42,9 @@ export class ViewInfo {
39
42
  readonly group: string;
40
43
 
41
44
  /**
42
- * Should this view be pinned by users by default?
43
- * This value is intended to be used for global views only.
45
+ * True if this view should be pinned by default to all users' menus, where it will appear
46
+ * unless the user has explicitly unpinned it. Only applicable for global views, can be enabled
47
+ * by view managers to promote especially important global views and ensure users see them.
44
48
  */
45
49
  readonly isDefaultPinned: boolean;
46
50
 
@@ -99,6 +99,7 @@ export class HoistAppModel extends HoistModel {
99
99
  const XH = window['XH'];
100
100
  await XH.prefService.clearAllAsync();
101
101
  XH.localStorageService.clear();
102
+ XH.sessionStorageService.clear();
102
103
  }
103
104
  }
104
105
 
@@ -202,7 +202,7 @@ export class ExceptionHandler {
202
202
  XH.track({
203
203
  category: 'Client Error',
204
204
  severity: exception.isRoutine ? 'INFO' : 'ERROR',
205
- message: exception.message ?? 'Client Error',
205
+ message: exception.message || 'Client Error',
206
206
  correlationId: exception.correlationId,
207
207
  data,
208
208
  logData: ['userAlerted']
@@ -6,8 +6,8 @@
6
6
  */
7
7
 
8
8
  import {RuleLike} from '@xh/hoist/data';
9
- import {MouseEvent, ReactElement, ReactNode, isValidElement} from 'react';
10
9
  import {isString} from 'lodash';
10
+ import {isValidElement, MouseEvent, ReactElement, ReactNode} from 'react';
11
11
  import {LoadSpec} from '../load';
12
12
  import {Intent, PlainObject, Thunkable} from './Types';
13
13
 
@@ -224,7 +224,7 @@ export interface TrackOptions {
224
224
  correlationId?: string;
225
225
 
226
226
  /** App-supplied data to save along with track log.*/
227
- data?: PlainObject | PlainObject[];
227
+ data?: PlainObject | Array<unknown>;
228
228
 
229
229
  /**
230
230
  * Set true to log on the server all primitive values in the 'data' property.
@@ -4,17 +4,13 @@
4
4
  *
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
- import {MenuItemProps} from '@blueprintjs/core';
8
- import {hoistCmp, isMenuItem, MenuItemLike, XH} from '@xh/hoist/core';
7
+ import {hoistCmp, MenuItemLike, XH} from '@xh/hoist/core';
9
8
  import {ButtonProps, button} from '@xh/hoist/desktop/cmp/button';
10
9
  import '@xh/hoist/desktop/register';
11
10
  import {Icon} from '@xh/hoist/icon';
12
- import {menu, menuDivider, menuItem, popover} from '@xh/hoist/kit/blueprint';
13
- import {wait} from '@xh/hoist/promise';
14
- import {filterConsecutiveMenuSeparators, isOmitted} from '@xh/hoist/utils/impl';
11
+ import {menu, popover} from '@xh/hoist/kit/blueprint';
12
+ import {parseMenuItems} from '@xh/hoist/utils/impl';
15
13
  import {withDefault} from '@xh/hoist/utils/js';
16
- import {clone, isEmpty} from 'lodash';
17
- import {ReactNode} from 'react';
18
14
 
19
15
  export interface AppMenuButtonProps extends ButtonProps {
20
16
  /**
@@ -176,42 +172,3 @@ function buildMenuItems(props: AppMenuButtonProps) {
176
172
 
177
173
  return parseMenuItems([...extraItems, '-', ...defaultItems]);
178
174
  }
179
-
180
- function parseMenuItems(items: MenuItemLike[]): ReactNode[] {
181
- items = items.map(item => {
182
- if (!isMenuItem(item)) return item;
183
-
184
- item = clone(item);
185
- item.items = clone(item.items);
186
- item.prepareFn?.(item);
187
- return item;
188
- });
189
-
190
- return items
191
- .filter(it => !isMenuItem(it) || (!it.hidden && !isOmitted(it)))
192
- .filter(filterConsecutiveMenuSeparators())
193
- .map(item => {
194
- if (item === '-') return menuDivider();
195
- if (!isMenuItem(item)) return item;
196
-
197
- const {actionFn} = item;
198
-
199
- // Create menuItem from config
200
- const cfg: MenuItemProps = {
201
- text: item.text,
202
- icon: item.icon,
203
- intent: item.intent,
204
- className: item.className,
205
- onClick: actionFn ? e => wait().then(() => actionFn(e)) : null, // do async to allow menu to close
206
- disabled: item.disabled
207
- };
208
-
209
- // Recursively parse any submenus
210
- if (!isEmpty(item.items)) {
211
- cfg.children = parseMenuItems(item.items);
212
- cfg.popoverProps = {openOnTargetFocus: false};
213
- }
214
-
215
- return menuItem(cfg);
216
- });
217
- }
@@ -22,11 +22,6 @@
22
22
  color: var(--xh-text-color-muted);
23
23
  }
24
24
  }
25
-
26
- // We must account for the favorites icon when eliding text
27
- &--with-favorites {
28
- padding-right: 30px !important;
29
- }
30
25
  }
31
26
 
32
27
  // Outer box is sized via layoutSupport - all nested inner elements should stretch to fill.
@@ -39,6 +34,16 @@
39
34
  position: relative;
40
35
  }
41
36
 
37
+ &-popover {
38
+ // 60/40 split in favor of favorites in left/right orientation
39
+ &--faves-right,
40
+ &--faves-left {
41
+ .xh-grouping-chooser__editor {
42
+ width: 40%;
43
+ }
44
+ }
45
+ }
46
+
42
47
  &__list {
43
48
  margin-bottom: var(--xh-pad-half-px);
44
49
  background-color: var(--xh-bg-alt);
@@ -121,48 +126,47 @@
121
126
  padding: 2px var(--xh-tbar-item-pad-px);
122
127
  }
123
128
 
124
- &__favorite-icon {
125
- display: flex;
126
- align-items: center;
127
- justify-content: center;
128
- width: 30px;
129
- height: 20px;
130
- position: absolute;
131
- top: 0;
132
- right: 0;
133
- cursor: pointer;
134
- color: var(--xh-text-color-muted);
129
+ &__favorites {
130
+ &--top {
131
+ border-bottom: 1px solid var(--xh-popup-border-color);
132
+ }
135
133
 
136
- &:hover {
137
- color: var(--xh-text-color);
134
+ &--right {
135
+ flex: 1;
136
+ border-left: 1px solid var(--xh-popup-border-color);
138
137
  }
139
138
 
140
- .xh-toolbar--compact & {
141
- height: 15px;
139
+ &--bottom {
140
+ border-top: 1px solid var(--xh-popup-border-color);
142
141
  }
143
142
 
144
- // Matched to FilterChooser equivalent.
145
- & > .svg-inline--fa {
146
- width: 12px;
143
+ &--left {
144
+ flex: 1;
145
+ border-right: 1px solid var(--xh-popup-border-color);
147
146
  }
148
- }
149
147
 
150
- &__favorite {
151
- align-items: center;
152
- max-width: 50vw;
148
+ --xh-menu-border: none;
153
149
 
154
- .xh-button {
155
- padding: 0 !important;
156
- min-width: 20px !important;
157
- min-height: 20px !important;
158
- visibility: hidden;
150
+ .bp5-menu {
151
+ padding: 0;
159
152
  }
160
153
 
161
- &:hover {
162
- background-color: var(--xh-bg-highlight);
154
+ &__favorite {
155
+ align-items: center;
163
156
 
164
157
  .xh-button {
165
- visibility: unset;
158
+ padding: 0 !important;
159
+ min-width: 20px !important;
160
+ min-height: 20px !important;
161
+ visibility: hidden;
162
+ }
163
+
164
+ &:hover {
165
+ background-color: var(--xh-bg-highlight);
166
+
167
+ .xh-button {
168
+ visibility: unset;
169
+ }
166
170
  }
167
171
  }
168
172
  }