@carbon/react 1.48.0-rc.0 → 1.48.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/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +855 -814
- package/es/components/ComboButton/index.js +1 -0
- package/es/components/Menu/Menu.d.ts +59 -0
- package/es/components/Menu/Menu.js +33 -10
- package/es/components/Menu/MenuContext.d.ts +32 -0
- package/es/components/Menu/MenuItem.d.ts +119 -0
- package/es/components/Menu/MenuItem.js +17 -12
- package/es/components/Menu/index.d.ts +9 -0
- package/es/components/MenuButton/index.js +1 -0
- package/es/components/OverflowMenu/next/index.js +1 -0
- package/es/components/Slug/index.js +5 -1
- package/es/components/Tooltip/Tooltip.js +1 -9
- package/es/index.js +2 -2
- package/lib/components/ComboButton/index.js +1 -0
- package/lib/components/Menu/Menu.d.ts +59 -0
- package/lib/components/Menu/Menu.js +33 -10
- package/lib/components/Menu/MenuContext.d.ts +32 -0
- package/lib/components/Menu/MenuItem.d.ts +119 -0
- package/lib/components/Menu/MenuItem.js +17 -12
- package/lib/components/Menu/index.d.ts +9 -0
- package/lib/components/MenuButton/index.js +1 -0
- package/lib/components/OverflowMenu/next/index.js +1 -0
- package/lib/components/Slug/index.js +5 -1
- package/lib/components/Tooltip/Tooltip.js +0 -8
- package/lib/index.js +8 -8
- package/package.json +5 -5
|
@@ -14,6 +14,7 @@ import { IconButton } from '../IconButton/index.js';
|
|
|
14
14
|
import Button from '../Button/Button.js';
|
|
15
15
|
import '../Button/Button.Skeleton.js';
|
|
16
16
|
import { Menu } from '../Menu/Menu.js';
|
|
17
|
+
import '../Menu/MenuItem.js';
|
|
17
18
|
import { useAttachedMenu } from '../../internal/useAttachedMenu.js';
|
|
18
19
|
import { useId } from '../../internal/useId.js';
|
|
19
20
|
import { useMergedRefs } from '../../internal/useMergedRefs.js';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2023
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
interface MenuProps extends React.HTMLAttributes<HTMLUListElement> {
|
|
9
|
+
/**
|
|
10
|
+
* A collection of MenuItems to be rendered within this Menu.
|
|
11
|
+
*/
|
|
12
|
+
children?: React.ReactNode;
|
|
13
|
+
/**
|
|
14
|
+
* Additional CSS class names.
|
|
15
|
+
*/
|
|
16
|
+
className?: string;
|
|
17
|
+
/**
|
|
18
|
+
* A label describing the Menu.
|
|
19
|
+
*/
|
|
20
|
+
label?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The mode of this menu. Defaults to full.
|
|
23
|
+
* `full` supports nesting and selectable menu items, but no icons.
|
|
24
|
+
* `basic` supports icons but no nesting or selectable menu items.
|
|
25
|
+
*
|
|
26
|
+
* **This prop is not intended for use and will be set by the respective implementation (like useContextMenu, MenuButton, and ComboButton).**
|
|
27
|
+
*/
|
|
28
|
+
mode?: 'full' | 'basic';
|
|
29
|
+
/**
|
|
30
|
+
* Provide an optional function to be called when the Menu should be closed.
|
|
31
|
+
*/
|
|
32
|
+
onClose?: () => void;
|
|
33
|
+
/**
|
|
34
|
+
* Provide an optional function to be called when the Menu is opened.
|
|
35
|
+
*/
|
|
36
|
+
onOpen?: () => void;
|
|
37
|
+
/**
|
|
38
|
+
* Whether the Menu is open or not.
|
|
39
|
+
*/
|
|
40
|
+
open?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Specify the size of the Menu.
|
|
43
|
+
*/
|
|
44
|
+
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
45
|
+
/**
|
|
46
|
+
* Specify a DOM node where the Menu should be rendered in. Defaults to document.body.
|
|
47
|
+
*/
|
|
48
|
+
target?: any;
|
|
49
|
+
/**
|
|
50
|
+
* Specify the x position of the Menu. Either pass a single number or an array with two numbers describing your activator's boundaries ([x1, x2])
|
|
51
|
+
*/
|
|
52
|
+
x?: number | (number | null | undefined)[];
|
|
53
|
+
/**
|
|
54
|
+
* Specify the y position of the Menu. Either pass a single number or an array with two numbers describing your activator's boundaries ([y1, y2])
|
|
55
|
+
*/
|
|
56
|
+
y?: number | (number | null | undefined)[];
|
|
57
|
+
}
|
|
58
|
+
declare const Menu: React.ForwardRefExoticComponent<MenuProps & React.RefAttributes<HTMLUListElement>>;
|
|
59
|
+
export { Menu };
|
|
@@ -56,7 +56,7 @@ const Menu = /*#__PURE__*/React__default.forwardRef(function Menu(_ref, forwardR
|
|
|
56
56
|
dispatch: childDispatch
|
|
57
57
|
};
|
|
58
58
|
}, [childState, childDispatch]);
|
|
59
|
-
const menu = useRef();
|
|
59
|
+
const menu = useRef(null);
|
|
60
60
|
const ref = useMergedRefs([forwardRef, menu]);
|
|
61
61
|
const [position, setPosition] = useState([-1, -1]);
|
|
62
62
|
const focusableItems = childContext.state.items.filter(item => !item.disabled && item.ref.current);
|
|
@@ -144,11 +144,14 @@ const Menu = /*#__PURE__*/React__default.forwardRef(function Menu(_ref, forwardR
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
function handleBlur(e) {
|
|
147
|
-
if (open && onClose && isRoot && !menu.current
|
|
147
|
+
if (open && onClose && isRoot && !menu.current?.contains(e.relatedTarget)) {
|
|
148
148
|
handleClose(e);
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
function fitValue(range, axis) {
|
|
152
|
+
if (!menu.current) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
152
155
|
const {
|
|
153
156
|
width,
|
|
154
157
|
height
|
|
@@ -187,18 +190,38 @@ const Menu = /*#__PURE__*/React__default.forwardRef(function Menu(_ref, forwardR
|
|
|
187
190
|
reversedAnchor - size >= 0 ? reversedAnchor - size + offset : false,
|
|
188
191
|
// align at max (second fallback)
|
|
189
192
|
max - spacing - size];
|
|
193
|
+
|
|
194
|
+
// Previous array `options`, has at least one item that is a number (the last one - second fallback).
|
|
195
|
+
// That guarantees that the return of `find()` will always be a number
|
|
196
|
+
// and we can safely add the numeric casting `as number`.
|
|
190
197
|
const bestOption = options.find(option => option !== false);
|
|
191
198
|
return bestOption >= spacing ? bestOption : spacing;
|
|
192
199
|
}
|
|
200
|
+
function notEmpty(value) {
|
|
201
|
+
return value !== null && value !== undefined;
|
|
202
|
+
}
|
|
203
|
+
function getPosition(x) {
|
|
204
|
+
if (Array.isArray(x)) {
|
|
205
|
+
// has to be of length 2
|
|
206
|
+
const filtered = x.filter(notEmpty);
|
|
207
|
+
if (filtered.length === 2) {
|
|
208
|
+
return filtered;
|
|
209
|
+
} else {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
return [x, x];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
193
216
|
function calculatePosition() {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
return [
|
|
217
|
+
const ranges = {
|
|
218
|
+
x: getPosition(x),
|
|
219
|
+
y: getPosition(y)
|
|
220
|
+
};
|
|
221
|
+
if (!ranges.x || !ranges.y) {
|
|
222
|
+
return [-1, -1];
|
|
200
223
|
}
|
|
201
|
-
return [-1, -1];
|
|
224
|
+
return [fitValue(ranges.x, 'x') ?? -1, fitValue(ranges.y, 'y') ?? -1];
|
|
202
225
|
}
|
|
203
226
|
useEffect(() => {
|
|
204
227
|
if (open && focusableItems.length > 0) {
|
|
@@ -212,7 +235,7 @@ const Menu = /*#__PURE__*/React__default.forwardRef(function Menu(_ref, forwardR
|
|
|
212
235
|
} else {
|
|
213
236
|
// reset position when menu is closed in order for the --shown
|
|
214
237
|
// modifier to be applied correctly
|
|
215
|
-
setPosition(-1, -1);
|
|
238
|
+
setPosition([-1, -1]);
|
|
216
239
|
}
|
|
217
240
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
218
241
|
}, [open]);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2023
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
type ActionType = {
|
|
9
|
+
type: 'enableIcons' | 'registerItem';
|
|
10
|
+
payload: any;
|
|
11
|
+
};
|
|
12
|
+
type StateType = {
|
|
13
|
+
isRoot: boolean;
|
|
14
|
+
mode: 'full' | 'basic';
|
|
15
|
+
hasIcons: boolean;
|
|
16
|
+
size: 'xs' | 'sm' | 'md' | 'lg' | null;
|
|
17
|
+
items: any[];
|
|
18
|
+
requestCloseRoot: (e: Pick<React.KeyboardEvent<HTMLUListElement>, 'type'>) => void;
|
|
19
|
+
};
|
|
20
|
+
declare function menuReducer(state: StateType, action: ActionType): {
|
|
21
|
+
hasIcons: boolean;
|
|
22
|
+
isRoot: boolean;
|
|
23
|
+
mode: "full" | "basic";
|
|
24
|
+
size: "sm" | "md" | "lg" | "xs" | null;
|
|
25
|
+
items: any[];
|
|
26
|
+
requestCloseRoot: (e: Pick<React.KeyboardEvent<HTMLUListElement>, "type">) => void;
|
|
27
|
+
};
|
|
28
|
+
declare const MenuContext: React.Context<{
|
|
29
|
+
state: StateType;
|
|
30
|
+
dispatch: React.Dispatch<any>;
|
|
31
|
+
}>;
|
|
32
|
+
export { MenuContext, menuReducer };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2023
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
interface MenuItemProps extends React.LiHTMLAttributes<HTMLLIElement> {
|
|
9
|
+
/**
|
|
10
|
+
* Optionally provide another Menu to create a submenu. props.children can't be used to specify the content of the MenuItem itself. Use props.label instead.
|
|
11
|
+
*/
|
|
12
|
+
children?: React.ReactNode;
|
|
13
|
+
/**
|
|
14
|
+
* Additional CSS class names.
|
|
15
|
+
*/
|
|
16
|
+
className?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Specify whether the MenuItem is disabled or not.
|
|
19
|
+
*/
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Specify the kind of the MenuItem.
|
|
23
|
+
*/
|
|
24
|
+
kind?: 'default' | 'danger';
|
|
25
|
+
/**
|
|
26
|
+
* A required label titling the MenuItem. Will be rendered as its text content.
|
|
27
|
+
*/
|
|
28
|
+
label: string;
|
|
29
|
+
/**
|
|
30
|
+
* Provide an optional function to be called when the MenuItem is clicked.
|
|
31
|
+
*/
|
|
32
|
+
onClick?: (event: React.KeyboardEvent<HTMLLIElement> | React.MouseEvent<HTMLLIElement>) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Only applicable if the parent menu is in `basic` mode. Sets the menu item's icon.
|
|
35
|
+
*/
|
|
36
|
+
renderIcon?: any;
|
|
37
|
+
/**
|
|
38
|
+
* Provide a shortcut for the action of this MenuItem. Note that the component will only render it as a hint but not actually register the shortcut.
|
|
39
|
+
*/
|
|
40
|
+
shortcut?: string;
|
|
41
|
+
}
|
|
42
|
+
declare const MenuItem: React.ForwardRefExoticComponent<MenuItemProps & React.RefAttributes<HTMLLIElement>>;
|
|
43
|
+
interface MenuItemSelectableProps {
|
|
44
|
+
/**
|
|
45
|
+
* Additional CSS class names.
|
|
46
|
+
*/
|
|
47
|
+
className?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Specify whether the option should be selected by default.
|
|
50
|
+
*/
|
|
51
|
+
defaultSelected?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* A required label titling this option.
|
|
54
|
+
*/
|
|
55
|
+
label: string;
|
|
56
|
+
/**
|
|
57
|
+
* Provide an optional function to be called when the selection state changes.
|
|
58
|
+
*/
|
|
59
|
+
onChange?: React.ChangeEventHandler<HTMLLIElement>;
|
|
60
|
+
/**
|
|
61
|
+
* Pass a bool to props.selected to control the state of this option.
|
|
62
|
+
*/
|
|
63
|
+
selected?: boolean;
|
|
64
|
+
}
|
|
65
|
+
declare const MenuItemSelectable: React.ForwardRefExoticComponent<MenuItemSelectableProps & React.RefAttributes<HTMLLIElement>>;
|
|
66
|
+
interface MenuItemGroupProps {
|
|
67
|
+
/**
|
|
68
|
+
* A collection of MenuItems to be rendered within this group.
|
|
69
|
+
*/
|
|
70
|
+
children?: React.ReactNode;
|
|
71
|
+
/**
|
|
72
|
+
* Additional CSS class names.
|
|
73
|
+
*/
|
|
74
|
+
className?: string;
|
|
75
|
+
/**
|
|
76
|
+
* A required label titling this group.
|
|
77
|
+
*/
|
|
78
|
+
label: string;
|
|
79
|
+
}
|
|
80
|
+
declare const MenuItemGroup: React.ForwardRefExoticComponent<MenuItemGroupProps & React.RefAttributes<HTMLLIElement>>;
|
|
81
|
+
interface MenuItemRadioGroupProps {
|
|
82
|
+
/**
|
|
83
|
+
* Additional CSS class names.
|
|
84
|
+
*/
|
|
85
|
+
className?: string;
|
|
86
|
+
/**
|
|
87
|
+
* Specify the default selected item. Must match the type of props.items.
|
|
88
|
+
*/
|
|
89
|
+
defaultSelectedItem?: any;
|
|
90
|
+
/**
|
|
91
|
+
* Provide a function to convert an item to the string that will be rendered. Defaults to item.toString().
|
|
92
|
+
*/
|
|
93
|
+
itemToString?: (item: any) => string;
|
|
94
|
+
/**
|
|
95
|
+
* Provide the options for this radio group. Can be of any type, as long as you provide an appropriate props.itemToString function.
|
|
96
|
+
*/
|
|
97
|
+
items?: any[];
|
|
98
|
+
/**
|
|
99
|
+
* A required label titling this radio group.
|
|
100
|
+
*/
|
|
101
|
+
label: string;
|
|
102
|
+
/**
|
|
103
|
+
* Provide an optional function to be called when the selection changes.
|
|
104
|
+
*/
|
|
105
|
+
onChange?: React.ChangeEventHandler<HTMLLIElement>;
|
|
106
|
+
/**
|
|
107
|
+
* Provide props.selectedItem to control the state of this radio group. Must match the type of props.items.
|
|
108
|
+
*/
|
|
109
|
+
selectedItem?: any;
|
|
110
|
+
}
|
|
111
|
+
declare const MenuItemRadioGroup: React.ForwardRefExoticComponent<MenuItemRadioGroupProps & React.RefAttributes<HTMLLIElement>>;
|
|
112
|
+
interface MenuItemDividerProps {
|
|
113
|
+
/**
|
|
114
|
+
* Additional CSS class names.
|
|
115
|
+
*/
|
|
116
|
+
className?: string;
|
|
117
|
+
}
|
|
118
|
+
declare const MenuItemDivider: React.ForwardRefExoticComponent<MenuItemDividerProps & React.RefAttributes<HTMLLIElement>>;
|
|
119
|
+
export { MenuItem, MenuItemSelectable, MenuItemGroup, MenuItemRadioGroup, MenuItemDivider, };
|
|
@@ -39,7 +39,7 @@ const MenuItem = /*#__PURE__*/React__default.forwardRef(function MenuItem(_ref,
|
|
|
39
39
|
} = _ref;
|
|
40
40
|
const prefix = usePrefix();
|
|
41
41
|
const context = useContext(MenuContext);
|
|
42
|
-
const menuItem = useRef();
|
|
42
|
+
const menuItem = useRef(null);
|
|
43
43
|
const ref = useMergedRefs([forwardRef, menuItem]);
|
|
44
44
|
const [boundaries, setBoundaries] = useState({
|
|
45
45
|
x: -1,
|
|
@@ -61,6 +61,9 @@ const MenuItem = /*#__PURE__*/React__default.forwardRef(function MenuItem(_ref,
|
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
function openSubmenu() {
|
|
64
|
+
if (!menuItem.current) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
64
67
|
const {
|
|
65
68
|
x,
|
|
66
69
|
y,
|
|
@@ -105,9 +108,11 @@ const MenuItem = /*#__PURE__*/React__default.forwardRef(function MenuItem(_ref,
|
|
|
105
108
|
}, hoverIntentDelay);
|
|
106
109
|
}
|
|
107
110
|
function handleMouseLeave() {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
if (hoverIntentTimeout.current) {
|
|
112
|
+
clearTimeout(hoverIntentTimeout.current);
|
|
113
|
+
closeSubmenu();
|
|
114
|
+
menuItem.current?.focus();
|
|
115
|
+
}
|
|
111
116
|
}
|
|
112
117
|
function handleKeyDown(e) {
|
|
113
118
|
if (hasChildren && match(e, ArrowRight)) {
|
|
@@ -157,13 +162,13 @@ const MenuItem = /*#__PURE__*/React__default.forwardRef(function MenuItem(_ref,
|
|
|
157
162
|
}, rest, {
|
|
158
163
|
ref: ref,
|
|
159
164
|
className: classNames,
|
|
160
|
-
tabIndex:
|
|
161
|
-
"aria-disabled": isDisabled
|
|
162
|
-
"aria-haspopup": hasChildren ||
|
|
163
|
-
"aria-expanded": hasChildren ? submenuOpen :
|
|
165
|
+
tabIndex: -1,
|
|
166
|
+
"aria-disabled": isDisabled,
|
|
167
|
+
"aria-haspopup": hasChildren || undefined,
|
|
168
|
+
"aria-expanded": hasChildren ? submenuOpen : undefined,
|
|
164
169
|
onClick: handleClick,
|
|
165
|
-
onMouseEnter: hasChildren ? handleMouseEnter :
|
|
166
|
-
onMouseLeave: hasChildren ? handleMouseLeave :
|
|
170
|
+
onMouseEnter: hasChildren ? handleMouseEnter : undefined,
|
|
171
|
+
onMouseLeave: hasChildren ? handleMouseLeave : undefined,
|
|
167
172
|
onKeyDown: handleKeyDown
|
|
168
173
|
}), /*#__PURE__*/React__default.createElement("div", {
|
|
169
174
|
className: `${prefix}--menu-item__icon`
|
|
@@ -180,7 +185,7 @@ const MenuItem = /*#__PURE__*/React__default.forwardRef(function MenuItem(_ref,
|
|
|
180
185
|
open: submenuOpen,
|
|
181
186
|
onClose: () => {
|
|
182
187
|
closeSubmenu();
|
|
183
|
-
menuItem.current
|
|
188
|
+
menuItem.current?.focus();
|
|
184
189
|
},
|
|
185
190
|
x: boundaries.x,
|
|
186
191
|
y: boundaries.y
|
|
@@ -360,7 +365,7 @@ const MenuItemRadioGroup = /*#__PURE__*/React__default.forwardRef(function MenuI
|
|
|
360
365
|
}, /*#__PURE__*/React__default.createElement("ul", _extends({}, rest, {
|
|
361
366
|
role: "group",
|
|
362
367
|
"aria-label": label
|
|
363
|
-
}), items
|
|
368
|
+
}), items?.map((item, i) => /*#__PURE__*/React__default.createElement(MenuItem, {
|
|
364
369
|
key: i,
|
|
365
370
|
label: itemToString(item),
|
|
366
371
|
role: "menuitemradio",
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2023
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import { Menu } from './Menu';
|
|
8
|
+
import { MenuItem, MenuItemDivider, MenuItemGroup, MenuItemRadioGroup, MenuItemSelectable } from './MenuItem';
|
|
9
|
+
export { Menu, MenuItem, MenuItemDivider, MenuItemGroup, MenuItemRadioGroup, MenuItemSelectable, };
|
|
@@ -13,6 +13,7 @@ import { ChevronDown } from '@carbon/icons-react';
|
|
|
13
13
|
import Button from '../Button/Button.js';
|
|
14
14
|
import '../Button/Button.Skeleton.js';
|
|
15
15
|
import { Menu } from '../Menu/Menu.js';
|
|
16
|
+
import '../Menu/MenuItem.js';
|
|
16
17
|
import { useAttachedMenu } from '../../internal/useAttachedMenu.js';
|
|
17
18
|
import { useId } from '../../internal/useId.js';
|
|
18
19
|
import { useMergedRefs } from '../../internal/useMergedRefs.js';
|
|
@@ -12,6 +12,7 @@ import cx from 'classnames';
|
|
|
12
12
|
import { OverflowMenuVertical } from '@carbon/icons-react';
|
|
13
13
|
import { IconButton } from '../../IconButton/index.js';
|
|
14
14
|
import { Menu } from '../../Menu/Menu.js';
|
|
15
|
+
import '../../Menu/MenuItem.js';
|
|
15
16
|
import { useId } from '../../../internal/useId.js';
|
|
16
17
|
import { usePrefix } from '../../../internal/usePrefix.js';
|
|
17
18
|
import { useAttachedMenu } from '../../../internal/useAttachedMenu.js';
|
|
@@ -22,14 +22,17 @@ const SlugContent = /*#__PURE__*/React__default.forwardRef(function SlugContent(
|
|
|
22
22
|
className
|
|
23
23
|
} = _ref;
|
|
24
24
|
const prefix = usePrefix();
|
|
25
|
+
const hasSlugActions = React__default.Children.toArray(children).some(child => child.type?.displayName === 'SlugActions');
|
|
25
26
|
const slugContentClasses = cx(className, {
|
|
26
|
-
[`${prefix}--slug-content`]: true
|
|
27
|
+
[`${prefix}--slug-content`]: true,
|
|
28
|
+
[`${prefix}--slug-content--with-actions`]: hasSlugActions
|
|
27
29
|
});
|
|
28
30
|
return /*#__PURE__*/React__default.createElement(ToggletipContent, {
|
|
29
31
|
className: slugContentClasses,
|
|
30
32
|
ref: ref
|
|
31
33
|
}, children);
|
|
32
34
|
});
|
|
35
|
+
SlugContent.displayName = 'SlugContent';
|
|
33
36
|
SlugContent.propTypes = {
|
|
34
37
|
/**
|
|
35
38
|
* Specify the content you want rendered inside the slug ToggleTip
|
|
@@ -54,6 +57,7 @@ const SlugActions = /*#__PURE__*/React__default.forwardRef(function SlugActions(
|
|
|
54
57
|
ref: ref
|
|
55
58
|
}, children);
|
|
56
59
|
});
|
|
60
|
+
SlugActions.displayName = 'SlugActions';
|
|
57
61
|
SlugActions.propTypes = {
|
|
58
62
|
/**
|
|
59
63
|
* Specify the content you want rendered inside the slug callout toolbar
|
|
@@ -12,7 +12,7 @@ import React__default, { useRef, useState, useCallback, useEffect } from 'react'
|
|
|
12
12
|
import { Popover, PopoverContent } from '../Popover/index.js';
|
|
13
13
|
import { useDelayedState } from '../../internal/useDelayedState.js';
|
|
14
14
|
import { useId } from '../../internal/useId.js';
|
|
15
|
-
import { useNoInteractiveChildren
|
|
15
|
+
import { useNoInteractiveChildren } from '../../internal/useNoInteractiveChildren.js';
|
|
16
16
|
import { usePrefix } from '../../internal/usePrefix.js';
|
|
17
17
|
import { match } from '../../internal/keyboard/match.js';
|
|
18
18
|
import { Escape, Enter, Space } from '../../internal/keyboard/keys.js';
|
|
@@ -110,14 +110,6 @@ function Tooltip(_ref) {
|
|
|
110
110
|
}
|
|
111
111
|
}, [isPointerIntersecting, leaveDelayMs, setOpen]);
|
|
112
112
|
useNoInteractiveChildren(tooltipRef, 'The Tooltip component must have no interactive content rendered by the' + '`label` or `description` prop');
|
|
113
|
-
useEffect(() => {
|
|
114
|
-
if (containerRef !== null && containerRef.current) {
|
|
115
|
-
const interactiveContent = getInteractiveContent(containerRef.current);
|
|
116
|
-
if (!interactiveContent) {
|
|
117
|
-
setOpen(false);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
113
|
useEffect(() => {
|
|
122
114
|
if (isDragging) {
|
|
123
115
|
// Register drag stop handlers.
|
package/es/index.js
CHANGED
|
@@ -60,6 +60,8 @@ export { default as InlineLoading } from './components/InlineLoading/InlineLoadi
|
|
|
60
60
|
export { default as Link } from './components/Link/Link.js';
|
|
61
61
|
export { default as ListItem } from './components/ListItem/ListItem.js';
|
|
62
62
|
export { default as Loading } from './components/Loading/Loading.js';
|
|
63
|
+
export { Menu } from './components/Menu/Menu.js';
|
|
64
|
+
export { MenuItem, MenuItemDivider, MenuItemGroup, MenuItemRadioGroup, MenuItemSelectable } from './components/Menu/MenuItem.js';
|
|
63
65
|
export { MenuButton } from './components/MenuButton/index.js';
|
|
64
66
|
export { default as Modal } from './components/Modal/Modal.js';
|
|
65
67
|
export { default as ModalWrapper } from './components/ModalWrapper/ModalWrapper.js';
|
|
@@ -212,8 +214,6 @@ export { default as TableToolbarContent } from './components/DataTable/TableTool
|
|
|
212
214
|
export { default as TableToolbarSearch } from './components/DataTable/TableToolbarSearch.js';
|
|
213
215
|
export { default as TableToolbarMenu } from './components/DataTable/TableToolbarMenu.js';
|
|
214
216
|
export { default as FilterableMultiSelect } from './components/MultiSelect/FilterableMultiSelect.js';
|
|
215
|
-
export { Menu } from './components/Menu/Menu.js';
|
|
216
|
-
export { MenuItem, MenuItemDivider, MenuItemGroup, MenuItemRadioGroup, MenuItemSelectable } from './components/Menu/MenuItem.js';
|
|
217
217
|
export { default as MultiSelect } from './components/MultiSelect/MultiSelect.js';
|
|
218
218
|
export { default as Pagination } from './components/Pagination/Pagination.js';
|
|
219
219
|
export { default as PaginationNav } from './components/PaginationNav/PaginationNav.js';
|
|
@@ -18,6 +18,7 @@ var index = require('../IconButton/index.js');
|
|
|
18
18
|
var Button = require('../Button/Button.js');
|
|
19
19
|
require('../Button/Button.Skeleton.js');
|
|
20
20
|
var Menu = require('../Menu/Menu.js');
|
|
21
|
+
require('../Menu/MenuItem.js');
|
|
21
22
|
var useAttachedMenu = require('../../internal/useAttachedMenu.js');
|
|
22
23
|
var useId = require('../../internal/useId.js');
|
|
23
24
|
var useMergedRefs = require('../../internal/useMergedRefs.js');
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2023
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
interface MenuProps extends React.HTMLAttributes<HTMLUListElement> {
|
|
9
|
+
/**
|
|
10
|
+
* A collection of MenuItems to be rendered within this Menu.
|
|
11
|
+
*/
|
|
12
|
+
children?: React.ReactNode;
|
|
13
|
+
/**
|
|
14
|
+
* Additional CSS class names.
|
|
15
|
+
*/
|
|
16
|
+
className?: string;
|
|
17
|
+
/**
|
|
18
|
+
* A label describing the Menu.
|
|
19
|
+
*/
|
|
20
|
+
label?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The mode of this menu. Defaults to full.
|
|
23
|
+
* `full` supports nesting and selectable menu items, but no icons.
|
|
24
|
+
* `basic` supports icons but no nesting or selectable menu items.
|
|
25
|
+
*
|
|
26
|
+
* **This prop is not intended for use and will be set by the respective implementation (like useContextMenu, MenuButton, and ComboButton).**
|
|
27
|
+
*/
|
|
28
|
+
mode?: 'full' | 'basic';
|
|
29
|
+
/**
|
|
30
|
+
* Provide an optional function to be called when the Menu should be closed.
|
|
31
|
+
*/
|
|
32
|
+
onClose?: () => void;
|
|
33
|
+
/**
|
|
34
|
+
* Provide an optional function to be called when the Menu is opened.
|
|
35
|
+
*/
|
|
36
|
+
onOpen?: () => void;
|
|
37
|
+
/**
|
|
38
|
+
* Whether the Menu is open or not.
|
|
39
|
+
*/
|
|
40
|
+
open?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Specify the size of the Menu.
|
|
43
|
+
*/
|
|
44
|
+
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
45
|
+
/**
|
|
46
|
+
* Specify a DOM node where the Menu should be rendered in. Defaults to document.body.
|
|
47
|
+
*/
|
|
48
|
+
target?: any;
|
|
49
|
+
/**
|
|
50
|
+
* Specify the x position of the Menu. Either pass a single number or an array with two numbers describing your activator's boundaries ([x1, x2])
|
|
51
|
+
*/
|
|
52
|
+
x?: number | (number | null | undefined)[];
|
|
53
|
+
/**
|
|
54
|
+
* Specify the y position of the Menu. Either pass a single number or an array with two numbers describing your activator's boundaries ([y1, y2])
|
|
55
|
+
*/
|
|
56
|
+
y?: number | (number | null | undefined)[];
|
|
57
|
+
}
|
|
58
|
+
declare const Menu: React.ForwardRefExoticComponent<MenuProps & React.RefAttributes<HTMLUListElement>>;
|
|
59
|
+
export { Menu };
|
|
@@ -66,7 +66,7 @@ const Menu = /*#__PURE__*/React__default["default"].forwardRef(function Menu(_re
|
|
|
66
66
|
dispatch: childDispatch
|
|
67
67
|
};
|
|
68
68
|
}, [childState, childDispatch]);
|
|
69
|
-
const menu = React.useRef();
|
|
69
|
+
const menu = React.useRef(null);
|
|
70
70
|
const ref = useMergedRefs.useMergedRefs([forwardRef, menu]);
|
|
71
71
|
const [position, setPosition] = React.useState([-1, -1]);
|
|
72
72
|
const focusableItems = childContext.state.items.filter(item => !item.disabled && item.ref.current);
|
|
@@ -154,11 +154,14 @@ const Menu = /*#__PURE__*/React__default["default"].forwardRef(function Menu(_re
|
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
function handleBlur(e) {
|
|
157
|
-
if (open && onClose && isRoot && !menu.current
|
|
157
|
+
if (open && onClose && isRoot && !menu.current?.contains(e.relatedTarget)) {
|
|
158
158
|
handleClose(e);
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
function fitValue(range, axis) {
|
|
162
|
+
if (!menu.current) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
162
165
|
const {
|
|
163
166
|
width,
|
|
164
167
|
height
|
|
@@ -197,18 +200,38 @@ const Menu = /*#__PURE__*/React__default["default"].forwardRef(function Menu(_re
|
|
|
197
200
|
reversedAnchor - size >= 0 ? reversedAnchor - size + offset : false,
|
|
198
201
|
// align at max (second fallback)
|
|
199
202
|
max - spacing - size];
|
|
203
|
+
|
|
204
|
+
// Previous array `options`, has at least one item that is a number (the last one - second fallback).
|
|
205
|
+
// That guarantees that the return of `find()` will always be a number
|
|
206
|
+
// and we can safely add the numeric casting `as number`.
|
|
200
207
|
const bestOption = options.find(option => option !== false);
|
|
201
208
|
return bestOption >= spacing ? bestOption : spacing;
|
|
202
209
|
}
|
|
210
|
+
function notEmpty(value) {
|
|
211
|
+
return value !== null && value !== undefined;
|
|
212
|
+
}
|
|
213
|
+
function getPosition(x) {
|
|
214
|
+
if (Array.isArray(x)) {
|
|
215
|
+
// has to be of length 2
|
|
216
|
+
const filtered = x.filter(notEmpty);
|
|
217
|
+
if (filtered.length === 2) {
|
|
218
|
+
return filtered;
|
|
219
|
+
} else {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
return [x, x];
|
|
224
|
+
}
|
|
225
|
+
}
|
|
203
226
|
function calculatePosition() {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
return [
|
|
227
|
+
const ranges = {
|
|
228
|
+
x: getPosition(x),
|
|
229
|
+
y: getPosition(y)
|
|
230
|
+
};
|
|
231
|
+
if (!ranges.x || !ranges.y) {
|
|
232
|
+
return [-1, -1];
|
|
210
233
|
}
|
|
211
|
-
return [-1, -1];
|
|
234
|
+
return [fitValue(ranges.x, 'x') ?? -1, fitValue(ranges.y, 'y') ?? -1];
|
|
212
235
|
}
|
|
213
236
|
React.useEffect(() => {
|
|
214
237
|
if (open && focusableItems.length > 0) {
|
|
@@ -222,7 +245,7 @@ const Menu = /*#__PURE__*/React__default["default"].forwardRef(function Menu(_re
|
|
|
222
245
|
} else {
|
|
223
246
|
// reset position when menu is closed in order for the --shown
|
|
224
247
|
// modifier to be applied correctly
|
|
225
|
-
setPosition(-1, -1);
|
|
248
|
+
setPosition([-1, -1]);
|
|
226
249
|
}
|
|
227
250
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
228
251
|
}, [open]);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2023
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
type ActionType = {
|
|
9
|
+
type: 'enableIcons' | 'registerItem';
|
|
10
|
+
payload: any;
|
|
11
|
+
};
|
|
12
|
+
type StateType = {
|
|
13
|
+
isRoot: boolean;
|
|
14
|
+
mode: 'full' | 'basic';
|
|
15
|
+
hasIcons: boolean;
|
|
16
|
+
size: 'xs' | 'sm' | 'md' | 'lg' | null;
|
|
17
|
+
items: any[];
|
|
18
|
+
requestCloseRoot: (e: Pick<React.KeyboardEvent<HTMLUListElement>, 'type'>) => void;
|
|
19
|
+
};
|
|
20
|
+
declare function menuReducer(state: StateType, action: ActionType): {
|
|
21
|
+
hasIcons: boolean;
|
|
22
|
+
isRoot: boolean;
|
|
23
|
+
mode: "full" | "basic";
|
|
24
|
+
size: "sm" | "md" | "lg" | "xs" | null;
|
|
25
|
+
items: any[];
|
|
26
|
+
requestCloseRoot: (e: Pick<React.KeyboardEvent<HTMLUListElement>, "type">) => void;
|
|
27
|
+
};
|
|
28
|
+
declare const MenuContext: React.Context<{
|
|
29
|
+
state: StateType;
|
|
30
|
+
dispatch: React.Dispatch<any>;
|
|
31
|
+
}>;
|
|
32
|
+
export { MenuContext, menuReducer };
|