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" ? ( icon ) : ( defaultIcon ))} {activeIcon && !inSubMenu && (typeof activeIcon === "string" ? ( icon ) : ( 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";