Skip to content

Commit 83b1306

Browse files
authoredApr 26, 2022
fix(Menu): 修复当嵌套多个子菜单时,会有遮住子菜单问题 # 779 (#797)
1 parent 971dc8b commit 83b1306

File tree

2 files changed

+91
-44
lines changed

2 files changed

+91
-44
lines changed
 

‎packages/react-menu/src/Menu.tsx

+24-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useMemo } from 'react';
1+
import React, { useMemo, createContext } from 'react';
22
import { IProps, HTMLUlProps } from '@uiw/utils';
33
import { MenuItem } from './MenuItem';
44
import { MenuDivider } from './Divider';
@@ -19,8 +19,17 @@ export interface MenuProps extends IProps, HTMLUlProps {
1919
inlineIndent?: number;
2020
bordered?: boolean;
2121
}
22+
interface MenuContextType {
23+
height: number;
24+
ele: EventTarget | null;
25+
}
26+
export const ThemeContext = createContext(
27+
{} as MenuContextType & {
28+
setContextHeight: React.Dispatch<React.SetStateAction<MenuContextType>>;
29+
},
30+
);
2231

23-
const Menu = React.forwardRef<HTMLUListElement, MenuProps>((props, ref) => {
32+
export const Menu = React.forwardRef<HTMLUListElement, MenuProps>((props, ref) => {
2433
const {
2534
prefixCls = 'w-menu',
2635
className,
@@ -45,7 +54,6 @@ const Menu = React.forwardRef<HTMLUListElement, MenuProps>((props, ref) => {
4554
.trim(),
4655
[prefixCls, bordered, inlineCollapsed, theme, className],
4756
);
48-
4957
return (
5058
<ul {...htmlProps} ref={ref} className={cls} data-menu="menu">
5159
{React.Children.map(children, (child: React.ReactNode, key) => {
@@ -61,16 +69,24 @@ const Menu = React.forwardRef<HTMLUListElement, MenuProps>((props, ref) => {
6169
);
6270
});
6371

72+
export const ContextMenu = React.forwardRef<HTMLUListElement, MenuProps>((props, ref) => {
73+
const [contextHeight, setContextHeight] = React.useState<MenuContextType>({ height: 0, ele: null });
74+
return (
75+
<ThemeContext.Provider value={{ ...contextHeight, setContextHeight }}>
76+
<Menu {...props} ref={ref} />
77+
</ThemeContext.Provider>
78+
);
79+
});
6480
Menu.displayName = 'uiw.Menu';
81+
ContextMenu.displayName = 'uiw.Menu';
6582

6683
type Menu = typeof Menu & {
6784
Item: typeof MenuItem;
6885
SubMenu: typeof SubMenu;
6986
Divider: typeof MenuDivider;
7087
};
7188

72-
(Menu as Menu).Item = MenuItem;
73-
(Menu as Menu).SubMenu = SubMenu;
74-
(Menu as Menu).Divider = MenuDivider;
75-
76-
export default Menu as Menu;
89+
(ContextMenu as Menu).Item = MenuItem;
90+
(ContextMenu as Menu).SubMenu = SubMenu;
91+
(ContextMenu as Menu).Divider = MenuDivider;
92+
export default ContextMenu as Menu;

‎packages/react-menu/src/SubMenu.tsx

+67-36
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import React, { useMemo, useState } from 'react';
1+
import React, { useMemo, useState, useContext } from 'react';
22
import { CSSTransitionProps } from 'react-transition-group/CSSTransition';
33
import OverlayTrigger, { OverlayTriggerProps, OverlayTriggerRef } from '@uiw/react-overlay-trigger';
44
import Icon from '@uiw/react-icon';
55
import { IProps } from '@uiw/utils';
66
import { MenuItem, MenuItemProps, TagType } from './MenuItem';
7-
import Menu, { MenuProps } from './Menu';
7+
import { MenuProps, Menu, ThemeContext } from './Menu';
88
import './style/submenu.less';
99

1010
export interface SubMenuProps<T extends TagType> extends IProps, MenuItemProps<T> {
@@ -49,6 +49,7 @@ function IconView({ prefixCls, collapse, isOpen }: { prefixCls?: string; collaps
4949
[prefixCls, collapse, isOpen],
5050
);
5151
}
52+
5253
export const SubMenu = React.forwardRef(function <Tag extends TagType = 'a'>(
5354
props: SubMenuProps<Tag>,
5455
ref: React.Ref<HTMLLIElement>,
@@ -72,7 +73,20 @@ export const SubMenu = React.forwardRef(function <Tag extends TagType = 'a'>(
7273
className: [prefixCls ? `${prefixCls}-overlay` : null].filter(Boolean).join(' ').trim(),
7374
};
7475
const popupRef = React.useRef<OverlayTriggerRef>(null);
76+
const refNode = React.useRef<HTMLElement | null>();
77+
const currentHeight = React.useRef<number>(0);
78+
const elementSource = React.useRef<EventTarget | null>();
7579
const [isOpen, setIsOpen] = useState(!!overlayProps.isOpen);
80+
const { height, setContextHeight, ele } = useContext(ThemeContext);
81+
82+
React.useEffect(() => {
83+
if (refNode.current && refNode.current.style && ele === elementSource.current) {
84+
const currentHeight = refNode.current!.style.height;
85+
if (height + 'px' === currentHeight) return;
86+
refNode.current!.style.height = Number(currentHeight.substr(0, currentHeight.length - 2)) + height + 'px';
87+
}
88+
}, [height]);
89+
7690
useMemo(() => {
7791
if (collapse) setIsOpen(false);
7892
}, [collapse]);
@@ -93,21 +107,28 @@ export const SubMenu = React.forwardRef(function <Tag extends TagType = 'a'>(
93107
}
94108
function onExiting(node: HTMLElement) {
95109
node.style.height = '0px';
110+
setContextHeight({
111+
height: -currentHeight.current,
112+
ele: elementSource.current!,
113+
});
96114
}
97115
function onEnter(node: HTMLElement) {
98116
node.style.height = '1px';
99117
setIsOpen(true);
118+
currentHeight.current = popupRef.current!.overlayDom.current!.getBoundingClientRect().height;
119+
setContextHeight({
120+
height: currentHeight.current,
121+
ele: elementSource.current!,
122+
});
100123
}
101124
function onEntering(node: HTMLElement) {
102125
node.style.height = `${node.scrollHeight}px`;
103126
}
104127
function onEntered(node: HTMLElement) {
105-
node.style.height = 'initial';
106-
if (popupRef.current && popupRef.current.overlayDom) {
107-
node.style.height = popupRef.current.overlayDom.current!.getBoundingClientRect().height + 'px';
108-
}
128+
// node.style.height = 'initial';
129+
node.style.height = currentHeight.current + 'px';
130+
refNode.current = node;
109131
}
110-
111132
if (!collapse) {
112133
delete menuProps.onClick;
113134
menuProps.bordered = false;
@@ -130,36 +151,46 @@ export const SubMenu = React.forwardRef(function <Tag extends TagType = 'a'>(
130151
menuProps.onClick = onClick;
131152
}
132153
return (
133-
<li data-menu="subitem" ref={ref}>
134-
<OverlayTrigger
135-
placement="rightTop"
136-
autoAdjustOverflow
137-
disabled={disabled}
138-
isOpen={isOpen}
139-
usePortal={false}
140-
isOutside
141-
{...overlayTriggerProps}
142-
{...overlayProps}
143-
ref={popupRef}
144-
overlay={<Menu {...menuProps} style={!collapse ? { paddingLeft: inlineIndent } : {}} />}
145-
>
146-
<MenuItem
147-
{...other}
148-
ref={null}
154+
<div
155+
onClick={(e) => {
156+
if (collapse) {
157+
e.stopPropagation();
158+
return;
159+
}
160+
elementSource.current = e.target;
161+
}}
162+
>
163+
<li data-menu="subitem" ref={ref}>
164+
<OverlayTrigger
165+
placement="rightTop"
166+
autoAdjustOverflow
149167
disabled={disabled}
150-
isSubMenuItem
151-
addonAfter={<IconView collapse={collapse} prefixCls={prefixCls} isOpen={isOpen} />}
152-
className={[
153-
prefixCls ? `${prefixCls}-title` : null,
154-
!collapse ? `${prefixCls}-collapse-title` : null,
155-
className,
156-
]
157-
.filter(Boolean)
158-
.join(' ')
159-
.trim()}
160-
/>
161-
</OverlayTrigger>
162-
</li>
168+
isOpen={isOpen}
169+
usePortal={false}
170+
isOutside
171+
{...overlayTriggerProps}
172+
{...overlayProps}
173+
ref={popupRef}
174+
overlay={<Menu {...menuProps} style={!collapse ? { paddingLeft: inlineIndent } : {}} />}
175+
>
176+
<MenuItem
177+
{...other}
178+
ref={null}
179+
disabled={disabled}
180+
isSubMenuItem
181+
addonAfter={<IconView collapse={collapse} prefixCls={prefixCls} isOpen={isOpen} />}
182+
className={[
183+
prefixCls ? `${prefixCls}-title` : null,
184+
!collapse ? `${prefixCls}-collapse-title` : null,
185+
className,
186+
]
187+
.filter(Boolean)
188+
.join(' ')
189+
.trim()}
190+
/>
191+
</OverlayTrigger>
192+
</li>
193+
</div>
163194
);
164195
});
165196

0 commit comments

Comments
 (0)
Please sign in to comment.