Skip to content

Commit adbfd23

Browse files
authored
feat: support items (#438)
* chore: popout interface * chore: popup convert * docs: demo update * chore: fix compile * test: add test case * test: coverage * test: covearege * refactor: to items
1 parent 5aeca9e commit adbfd23

File tree

13 files changed

+381
-90
lines changed

13 files changed

+381
-90
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ module.exports = {
1515
'@typescript-eslint/no-explicit-any': 0,
1616
'jsx-a11y/label-has-associated-control': 0,
1717
'jsx-a11y/label-has-for': 0,
18+
'@typescript-eslint/no-empty-interface': "off",
1819
},
1920
};

docs/demo/options.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## options
2+
3+
<code src="../examples/options.tsx">

docs/examples/options.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/* eslint no-console:0 */
2+
3+
import React from 'react';
4+
import Menu from '../../src';
5+
import '../../assets/index.less';
6+
7+
export default () => (
8+
<Menu
9+
options={[
10+
{
11+
// MenuItem
12+
title: 'Top Menu Item',
13+
key: 'top',
14+
},
15+
{
16+
// MenuGroup
17+
type: 'group',
18+
title: 'Top Menu Group without children',
19+
},
20+
{
21+
// MenuGroup
22+
type: 'group',
23+
title: 'Top Menu Group with children',
24+
children: [
25+
{
26+
// MenuItem
27+
title: 'Menu Item 1',
28+
key: 'inner1',
29+
},
30+
{
31+
// Divider
32+
type: 'divider',
33+
},
34+
{
35+
// MenuItem
36+
title: 'Menu Item 2',
37+
key: 'inner2',
38+
},
39+
],
40+
},
41+
{
42+
// SubMenu
43+
title: 'SubMenu',
44+
key: 'sub1',
45+
children: [
46+
{
47+
// MenuItem
48+
title: 'Menu Item 1-1',
49+
key: 'inner11',
50+
},
51+
52+
{
53+
// SubMenu
54+
title: 'SubMenu inner',
55+
key: 'sub1-1',
56+
children: [
57+
{
58+
// MenuItem
59+
title: 'Menu Item 111',
60+
key: 'inner1-1-1',
61+
},
62+
],
63+
},
64+
],
65+
},
66+
]}
67+
/>
68+
);

src/Divider.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ import * as React from 'react';
22
import classNames from 'classnames';
33
import { MenuContext } from './context/MenuContext';
44
import { useMeasure } from './context/PathContext';
5+
import type { MenuDividerType } from './interface';
56

6-
export interface DividerProps {
7-
className?: string;
8-
style?: React.CSSProperties;
9-
}
7+
export type DividerProps = Omit<MenuDividerType, 'type'>;
108

119
export default function Divider({ className, style }: DividerProps) {
1210
const { prefixCls } = React.useContext(MenuContext);

src/Menu.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ import type {
1414
TriggerSubMenuAction,
1515
SelectInfo,
1616
RenderIconType,
17+
ItemType,
1718
} from './interface';
1819
import MenuItem from './MenuItem';
19-
import { parseChildren } from './utils/nodeUtil';
20+
import { parseItems } from './utils/nodeUtil';
2021
import MenuContextProvider from './context/MenuContext';
2122
import useMemoCallback from './hooks/useMemoCallback';
2223
import { warnItemProp } from './utils/warnUtil';
@@ -52,6 +53,8 @@ export interface MenuProps
5253
> {
5354
prefixCls?: string;
5455

56+
items?: ItemType[];
57+
/** @deprecated Please use `items` instead */
5558
children?: React.ReactNode;
5659

5760
disabled?: boolean;
@@ -152,6 +155,7 @@ const Menu = React.forwardRef<HTMLUListElement, MenuProps>((props, ref) => {
152155
style,
153156
className,
154157
tabIndex = 0,
158+
items,
155159
children,
156160
direction,
157161

@@ -220,7 +224,11 @@ const Menu = React.forwardRef<HTMLUListElement, MenuProps>((props, ref) => {
220224
...restProps
221225
} = props as LegacyMenuProps;
222226

223-
const childList: React.ReactElement[] = parseChildren(children, EMPTY_LIST);
227+
const childList: React.ReactElement[] = parseItems(
228+
children,
229+
items,
230+
EMPTY_LIST,
231+
);
224232
const [mounted, setMounted] = React.useState(false);
225233

226234
const containerRef = React.useRef<HTMLUListElement>();

src/MenuItem.tsx

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,7 @@ import Overflow from 'rc-overflow';
44
import warning from 'rc-util/lib/warning';
55
import KeyCode from 'rc-util/lib/KeyCode';
66
import omit from 'rc-util/lib/omit';
7-
import type {
8-
MenuClickEventHandler,
9-
MenuInfo,
10-
MenuHoverEventHandler,
11-
RenderIconType,
12-
} from './interface';
7+
import type { MenuInfo, MenuItemType } from './interface';
138
import { MenuContext } from './context/MenuContext';
149
import useActive from './hooks/useActive';
1510
import { warnItemProp } from './utils/warnUtil';
@@ -20,10 +15,11 @@ import { useMenuId } from './context/IdContext';
2015
import PrivateContext from './context/PrivateContext';
2116

2217
export interface MenuItemProps
23-
extends Omit<
24-
React.HTMLAttributes<HTMLLIElement>,
25-
'onClick' | 'onMouseEnter' | 'onMouseLeave' | 'onSelect'
26-
> {
18+
extends Omit<MenuItemType, 'title' | 'key'>,
19+
Omit<
20+
React.HTMLAttributes<HTMLLIElement>,
21+
'onClick' | 'onMouseEnter' | 'onMouseLeave' | 'onSelect'
22+
> {
2723
children?: React.ReactNode;
2824

2925
/** @private Internal filled key. Do not set it directly */
@@ -32,18 +28,8 @@ export interface MenuItemProps
3228
/** @private Do not use. Private warning empty usage */
3329
warnKey?: boolean;
3430

35-
disabled?: boolean;
36-
37-
itemIcon?: RenderIconType;
38-
3931
/** @deprecated No place to use this. Should remove */
4032
attribute?: Record<string, string>;
41-
// >>>>> Active
42-
onMouseEnter?: MenuHoverEventHandler;
43-
onMouseLeave?: MenuHoverEventHandler;
44-
45-
// >>>>> Events
46-
onClick?: MenuClickEventHandler;
4733
}
4834

4935
// Since Menu event provide the `info.item` which point to the MenuItem node instance.

src/MenuItemGroup.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import omit from 'rc-util/lib/omit';
44
import { parseChildren } from './utils/nodeUtil';
55
import { MenuContext } from './context/MenuContext';
66
import { useFullPath, useMeasure } from './context/PathContext';
7+
import type { MenuItemGroupType } from './interface';
78

8-
export interface MenuItemGroupProps {
9-
className?: string;
10-
title?: React.ReactNode;
9+
export interface MenuItemGroupProps
10+
extends Omit<MenuItemGroupType, 'type' | 'children'> {
1111
children?: React.ReactNode;
1212

1313
/** @private Internal filled key. Do not set it directly */

src/SubMenu/index.tsx

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,7 @@ import Overflow from 'rc-overflow';
44
import warning from 'rc-util/lib/warning';
55
import SubMenuList from './SubMenuList';
66
import { parseChildren } from '../utils/nodeUtil';
7-
import type {
8-
MenuClickEventHandler,
9-
MenuHoverEventHandler,
10-
MenuInfo,
11-
MenuTitleInfo,
12-
RenderIconType,
13-
} from '../interface';
7+
import type { MenuInfo, SubMenuType } from '../interface';
148
import MenuContextProvider, { MenuContext } from '../context/MenuContext';
159
import useMemoCallback from '../hooks/useMemoCallback';
1610
import PopupTrigger from './PopupTrigger';
@@ -28,14 +22,9 @@ import {
2822
import { useMenuId } from '../context/IdContext';
2923
import PrivateContext from '../context/PrivateContext';
3024

31-
export interface SubMenuProps {
32-
style?: React.CSSProperties;
33-
className?: string;
34-
35-
title?: React.ReactNode;
25+
export interface SubMenuProps extends Omit<SubMenuType, 'key' | 'children'> {
3626
children?: React.ReactNode;
3727

38-
disabled?: boolean;
3928
/** @private Used for rest popup. Do not use in your prod */
4029
internalPopupClose?: boolean;
4130

@@ -45,24 +34,6 @@ export interface SubMenuProps {
4534
/** @private Do not use. Private warning empty usage */
4635
warnKey?: boolean;
4736

48-
// >>>>> Icon
49-
itemIcon?: RenderIconType;
50-
expandIcon?: RenderIconType;
51-
52-
// >>>>> Active
53-
onMouseEnter?: MenuHoverEventHandler;
54-
onMouseLeave?: MenuHoverEventHandler;
55-
56-
// >>>>> Popup
57-
popupClassName?: string;
58-
popupOffset?: number[];
59-
60-
// >>>>> Events
61-
onClick?: MenuClickEventHandler;
62-
onTitleClick?: (info: MenuTitleInfo) => void;
63-
onTitleMouseEnter?: MenuHoverEventHandler;
64-
onTitleMouseLeave?: MenuHoverEventHandler;
65-
6637
// >>>>>>>>>>>>>>>>>>>>> Next Round <<<<<<<<<<<<<<<<<<<<<<<
6738
// onDestroy?: DestroyEventHandler;
6839
}

src/interface.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,74 @@
11
import type * as React from 'react';
22

3+
// ========================= Options =========================
4+
interface ItemSharedProps {
5+
style?: React.CSSProperties;
6+
className?: string;
7+
}
8+
9+
export interface SubMenuType extends ItemSharedProps {
10+
title?: React.ReactNode;
11+
12+
children: ItemType[];
13+
14+
disabled?: boolean;
15+
16+
key: string;
17+
18+
// >>>>> Icon
19+
itemIcon?: RenderIconType;
20+
expandIcon?: RenderIconType;
21+
22+
// >>>>> Active
23+
onMouseEnter?: MenuHoverEventHandler;
24+
onMouseLeave?: MenuHoverEventHandler;
25+
26+
// >>>>> Popup
27+
popupClassName?: string;
28+
popupOffset?: number[];
29+
30+
// >>>>> Events
31+
onClick?: MenuClickEventHandler;
32+
onTitleClick?: (info: MenuTitleInfo) => void;
33+
onTitleMouseEnter?: MenuHoverEventHandler;
34+
onTitleMouseLeave?: MenuHoverEventHandler;
35+
}
36+
37+
export interface MenuItemType extends ItemSharedProps {
38+
title?: React.ReactNode;
39+
40+
disabled?: boolean;
41+
42+
itemIcon?: RenderIconType;
43+
44+
key: string;
45+
46+
// >>>>> Active
47+
onMouseEnter?: MenuHoverEventHandler;
48+
onMouseLeave?: MenuHoverEventHandler;
49+
50+
// >>>>> Events
51+
onClick?: MenuClickEventHandler;
52+
}
53+
54+
export interface MenuItemGroupType extends ItemSharedProps {
55+
type: 'group';
56+
57+
title?: React.ReactNode;
58+
59+
children?: ItemType[];
60+
}
61+
62+
export interface MenuDividerType extends ItemSharedProps {
63+
type: 'divider';
64+
}
65+
66+
export type ItemType =
67+
| SubMenuType
68+
| MenuItemType
69+
| MenuItemGroupType
70+
| MenuDividerType;
71+
372
// ========================== Basic ==========================
473
export type MenuMode = 'horizontal' | 'vertical' | 'inline';
574

src/utils/nodeUtil.ts

Lines changed: 0 additions & 30 deletions
This file was deleted.

0 commit comments

Comments
 (0)