diff --git a/src/Image.tsx b/src/Image.tsx index 71e2e1f2..7b5f7519 100644 --- a/src/Image.tsx +++ b/src/Image.tsx @@ -35,6 +35,8 @@ export interface ImagePreviewType getContainer?: GetContainer | false; mask?: React.ReactNode; maskClassName?: string; + classNames?: Partial>; + styles?: Partial>; icons?: PreviewProps['icons']; scaleStep?: number; movable?: boolean; @@ -49,6 +51,8 @@ export interface ImagePreviewType ) => React.ReactNode; } +export type SemanticName = 'root' | 'actions' | 'mask'; + export interface ImageProps extends Omit, 'placeholder' | 'onClick'> { // Original @@ -92,7 +96,6 @@ const ImageInternal: CompoundedComponent = props => { wrapperClassName, wrapperStyle, rootClassName, - ...otherProps } = props; @@ -104,6 +107,8 @@ const ImageInternal: CompoundedComponent = props => { getContainer: getPreviewContainer = undefined, mask: previewMask, maskClassName, + classNames: imageClassNames, + styles, movable, icons, scaleStep, @@ -222,9 +227,10 @@ const ImageInternal: CompoundedComponent = props => { {/* Preview Click Mask */} {previewMask && canPreview && (
{previewMask} @@ -252,6 +258,8 @@ const ImageInternal: CompoundedComponent = props => { imageRender={imageRender} imgCommonProps={imgCommonProps} toolbarRender={toolbarRender} + classNames={imageClassNames} + styles={styles} {...dialogProps} /> )} diff --git a/src/Operations.tsx b/src/Operations.tsx index e7091c17..a47abd64 100644 --- a/src/Operations.tsx +++ b/src/Operations.tsx @@ -4,10 +4,11 @@ import CSSMotion from 'rc-motion'; import KeyCode from 'rc-util/lib/KeyCode'; import * as React from 'react'; import { useContext } from 'react'; -import type { ImgInfo } from './Image'; +import type { ImgInfo, SemanticName } from './Image'; import type { PreviewProps, ToolbarRenderInfoType } from './Preview'; import { PreviewGroupContext } from './context'; import type { TransformType } from './hooks/useImageTransform'; +import classNames from 'classnames'; type OperationType = | 'prev' @@ -61,6 +62,8 @@ interface OperationsProps ) => React.ReactNode; zIndex?: number; image?: ImgInfo; + classNames?: Partial>; + styles?: Partial>; } const Operations: React.FC = props => { @@ -93,6 +96,8 @@ const Operations: React.FC = props => { toolbarRender, zIndex, image, + classNames: imageClassNames, + styles, } = props; const groupContext = useContext(PreviewGroupContext); const { rotateLeft, rotateRight, zoomIn, zoomOut, close, left, right, flipX, flipY } = icons; @@ -195,7 +200,10 @@ const Operations: React.FC = props => { }); const toolbarNode = ( -
+
{flipYNode} {flipXNode} {rotateLeftNode} diff --git a/src/Preview.tsx b/src/Preview.tsx index 42e2b400..311db1c9 100644 --- a/src/Preview.tsx +++ b/src/Preview.tsx @@ -4,7 +4,7 @@ import Dialog from 'rc-dialog'; import addEventListener from 'rc-util/lib/Dom/addEventListener'; import KeyCode from 'rc-util/lib/KeyCode'; import React, { useContext, useEffect, useRef, useState } from 'react'; -import type { ImgInfo } from './Image'; +import type { ImgInfo, SemanticName } from './Image'; import Operations from './Operations'; import { PreviewGroupContext } from './context'; import type { TransformAction, TransformType } from './hooks/useImageTransform'; @@ -42,7 +42,7 @@ export type ToolbarRenderInfoType = { image: ImgInfo; }; -export interface PreviewProps extends Omit { +export interface PreviewProps extends Omit { imgCommonProps?: React.ImgHTMLAttributes; src?: string; alt?: string; @@ -82,6 +82,11 @@ export interface PreviewProps extends Omit { info: ToolbarRenderInfoType, ) => React.ReactNode; onChange?: (current, prev) => void; + classNames?: Partial>; + styles?: Partial> & { + /** Temporarily used in PurePanel, not used externally by antd */ + wrapper?: React.CSSProperties; + }; } interface PreviewImageProps extends React.ImgHTMLAttributes { @@ -134,6 +139,8 @@ const Preview: React.FC = props => { toolbarRender, onTransform, onChange, + classNames: imageClassNames, + styles, ...restProps } = props; @@ -220,7 +227,7 @@ const Preview: React.FC = props => { setEnableTransition(false); resetTransform(offset < 0 ? 'prev' : 'next'); onChange?.(position, current); - } + }; const onKeyDown = (event: KeyboardEvent) => { if (!visible || !showLeftOrRightSwitches) return; @@ -264,8 +271,9 @@ const Preview: React.FC = props => { className={`${prefixCls}-img`} alt={alt} style={{ - transform: `translate3d(${transform.x}px, ${transform.y}px, 0) scale3d(${transform.flipX ? '-' : '' - }${scale}, ${transform.flipY ? '-' : ''}${scale}, 1) rotate(${rotate}deg)`, + transform: `translate3d(${transform.x}px, ${transform.y}px, 0) scale3d(${ + transform.flipX ? '-' : '' + }${scale}, ${transform.flipY ? '-' : ''}${scale}, 1) rotate(${rotate}deg)`, transitionDuration: (!enableTransition || isTouching) && '0s', }} fallback={fallback} @@ -298,8 +306,14 @@ const Preview: React.FC = props => { visible={visible} classNames={{ wrapper: wrapClassName, + mask: imageClassNames?.mask, }} - rootClassName={rootClassName} + styles={{ + mask: styles?.mask, + wrapper: styles?.wrapper, + }} + style={styles?.root} + rootClassName={classnames(rootClassName, imageClassNames?.root)} getContainer={getContainer} {...restProps} afterClose={onAfterClose} @@ -339,6 +353,8 @@ const Preview: React.FC = props => { onReset={onReset} zIndex={restProps.zIndex !== undefined ? restProps.zIndex + 1 : undefined} image={image} + classNames={imageClassNames} + styles={styles} /> ); diff --git a/tests/preview.test.tsx b/tests/preview.test.tsx index 04890740..ccb48a1e 100644 --- a/tests/preview.test.tsx +++ b/tests/preview.test.tsx @@ -1030,4 +1030,36 @@ describe('Preview', () => { expect(previewImg).not.toHaveAttribute('width'); expect(previewImg).not.toHaveAttribute('height'); }); + it('support classnames and styles', () => { + const customClassnames = { + mask: 'custom-mask', + actions: 'custom-actions', + root: 'custom-root', + }; + const customStyles = { + mask: { color: 'red' }, + actions: { backgroundColor: 'blue' }, + root: { border: '1px solid green' }, + }; + const { baseElement } = render( + , + ); + const mask = document.querySelector('.rc-image-mask'); + const actions = baseElement.querySelector('.rc-image-preview-operations'); + expect(mask).toHaveClass(customClassnames.mask); + expect(mask).toHaveStyle(customStyles.mask); + expect(actions).toHaveClass(customClassnames.actions); + expect(actions).toHaveStyle(customStyles.actions); + expect(baseElement.querySelector('.rc-image-preview-root')).toHaveClass(customClassnames.root); + expect(baseElement.querySelector('.rc-image-preview')).toHaveStyle(customStyles.root); + }); });