ant-design/components/image/index.tsx
二货爱吃白萝卜 0a50c71d59
refactor: Align the structure of semantic interface (#53453)
* chore: looper

* refactor: merge logic

* chore: comment

* chore: image semantic

* test: fix test case

* chore: fix lint

* chore: fix lint

* chore: show the strcture

* chore: desc update
2025-04-09 17:58:16 +08:00

183 lines
5.3 KiB
TypeScript

import * as React from 'react';
import EyeOutlined from '@ant-design/icons/EyeOutlined';
import RcImage from '@rc-component/image';
import type { ImageProps as RcImageProps } from '@rc-component/image';
import classnames from 'classnames';
import useMergeSemantic from '../_util/hooks/useMergeSemantic';
import { devUseWarning } from '../_util/warning';
import { useComponentConfig } from '../config-provider/context';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import { useLocale } from '../locale';
import useMergedPreviewConfig from './hooks/useMergedPreviewConfig';
import usePreviewConfig from './hooks/usePreviewConfig';
import PreviewGroup, { icons } from './PreviewGroup';
import useStyle from './style';
type OriginPreviewConfig = NonNullable<Exclude<RcImageProps['preview'], boolean>>;
export type DeprecatedPreviewConfig = {
/** @deprecated Use `open` instead */
visible?: boolean;
/** @deprecated Use `classNames.root` instead */
rootClassName?: string;
/**
* @deprecated This has been removed.
* Preview will always be rendered after show.
*/
forceRender?: boolean;
/**
* @deprecated This has been removed.
* Preview will always be rendered after show.
*/
destroyOnClose?: boolean;
/** @deprecated Use `actionsRender` instead */
toolbarRender?: OriginPreviewConfig['actionsRender'];
};
export type PreviewConfig = OriginPreviewConfig &
DeprecatedPreviewConfig & {
/** @deprecated Use `onOpenChange` instead */
onVisibleChange?: (visible: boolean, prevVisible: boolean) => void;
/** @deprecated Use `classNames.cover` instead */
maskClassName?: string;
/** @deprecated Use `cover` instead */
mask?: React.ReactNode;
};
export interface CompositionImage<P> extends React.FC<P> {
PreviewGroup: typeof PreviewGroup;
}
export interface ImageProps extends Omit<RcImageProps, 'preview'> {
preview?: boolean | PreviewConfig;
/** @deprecated Use `styles.root` instead */
wrapperStyle?: React.CSSProperties;
}
const Image: CompositionImage<ImageProps> = (props) => {
const {
prefixCls: customizePrefixCls,
preview,
className,
rootClassName,
style,
styles,
classNames: imageClassNames,
wrapperStyle,
...otherProps
} = props;
// =============================== MISC ===============================
// Context
const {
getPrefixCls,
getPopupContainer: getContextPopupContainer,
className: contextClassName,
style: contextStyle,
preview: contextPreview,
styles: contextStyles,
classNames: contextClassNames,
} = useComponentConfig('image');
// ============================== Locale ==============================
const [imageLocale] = useLocale('Image');
const prefixCls = getPrefixCls('image', customizePrefixCls);
// ============================= Warning ==============================
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('Image');
warning.deprecated(!wrapperStyle, 'wrapperStyle', 'styles.root');
}
// ============================== Styles ==============================
const rootCls = useCSSVarCls(prefixCls);
const [hashId, cssVarCls] = useStyle(prefixCls, rootCls);
const mergedRootClassName = classnames(rootClassName, hashId, cssVarCls, rootCls);
const mergedClassName = classnames(className, hashId, contextClassName);
// ============================= Preview ==============================
const [previewConfig, previewRootClassName, previewMaskClassName] = usePreviewConfig(preview);
const [contextPreviewConfig, contextPreviewRootClassName, contextPreviewMaskClassName] =
usePreviewConfig(contextPreview);
const mergedPreviewConfig = useMergedPreviewConfig(
// Preview config
previewConfig,
contextPreviewConfig,
// MISC
prefixCls,
mergedRootClassName,
getContextPopupContainer,
icons,
// Image only: fallback cover
<div className={`${prefixCls}-cover-info`}>
<EyeOutlined />
{imageLocale?.preview}
</div>,
);
// ============================= Semantic =============================
const mergedLegacyClassNames = React.useMemo(
() => ({
cover: classnames(contextPreviewMaskClassName, previewMaskClassName),
popup: {
root: classnames(contextPreviewRootClassName, previewRootClassName),
},
}),
[
previewRootClassName,
previewMaskClassName,
contextPreviewRootClassName,
contextPreviewMaskClassName,
],
);
const [mergedClassNames, mergedStyles] = useMergeSemantic(
[contextClassNames, imageClassNames, mergedLegacyClassNames],
[
contextStyles,
{
root: wrapperStyle,
},
styles,
],
{
popup: {
_default: 'root',
},
},
);
const mergedStyle: React.CSSProperties = { ...contextStyle, ...style };
// ============================== Render ==============================
return (
<RcImage
prefixCls={prefixCls}
preview={mergedPreviewConfig || false}
rootClassName={mergedRootClassName}
className={mergedClassName}
style={mergedStyle}
{...otherProps}
classNames={mergedClassNames}
styles={mergedStyles}
/>
);
};
export type { PreviewConfig as ImagePreviewType };
Image.PreviewGroup = PreviewGroup;
if (process.env.NODE_ENV !== 'production') {
Image.displayName = 'Image';
}
export default Image;