@patternfly/react-core 4.227.8 → 4.227.9
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 +8 -0
- package/dist/esm/components/Menu/MenuItem.d.ts +3 -1
- package/dist/esm/components/Menu/MenuItem.d.ts.map +1 -1
- package/dist/esm/components/Menu/MenuItem.js +2 -2
- package/dist/esm/components/Menu/MenuItem.js.map +1 -1
- package/dist/esm/components/MenuToggle/MenuToggle.d.ts +6 -12
- package/dist/esm/components/MenuToggle/MenuToggle.d.ts.map +1 -1
- package/dist/esm/components/MenuToggle/MenuToggle.js +12 -8
- package/dist/esm/components/MenuToggle/MenuToggle.js.map +1 -1
- package/dist/esm/components/TextInputGroup/TextInputGroup.d.ts +2 -0
- package/dist/esm/components/TextInputGroup/TextInputGroup.d.ts.map +1 -1
- package/dist/esm/components/TextInputGroup/TextInputGroup.js +2 -2
- package/dist/esm/components/TextInputGroup/TextInputGroup.js.map +1 -1
- package/dist/esm/components/TextInputGroup/TextInputGroupMain.d.ts +1 -1
- package/dist/esm/components/TextInputGroup/TextInputGroupMain.d.ts.map +1 -1
- package/dist/js/components/Menu/MenuItem.d.ts +3 -1
- package/dist/js/components/Menu/MenuItem.d.ts.map +1 -1
- package/dist/js/components/Menu/MenuItem.js +2 -2
- package/dist/js/components/Menu/MenuItem.js.map +1 -1
- package/dist/js/components/MenuToggle/MenuToggle.d.ts +6 -12
- package/dist/js/components/MenuToggle/MenuToggle.d.ts.map +1 -1
- package/dist/js/components/MenuToggle/MenuToggle.js +12 -8
- package/dist/js/components/MenuToggle/MenuToggle.js.map +1 -1
- package/dist/js/components/TextInputGroup/TextInputGroup.d.ts +2 -0
- package/dist/js/components/TextInputGroup/TextInputGroup.d.ts.map +1 -1
- package/dist/js/components/TextInputGroup/TextInputGroup.js +2 -2
- package/dist/js/components/TextInputGroup/TextInputGroup.js.map +1 -1
- package/dist/js/components/TextInputGroup/TextInputGroupMain.d.ts +1 -1
- package/dist/js/components/TextInputGroup/TextInputGroupMain.d.ts.map +1 -1
- package/dist/umd/react-core.css +815 -818
- package/dist/umd/react-core.js +17 -12
- package/dist/umd/react-core.min.css +15 -18
- package/dist/umd/react-core.min.js +5 -5
- package/package.json +6 -6
- package/src/components/Menu/MenuItem.tsx +4 -0
- package/src/components/MenuToggle/MenuToggle.tsx +63 -25
- package/src/components/TextInputGroup/TextInputGroup.tsx +9 -1
- package/src/components/TextInputGroup/TextInputGroupMain.tsx +1 -1
- package/src/demos/ComposableMenu/ComposableMenu.md +8 -0
- package/src/demos/ComposableMenu/examples/ComposableTypeaheadSelect.tsx +196 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patternfly/react-core",
|
|
3
|
-
"version": "4.227.
|
|
3
|
+
"version": "4.227.9",
|
|
4
4
|
"description": "This library provides a set of common React components for use with the PatternFly reference implementation.",
|
|
5
5
|
"main": "dist/js/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -34,16 +34,16 @@
|
|
|
34
34
|
"generate": "node scripts/copyStyles.js"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@patternfly/react-icons": "^4.78.
|
|
38
|
-
"@patternfly/react-styles": "^4.77.
|
|
39
|
-
"@patternfly/react-tokens": "^4.79.
|
|
37
|
+
"@patternfly/react-icons": "^4.78.9",
|
|
38
|
+
"@patternfly/react-styles": "^4.77.9",
|
|
39
|
+
"@patternfly/react-tokens": "^4.79.9",
|
|
40
40
|
"focus-trap": "6.9.2",
|
|
41
41
|
"react-dropzone": "9.0.0",
|
|
42
42
|
"tippy.js": "5.1.2",
|
|
43
43
|
"tslib": "^2.0.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@patternfly/patternfly": "4.
|
|
46
|
+
"@patternfly/patternfly": "4.205.1",
|
|
47
47
|
"@rollup/plugin-commonjs": "^21.0.0",
|
|
48
48
|
"@rollup/plugin-node-resolve": "^13.0.0",
|
|
49
49
|
"@rollup/plugin-replace": "^3.0.0",
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"react": "^16.8.0 || ^17.0.0",
|
|
61
61
|
"react-dom": "^16.8.0 || ^17.0.0"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "997f82511b3461fe6b960fc9412b17ee94864352"
|
|
64
64
|
}
|
|
@@ -50,6 +50,8 @@ export interface MenuItemProps extends Omit<React.HTMLProps<HTMLLIElement>, 'onC
|
|
|
50
50
|
isExternalLink?: boolean;
|
|
51
51
|
/** Flag indicating if the option is selected */
|
|
52
52
|
isSelected?: boolean;
|
|
53
|
+
/** Flag indicating the item is focused */
|
|
54
|
+
isFocused?: boolean;
|
|
53
55
|
/** @beta Flyout menu */
|
|
54
56
|
flyoutMenu?: React.ReactElement;
|
|
55
57
|
/** @beta Callback function when mouse leaves trigger */
|
|
@@ -88,6 +90,7 @@ const MenuItemBase: React.FunctionComponent<MenuItemProps> = ({
|
|
|
88
90
|
isDisabled = false,
|
|
89
91
|
isExternalLink = false,
|
|
90
92
|
isSelected = null,
|
|
93
|
+
isFocused,
|
|
91
94
|
icon,
|
|
92
95
|
actions,
|
|
93
96
|
onShowFlyout,
|
|
@@ -290,6 +293,7 @@ const MenuItemBase: React.FunctionComponent<MenuItemProps> = ({
|
|
|
290
293
|
_isOnPath && styles.modifiers.currentPath,
|
|
291
294
|
isLoadButton && styles.modifiers.load,
|
|
292
295
|
isLoading && styles.modifiers.loading,
|
|
296
|
+
isFocused && styles.modifiers.focus,
|
|
293
297
|
className
|
|
294
298
|
)}
|
|
295
299
|
onMouseOver={onMouseOver}
|
|
@@ -4,8 +4,10 @@ import { css } from '@patternfly/react-styles';
|
|
|
4
4
|
import CaretDownIcon from '@patternfly/react-icons/dist/esm/icons/caret-down-icon';
|
|
5
5
|
import { BadgeProps } from '../Badge';
|
|
6
6
|
|
|
7
|
+
export type MenuToggleElement = HTMLDivElement | HTMLButtonElement;
|
|
8
|
+
|
|
7
9
|
export interface MenuToggleProps
|
|
8
|
-
extends Omit<React.DetailedHTMLProps<React.
|
|
10
|
+
extends Omit<React.DetailedHTMLProps<React.HTMLAttributes<MenuToggleElement>, MenuToggleElement>, 'ref'> {
|
|
9
11
|
/** Content rendered inside the toggle */
|
|
10
12
|
children?: React.ReactNode;
|
|
11
13
|
/** Additional classes added to the toggle */
|
|
@@ -19,18 +21,18 @@ export interface MenuToggleProps
|
|
|
19
21
|
/** Flag indicating the toggle takes up the full width of its parent */
|
|
20
22
|
isFullWidth?: boolean;
|
|
21
23
|
/** Variant styles of the menu toggle */
|
|
22
|
-
variant?: 'default' | 'plain' | 'primary' | 'plainText' | 'secondary';
|
|
24
|
+
variant?: 'default' | 'plain' | 'primary' | 'plainText' | 'secondary' | 'typeahead';
|
|
23
25
|
/** Optional icon rendered inside the toggle, before the children content */
|
|
24
26
|
icon?: React.ReactNode;
|
|
25
27
|
/** Optional badge rendered inside the toggle, after the children content */
|
|
26
28
|
badge?: BadgeProps | React.ReactNode;
|
|
27
29
|
/** Forwarded ref */
|
|
28
|
-
innerRef?: React.Ref<
|
|
30
|
+
innerRef?: React.Ref<MenuToggleElement>;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
export class MenuToggleBase extends React.Component<MenuToggleProps> {
|
|
32
34
|
displayName = 'MenuToggleBase';
|
|
33
|
-
static defaultProps = {
|
|
35
|
+
static defaultProps: MenuToggleProps = {
|
|
34
36
|
className: '',
|
|
35
37
|
isExpanded: false,
|
|
36
38
|
isDisabled: false,
|
|
@@ -51,24 +53,63 @@ export class MenuToggleBase extends React.Component<MenuToggleProps> {
|
|
|
51
53
|
isFullWidth,
|
|
52
54
|
variant,
|
|
53
55
|
innerRef,
|
|
54
|
-
|
|
56
|
+
onClick,
|
|
57
|
+
'aria-label': ariaLabel,
|
|
58
|
+
...otherProps
|
|
55
59
|
} = this.props;
|
|
56
|
-
|
|
57
60
|
const isPlain = variant === 'plain';
|
|
58
61
|
const isPlainText = variant === 'plainText';
|
|
62
|
+
const isTypeahead = variant === 'typeahead';
|
|
63
|
+
const toggleControls = (
|
|
64
|
+
<span className={css(styles.menuToggleControls)}>
|
|
65
|
+
<span className={css(styles.menuToggleToggleIcon)}>
|
|
66
|
+
<CaretDownIcon aria-hidden />
|
|
67
|
+
</span>
|
|
68
|
+
</span>
|
|
69
|
+
);
|
|
59
70
|
|
|
60
71
|
const content = (
|
|
61
|
-
|
|
72
|
+
<>
|
|
62
73
|
{icon && <span className={css(styles.menuToggleIcon)}>{icon}</span>}
|
|
63
|
-
<span className="pf-c-menu-toggle__text">{children}</span>
|
|
64
|
-
{badge && <span className={css(styles.menuToggleCount)}>{badge
|
|
65
|
-
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
74
|
+
{isTypeahead ? children : <span className="pf-c-menu-toggle__text">{children}</span>}
|
|
75
|
+
{badge && <span className={css(styles.menuToggleCount)}>{badge}</span>}
|
|
76
|
+
{isTypeahead ? (
|
|
77
|
+
<button
|
|
78
|
+
type="button"
|
|
79
|
+
className={css(styles.menuToggleButton)}
|
|
80
|
+
aria-expanded={isExpanded}
|
|
81
|
+
onClick={onClick}
|
|
82
|
+
aria-label="Menu toggle"
|
|
83
|
+
>
|
|
84
|
+
{toggleControls}
|
|
85
|
+
</button>
|
|
86
|
+
) : (
|
|
87
|
+
toggleControls
|
|
88
|
+
)}
|
|
89
|
+
</>
|
|
71
90
|
);
|
|
91
|
+
|
|
92
|
+
const componentProps = {
|
|
93
|
+
className: css(
|
|
94
|
+
styles.menuToggle,
|
|
95
|
+
isExpanded && styles.modifiers.expanded,
|
|
96
|
+
variant === 'primary' && styles.modifiers.primary,
|
|
97
|
+
(isPlain || isPlainText) && styles.modifiers.plain,
|
|
98
|
+
isPlainText && styles.modifiers.text,
|
|
99
|
+
isFullHeight && styles.modifiers.fullHeight,
|
|
100
|
+
isTypeahead && styles.modifiers.typeahead,
|
|
101
|
+
isFullWidth && styles.modifiers.fullWidth,
|
|
102
|
+
className
|
|
103
|
+
),
|
|
104
|
+
children: isPlain ? children : content,
|
|
105
|
+
...(isDisabled && { disabled: true }),
|
|
106
|
+
...otherProps
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
if (isTypeahead) {
|
|
110
|
+
return <div ref={innerRef as React.Ref<HTMLDivElement>} {...componentProps} />;
|
|
111
|
+
}
|
|
112
|
+
|
|
72
113
|
return (
|
|
73
114
|
<button
|
|
74
115
|
className={css(
|
|
@@ -82,21 +123,18 @@ export class MenuToggleBase extends React.Component<MenuToggleProps> {
|
|
|
82
123
|
isFullWidth && styles.modifiers.fullWidth,
|
|
83
124
|
className
|
|
84
125
|
)}
|
|
126
|
+
ref={innerRef as React.Ref<HTMLButtonElement>}
|
|
85
127
|
type="button"
|
|
86
|
-
aria-
|
|
87
|
-
|
|
88
|
-
{
|
|
89
|
-
{...
|
|
90
|
-
|
|
91
|
-
>
|
|
92
|
-
{isPlain && children}
|
|
93
|
-
{!isPlain && content}
|
|
94
|
-
</button>
|
|
128
|
+
aria-label={ariaLabel}
|
|
129
|
+
aria-expanded={isExpanded}
|
|
130
|
+
onClick={onClick}
|
|
131
|
+
{...componentProps}
|
|
132
|
+
/>
|
|
95
133
|
);
|
|
96
134
|
}
|
|
97
135
|
}
|
|
98
136
|
|
|
99
|
-
export const MenuToggle = React.forwardRef((props: MenuToggleProps, ref: React.Ref<
|
|
137
|
+
export const MenuToggle = React.forwardRef((props: MenuToggleProps, ref: React.Ref<MenuToggleElement>) => (
|
|
100
138
|
<MenuToggleBase innerRef={ref} {...props} />
|
|
101
139
|
));
|
|
102
140
|
|
|
@@ -9,6 +9,8 @@ export interface TextInputGroupProps extends React.HTMLProps<HTMLDivElement> {
|
|
|
9
9
|
className?: string;
|
|
10
10
|
/** Adds disabled styling and a disabled context value which text input group main hooks into for the input itself */
|
|
11
11
|
isDisabled?: boolean;
|
|
12
|
+
/** Flag to indicate the toggle has no border or background */
|
|
13
|
+
isPlain?: boolean;
|
|
12
14
|
/** @hide A reference object to attach to the input box */
|
|
13
15
|
innerRef?: React.RefObject<any>;
|
|
14
16
|
}
|
|
@@ -21,6 +23,7 @@ export const TextInputGroup: React.FunctionComponent<TextInputGroupProps> = ({
|
|
|
21
23
|
children,
|
|
22
24
|
className,
|
|
23
25
|
isDisabled,
|
|
26
|
+
isPlain,
|
|
24
27
|
innerRef,
|
|
25
28
|
...props
|
|
26
29
|
}: TextInputGroupProps) => {
|
|
@@ -30,7 +33,12 @@ export const TextInputGroup: React.FunctionComponent<TextInputGroupProps> = ({
|
|
|
30
33
|
<TextInputGroupContext.Provider value={{ isDisabled }}>
|
|
31
34
|
<div
|
|
32
35
|
ref={textInputGroupRef}
|
|
33
|
-
className={css(
|
|
36
|
+
className={css(
|
|
37
|
+
styles.textInputGroup,
|
|
38
|
+
isDisabled && styles.modifiers.disabled,
|
|
39
|
+
isPlain && styles.modifiers.plain,
|
|
40
|
+
className
|
|
41
|
+
)}
|
|
34
42
|
{...props}
|
|
35
43
|
>
|
|
36
44
|
{children}
|
|
@@ -37,7 +37,7 @@ export interface TextInputGroupMainProps extends Omit<React.HTMLProps<HTMLDivEle
|
|
|
37
37
|
value?: string | number;
|
|
38
38
|
/** Placeholder value for the input */
|
|
39
39
|
placeholder?: string;
|
|
40
|
-
/**
|
|
40
|
+
/** A reference object to attach to the input box */
|
|
41
41
|
innerRef?: React.RefObject<any>;
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -20,6 +20,9 @@ import pfIcon from './examples/pf-logo-small.svg';
|
|
|
20
20
|
import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
|
|
21
21
|
import CaretDownIcon from '@patternfly/react-icons/dist/esm/icons/caret-down-icon';
|
|
22
22
|
import avatarImg from './examples/avatarImg.svg';
|
|
23
|
+
import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
|
|
24
|
+
import { css } from '@patternfly/react-styles';
|
|
25
|
+
import styles from '@patternfly/react-styles/css/components/Menu/menu';
|
|
23
26
|
|
|
24
27
|
## Demos
|
|
25
28
|
|
|
@@ -45,6 +48,11 @@ Composable menus currently require consumer keyboard handling and use of our und
|
|
|
45
48
|
```ts isBeta file="./examples/ComposableSimpleCheckboxSelect.tsx"
|
|
46
49
|
```
|
|
47
50
|
|
|
51
|
+
### Composable typeahead select
|
|
52
|
+
|
|
53
|
+
```ts file="./examples/ComposableTypeaheadSelect.tsx"
|
|
54
|
+
```
|
|
55
|
+
|
|
48
56
|
### Composable drilldown menu
|
|
49
57
|
|
|
50
58
|
```ts isBeta file="./examples/ComposableDrilldownMenu.tsx"
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Menu,
|
|
5
|
+
MenuContent,
|
|
6
|
+
MenuList,
|
|
7
|
+
MenuItem,
|
|
8
|
+
Popper,
|
|
9
|
+
MenuToggle,
|
|
10
|
+
TextInputGroup,
|
|
11
|
+
MenuItemProps,
|
|
12
|
+
Button,
|
|
13
|
+
TextInputGroupUtilities,
|
|
14
|
+
TextInputGroupMain,
|
|
15
|
+
MenuToggleElement
|
|
16
|
+
} from '@patternfly/react-core';
|
|
17
|
+
import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
|
|
18
|
+
import TableIcon from '@patternfly/react-icons/dist/esm/icons/table-icon';
|
|
19
|
+
|
|
20
|
+
const intitalMenuItems: MenuItemProps[] = [
|
|
21
|
+
{ itemId: 'Option 1', children: 'Option 1' },
|
|
22
|
+
{ itemId: 'Option 2', children: 'Option 2' },
|
|
23
|
+
{ itemId: 'Option 3', children: 'Option 3', icon: <TableIcon aria-hidden /> }
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
export const ComposableTypeaheadSelect: React.FunctionComponent = () => {
|
|
27
|
+
const [isMenuOpen, setIsMenuOpen] = React.useState(false);
|
|
28
|
+
const [inputValue, setInputValue] = React.useState<string>('');
|
|
29
|
+
const [menuItems, setMenuItems] = React.useState<MenuItemProps[]>(intitalMenuItems);
|
|
30
|
+
const [focusedItemIndex, setFocusedItemIndex] = React.useState<number | null>(null);
|
|
31
|
+
const [isSelected, setIsSelected] = React.useState(false);
|
|
32
|
+
|
|
33
|
+
const menuToggleRef = React.useRef<MenuToggleElement>({} as MenuToggleElement);
|
|
34
|
+
const textInputRef = React.useRef<HTMLInputElement>();
|
|
35
|
+
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
let newMenuItems: MenuItemProps[] = intitalMenuItems;
|
|
38
|
+
|
|
39
|
+
// Filter menu items based on the text input value when one exists
|
|
40
|
+
if (inputValue) {
|
|
41
|
+
newMenuItems = intitalMenuItems.filter(menuItem =>
|
|
42
|
+
String(menuItem.children)
|
|
43
|
+
.toLowerCase()
|
|
44
|
+
.includes(inputValue.toLowerCase())
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// When no options are found after filtering, display 'No results found'
|
|
48
|
+
if (!newMenuItems.length) {
|
|
49
|
+
newMenuItems = [{ isDisabled: true, children: 'No results found' }];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setMenuItems(newMenuItems);
|
|
54
|
+
}, [inputValue]);
|
|
55
|
+
|
|
56
|
+
const focusOnInput = () => menuToggleRef.current?.querySelector('input')?.focus();
|
|
57
|
+
|
|
58
|
+
const onMenuSelect = (_event: React.MouseEvent<Element, MouseEvent>, itemId: string | number) => {
|
|
59
|
+
if (itemId) {
|
|
60
|
+
setInputValue(itemId.toString());
|
|
61
|
+
setIsSelected(true);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
setIsMenuOpen(false);
|
|
65
|
+
setFocusedItemIndex(null);
|
|
66
|
+
focusOnInput();
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const handleMenuArrowKeys = (key: string) => {
|
|
70
|
+
let indexToFocus;
|
|
71
|
+
|
|
72
|
+
if (isMenuOpen) {
|
|
73
|
+
if (key === 'ArrowUp') {
|
|
74
|
+
// When no index is set or at the first index, focus to the last, otherwise decrement focus index
|
|
75
|
+
if (focusedItemIndex === null || focusedItemIndex === 0) {
|
|
76
|
+
indexToFocus = menuItems.length - 1;
|
|
77
|
+
} else {
|
|
78
|
+
indexToFocus = focusedItemIndex - 1;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (key === 'ArrowDown') {
|
|
83
|
+
// When no index is set or at the last index, focus to the first, otherwise increment focus index
|
|
84
|
+
if (focusedItemIndex === null || focusedItemIndex === menuItems.length - 1) {
|
|
85
|
+
indexToFocus = 0;
|
|
86
|
+
} else {
|
|
87
|
+
indexToFocus = focusedItemIndex + 1;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
setFocusedItemIndex(indexToFocus);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const onInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
96
|
+
const enabledMenuItems = menuItems.filter(menuItem => !menuItem.isDisabled);
|
|
97
|
+
const [firstMenuItem] = enabledMenuItems;
|
|
98
|
+
const focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem;
|
|
99
|
+
|
|
100
|
+
switch (event.key) {
|
|
101
|
+
// Select the first available option
|
|
102
|
+
case 'Enter':
|
|
103
|
+
if (isMenuOpen) {
|
|
104
|
+
setInputValue(String(focusedItem.children));
|
|
105
|
+
setIsSelected(true);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
setIsMenuOpen(prevIsOpen => !prevIsOpen);
|
|
109
|
+
setFocusedItemIndex(null);
|
|
110
|
+
focusOnInput();
|
|
111
|
+
|
|
112
|
+
break;
|
|
113
|
+
case 'Tab':
|
|
114
|
+
case 'Escape':
|
|
115
|
+
setIsMenuOpen(false);
|
|
116
|
+
break;
|
|
117
|
+
case 'ArrowUp':
|
|
118
|
+
case 'ArrowDown':
|
|
119
|
+
handleMenuArrowKeys(event.key);
|
|
120
|
+
break;
|
|
121
|
+
default:
|
|
122
|
+
!isMenuOpen && setIsMenuOpen(true);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// Close the menu when a click occurs outside of the menu toggle content
|
|
127
|
+
const onDocumentClick = (event: MouseEvent) => {
|
|
128
|
+
if (!menuToggleRef.current?.contains(event?.target as HTMLElement)) {
|
|
129
|
+
setIsMenuOpen(false);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const toggleMenuOpen = () => {
|
|
134
|
+
setIsMenuOpen(prevIsOpen => !prevIsOpen);
|
|
135
|
+
textInputRef.current?.focus();
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const onTextInputChange = (value: string) => {
|
|
139
|
+
setInputValue(value);
|
|
140
|
+
setIsSelected(false);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<Popper
|
|
145
|
+
trigger={
|
|
146
|
+
<MenuToggle
|
|
147
|
+
variant="typeahead"
|
|
148
|
+
onClick={toggleMenuOpen}
|
|
149
|
+
innerRef={menuToggleRef}
|
|
150
|
+
isExpanded={isMenuOpen}
|
|
151
|
+
isFullWidth
|
|
152
|
+
>
|
|
153
|
+
<TextInputGroup isPlain>
|
|
154
|
+
<TextInputGroupMain
|
|
155
|
+
value={inputValue}
|
|
156
|
+
onClick={toggleMenuOpen}
|
|
157
|
+
onChange={onTextInputChange}
|
|
158
|
+
onKeyDown={onInputKeyDown}
|
|
159
|
+
id="typeahead-select-input"
|
|
160
|
+
autoComplete="off"
|
|
161
|
+
innerRef={textInputRef}
|
|
162
|
+
/>
|
|
163
|
+
|
|
164
|
+
<TextInputGroupUtilities>
|
|
165
|
+
{!!inputValue && (
|
|
166
|
+
<Button variant="plain" onClick={() => setInputValue('')} aria-label="Clear input value">
|
|
167
|
+
<TimesIcon aria-hidden />
|
|
168
|
+
</Button>
|
|
169
|
+
)}
|
|
170
|
+
</TextInputGroupUtilities>
|
|
171
|
+
</TextInputGroup>
|
|
172
|
+
</MenuToggle>
|
|
173
|
+
}
|
|
174
|
+
popper={
|
|
175
|
+
<Menu id="select-menu" onSelect={onMenuSelect} selected={isSelected && inputValue}>
|
|
176
|
+
<MenuContent>
|
|
177
|
+
<MenuList>
|
|
178
|
+
{menuItems.map((itemProps, index) => (
|
|
179
|
+
<MenuItem
|
|
180
|
+
key={itemProps.itemId || itemProps.children}
|
|
181
|
+
isFocused={focusedItemIndex === index}
|
|
182
|
+
className={itemProps.className}
|
|
183
|
+
onClick={() => setIsSelected(true)}
|
|
184
|
+
{...itemProps}
|
|
185
|
+
ref={null}
|
|
186
|
+
/>
|
|
187
|
+
))}
|
|
188
|
+
</MenuList>
|
|
189
|
+
</MenuContent>
|
|
190
|
+
</Menu>
|
|
191
|
+
}
|
|
192
|
+
isVisible={isMenuOpen}
|
|
193
|
+
onDocumentClick={onDocumentClick}
|
|
194
|
+
/>
|
|
195
|
+
);
|
|
196
|
+
};
|