import * as React from 'react'; import classNames from 'classnames'; import omit from 'rc-util/lib/omit'; import type { PresetColorType, PresetStatusColorType } from '../_util/colors'; import { isPresetColor, isPresetStatusColor } from '../_util/colors'; import type { ClosableType } from '../_util/hooks/useClosable'; import useClosable, { pickClosable } from '../_util/hooks/useClosable'; import { replaceElement } from '../_util/reactNode'; import type { LiteralUnion } from '../_util/type'; import { devUseWarning } from '../_util/warning'; import Wave from '../_util/wave'; import { ConfigContext } from '../config-provider'; import CheckableTag from './CheckableTag'; import useStyle from './style'; import PresetCmp from './style/presetCmp'; import StatusCmp from './style/statusCmp'; export type { CheckableTagProps } from './CheckableTag'; export interface TagProps extends React.HTMLAttributes { prefixCls?: string; className?: string; rootClassName?: string; color?: LiteralUnion; /** Advised to use closeIcon instead. */ closable?: ClosableType; closeIcon?: React.ReactNode; /** @deprecated `visible` will be removed in next major version. */ visible?: boolean; onClose?: (e: React.MouseEvent) => void; style?: React.CSSProperties; icon?: React.ReactNode; bordered?: boolean; } const InternalTag = React.forwardRef((tagProps, ref) => { const { prefixCls: customizePrefixCls, className, rootClassName, style, children, icon, color, onClose, bordered = true, visible: deprecatedVisible, ...props } = tagProps; const { getPrefixCls, direction, tag: tagContext } = React.useContext(ConfigContext); const [visible, setVisible] = React.useState(true); const domProps = omit(props, ['closeIcon', 'closable']); // Warning for deprecated usage if (process.env.NODE_ENV !== 'production') { const warning = devUseWarning('Tag'); warning.deprecated(!('visible' in tagProps), 'visible', 'visible && '); } React.useEffect(() => { if (deprecatedVisible !== undefined) { setVisible(deprecatedVisible!); } }, [deprecatedVisible]); const isPreset = isPresetColor(color); const isStatus = isPresetStatusColor(color); const isInternalColor = isPreset || isStatus; const tagStyle: React.CSSProperties = { backgroundColor: color && !isInternalColor ? color : undefined, ...tagContext?.style, ...style, }; const prefixCls = getPrefixCls('tag', customizePrefixCls); const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls); // Style const tagClassName = classNames( prefixCls, tagContext?.className, { [`${prefixCls}-${color}`]: isInternalColor, [`${prefixCls}-has-color`]: color && !isInternalColor, [`${prefixCls}-hidden`]: !visible, [`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-borderless`]: !bordered, }, className, rootClassName, hashId, cssVarCls, ); const handleCloseClick = (e: React.MouseEvent) => { e.stopPropagation(); onClose?.(e); if (e.defaultPrevented) { return; } setVisible(false); }; const [, mergedCloseIcon] = useClosable(pickClosable(tagProps), pickClosable(tagContext), { closable: false, closeIconRender: (iconNode: React.ReactNode) => { const replacement = ( {iconNode} ); return replaceElement(iconNode, replacement, (originProps) => ({ onClick: (e: React.MouseEvent) => { originProps?.onClick?.(e); handleCloseClick(e); }, className: classNames(originProps?.className, `${prefixCls}-close-icon`), })); }, }); const isNeedWave = typeof props.onClick === 'function' || (children && (children as React.ReactElement).type === 'a'); const iconNode: React.ReactNode = icon || null; const kids: React.ReactNode = iconNode ? ( <> {iconNode} {children && {children}} ) : ( children ); const tagNode: React.ReactNode = ( {kids} {mergedCloseIcon} {isPreset && } {isStatus && } ); return wrapCSSVar(isNeedWave ? {tagNode} : tagNode); }); export type TagType = typeof InternalTag & { CheckableTag: typeof CheckableTag; }; const Tag = InternalTag as TagType; if (process.env.NODE_ENV !== 'production') { Tag.displayName = 'Tag'; } Tag.CheckableTag = CheckableTag; export default Tag;