From d438e6dc1ffb75cb50b7416ebfb746ddb6a51623 Mon Sep 17 00:00:00 2001 From: thinkasany <480968828@qq.com> Date: Tue, 18 Feb 2025 17:40:19 +0800 Subject: [PATCH 1/3] feat: support classnames and styles --- src/Image.tsx | 12 ++++++++++-- src/Operations.tsx | 12 ++++++++++-- src/Preview.tsx | 4 ++++ tests/basic.test.tsx | 19 +++++++++++++++++++ tests/preview.test.tsx | 20 ++++++++++++++++++++ 5 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/Image.tsx b/src/Image.tsx index 71e2e1f2..33d9ce9e 100644 --- a/src/Image.tsx +++ b/src/Image.tsx @@ -49,6 +49,8 @@ export interface ImagePreviewType ) => React.ReactNode; } +export type SemanticName = 'actions' | 'mask'; + export interface ImageProps extends Omit, 'placeholder' | 'onClick'> { // Original @@ -67,6 +69,8 @@ export interface ImageProps onPreviewClose?: (value: boolean, prevValue: boolean) => void; onClick?: (e: React.MouseEvent) => void; onError?: (e: React.SyntheticEvent) => void; + classNames?: Partial>; + styles?: Partial>; } interface CompoundedComponent

extends React.FC

{ @@ -92,7 +96,8 @@ const ImageInternal: CompoundedComponent = props => { wrapperClassName, wrapperStyle, rootClassName, - + classNames: imageClassNames, + styles, ...otherProps } = props; @@ -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..e953471b 100644 --- a/src/Preview.tsx +++ b/src/Preview.tsx @@ -134,6 +134,8 @@ const Preview: React.FC = props => { toolbarRender, onTransform, onChange, + classNames: imageClassNames, + styles, ...restProps } = props; @@ -339,6 +341,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/basic.test.tsx b/tests/basic.test.tsx index a58d5e7a..b91540e9 100644 --- a/tests/basic.test.tsx +++ b/tests/basic.test.tsx @@ -97,4 +97,23 @@ describe('Basic', () => { const operationsElement = baseElement.querySelector('.rc-image-preview-operations-wrapper'); expect(operationsElement).toHaveStyle({ zIndex: 10000 }); }); + it('support classnames and styles', () => { + const customClassnames = { + actions: 'custom-actions', + }; + const customStyles = { + actions: { backgroundColor: 'blue' }, + }; + const { baseElement } = render( + , + ); + const actionsElement = baseElement.querySelector('.rc-image-preview-operations'); + expect(actionsElement).toHaveClass('custom-actions'); + expect(actionsElement).toHaveStyle({ backgroundColor: 'blue' }); + }); }); diff --git a/tests/preview.test.tsx b/tests/preview.test.tsx index 04890740..9fec2a15 100644 --- a/tests/preview.test.tsx +++ b/tests/preview.test.tsx @@ -1030,4 +1030,24 @@ describe('Preview', () => { expect(previewImg).not.toHaveAttribute('width'); expect(previewImg).not.toHaveAttribute('height'); }); + it('support classnames and styles', () => { + const customClassnames = { + mask: 'custom-mask', + }; + const customStyles = { + mask: { color: 'red' }, + }; + render( + , + ); + expect(document.querySelector('.rc-image-mask')).toHaveClass('custom-mask'); + expect(document.querySelector('.rc-image-mask')).toHaveStyle({ color: 'red' }); + }); }); From bea7e8b027b20bf398c798027a5041f988bba90c Mon Sep 17 00:00:00 2001 From: thinkasany <480968828@qq.com> Date: Mon, 3 Mar 2025 17:15:37 +0800 Subject: [PATCH 2/3] fix --- src/Image.tsx | 10 +++++----- src/Preview.tsx | 21 +++++++++++++++------ tests/basic.test.tsx | 19 ------------------- tests/preview.test.tsx | 22 +++++++++++++++++----- 4 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/Image.tsx b/src/Image.tsx index 33d9ce9e..82957d22 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,7 +51,7 @@ export interface ImagePreviewType ) => React.ReactNode; } -export type SemanticName = 'actions' | 'mask'; +export type SemanticName = 'root' | 'actions' | 'mask' | 'wrapper'; export interface ImageProps extends Omit, 'placeholder' | 'onClick'> { @@ -69,8 +71,6 @@ export interface ImageProps onPreviewClose?: (value: boolean, prevValue: boolean) => void; onClick?: (e: React.MouseEvent) => void; onError?: (e: React.SyntheticEvent) => void; - classNames?: Partial>; - styles?: Partial>; } interface CompoundedComponent

extends React.FC

{ @@ -96,8 +96,6 @@ const ImageInternal: CompoundedComponent = props => { wrapperClassName, wrapperStyle, rootClassName, - classNames: imageClassNames, - styles, ...otherProps } = props; @@ -109,6 +107,8 @@ const ImageInternal: CompoundedComponent = props => { getContainer: getPreviewContainer = undefined, mask: previewMask, maskClassName, + classNames: imageClassNames, + styles, movable, icons, scaleStep, diff --git a/src/Preview.tsx b/src/Preview.tsx index e953471b..33109811 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,8 @@ export interface PreviewProps extends Omit { info: ToolbarRenderInfoType, ) => React.ReactNode; onChange?: (current, prev) => void; + classNames?: Partial>; + styles?: Partial>; } interface PreviewImageProps extends React.ImgHTMLAttributes { @@ -222,7 +224,7 @@ const Preview: React.FC = props => { setEnableTransition(false); resetTransform(offset < 0 ? 'prev' : 'next'); onChange?.(position, current); - } + }; const onKeyDown = (event: KeyboardEvent) => { if (!visible || !showLeftOrRightSwitches) return; @@ -266,8 +268,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} @@ -300,8 +303,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} diff --git a/tests/basic.test.tsx b/tests/basic.test.tsx index b91540e9..a58d5e7a 100644 --- a/tests/basic.test.tsx +++ b/tests/basic.test.tsx @@ -97,23 +97,4 @@ describe('Basic', () => { const operationsElement = baseElement.querySelector('.rc-image-preview-operations-wrapper'); expect(operationsElement).toHaveStyle({ zIndex: 10000 }); }); - it('support classnames and styles', () => { - const customClassnames = { - actions: 'custom-actions', - }; - const customStyles = { - actions: { backgroundColor: 'blue' }, - }; - const { baseElement } = render( - , - ); - const actionsElement = baseElement.querySelector('.rc-image-preview-operations'); - expect(actionsElement).toHaveClass('custom-actions'); - expect(actionsElement).toHaveStyle({ backgroundColor: 'blue' }); - }); }); diff --git a/tests/preview.test.tsx b/tests/preview.test.tsx index 9fec2a15..ccb48a1e 100644 --- a/tests/preview.test.tsx +++ b/tests/preview.test.tsx @@ -1033,21 +1033,33 @@ describe('Preview', () => { 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' }, }; - render( + const { baseElement } = render( , ); - expect(document.querySelector('.rc-image-mask')).toHaveClass('custom-mask'); - expect(document.querySelector('.rc-image-mask')).toHaveStyle({ color: 'red' }); + 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); }); }); From 2d817859a799dae5aaf871f12605eb5d08d0539d Mon Sep 17 00:00:00 2001 From: thinkasany <480968828@qq.com> Date: Wed, 5 Mar 2025 14:33:27 +0800 Subject: [PATCH 3/3] Temporarily used in PurePanel, not used externally --- src/Image.tsx | 2 +- src/Preview.tsx | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Image.tsx b/src/Image.tsx index 82957d22..7b5f7519 100644 --- a/src/Image.tsx +++ b/src/Image.tsx @@ -51,7 +51,7 @@ export interface ImagePreviewType ) => React.ReactNode; } -export type SemanticName = 'root' | 'actions' | 'mask' | 'wrapper'; +export type SemanticName = 'root' | 'actions' | 'mask'; export interface ImageProps extends Omit, 'placeholder' | 'onClick'> { diff --git a/src/Preview.tsx b/src/Preview.tsx index 33109811..311db1c9 100644 --- a/src/Preview.tsx +++ b/src/Preview.tsx @@ -83,7 +83,10 @@ export interface PreviewProps extends Omit React.ReactNode; onChange?: (current, prev) => void; classNames?: Partial>; - styles?: Partial>; + styles?: Partial> & { + /** Temporarily used in PurePanel, not used externally by antd */ + wrapper?: React.CSSProperties; + }; } interface PreviewImageProps extends React.ImgHTMLAttributes {