ant-design/components/menu/MenuItem.tsx
ice 6a62d9e7ea
fix(menu): menu-title-content style (#51425)
* fix(dropdown): dropdown menu title content style

* chore: update rc-mentions to version 2.17.0 and rc-tabs to version 15.4.0

* test: add extra style debug

* test: snap

* test: snap

* Refactor menu demo: remove extra-style-debug component and rename extra-style-debug files to extra-style

* Refactor menu demo: remove extra-style-debug component and rename extra-style-debug files to extra-style

* Refactor dropdown and menu styles: Add width to extra-style elements

* test: snap

* fix: Add Space component to menu demo

The Space component was missing from the import statement in the menu demo file. This caused a compilation error. The fix adds the Space component to the import statement, resolving the issue.

Refactor the menu demo to use Space component for vertical spacing between menus. This improves the visual layout and readability of the menus.

Fixes #51492

* test: snap

* refactor: Update dropdown and menu title content class names

---------

Co-authored-by: afc163 <afc163@gmail.com>
2024-11-07 20:00:06 +08:00

127 lines
3.8 KiB
TypeScript

import * as React from 'react';
import classNames from 'classnames';
import type { MenuItemProps as RcMenuItemProps } from 'rc-menu';
import { Item } from 'rc-menu';
import toArray from 'rc-util/lib/Children/toArray';
import omit from 'rc-util/lib/omit';
import { cloneElement } from '../_util/reactNode';
import type { SiderContextProps } from '../layout/Sider';
import { SiderContext } from '../layout/Sider';
import type { TooltipProps } from '../tooltip';
import Tooltip from '../tooltip';
import type { MenuContextProps } from './MenuContext';
import MenuContext from './MenuContext';
export interface MenuItemProps extends Omit<RcMenuItemProps, 'title'> {
icon?: React.ReactNode;
danger?: boolean;
title?: React.ReactNode;
}
type MenuItemComponent = React.FC<MenuItemProps>;
type RestArgs<T> = T extends (arg: any, ...args: infer P) => any ? P : never;
type GenericProps<T = unknown> = T extends infer U extends MenuItemProps
? unknown extends U
? MenuItemProps
: U
: MenuItemProps;
type GenericComponent = Omit<MenuItemComponent, ''> &
(<T extends MenuItemProps>(
props: GenericProps<T>,
...args: RestArgs<MenuItemComponent>
) => ReturnType<MenuItemComponent>);
const MenuItem: GenericComponent = (props) => {
const { className, children, icon, title, danger, extra } = props;
const {
prefixCls,
firstLevel,
direction,
disableMenuItemTitleTooltip,
inlineCollapsed: isInlineCollapsed,
} = React.useContext<MenuContextProps>(MenuContext);
const renderItemChildren = (inlineCollapsed: boolean) => {
const label = (children as React.ReactNode[])?.[0];
const wrapNode = (
<span
className={classNames(`${prefixCls}-title-content`, {
[`${prefixCls}-title-content-with-extra`]: !!extra || extra === 0,
})}
>
{children}
</span>
);
// inline-collapsed.md demo 依赖 span 来隐藏文字,有 icon 属性,则内部包裹一个 span
// ref: https://github.com/ant-design/ant-design/pull/23456
if (!icon || (React.isValidElement(children) && children.type === 'span')) {
if (children && inlineCollapsed && firstLevel && typeof label === 'string') {
return <div className={`${prefixCls}-inline-collapsed-noicon`}>{label.charAt(0)}</div>;
}
}
return wrapNode;
};
const { siderCollapsed } = React.useContext<SiderContextProps>(SiderContext);
let tooltipTitle = title;
if (typeof title === 'undefined') {
tooltipTitle = firstLevel ? children : '';
} else if (title === false) {
tooltipTitle = '';
}
const tooltipProps: TooltipProps = { title: tooltipTitle };
if (!siderCollapsed && !isInlineCollapsed) {
tooltipProps.title = null;
// Reset `open` to fix control mode tooltip display not correct
// ref: https://github.com/ant-design/ant-design/issues/16742
tooltipProps.open = false;
}
const childrenLength = toArray(children).length;
let returnNode = (
<Item
{...omit(props, ['title', 'icon', 'danger'])}
className={classNames(
{
[`${prefixCls}-item-danger`]: danger,
[`${prefixCls}-item-only-child`]: (icon ? childrenLength + 1 : childrenLength) === 1,
},
className,
)}
title={typeof title === 'string' ? title : undefined}
>
{cloneElement(icon, {
className: classNames(
React.isValidElement(icon) ? icon.props?.className : '',
`${prefixCls}-item-icon`,
),
})}
{renderItemChildren(isInlineCollapsed)}
</Item>
);
if (!disableMenuItemTitleTooltip) {
returnNode = (
<Tooltip
{...tooltipProps}
placement={direction === 'rtl' ? 'left' : 'right'}
overlayClassName={`${prefixCls}-inline-collapsed-tooltip`}
>
{returnNode}
</Tooltip>
);
}
return returnNode;
};
export default MenuItem;