diff --git a/src/image-viewer/ImageViewer.tsx b/src/image-viewer/ImageViewer.tsx index 480914a37f..e27a9d5086 100644 --- a/src/image-viewer/ImageViewer.tsx +++ b/src/image-viewer/ImageViewer.tsx @@ -62,6 +62,7 @@ const ImageViewer: React.FC = (originalProps) => { closeOnOverlay={props.closeOnOverlay} closeBtn={props.closeBtn} showOverlay={props.showOverlay} + closeOnEscKeydown={props.closeOnEscKeydown} onClose={close} onOpen={open} />, diff --git a/src/image-viewer/ImageViewerModal.tsx b/src/image-viewer/ImageViewerModal.tsx index 98f520f962..75267b5053 100644 --- a/src/image-viewer/ImageViewerModal.tsx +++ b/src/image-viewer/ImageViewerModal.tsx @@ -290,6 +290,7 @@ interface ImageModalProps { isMini: boolean; draggable: boolean; closeBtn: boolean | TNode; + closeOnEscKeydown?: boolean; onIndexChange?: (index: number, context: { trigger: 'prev' | 'next' }) => void; } @@ -309,6 +310,7 @@ export const ImageModal: React.FC = (props) => { onClose, visible, title, + closeOnEscKeydown, ...resProps } = props; const { classPrefix } = useConfig(); @@ -357,10 +359,10 @@ export const ImageModal: React.FC = (props) => { case 'ArrowDown': return onZoomOut(); case 'Escape': - return onClose?.({ trigger: 'esc', e: event }); + return closeOnEscKeydown && onClose?.({ trigger: 'esc', e: event }); } }, - [next, onClose, prev, onZoom, onZoomOut], + [next, onClose, prev, onZoom, onZoomOut, closeOnEscKeydown], ); useEffect(() => { diff --git a/src/image-viewer/__tests__/image-viewer.test.tsx b/src/image-viewer/__tests__/image-viewer.test.tsx index 06ef875a71..8955981a66 100644 --- a/src/image-viewer/__tests__/image-viewer.test.tsx +++ b/src/image-viewer/__tests__/image-viewer.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, act, fireEvent, waitFor, vi, mockTimeout } from '@test/utils'; +import { render, act, fireEvent, waitFor, vi, mockTimeout, mockDelay } from '@test/utils'; import userEvent from '@testing-library/user-event'; import { ImageViewer } from '../index'; @@ -175,4 +175,55 @@ describe('ImageViewerModal', () => { }); expect(getByText('closeBtn')).toBeTruthy(); }); + + test('closeOnEscKeydown is false', async () => { + const user = userEvent.setup(); + const BasicImageViewer = () => { + const trigger = ({ onOpen }) => {triggerText}; + return ; + }; + const { getByText } = render(); + + // 模拟鼠标点击 + act(() => { + fireEvent.click(getByText(triggerText)); + }); + expect(document.querySelector('.t-image-viewer-preview-image')).toBeInTheDocument(); + + // 模拟键盘事件 + await user.type(document.body, '{Escape}'); + await mockDelay(300); + expect(document.querySelector('.t-image-viewer-preview-image')).toBeInTheDocument(); + }); + + test('imageScale defaultScale', async () => { + const BasicImageViewer = () => { + const trigger = ({ onOpen }) => {triggerText}; + return ( + + ); + }; + const { getByText } = render(); + + // 模拟鼠标点击 + act(() => { + fireEvent.click(getByText(triggerText)); + }); + + await mockDelay(); + + expect(document.querySelector('.t-image-viewer__modal-image')).toBeInTheDocument(); + expect(document.querySelector('.t-image-viewer__modal-image')).toHaveStyle({ + transform: 'rotateZ(0deg) scale(2)', + }); + }); }); diff --git a/src/image-viewer/defaultProps.ts b/src/image-viewer/defaultProps.ts index e0b99ebe3b..d6d4a553c7 100644 --- a/src/image-viewer/defaultProps.ts +++ b/src/image-viewer/defaultProps.ts @@ -6,6 +6,7 @@ import { TdImageViewerProps } from './type'; export const imageViewerDefaultProps: TdImageViewerProps = { closeBtn: true, + closeOnEscKeydown: true, draggable: undefined, images: [], defaultIndex: 0, diff --git a/src/image-viewer/hooks/useImageScale.ts b/src/image-viewer/hooks/useImageScale.ts index a2e2fee541..e3b05da665 100644 --- a/src/image-viewer/hooks/useImageScale.ts +++ b/src/image-viewer/hooks/useImageScale.ts @@ -1,7 +1,8 @@ import { ImageScale } from '../type'; -const useImageScale = (imageScale) => { +const useImageScale = (imageScale: ImageScale) => { const result: ImageScale = { + ...imageScale, max: 2, min: 0.5, step: 0.5, @@ -9,6 +10,15 @@ const useImageScale = (imageScale) => { if (imageScale?.min !== undefined) result.min = imageScale.min; if (imageScale?.max !== undefined) result.max = imageScale.max; if (imageScale?.step !== undefined) result.step = imageScale.step; + // defaultScale 不能超出本身设置的最大和最小值 + if (imageScale?.defaultScale !== undefined) { + if (imageScale.defaultScale > result.max) { + result.defaultScale = result.max; + } + if (imageScale.defaultScale < result.min) { + result.defaultScale = result.min; + } + } return result; }; diff --git a/src/image-viewer/hooks/useScale.ts b/src/image-viewer/hooks/useScale.ts index e802744dce..4280222faf 100644 --- a/src/image-viewer/hooks/useScale.ts +++ b/src/image-viewer/hooks/useScale.ts @@ -1,8 +1,9 @@ // 缩放控制 import { useCallback, useState } from 'react'; +import { ImageScale } from '../type'; -const useScale = (imageScale) => { - const [scale, setScale] = useState(1); +const useScale = (imageScale: ImageScale) => { + const [scale, setScale] = useState(() => imageScale.defaultScale ?? 1); const onZoom = useCallback(() => { setScale((scale) => { const newScale = scale + imageScale.step; @@ -21,7 +22,7 @@ const useScale = (imageScale) => { }); }, [imageScale]); - const onResetScale = useCallback(() => setScale(1), []); + const onResetScale = useCallback(() => setScale(imageScale.defaultScale ?? 1), [imageScale]); return { scale, diff --git a/src/image-viewer/image-viewer.en-US.md b/src/image-viewer/image-viewer.en-US.md index 57a1e380ce..818d441d63 100644 --- a/src/image-viewer/image-viewer.en-US.md +++ b/src/image-viewer/image-viewer.en-US.md @@ -8,9 +8,10 @@ name | type | default | description | required className | String | - | 类名 | N style | Object | - | 样式,Typescript:`React.CSSProperties` | N closeBtn | TNode | true | Typescript:`boolean \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N +closeOnEscKeydown | Boolean | true | trigger image viewer close event on `ESC` keydown | N closeOnOverlay | Boolean | - | \- | N draggable | Boolean | undefined | \- | N -imageScale | Object | - | Typescript:`ImageScale` `interface ImageScale { max: number; min: number; step: number }`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/image-viewer/type.ts) | N +imageScale | Object | - | Typescript:`ImageScale` `interface ImageScale { max: number; min: number; step: number; defaultScale?: number; }`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/image-viewer/type.ts) | N images | Array | [] | Typescript:`Array` `interface ImageInfo { mainImage: string \| File; thumbnail?: string \| File; download?: boolean }`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/image-viewer/type.ts) | N index | Number | 0 | \- | N defaultIndex | Number | 0 | uncontrolled property | N @@ -20,8 +21,8 @@ showOverlay | Boolean | undefined | \- | N title | TNode | - | preview title。Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N trigger | TNode | - | trigger element。Typescript:`TNode \| TNode<{ open: () => void }>`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N viewerScale | Object | - | Typescript:`ImageViewerScale` `interface ImageViewerScale { minWidth: number; minHeight: number }`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/image-viewer/type.ts) | N -visible | Boolean | false | \- | N -defaultVisible | Boolean | false | uncontrolled property | N +visible | Boolean | false | hide or show image viewer | N +defaultVisible | Boolean | false | hide or show image viewer。uncontrolled property | N zIndex | Number | - | \- | N onClose | Function | | Typescript:`(context: { trigger: 'close-btn' \| 'overlay' \| 'esc'; e: MouseEvent \| KeyboardEvent }) => void`
| N onIndexChange | Function | | Typescript:`(index: number, context: { trigger: 'prev' \| 'next' \| 'current' }) => void`
| N diff --git a/src/image-viewer/image-viewer.md b/src/image-viewer/image-viewer.md index 80211f04f5..e6eca93f3d 100644 --- a/src/image-viewer/image-viewer.md +++ b/src/image-viewer/image-viewer.md @@ -8,9 +8,10 @@ className | String | - | 类名 | N style | Object | - | 样式,TS 类型:`React.CSSProperties` | N closeBtn | TNode | true | 是否展示关闭按钮,值为 `true` 显示默认关闭按钮;值为 `false` 则不显示关闭按钮;也可以完全自定义关闭按钮。TS 类型:`boolean \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N +closeOnEscKeydown | Boolean | true | 按下 ESC 时是否触发图片预览器关闭事件 | N closeOnOverlay | Boolean | - | 是否在点击遮罩层时,触发预览关闭 | N draggable | Boolean | undefined | 是否允许拖拽调整位置。`mode=modal` 时,默认不允许拖拽;`mode=modeless` 时,默认允许拖拽 | N -imageScale | Object | - | 图片缩放相关配置。`imageScale.max` 缩放的最大比例;`imageScale.min` 缩放的最小比例;`imageScale.step` 缩放的步长速度。TS 类型:`ImageScale` `interface ImageScale { max: number; min: number; step: number }`。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/image-viewer/type.ts) | N +imageScale | Object | - | 图片缩放相关配置。`imageScale.max` 缩放的最大比例;`imageScale.min` 缩放的最小比例;`imageScale.step` 缩放的步长速度; `imageScale.defaultScale` 默认的缩放比例。TS 类型:`ImageScale` `interface ImageScale { max: number; min: number; step: number; defaultScale?: number; }`。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/image-viewer/type.ts) | N images | Array | [] | 图片数组。`mainImage` 表示主图,必传;`thumbnail` 表示缩略图,如果不存在,则使用主图显示;`download` 是否允许下载图片,默认允许下载。示例: `['img_url_1', 'img_url_2']`,`[{ thumbnail: 'small_image_url', mainImage: 'big_image_url', download: false }]`。TS 类型:`Array` `interface ImageInfo { mainImage: string \| File; thumbnail?: string \| File; download?: boolean }`。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/image-viewer/type.ts) | N index | Number | 0 | 当前预览图片所在的下标 | N defaultIndex | Number | 0 | 当前预览图片所在的下标。非受控属性 | N diff --git a/src/image-viewer/type.ts b/src/image-viewer/type.ts index 790113d7c1..b1fa3d2f40 100644 --- a/src/image-viewer/type.ts +++ b/src/image-viewer/type.ts @@ -13,6 +13,11 @@ export interface TdImageViewerProps { * @default true */ closeBtn?: TNode; + /** + * 按下 ESC 时是否触发图片预览器关闭事件 + * @default true + */ + closeOnEscKeydown?: boolean; /** * 是否在点击遮罩层时,触发预览关闭 */ @@ -22,7 +27,7 @@ export interface TdImageViewerProps { */ draggable?: boolean; /** - * 图片缩放相关配置。`imageScale.max` 缩放的最大比例;`imageScale.min` 缩放的最小比例;`imageScale.step` 缩放的步长速度 + * 图片缩放相关配置。`imageScale.max` 缩放的最大比例;`imageScale.min` 缩放的最小比例;`imageScale.step` 缩放的步长速度; `imageScale.defaultScale` 默认的缩放比例 */ imageScale?: ImageScale; /** @@ -94,6 +99,7 @@ export interface ImageScale { max: number; min: number; step: number; + defaultScale?: number; } export interface ImageInfo {