import React, {
forwardRef,
useContext,
useEffect,
useMemo,
useRef,
} from "react";
import classNames from "classnames";
import scrollIntoView from "scroll-into-view-if-needed";
import { StyledProps } from "../_type";
import { callBoth } from "../_util/call-both";
import { useConfig } from "../_util/config-context";
import { Badge } from "../badge";
import { Icon } from "../icon";
import { uuid } from "../_util/uuid";
import { MenuContext, SubMenuContext } from "./MenuContext";
import { noop } from "../_util/noop";
import { isChildOfType } from "../_util/is-child-of-type";
import { mergeRefs } from "../util";
export interface MenuItemProps extends StyledProps {
/**
* 标题
*/
title?: React.ReactNode;
/**
* 菜单折叠后标题处显示的图标 URL 或自定义节点内容
*
* **传入 `string` 类型将作为 URL 解析**
*
* **传递一组 URL 时,第一个 URL 会作为未激活态图标,第二个 URL 会作为激活态图标**
*
* @docType [React.ReactNode, React.ReactNode] | React.ReactNode
*/
icon?: React.ReactNode | [React.ReactNode, React.ReactNode];
/**
* 标题旁标签内容
* @since 2.3.0
*/
tag?: React.ReactNode;
/**
* 是否为外部链接(显示外部链接图标)
* @since 2.3.0
*/
external?: boolean;
/**
* 是否为选中状态
*
* @default false
*/
selected?: boolean;
/**
* 点击回调
*/
onClick?: (event: React.MouseEvent) => void;
/**
* 自定义渲染
*
* @default children => {children}
*/
render?: (children: JSX.Element) => JSX.Element;
}
const defaultRender = children => {children};
export const MenuItem = forwardRef(function MenuItem(
{
title,
icon,
tag,
selected,
external,
className,
onClick = noop,
render = defaultRender,
...props
}: MenuItemProps,
fRef: React.Ref
) {
const ref = useRef(null);
const { classPrefix } = useConfig();
const { setMobileCollapsed } = useContext(MenuContext);
const { inSubMenu, onSelectedChange, onTagChange } = useContext(
SubMenuContext
);
const idRef = useRef(uuid());
useEffect(() => {
if (ref.current && selected) {
scrollIntoView(ref.current, {
scrollMode: "if-needed",
block: "nearest",
inline: "nearest",
});
}
}, [ref, selected]);
useEffect(() => {
onTagChange(idRef.current, !!tag);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [tag]);
useEffect(
() => () => {
onTagChange(idRef.current, false);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
useEffect(() => {
onSelectedChange(idRef.current, selected);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selected]);
const tagBadge = useMemo(
() => (isChildOfType(tag, Badge) ? tag : {tag}),
[tag]
);
const [defaultIcon, activeIcon] = Array.isArray(icon) ? icon : [icon, icon];
const children = render(
<>
{defaultIcon &&
!inSubMenu &&
(typeof defaultIcon === "string" ? (
) : (
defaultIcon
))}
{activeIcon &&
!inSubMenu &&
(typeof activeIcon === "string" ? (
) : (
activeIcon
))}
{title}
{external && }
{!!tag && tagBadge}
>
);
return (
setMobileCollapsed(false)}
{...props}
>
{React.cloneElement(children, {
className: classNames(
`${classPrefix}-menu__item`,
children.props.className
),
onClick: callBoth(onClick, children.props.onClick),
})}
);
});
MenuItem.displayName = "MenuItem";