@xh/hoist 74.0.0-SNAPSHOT.1749050851202 → 74.0.0-SNAPSHOT.1749597895949
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 +6 -0
- package/build/types/cmp/chart/ChartModel.d.ts +8 -3
- package/build/types/cmp/chart/Types.d.ts +20 -0
- package/build/types/cmp/chart/impl/ChartContextMenuItems.d.ts +4 -0
- package/build/types/core/types/Interfaces.d.ts +32 -9
- package/build/types/desktop/cmp/contextmenu/ContextMenu.d.ts +2 -7
- package/build/types/desktop/cmp/dash/canvas/impl/utils.d.ts +2 -1
- package/build/types/desktop/cmp/filter/FilterChooser.d.ts +2 -0
- package/build/types/desktop/cmp/grouping/GroupingChooser.d.ts +3 -0
- package/build/types/desktop/cmp/panel/Panel.d.ts +1 -2
- package/build/types/desktop/hooks/UseContextMenu.d.ts +1 -1
- package/cmp/chart/Chart.ts +14 -56
- package/cmp/chart/ChartModel.ts +44 -12
- package/cmp/chart/Types.ts +33 -0
- package/cmp/chart/impl/ChartContextMenuItems.ts +129 -0
- package/cmp/chart/impl/zoomout.ts +2 -1
- package/core/types/Interfaces.ts +43 -10
- package/desktop/cmp/button/AppMenuButton.ts +3 -7
- package/desktop/cmp/contextmenu/ContextMenu.ts +10 -19
- package/desktop/cmp/dash/canvas/DashCanvas.ts +2 -1
- package/desktop/cmp/dash/canvas/impl/DashCanvasContextMenu.ts +4 -4
- package/desktop/cmp/dash/canvas/impl/utils.ts +2 -1
- package/desktop/cmp/dash/container/DashContainerModel.ts +2 -1
- package/desktop/cmp/dash/container/impl/DashContainerContextMenu.ts +5 -5
- package/desktop/cmp/filter/FilterChooser.ts +12 -6
- package/desktop/cmp/grouping/GroupingChooser.ts +9 -4
- package/desktop/cmp/panel/Panel.ts +2 -2
- package/desktop/hooks/UseContextMenu.ts +5 -4
- package/mobile/cmp/menu/impl/Menu.ts +3 -7
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
package/core/types/Interfaces.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {RuleLike} from '@xh/hoist/data';
|
|
9
|
-
import {MouseEvent, ReactElement, ReactNode} from 'react';
|
|
9
|
+
import {MouseEvent, ReactElement, ReactNode, isValidElement} from 'react';
|
|
10
|
+
import {isString} from 'lodash';
|
|
10
11
|
import {LoadSpec} from '../load';
|
|
11
12
|
import {Intent, PlainObject, Thunkable} from './Types';
|
|
12
13
|
|
|
@@ -265,12 +266,41 @@ export interface TrackOptions {
|
|
|
265
266
|
omit?: Thunkable<boolean>;
|
|
266
267
|
}
|
|
267
268
|
|
|
269
|
+
/**
|
|
270
|
+
* The base `MenuToken` type. '-' is interpreted as the standard textless divider.
|
|
271
|
+
* Components will likely extend this type to support other strings like 'copyToClipboard',
|
|
272
|
+
* 'print', etc. which the component then converts into a {@link MenuItem}.
|
|
273
|
+
*/
|
|
274
|
+
export type MenuToken = '-';
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* `MenuContext` is the set of contextual arguments passed to a {@link MenuItem}'s
|
|
278
|
+
* `actionFn` and `prepareFn`. `contextMenuEvent` is the right click event that opened the
|
|
279
|
+
* context menu. It is optional because the `contextMenu` component can also be used on
|
|
280
|
+
* popover buttons, where there is no `contextMenuEvent`.
|
|
281
|
+
*
|
|
282
|
+
* Components offering a built-in {@link contextMenu} can extend `MenuContext` to add values
|
|
283
|
+
* relevant to the component. See for example {@link ChartMenuContext}.
|
|
284
|
+
*/
|
|
285
|
+
export interface MenuContext {
|
|
286
|
+
contextMenuEvent?: MouseEvent | PointerEvent;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* A context menu is specified as an array of items, a function to generate one from a click, or
|
|
291
|
+
* a full element representing a contextMenu Component.
|
|
292
|
+
*/
|
|
293
|
+
export type ContextMenuSpec<T = MenuToken, C = MenuContext> =
|
|
294
|
+
| MenuItemLike<T, C>[]
|
|
295
|
+
| ((e: MouseEvent | PointerEvent, context: C) => MenuItemLike<T, C>[])
|
|
296
|
+
| boolean;
|
|
297
|
+
|
|
268
298
|
/**
|
|
269
299
|
* Basic interface for a MenuItem to appear in a menu.
|
|
270
300
|
*
|
|
271
301
|
* MenuItems can be displayed within a context menu, or shown when clicking on a button.
|
|
272
302
|
*/
|
|
273
|
-
export interface MenuItem {
|
|
303
|
+
export interface MenuItem<T = MenuToken, C = MenuContext> {
|
|
274
304
|
/** Label to be displayed. */
|
|
275
305
|
text: ReactNode;
|
|
276
306
|
|
|
@@ -284,13 +314,13 @@ export interface MenuItem {
|
|
|
284
314
|
className?: string;
|
|
285
315
|
|
|
286
316
|
/** Executed when the user clicks the menu item. */
|
|
287
|
-
actionFn?: (e: MouseEvent | PointerEvent) => void;
|
|
317
|
+
actionFn?: (e: MouseEvent | PointerEvent, context?: C) => void;
|
|
288
318
|
|
|
289
319
|
/** Executed before the item is shown. Use to adjust properties dynamically. */
|
|
290
|
-
prepareFn?: (me: MenuItem) => void;
|
|
320
|
+
prepareFn?: (me: MenuItem<T, C>, context?: C) => void;
|
|
291
321
|
|
|
292
322
|
/** Child menu items. */
|
|
293
|
-
items?: MenuItemLike[];
|
|
323
|
+
items?: MenuItemLike<T, C>[];
|
|
294
324
|
|
|
295
325
|
/** True to disable this item. */
|
|
296
326
|
disabled?: boolean;
|
|
@@ -304,12 +334,15 @@ export interface MenuItem {
|
|
|
304
334
|
|
|
305
335
|
/**
|
|
306
336
|
* An item that can exist in a Menu.
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
* be de-duped if appearing at the beginning, or end, or adjacent to another divider at render time.
|
|
337
|
+
* Components may accept token strings, in addition, '-' will be interpreted as the standard
|
|
338
|
+
* textless divider that will also be de-duped if appearing at the beginning, or end, or adjacent
|
|
339
|
+
* to another divider at render time. Also allows for a ReactNode for flexible display.
|
|
311
340
|
*/
|
|
312
|
-
export type MenuItemLike = MenuItem |
|
|
341
|
+
export type MenuItemLike<T = MenuToken, C = MenuContext> = MenuItem<T, C> | T | ReactElement;
|
|
342
|
+
|
|
343
|
+
export function isMenuItem<T, C>(item: MenuItemLike<T, C>): item is MenuItem<T, C> {
|
|
344
|
+
return !isString(item) && !isValidElement(item);
|
|
345
|
+
}
|
|
313
346
|
|
|
314
347
|
/**
|
|
315
348
|
* An option to be passed to Select controls
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {MenuItemProps} from '@blueprintjs/core';
|
|
8
|
-
import {hoistCmp,
|
|
8
|
+
import {hoistCmp, isMenuItem, MenuItemLike, XH} from '@xh/hoist/core';
|
|
9
9
|
import {ButtonProps, button} from '@xh/hoist/desktop/cmp/button';
|
|
10
10
|
import '@xh/hoist/desktop/register';
|
|
11
11
|
import {Icon} from '@xh/hoist/icon';
|
|
@@ -13,8 +13,8 @@ import {menu, menuDivider, menuItem, popover} from '@xh/hoist/kit/blueprint';
|
|
|
13
13
|
import {wait} from '@xh/hoist/promise';
|
|
14
14
|
import {filterConsecutiveMenuSeparators, isOmitted} from '@xh/hoist/utils/impl';
|
|
15
15
|
import {withDefault} from '@xh/hoist/utils/js';
|
|
16
|
-
import {clone, isEmpty
|
|
17
|
-
import {
|
|
16
|
+
import {clone, isEmpty} from 'lodash';
|
|
17
|
+
import {ReactNode} from 'react';
|
|
18
18
|
|
|
19
19
|
export interface AppMenuButtonProps extends ButtonProps {
|
|
20
20
|
/**
|
|
@@ -215,7 +215,3 @@ function parseMenuItems(items: MenuItemLike[]): ReactNode[] {
|
|
|
215
215
|
return menuItem(cfg);
|
|
216
216
|
});
|
|
217
217
|
}
|
|
218
|
-
|
|
219
|
-
function isMenuItem(item: MenuItemLike): item is MenuItem {
|
|
220
|
-
return !isString(item) && !isValidElement(item);
|
|
221
|
-
}
|
|
@@ -4,22 +4,17 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {hoistCmp, HoistProps,
|
|
7
|
+
import {hoistCmp, HoistProps, isMenuItem, MenuContext, MenuItemLike} from '@xh/hoist/core';
|
|
8
8
|
import '@xh/hoist/desktop/register';
|
|
9
9
|
import {menu, menuDivider, menuItem} from '@xh/hoist/kit/blueprint';
|
|
10
10
|
import {wait} from '@xh/hoist/promise';
|
|
11
11
|
import {filterConsecutiveMenuSeparators, isOmitted} from '@xh/hoist/utils/impl';
|
|
12
|
-
import {clone, isEmpty
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* A context menu is specified as an array of items, a function to generate one from a click, or
|
|
17
|
-
* a full element representing a contextMenu Component.
|
|
18
|
-
*/
|
|
19
|
-
export type ContextMenuSpec = MenuItemLike[] | ((e: MouseEvent) => MenuItemLike[]) | ReactElement;
|
|
12
|
+
import {clone, isEmpty} from 'lodash';
|
|
13
|
+
import {ReactNode} from 'react';
|
|
20
14
|
|
|
21
15
|
export interface ContextMenuProps extends HoistProps {
|
|
22
16
|
menuItems: MenuItemLike[];
|
|
17
|
+
context?: MenuContext;
|
|
23
18
|
}
|
|
24
19
|
|
|
25
20
|
/**
|
|
@@ -36,8 +31,8 @@ export const [ContextMenu, contextMenu] = hoistCmp.withFactory<ContextMenuProps>
|
|
|
36
31
|
model: false,
|
|
37
32
|
observer: false,
|
|
38
33
|
|
|
39
|
-
render({menuItems}) {
|
|
40
|
-
const items = parseItems(menuItems);
|
|
34
|
+
render({menuItems, context}) {
|
|
35
|
+
const items = parseItems(menuItems, context);
|
|
41
36
|
return isEmpty(items) ? null : menu(items);
|
|
42
37
|
}
|
|
43
38
|
});
|
|
@@ -45,13 +40,13 @@ export const [ContextMenu, contextMenu] = hoistCmp.withFactory<ContextMenuProps>
|
|
|
45
40
|
//---------------------------
|
|
46
41
|
// Implementation
|
|
47
42
|
//---------------------------
|
|
48
|
-
function parseItems(items: MenuItemLike[]): ReactNode[] {
|
|
43
|
+
function parseItems(items: MenuItemLike[], context: MenuContext): ReactNode[] {
|
|
49
44
|
items = items.map(item => {
|
|
50
45
|
if (!isMenuItem(item)) return item;
|
|
51
46
|
|
|
52
47
|
item = clone(item);
|
|
53
48
|
item.items = clone(item.items);
|
|
54
|
-
item.prepareFn?.(item);
|
|
49
|
+
item.prepareFn?.(item, context);
|
|
55
50
|
return item;
|
|
56
51
|
});
|
|
57
52
|
|
|
@@ -64,20 +59,16 @@ function parseItems(items: MenuItemLike[]): ReactNode[] {
|
|
|
64
59
|
if (!isMenuItem(item)) return item;
|
|
65
60
|
|
|
66
61
|
// Process items
|
|
67
|
-
const items = item.items ? parseItems(item.items) : null;
|
|
62
|
+
const items = item.items ? parseItems(item.items, context) : null;
|
|
68
63
|
return menuItem({
|
|
69
64
|
text: item.text,
|
|
70
65
|
icon: item.icon,
|
|
71
66
|
intent: item.intent,
|
|
72
67
|
className: item.className,
|
|
73
|
-
onClick: item.actionFn ? e => wait().then(() => item.actionFn(e)) : null, // do async to allow menu to close
|
|
68
|
+
onClick: item.actionFn ? e => wait().then(() => item.actionFn(e, context)) : null, // do async to allow menu to close
|
|
74
69
|
popoverProps: {usePortal: true},
|
|
75
70
|
disabled: item.disabled,
|
|
76
71
|
items
|
|
77
72
|
});
|
|
78
73
|
});
|
|
79
74
|
}
|
|
80
|
-
|
|
81
|
-
function isMenuItem(item: MenuItemLike): item is MenuItem {
|
|
82
|
-
return !isString(item) && !isValidElement(item);
|
|
83
|
-
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {hoistCmp} from '@xh/hoist/core';
|
|
8
|
+
import {hoistCmp, type MenuItemLike} from '@xh/hoist/core';
|
|
9
9
|
import {contextMenu} from '@xh/hoist/desktop/cmp/contextmenu';
|
|
10
10
|
import {createViewMenuItems} from '@xh/hoist/desktop/cmp/dash/canvas/impl/utils';
|
|
11
11
|
import {Icon} from '@xh/hoist/icon';
|
|
@@ -21,16 +21,16 @@ import {isEmpty} from 'lodash';
|
|
|
21
21
|
export const dashCanvasContextMenu = hoistCmp.factory({
|
|
22
22
|
model: null,
|
|
23
23
|
observer: null,
|
|
24
|
-
render({dashCanvasModel, position}) {
|
|
24
|
+
render({dashCanvasModel, position, contextMenuEvent}) {
|
|
25
25
|
const menuItems = createMenuItems({dashCanvasModel, position});
|
|
26
|
-
return contextMenu({menuItems});
|
|
26
|
+
return contextMenu({menuItems, context: {contextMenuEvent}});
|
|
27
27
|
}
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
//---------------------------
|
|
31
31
|
// Implementation
|
|
32
32
|
//---------------------------
|
|
33
|
-
function createMenuItems({dashCanvasModel, position}) {
|
|
33
|
+
function createMenuItems({dashCanvasModel, position}): MenuItemLike[] {
|
|
34
34
|
const addMenuItems = createViewMenuItems({dashCanvasModel, position}),
|
|
35
35
|
{extraMenuItems, contentLocked, refreshContextModel} = dashCanvasModel;
|
|
36
36
|
return [
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import {type MenuItemLike} from '@xh/hoist/core';
|
|
8
9
|
import {Icon} from '@xh/hoist/icon';
|
|
9
10
|
import {DashCanvasModel} from '../DashCanvasModel';
|
|
10
11
|
|
|
@@ -17,7 +18,7 @@ export function createViewMenuItems({
|
|
|
17
18
|
position = null,
|
|
18
19
|
viewId = null,
|
|
19
20
|
replaceExisting = false
|
|
20
|
-
}) {
|
|
21
|
+
}): MenuItemLike[] {
|
|
21
22
|
if (!dashCanvasModel.ref.current) return [];
|
|
22
23
|
|
|
23
24
|
const groupedItems = {},
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {hoistCmp} from '@xh/hoist/core';
|
|
7
|
+
import {hoistCmp, type MenuItemLike} from '@xh/hoist/core';
|
|
8
8
|
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
9
9
|
import {contextMenu} from '@xh/hoist/desktop/cmp/contextmenu/ContextMenu';
|
|
10
10
|
import {Icon} from '@xh/hoist/icon';
|
|
@@ -23,9 +23,9 @@ import {DashContainerModel} from '../DashContainerModel';
|
|
|
23
23
|
export const dashContainerContextMenu = hoistCmp.factory({
|
|
24
24
|
model: null,
|
|
25
25
|
observer: null,
|
|
26
|
-
render(
|
|
27
|
-
const menuItems = createMenuItems(
|
|
28
|
-
return contextMenu({menuItems});
|
|
26
|
+
render({contextMenuEvent, ...rest}) {
|
|
27
|
+
const menuItems = createMenuItems(rest);
|
|
28
|
+
return contextMenu({menuItems, context: {contextMenuEvent}});
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
31
|
|
|
@@ -48,7 +48,7 @@ export const dashContainerAddViewButton = hoistCmp.factory<DashContainerModel>({
|
|
|
48
48
|
//---------------------------
|
|
49
49
|
// Implementation
|
|
50
50
|
//---------------------------
|
|
51
|
-
function createMenuItems(props) {
|
|
51
|
+
function createMenuItems(props): MenuItemLike[] {
|
|
52
52
|
const {dashContainerModel, viewModel} = props,
|
|
53
53
|
{renameLocked} = dashContainerModel,
|
|
54
54
|
ret = [];
|
|
@@ -17,7 +17,7 @@ import {splitLayoutProps} from '@xh/hoist/utils/react';
|
|
|
17
17
|
import classNames from 'classnames';
|
|
18
18
|
import {isEmpty, sortBy} from 'lodash';
|
|
19
19
|
import {badge} from '@xh/hoist/cmp/badge';
|
|
20
|
-
import {ReactElement} from 'react';
|
|
20
|
+
import {cloneElement, ReactElement} from 'react';
|
|
21
21
|
import './FilterChooser.scss';
|
|
22
22
|
|
|
23
23
|
export interface FilterChooserProps extends HoistProps<FilterChooserModel>, LayoutProps {
|
|
@@ -39,6 +39,8 @@ export interface FilterChooserProps extends HoistProps<FilterChooserModel>, Layo
|
|
|
39
39
|
menuWidth?: number;
|
|
40
40
|
/** Text to display when control is empty. */
|
|
41
41
|
placeholder?: string;
|
|
42
|
+
/** Icon clicked to launch favorites menu. (Defaults to Icon.favorite()) */
|
|
43
|
+
favoritesIcon?: ReactElement;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
/**
|
|
@@ -65,7 +67,8 @@ export const [FilterChooser, filterChooser] = hoistCmp.withFactory<FilterChooser
|
|
|
65
67
|
leftIcon,
|
|
66
68
|
maxMenuHeight,
|
|
67
69
|
menuPlacement,
|
|
68
|
-
menuWidth
|
|
70
|
+
menuWidth,
|
|
71
|
+
favoritesIcon
|
|
69
72
|
} = chooserProps,
|
|
70
73
|
disabled = unsupportedFilter || chooserProps.disabled,
|
|
71
74
|
placeholder = unsupportedFilter
|
|
@@ -115,7 +118,7 @@ export const [FilterChooser, filterChooser] = hoistCmp.withFactory<FilterChooser
|
|
|
115
118
|
})
|
|
116
119
|
},
|
|
117
120
|
components: {
|
|
118
|
-
DropdownIndicator: () =>
|
|
121
|
+
DropdownIndicator: () => favoritesIconCmp(model, favoritesIcon)
|
|
119
122
|
}
|
|
120
123
|
}
|
|
121
124
|
})
|
|
@@ -210,15 +213,18 @@ const messageOption = hoistCmp.factory({
|
|
|
210
213
|
//-----------------
|
|
211
214
|
// Favorites
|
|
212
215
|
//------------------
|
|
213
|
-
function
|
|
216
|
+
function favoritesIconCmp(model, favoritesIcon) {
|
|
214
217
|
if (!model.persistFavorites) return null;
|
|
215
|
-
|
|
218
|
+
|
|
219
|
+
const iconProps = {
|
|
216
220
|
className: classNames('xh-select__indicator', 'xh-filter-chooser-favorite-icon'),
|
|
217
221
|
onMouseDown: e => {
|
|
218
222
|
model.openFavoritesMenu();
|
|
219
223
|
e.stopPropagation();
|
|
220
224
|
}
|
|
221
|
-
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
return favoritesIcon ? cloneElement(favoritesIcon, iconProps) : Icon.favorite(iconProps);
|
|
222
228
|
}
|
|
223
229
|
|
|
224
230
|
const favoritesMenu = hoistCmp.factory<FilterChooserModel>({
|
|
@@ -16,6 +16,7 @@ import {menu, menuDivider, menuItem, popover} from '@xh/hoist/kit/blueprint';
|
|
|
16
16
|
import {dragDropContext, draggable, droppable} from '@xh/hoist/kit/react-beautiful-dnd';
|
|
17
17
|
import {elemWithin, getTestId, TEST_ID} from '@xh/hoist/utils/js';
|
|
18
18
|
import {splitLayoutProps} from '@xh/hoist/utils/react';
|
|
19
|
+
import {ReactElement} from 'react';
|
|
19
20
|
import classNames from 'classnames';
|
|
20
21
|
import {compact, isEmpty, sortBy} from 'lodash';
|
|
21
22
|
import './GroupingChooser.scss';
|
|
@@ -38,6 +39,9 @@ export interface GroupingChooserProps extends ButtonProps<GroupingChooserModel>
|
|
|
38
39
|
|
|
39
40
|
/** True (default) to style target button as an input field - blends better in toolbars. */
|
|
40
41
|
styleButtonAsInput?: boolean;
|
|
42
|
+
|
|
43
|
+
/** Icon clicked to launch favorites menu. Defaults to Icon.favorite() */
|
|
44
|
+
favoritesIcon?: ReactElement;
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
/**
|
|
@@ -62,6 +66,7 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
|
|
|
62
66
|
popoverPosition = 'bottom',
|
|
63
67
|
styleButtonAsInput = true,
|
|
64
68
|
testId,
|
|
69
|
+
favoritesIcon,
|
|
65
70
|
...rest
|
|
66
71
|
},
|
|
67
72
|
ref
|
|
@@ -101,7 +106,7 @@ export const [GroupingChooser, groupingChooser] = hoistCmp.withFactory<GroupingC
|
|
|
101
106
|
onClick: () => model.toggleEditor(),
|
|
102
107
|
testId
|
|
103
108
|
}),
|
|
104
|
-
|
|
109
|
+
favoritesIconCmp({testId: favoritesIconTestId, favoritesIcon})
|
|
105
110
|
),
|
|
106
111
|
content: favoritesIsOpen
|
|
107
112
|
? favoritesMenu({testId: favoritesMenuTestId})
|
|
@@ -315,11 +320,11 @@ function getDimOptions(dims, model) {
|
|
|
315
320
|
//------------------
|
|
316
321
|
// Favorites
|
|
317
322
|
//------------------
|
|
318
|
-
const
|
|
319
|
-
render({model, testId}) {
|
|
323
|
+
const favoritesIconCmp = hoistCmp.factory<GroupingChooserModel>({
|
|
324
|
+
render({model, testId, favoritesIcon}) {
|
|
320
325
|
if (!model.persistFavorites) return null;
|
|
321
326
|
return div({
|
|
322
|
-
item: Icon.favorite(),
|
|
327
|
+
item: favoritesIcon ?? Icon.favorite(),
|
|
323
328
|
className: 'xh-grouping-chooser__favorite-icon',
|
|
324
329
|
[TEST_ID]: testId,
|
|
325
330
|
onClick: e => {
|
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
Some,
|
|
16
16
|
TaskObserver,
|
|
17
17
|
useContextModel,
|
|
18
|
-
uses
|
|
18
|
+
uses,
|
|
19
|
+
type ContextMenuSpec
|
|
19
20
|
} from '@xh/hoist/core';
|
|
20
21
|
import {loadingIndicator} from '@xh/hoist/cmp/loadingindicator';
|
|
21
22
|
import {mask} from '@xh/hoist/cmp/mask';
|
|
@@ -27,7 +28,6 @@ import {logWarn} from '@xh/hoist/utils/js';
|
|
|
27
28
|
import {splitLayoutProps} from '@xh/hoist/utils/react';
|
|
28
29
|
import {castArray, omitBy} from 'lodash';
|
|
29
30
|
import {Children, isValidElement, ReactElement, ReactNode, useLayoutEffect, useRef} from 'react';
|
|
30
|
-
import {ContextMenuSpec} from '../contextmenu/ContextMenu';
|
|
31
31
|
import {modalSupport} from '../modalsupport/ModalSupport';
|
|
32
32
|
import {panelHeader} from './impl/PanelHeader';
|
|
33
33
|
import {resizeContainer} from './impl/ResizeContainer';
|
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import type {ContextMenuSpec} from '@xh/hoist/core';
|
|
8
|
+
import {contextMenu} from '@xh/hoist/desktop/cmp/contextmenu/ContextMenu';
|
|
8
9
|
import {showContextMenu} from '@xh/hoist/kit/blueprint';
|
|
9
10
|
import {logError} from '@xh/hoist/utils/js';
|
|
10
11
|
import {isArray, isEmpty, isFunction, isUndefined} from 'lodash';
|
|
11
|
-
import {cloneElement, isValidElement, ReactElement} from 'react';
|
|
12
|
+
import {cloneElement, isValidElement, MouseEvent, ReactElement} from 'react';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Hook to add a right-click context menu to a component.
|
|
@@ -21,7 +22,7 @@ import {cloneElement, isValidElement, ReactElement} from 'react';
|
|
|
21
22
|
export function useContextMenu(child?: ReactElement, spec?: ContextMenuSpec): ReactElement {
|
|
22
23
|
if (!child || isUndefined(spec)) return child;
|
|
23
24
|
|
|
24
|
-
const onContextMenu = (e: MouseEvent) => {
|
|
25
|
+
const onContextMenu = (e: MouseEvent | PointerEvent) => {
|
|
25
26
|
let contextMenuOutput: any = spec;
|
|
26
27
|
|
|
27
28
|
// 0) Skip if already consumed, otherwise consume (adapted from BP `ContextMenuTarget`).
|
|
@@ -34,7 +35,7 @@ export function useContextMenu(child?: ReactElement, spec?: ContextMenuSpec): Re
|
|
|
34
35
|
}
|
|
35
36
|
if (isArray(contextMenuOutput)) {
|
|
36
37
|
contextMenuOutput = !isEmpty(contextMenuOutput)
|
|
37
|
-
? contextMenu({menuItems: contextMenuOutput})
|
|
38
|
+
? contextMenu({menuItems: contextMenuOutput, context: {contextMenuEvent: e}})
|
|
38
39
|
: null;
|
|
39
40
|
}
|
|
40
41
|
if (contextMenuOutput && !isValidElement(contextMenuOutput)) {
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {div, hspacer, vbox} from '@xh/hoist/cmp/layout';
|
|
8
|
-
import {hoistCmp, HoistModel, useLocalModel,
|
|
8
|
+
import {hoistCmp, HoistModel, useLocalModel, MenuItemLike, isMenuItem} from '@xh/hoist/core';
|
|
9
9
|
import {listItem} from '@xh/hoist/kit/onsen';
|
|
10
10
|
import {makeObservable, bindable} from '@xh/hoist/mobx';
|
|
11
11
|
import {filterConsecutiveMenuSeparators, isOmitted} from '@xh/hoist/utils/impl';
|
|
12
12
|
import classNames from 'classnames';
|
|
13
|
-
import {clone, isEmpty
|
|
14
|
-
import {
|
|
13
|
+
import {clone, isEmpty} from 'lodash';
|
|
14
|
+
import {ReactNode, useEffect} from 'react';
|
|
15
15
|
|
|
16
16
|
import './Menu.scss';
|
|
17
17
|
|
|
@@ -107,7 +107,3 @@ class LocalMenuModel extends HoistModel {
|
|
|
107
107
|
});
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
|
|
111
|
-
function isMenuItem(item: MenuItemLike): item is MenuItem {
|
|
112
|
-
return !isString(item) && !isValidElement(item);
|
|
113
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "74.0.0-SNAPSHOT.
|
|
3
|
+
"version": "74.0.0-SNAPSHOT.1749597895949",
|
|
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",
|