@linzjs/step-ag-grid 1.4.5 → 1.4.6
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/dist/index.js +195 -163
- package/dist/index.js.map +1 -1
- package/dist/src/react-menu3/components/FocusableItem.d.ts +9 -1
- package/dist/src/react-menu3/components/Menu.d.ts +16 -2
- package/dist/src/react-menu3/components/MenuButton.d.ts +8 -1
- package/dist/src/react-menu3/components/MenuDivider.d.ts +3 -2
- package/dist/src/react-menu3/components/MenuGroup.d.ts +12 -2
- package/dist/src/react-menu3/components/MenuHeader.d.ts +4 -2
- package/dist/src/react-menu3/components/MenuItem.d.ts +59 -1
- package/dist/src/react-menu3/components/MenuList.d.ts +57 -30
- package/dist/src/react-menu3/components/MenuRadioGroup.d.ts +44 -2
- package/dist/src/react-menu3/components/SubMenu.d.ts +110 -1
- package/dist/src/react-menu3/hooks/useBEM.d.ts +4 -3
- package/dist/src/react-menu3/hooks/useCombinedRef.d.ts +2 -2
- package/dist/src/react-menu3/hooks/useItemEffect.d.ts +1 -1
- package/dist/src/react-menu3/hooks/useItemState.d.ts +4 -3
- package/dist/src/react-menu3/hooks/useItems.d.ts +6 -4
- package/dist/src/react-menu3/hooks/useMenuChange.d.ts +2 -1
- package/dist/src/react-menu3/hooks/useMenuState.d.ts +13 -10
- package/dist/src/react-menu3/hooks/useMenuStateAndFocus.d.ts +2 -10
- package/dist/src/react-menu3/index.d.ts +391 -0
- package/dist/src/react-menu3/positionUtils/getPositionHelpers.d.ts +10 -9
- package/dist/src/react-menu3/positionUtils/placeArrowHorizontal.d.ts +7 -6
- package/dist/src/react-menu3/positionUtils/placeArrowVertical.d.ts +7 -6
- package/dist/src/react-menu3/positionUtils/placeLeftorRight.d.ts +17 -19
- package/dist/src/react-menu3/positionUtils/placeToporBottom.d.ts +18 -20
- package/dist/src/react-menu3/positionUtils/positionContextMenu.d.ts +11 -6
- package/dist/src/react-menu3/positionUtils/positionMenu.d.ts +23 -18
- package/dist/src/react-menu3/utils/constants.d.ts +51 -9
- package/dist/src/react-menu3/utils/propTypes.d.ts +1 -0
- package/dist/src/react-menu3/utils/utils.d.ts +19 -10
- package/dist/src/react-menu3/utils/withHovering.d.ts +7 -1
- package/dist/src/stories/components/ReactMenu.stories.d.ts +8 -0
- package/dist/step-ag-grid.esm.js +197 -165
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +11 -11
- package/src/components/gridForm/GridFormDropDown.tsx +1 -1
- package/src/components/gridForm/GridFormMultiSelect.tsx +10 -4
- package/src/react-menu3/components/ControlledMenu.tsx +2 -1
- package/src/react-menu3/components/FocusableItem.tsx +58 -53
- package/src/react-menu3/components/Menu.tsx +40 -28
- package/src/react-menu3/components/MenuButton.tsx +30 -29
- package/src/react-menu3/components/MenuDivider.tsx +14 -18
- package/src/react-menu3/components/MenuGroup.tsx +25 -16
- package/src/react-menu3/components/MenuHeader.tsx +14 -18
- package/src/react-menu3/components/MenuItem.tsx +159 -106
- package/src/react-menu3/components/MenuList.tsx +104 -36
- package/src/react-menu3/components/MenuRadioGroup.tsx +54 -15
- package/src/react-menu3/components/SubMenu.tsx +307 -201
- package/src/react-menu3/hooks/useBEM.ts +4 -3
- package/src/react-menu3/hooks/useCombinedRef.ts +6 -12
- package/src/react-menu3/hooks/useItemEffect.ts +6 -5
- package/src/react-menu3/hooks/useItemState.ts +16 -12
- package/src/react-menu3/hooks/useItems.ts +11 -10
- package/src/react-menu3/hooks/useMenuChange.ts +3 -3
- package/src/react-menu3/hooks/useMenuState.ts +25 -7
- package/src/react-menu3/hooks/useMenuStateAndFocus.ts +6 -4
- package/src/react-menu3/index.d.ts +1 -0
- package/src/react-menu3/index.ts +431 -0
- package/src/react-menu3/positionUtils/getPositionHelpers.ts +18 -11
- package/src/react-menu3/positionUtils/placeArrowHorizontal.ts +12 -5
- package/src/react-menu3/positionUtils/placeArrowVertical.ts +12 -5
- package/src/react-menu3/positionUtils/placeLeftorRight.ts +33 -17
- package/src/react-menu3/positionUtils/placeToporBottom.ts +34 -18
- package/src/react-menu3/positionUtils/positionContextMenu.ts +15 -3
- package/src/react-menu3/positionUtils/positionMenu.ts +21 -14
- package/src/react-menu3/style-utils/index.ts +10 -7
- package/src/react-menu3/utils/constants.ts +63 -10
- package/src/react-menu3/utils/propTypes.ts +3 -1
- package/src/react-menu3/utils/utils.ts +40 -23
- package/src/react-menu3/utils/withHovering.tsx +11 -6
- package/src/stories/components/ReactMenu.stories.tsx +37 -0
|
@@ -1,130 +1,183 @@
|
|
|
1
|
-
|
|
2
|
-
import { useContext, useMemo } from "react";
|
|
3
|
-
import { any, string, bool, func, node, oneOf, oneOfType } from "prop-types";
|
|
1
|
+
import { Ref, useContext, useMemo } from "react";
|
|
4
2
|
import { useBEM, useItemState, useCombinedRef } from "../hooks";
|
|
5
3
|
import {
|
|
6
4
|
mergeProps,
|
|
7
5
|
commonProps,
|
|
8
6
|
safeCall,
|
|
9
|
-
stylePropTypes,
|
|
10
7
|
menuClass,
|
|
11
8
|
menuItemClass,
|
|
12
9
|
withHovering,
|
|
13
10
|
EventHandlersContext,
|
|
14
11
|
RadioGroupContext,
|
|
15
12
|
Keys,
|
|
13
|
+
RMEvent,
|
|
16
14
|
} from "../utils";
|
|
15
|
+
import { BaseProps, ClickEvent, EventHandler, Hoverable, MenuItemTypeProp, RenderProp } from "../index";
|
|
16
|
+
import { withHoveringResultProps } from "../utils/withHovering";
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
18
|
+
//
|
|
19
|
+
// MenuItem
|
|
20
|
+
// ----------------------------------------------------------------------
|
|
21
|
+
export type MenuItemModifiers = Readonly<{
|
|
22
|
+
/**
|
|
23
|
+
* 'radio' for radio item, 'checkbox' for checkbox item, or `undefined` for other items.
|
|
24
|
+
*/
|
|
25
|
+
type?: MenuItemTypeProp;
|
|
26
|
+
/**
|
|
27
|
+
* Indicates if the menu item is disabled.
|
|
28
|
+
*/
|
|
29
|
+
disabled: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Indicates if the menu item is being hovered and has focus.
|
|
32
|
+
*/
|
|
33
|
+
hover: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Indicates if the menu item is checked when it's a radio or checkbox item.
|
|
36
|
+
*/
|
|
37
|
+
checked: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Indicates if the menu item has a URL link.
|
|
40
|
+
*/
|
|
41
|
+
anchor: boolean;
|
|
42
|
+
}>;
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
export interface MenuItemProps extends Omit<BaseProps<MenuItemModifiers>, "onClick">, Hoverable {
|
|
45
|
+
/**
|
|
46
|
+
* Any value provided to this prop will be available in the event object of click events.
|
|
47
|
+
*
|
|
48
|
+
* It's useful for helping identify which menu item is clicked when you
|
|
49
|
+
* listen on `onItemClick` event on root menu component.
|
|
50
|
+
*/
|
|
51
|
+
value?: any;
|
|
52
|
+
/**
|
|
53
|
+
* If provided, menu item renders an HTML `<a>` element with this `href` attribute.
|
|
54
|
+
*/
|
|
55
|
+
href?: string;
|
|
56
|
+
rel?: string;
|
|
57
|
+
target?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Set this prop to make the item a checkbox or radio menu item.
|
|
60
|
+
*/
|
|
61
|
+
type?: MenuItemTypeProp;
|
|
62
|
+
/**
|
|
63
|
+
* Set `true` if a checkbox menu item is checked.
|
|
64
|
+
*
|
|
65
|
+
* *Please note radio menu item doesn't use this prop.*
|
|
66
|
+
*/
|
|
67
|
+
checked?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Event fired when the menu item is clicked.
|
|
70
|
+
*/
|
|
71
|
+
onClick?: EventHandler<ClickEvent>;
|
|
72
|
+
/**
|
|
73
|
+
* Any valid React node or a render function that returns one.
|
|
74
|
+
*/
|
|
75
|
+
children?: RenderProp<MenuItemModifiers>;
|
|
76
|
+
}
|
|
49
77
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
78
|
+
const MenuItemFr = ({
|
|
79
|
+
className,
|
|
80
|
+
value,
|
|
81
|
+
href,
|
|
82
|
+
type,
|
|
83
|
+
checked,
|
|
84
|
+
disabled,
|
|
85
|
+
children,
|
|
86
|
+
onClick,
|
|
87
|
+
isHovering,
|
|
88
|
+
menuItemRef,
|
|
89
|
+
externalRef,
|
|
90
|
+
...restProps
|
|
91
|
+
}: MenuItemProps & withHoveringResultProps) => {
|
|
92
|
+
const isDisabled = !!disabled;
|
|
93
|
+
const { setHover, ...restStateProps } = useItemState(menuItemRef, menuItemRef, isHovering, isDisabled);
|
|
94
|
+
const eventHandlers = useContext(EventHandlersContext);
|
|
95
|
+
const radioGroup = useContext(RadioGroupContext);
|
|
96
|
+
const isRadio = type === "radio";
|
|
97
|
+
const isCheckBox = type === "checkbox";
|
|
98
|
+
const isAnchor = !!href && !isDisabled && !isRadio && !isCheckBox;
|
|
99
|
+
const isChecked = isRadio ? radioGroup.value === value : isCheckBox ? !!checked : false;
|
|
58
100
|
|
|
59
|
-
|
|
60
|
-
|
|
101
|
+
// FIXME handle click seems to be a combination of multiple event types
|
|
102
|
+
const handleClick = (e: any) => {
|
|
103
|
+
if (isDisabled) {
|
|
104
|
+
e.stopPropagation();
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
61
108
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (isAnchor) {
|
|
66
|
-
itemRef.current.click();
|
|
67
|
-
} else {
|
|
68
|
-
handleClick(e);
|
|
69
|
-
}
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
109
|
+
const event: RMEvent = {
|
|
110
|
+
value,
|
|
111
|
+
syntheticEvent: e,
|
|
72
112
|
};
|
|
113
|
+
if (e.key !== undefined) event.key = e.key;
|
|
114
|
+
if (isCheckBox) event.checked = !isChecked;
|
|
115
|
+
if (isRadio) event.name = radioGroup.name;
|
|
116
|
+
safeCall(onClick, event);
|
|
117
|
+
if (isRadio) safeCall(radioGroup.onRadioChange, event);
|
|
118
|
+
eventHandlers.handleClick(event, isCheckBox || isRadio);
|
|
119
|
+
};
|
|
73
120
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
type,
|
|
77
|
-
disabled: isDisabled,
|
|
78
|
-
hover: isHovering,
|
|
79
|
-
checked: isChecked,
|
|
80
|
-
anchor: isAnchor,
|
|
81
|
-
}),
|
|
82
|
-
[type, isDisabled, isHovering, isChecked, isAnchor],
|
|
83
|
-
);
|
|
121
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
122
|
+
if (!isHovering) return;
|
|
84
123
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
124
|
+
switch (e.key) {
|
|
125
|
+
case Keys.ENTER:
|
|
126
|
+
case Keys.SPACE:
|
|
127
|
+
if (isAnchor) {
|
|
128
|
+
menuItemRef?.current && menuItemRef.current.click();
|
|
129
|
+
} else {
|
|
130
|
+
handleClick(e);
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
94
135
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
className: useBEM({ block: menuClass, element: menuItemClass, modifiers, className }),
|
|
106
|
-
children: useMemo(() => safeCall(children, modifiers), [children, modifiers]),
|
|
107
|
-
};
|
|
136
|
+
const modifiers = useMemo(
|
|
137
|
+
() => ({
|
|
138
|
+
type,
|
|
139
|
+
disabled: isDisabled,
|
|
140
|
+
hover: isHovering,
|
|
141
|
+
checked: isChecked,
|
|
142
|
+
anchor: isAnchor,
|
|
143
|
+
}),
|
|
144
|
+
[type, isDisabled, isHovering, isChecked, isAnchor],
|
|
145
|
+
);
|
|
108
146
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
)
|
|
147
|
+
const mergedProps = mergeProps(
|
|
148
|
+
{
|
|
149
|
+
...restStateProps,
|
|
150
|
+
onPointerDown: setHover,
|
|
151
|
+
onKeyDown: handleKeyDown,
|
|
152
|
+
onClick: handleClick,
|
|
153
|
+
},
|
|
154
|
+
restProps,
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// Order of props overriding (same in all components):
|
|
158
|
+
// 1. Preset props adhering to WAI-ARIA Authoring Practices.
|
|
159
|
+
// 2. Merged outer and local props
|
|
160
|
+
// 3. ref, className
|
|
161
|
+
const menuItemProps = {
|
|
162
|
+
role: isRadio ? "menuitemradio" : isCheckBox ? "menuitemcheckbox" : "menuitem",
|
|
163
|
+
"aria-checked": isRadio || isCheckBox ? isChecked : undefined,
|
|
164
|
+
...mergedProps,
|
|
165
|
+
...commonProps(isDisabled, isHovering),
|
|
166
|
+
ref: useCombinedRef(externalRef as Ref<any>, menuItemRef),
|
|
167
|
+
className: useBEM({ block: menuClass, element: menuItemClass, modifiers, className }),
|
|
168
|
+
children: useMemo(() => safeCall(children, modifiers), [children, modifiers]),
|
|
169
|
+
};
|
|
120
170
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
171
|
+
if (isAnchor) {
|
|
172
|
+
return (
|
|
173
|
+
<li role="presentation">
|
|
174
|
+
<a href={href} {...menuItemProps} />
|
|
175
|
+
</li>
|
|
176
|
+
);
|
|
177
|
+
} else {
|
|
178
|
+
return <li {...menuItemProps} />;
|
|
179
|
+
}
|
|
130
180
|
};
|
|
181
|
+
|
|
182
|
+
// FIXME matt as any
|
|
183
|
+
export const MenuItem = withHovering("MenuItem", MenuItemFr) as any as typeof MenuItemFr;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { useState, useReducer, useEffect, useRef, useMemo, useCallback, useContext } from "react";
|
|
1
|
+
import { useState, useReducer, useEffect, useRef, useMemo, useCallback, useContext, MutableRefObject } from "react";
|
|
3
2
|
import { flushSync } from "react-dom";
|
|
4
3
|
import { useBEM, useCombinedRef, useLayoutEffect, useItems } from "../hooks";
|
|
5
4
|
import { getPositionHelpers, positionMenu, positionContextMenu } from "../positionUtils";
|
|
@@ -23,6 +22,65 @@ import {
|
|
|
23
22
|
MenuListItemContext,
|
|
24
23
|
HoverItemContext,
|
|
25
24
|
} from "../utils";
|
|
25
|
+
import { EventHandler, FocusPosition, MenuCloseEvent, MenuDirection, MenuState, RootMenuProps } from "../index";
|
|
26
|
+
|
|
27
|
+
interface ExtraMenuProps {
|
|
28
|
+
isDisabled?: boolean;
|
|
29
|
+
ariaLabel?: string;
|
|
30
|
+
containerRef: MutableRefObject<HTMLElement>;
|
|
31
|
+
externalRef: MutableRefObject<HTMLUListElement>;
|
|
32
|
+
parentScrollingRef: MutableRefObject<any>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//
|
|
36
|
+
// ControlledMenu
|
|
37
|
+
// ----------------------------------------------------------------------
|
|
38
|
+
export interface ControlledMenuProps extends RootMenuProps, ExtraMenuProps {
|
|
39
|
+
/**
|
|
40
|
+
* Viewport coordinates to which context menu will be positioned.
|
|
41
|
+
*
|
|
42
|
+
* *Use this prop only for context menu*
|
|
43
|
+
*/
|
|
44
|
+
anchorPoint?: {
|
|
45
|
+
x: number;
|
|
46
|
+
y: number;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* A ref object attached to a DOM element to which menu will be positioned.
|
|
50
|
+
*
|
|
51
|
+
* *Don't set this prop for context menu*
|
|
52
|
+
*/
|
|
53
|
+
anchorRef?: React.MutableRefObject<HTMLElement>;
|
|
54
|
+
skipOpen?: React.RefObject<boolean>;
|
|
55
|
+
/**
|
|
56
|
+
* If `true`, the menu list element will gain focus after menu is open.
|
|
57
|
+
* @default true
|
|
58
|
+
*/
|
|
59
|
+
captureFocus?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Controls the state of menu. When the prop is `undefined`, menu will be unmounted from DOM.
|
|
62
|
+
*/
|
|
63
|
+
state?: MenuState;
|
|
64
|
+
/**
|
|
65
|
+
* Sets which menu item receives focus (hover) when menu opens.
|
|
66
|
+
* You will usually set this prop when the menu is opened by keyboard events.
|
|
67
|
+
*
|
|
68
|
+
* *Note: If you don't intend to update focus (hover) position,
|
|
69
|
+
* it's important to keep this prop's identity stable when your component re-renders.*
|
|
70
|
+
*/
|
|
71
|
+
menuItemFocus?: {
|
|
72
|
+
position?: FocusPosition;
|
|
73
|
+
alwaysUpdate?: boolean;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Set the return value of `useMenuState` to this prop.
|
|
77
|
+
*/
|
|
78
|
+
endTransition?: () => void;
|
|
79
|
+
/**
|
|
80
|
+
* Event fired when menu is about to close.
|
|
81
|
+
*/
|
|
82
|
+
onClose?: EventHandler<MenuCloseEvent>;
|
|
83
|
+
}
|
|
26
84
|
|
|
27
85
|
export const MenuList = ({
|
|
28
86
|
ariaLabel,
|
|
@@ -52,10 +110,10 @@ export const MenuList = ({
|
|
|
52
110
|
children,
|
|
53
111
|
onClose,
|
|
54
112
|
...restProps
|
|
55
|
-
}) => {
|
|
113
|
+
}: ControlledMenuProps) => {
|
|
56
114
|
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
|
|
57
|
-
const [arrowPosition, setArrowPosition] = useState({});
|
|
58
|
-
const [overflowData, setOverflowData] = useState();
|
|
115
|
+
const [arrowPosition, setArrowPosition] = useState({ x: 0, y: 0 });
|
|
116
|
+
const [overflowData, setOverflowData] = useState<{ height: number; overflowAmt: number | undefined }>();
|
|
59
117
|
const [expandedDirection, setExpandedDirection] = useState(direction);
|
|
60
118
|
const [openSubmenuCount, setOpenSubmenuCount] = useState(0);
|
|
61
119
|
const [reposSubmenu, forceReposSubmenu] = useReducer((c) => c + 1, 1);
|
|
@@ -70,9 +128,9 @@ export const MenuList = ({
|
|
|
70
128
|
viewScroll,
|
|
71
129
|
} = useContext(SettingsContext);
|
|
72
130
|
const reposFlag = useContext(MenuListContext).reposSubmenu || repositionFlag;
|
|
73
|
-
const menuRef = useRef(
|
|
74
|
-
const focusRef = useRef();
|
|
75
|
-
const arrowRef = useRef();
|
|
131
|
+
const menuRef = useRef<HTMLUListElement>({} as HTMLUListElement);
|
|
132
|
+
const focusRef = useRef<HTMLDivElement>(null);
|
|
133
|
+
const arrowRef = useRef<HTMLDivElement>(null);
|
|
76
134
|
const prevOpen = useRef(false);
|
|
77
135
|
const latestMenuSize = useRef({ width: 0, height: 0 });
|
|
78
136
|
const latestHandlePosition = useRef(() => {});
|
|
@@ -83,34 +141,37 @@ export const MenuList = ({
|
|
|
83
141
|
const closeTransition = getTransition(transition, "close");
|
|
84
142
|
const scrollNodes = scrollNodesRef.current;
|
|
85
143
|
|
|
86
|
-
const onKeyDown = (e) => {
|
|
144
|
+
const onKeyDown = (e: React.KeyboardEvent<HTMLElement>) => {
|
|
145
|
+
const elementTarget = e.target instanceof HTMLElement ? (e.target as HTMLElement) : null;
|
|
87
146
|
const isTextInputTarget =
|
|
88
|
-
|
|
147
|
+
elementTarget &&
|
|
148
|
+
(elementTarget.nodeName === "TEXTAREA" ||
|
|
149
|
+
(elementTarget.nodeName === "INPUT" && elementTarget.getAttribute("type") === "text"));
|
|
89
150
|
switch (e.key) {
|
|
90
151
|
case Keys.HOME:
|
|
91
152
|
// Don't eat home/end events on inputs
|
|
92
153
|
if (isTextInputTarget) return;
|
|
93
|
-
dispatch(HoverActionTypes.FIRST);
|
|
154
|
+
dispatch(HoverActionTypes.FIRST, null, 0);
|
|
94
155
|
break;
|
|
95
156
|
|
|
96
157
|
case Keys.END:
|
|
97
158
|
// Don't eat home/end events on inputs
|
|
98
159
|
if (isTextInputTarget) return;
|
|
99
|
-
dispatch(HoverActionTypes.LAST);
|
|
160
|
+
dispatch(HoverActionTypes.LAST, null, 0);
|
|
100
161
|
break;
|
|
101
162
|
|
|
102
163
|
case Keys.UP:
|
|
103
|
-
dispatch(HoverActionTypes.DECREASE, hoverItem);
|
|
164
|
+
dispatch(HoverActionTypes.DECREASE, hoverItem, 0);
|
|
104
165
|
break;
|
|
105
166
|
|
|
106
167
|
case Keys.DOWN:
|
|
107
|
-
dispatch(HoverActionTypes.INCREASE, hoverItem);
|
|
168
|
+
dispatch(HoverActionTypes.INCREASE, hoverItem, 0);
|
|
108
169
|
break;
|
|
109
170
|
|
|
110
171
|
// prevent browser from scrolling the page when SPACE is pressed
|
|
111
172
|
case Keys.SPACE:
|
|
112
173
|
// Don't preventDefault on children of FocusableItem
|
|
113
|
-
if (
|
|
174
|
+
if (elementTarget && elementTarget.className.includes(menuClass)) {
|
|
114
175
|
e.preventDefault();
|
|
115
176
|
}
|
|
116
177
|
return;
|
|
@@ -125,14 +186,14 @@ export const MenuList = ({
|
|
|
125
186
|
|
|
126
187
|
const onAnimationEnd = () => {
|
|
127
188
|
if (state === "closing") {
|
|
128
|
-
setOverflowData(); // reset overflowData after closing
|
|
189
|
+
setOverflowData(undefined); // reset overflowData after closing
|
|
129
190
|
}
|
|
130
191
|
|
|
131
|
-
safeCall(endTransition);
|
|
192
|
+
endTransition && safeCall(endTransition);
|
|
132
193
|
};
|
|
133
194
|
|
|
134
195
|
const handlePosition = useCallback(
|
|
135
|
-
(noOverflowCheck) => {
|
|
196
|
+
(noOverflowCheck?: boolean) => {
|
|
136
197
|
if (!containerRef.current) {
|
|
137
198
|
if (process.env.NODE_ENV !== "production") {
|
|
138
199
|
console.error(
|
|
@@ -146,12 +207,16 @@ export const MenuList = ({
|
|
|
146
207
|
scrollNodes.menu =
|
|
147
208
|
(boundingBoxRef
|
|
148
209
|
? boundingBoxRef.current // user explicitly sets boundingBoxRef
|
|
149
|
-
: getScrollAncestor(rootMenuRef
|
|
210
|
+
: getScrollAncestor(rootMenuRef?.current)) || window; // try to discover bounding box automatically
|
|
150
211
|
}
|
|
151
212
|
|
|
152
213
|
const positionHelpers = getPositionHelpers(containerRef, menuRef, scrollNodes.menu, boundingBoxPadding);
|
|
153
214
|
const { menuRect } = positionHelpers;
|
|
154
|
-
let results
|
|
215
|
+
let results: { computedDirection: MenuDirection; arrowX?: number; arrowY?: number; x: number; y: number } = {
|
|
216
|
+
computedDirection: "bottom",
|
|
217
|
+
x: 0,
|
|
218
|
+
y: 0,
|
|
219
|
+
};
|
|
155
220
|
if (anchorPoint) {
|
|
156
221
|
results = positionContextMenu({ positionHelpers, anchorPoint });
|
|
157
222
|
} else if (anchorRef) {
|
|
@@ -167,13 +232,14 @@ export const MenuList = ({
|
|
|
167
232
|
positionHelpers,
|
|
168
233
|
});
|
|
169
234
|
}
|
|
170
|
-
let {
|
|
235
|
+
let { y } = results;
|
|
236
|
+
const { x, arrowX, arrowY, computedDirection } = results;
|
|
171
237
|
let menuHeight = menuRect.height;
|
|
172
238
|
|
|
173
239
|
if (!noOverflowCheck && overflow !== "visible") {
|
|
174
240
|
const { getTopOverflow, getBottomOverflow } = positionHelpers;
|
|
175
241
|
|
|
176
|
-
let height, overflowAmt;
|
|
242
|
+
let height: number | undefined, overflowAmt: number | undefined;
|
|
177
243
|
const prevHeight = latestMenuSize.current.height;
|
|
178
244
|
const bottomOverflow = getBottomOverflow(y);
|
|
179
245
|
// When bottomOverflow is 0, menu is on the bottom edge of viewport
|
|
@@ -192,16 +258,16 @@ export const MenuList = ({
|
|
|
192
258
|
}
|
|
193
259
|
}
|
|
194
260
|
|
|
195
|
-
if (height >= 0) {
|
|
261
|
+
if (height != null && height >= 0) {
|
|
196
262
|
// To avoid triggering reposition in the next ResizeObserver callback
|
|
197
263
|
menuHeight = height;
|
|
198
264
|
setOverflowData({ height, overflowAmt });
|
|
199
265
|
} else {
|
|
200
|
-
setOverflowData();
|
|
266
|
+
setOverflowData(undefined);
|
|
201
267
|
}
|
|
202
268
|
}
|
|
203
269
|
|
|
204
|
-
if (arrow) setArrowPosition({ x: arrowX, y: arrowY });
|
|
270
|
+
if (arrow) setArrowPosition({ x: arrowX ?? 0, y: arrowY ?? 0 });
|
|
205
271
|
setMenuPosition({ x, y });
|
|
206
272
|
setExpandedDirection(computedDirection);
|
|
207
273
|
latestMenuSize.current = { width: menuRect.width, height: menuHeight };
|
|
@@ -267,11 +333,11 @@ export const MenuList = ({
|
|
|
267
333
|
};
|
|
268
334
|
|
|
269
335
|
const scrollObservers = scrollNodes.anchors.concat(viewScroll !== "initial" ? menuScroll : []);
|
|
270
|
-
scrollObservers.forEach((o) => o.addEventListener("scroll", handleScroll));
|
|
336
|
+
scrollObservers.forEach((o: any) => o.addEventListener("scroll", handleScroll));
|
|
271
337
|
return () => scrollObservers.forEach((o) => o.removeEventListener("scroll", handleScroll));
|
|
272
338
|
}, [rootAnchorRef, scrollNodes, isOpen, onClose, viewScroll, handlePosition]);
|
|
273
339
|
|
|
274
|
-
const hasOverflow = !!overflowData && overflowData.overflowAmt > 0;
|
|
340
|
+
const hasOverflow = !!overflowData && overflowData.overflowAmt != null && overflowData.overflowAmt > 0;
|
|
275
341
|
useEffect(() => {
|
|
276
342
|
if (hasOverflow || !isOpen || !parentScrollingRef) return;
|
|
277
343
|
|
|
@@ -313,18 +379,18 @@ export const MenuList = ({
|
|
|
313
379
|
|
|
314
380
|
useEffect(() => {
|
|
315
381
|
if (!isOpen) {
|
|
316
|
-
dispatch(HoverActionTypes.RESET);
|
|
317
|
-
if (!closeTransition) setOverflowData();
|
|
318
|
-
return;
|
|
382
|
+
dispatch(HoverActionTypes.RESET, undefined, 0);
|
|
383
|
+
if (!closeTransition) setOverflowData(undefined);
|
|
384
|
+
return () => {};
|
|
319
385
|
}
|
|
320
386
|
|
|
321
387
|
const { position, alwaysUpdate } = menuItemFocus || {};
|
|
322
388
|
const setItemFocus = () => {
|
|
323
389
|
if (position === FocusPositions.FIRST) {
|
|
324
|
-
dispatch(HoverActionTypes.FIRST);
|
|
390
|
+
dispatch(HoverActionTypes.FIRST, undefined, 0);
|
|
325
391
|
} else if (position === FocusPositions.LAST) {
|
|
326
|
-
dispatch(HoverActionTypes.LAST);
|
|
327
|
-
} else if (position >= -1) {
|
|
392
|
+
dispatch(HoverActionTypes.LAST, undefined, 0);
|
|
393
|
+
} else if (typeof position === "number" && position >= -1) {
|
|
328
394
|
dispatch(HoverActionTypes.SET_INDEX, undefined, position);
|
|
329
395
|
}
|
|
330
396
|
};
|
|
@@ -337,7 +403,7 @@ export const MenuList = ({
|
|
|
337
403
|
() => {
|
|
338
404
|
// If focus has already been set to a children element, don't set focus on menu or item
|
|
339
405
|
if (!menuRef.current.contains(document.activeElement)) {
|
|
340
|
-
focusRef.current
|
|
406
|
+
focusRef.current?.focus();
|
|
341
407
|
setItemFocus();
|
|
342
408
|
}
|
|
343
409
|
},
|
|
@@ -346,6 +412,7 @@ export const MenuList = ({
|
|
|
346
412
|
|
|
347
413
|
return () => clearTimeout(id);
|
|
348
414
|
}
|
|
415
|
+
return () => {};
|
|
349
416
|
}, [isOpen, openTransition, closeTransition, captureFocus, menuItemFocus, dispatch]);
|
|
350
417
|
|
|
351
418
|
const isSubmenuOpen = openSubmenuCount > 0;
|
|
@@ -360,7 +427,8 @@ export const MenuList = ({
|
|
|
360
427
|
[isOpen, isSubmenuOpen, dispatch, updateItems],
|
|
361
428
|
);
|
|
362
429
|
|
|
363
|
-
let maxHeight
|
|
430
|
+
let maxHeight: number | undefined;
|
|
431
|
+
let overflowAmt: number | undefined;
|
|
364
432
|
if (overflowData) {
|
|
365
433
|
setDownOverflow ? (overflowAmt = overflowData.overflowAmt) : (maxHeight = overflowData.height);
|
|
366
434
|
}
|
|
@@ -375,7 +443,7 @@ export const MenuList = ({
|
|
|
375
443
|
}),
|
|
376
444
|
[reposSubmenu, overflow, overflowAmt, expandedDirection],
|
|
377
445
|
);
|
|
378
|
-
const overflowStyle = maxHeight >= 0 ? { maxHeight, overflow } : undefined;
|
|
446
|
+
const overflowStyle = maxHeight != null && maxHeight >= 0 ? { maxHeight, overflow } : undefined;
|
|
379
447
|
|
|
380
448
|
const modifiers = useMemo(
|
|
381
449
|
() => ({
|
|
@@ -1,13 +1,57 @@
|
|
|
1
|
-
|
|
2
|
-
import { forwardRef, useMemo } from "react";
|
|
3
|
-
import { any, string, func } from "prop-types";
|
|
1
|
+
import { ForwardedRef, forwardRef, ReactNode, useMemo } from "react";
|
|
4
2
|
import { useBEM } from "../hooks";
|
|
5
|
-
import {
|
|
3
|
+
import { menuClass, radioGroupClass, RadioGroupContext } from "../utils";
|
|
4
|
+
import { BaseProps, Event, EventHandler } from "../index";
|
|
6
5
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
export interface RadioChangeEvent extends Event {
|
|
7
|
+
/**
|
|
8
|
+
* The `name` prop passed to the `MenuRadioGroup` when the menu item is in a radio group.
|
|
9
|
+
*/
|
|
10
|
+
name?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Set this property on event object to control whether to keep menu open after menu item is activated.
|
|
13
|
+
* Leaving it `undefined` will behave in accordance with WAI-ARIA Authoring Practices.
|
|
14
|
+
*/
|
|
15
|
+
keepOpen?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Setting this property on event object to `true` will skip `onItemClick` event on root menu component.
|
|
18
|
+
*/
|
|
19
|
+
stopPropagation?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* DOM event object (React synthetic event)
|
|
22
|
+
*/
|
|
23
|
+
syntheticEvent: MouseEvent | KeyboardEvent;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//
|
|
27
|
+
// MenuRadioGroup
|
|
28
|
+
// ----------------------------------------------------------------------
|
|
29
|
+
export interface MenuRadioGroupProps extends BaseProps {
|
|
30
|
+
/**
|
|
31
|
+
* Optionally set the radio group name.
|
|
32
|
+
*
|
|
33
|
+
* The name will be passed to the `onRadioChange` event.
|
|
34
|
+
* It's useful for identifying radio groups if you attach the same event handler to multiple groups.
|
|
35
|
+
*/
|
|
36
|
+
name?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Set value of the radio group.
|
|
39
|
+
*
|
|
40
|
+
* The child menu item which has the same value (strict equality ===) as the
|
|
41
|
+
* radio group is marked as checked.
|
|
42
|
+
*/
|
|
43
|
+
value?: any;
|
|
44
|
+
children?: ReactNode;
|
|
45
|
+
/**
|
|
46
|
+
* Event fired when a child menu item is clicked (selected).
|
|
47
|
+
*/
|
|
48
|
+
onRadioChange?: EventHandler<RadioChangeEvent>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const MenuRadioGroupFr = (
|
|
52
|
+
{ "aria-label": ariaLabel, className, name, value, onRadioChange, ...restProps }: MenuRadioGroupProps,
|
|
53
|
+
externalRef: ForwardedRef<HTMLUListElement>,
|
|
54
|
+
) => {
|
|
11
55
|
const contextValue = useMemo(() => ({ name, value, onRadioChange }), [name, value, onRadioChange]);
|
|
12
56
|
|
|
13
57
|
return (
|
|
@@ -23,11 +67,6 @@ export const MenuRadioGroup = forwardRef(function MenuRadioGroup(
|
|
|
23
67
|
</li>
|
|
24
68
|
</RadioGroupContext.Provider>
|
|
25
69
|
);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
MenuRadioGroup.propTypes = {
|
|
29
|
-
...stylePropTypes(),
|
|
30
|
-
name: string,
|
|
31
|
-
value: any,
|
|
32
|
-
onRadioChange: func,
|
|
33
70
|
};
|
|
71
|
+
|
|
72
|
+
export const MenuRadioGroup = forwardRef(MenuRadioGroupFr) as any as typeof MenuRadioGroupFr;
|