Skip to content

Commit e5b2560

Browse files
authored
Merge branch 'master' into dependabot/npm_and_yarn/types/jest-30.0.0
2 parents b8b31ad + d5540a7 commit e5b2560

File tree

11 files changed

+206
-46
lines changed

11 files changed

+206
-46
lines changed

assets/index.less

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
&-img {
2929
width: 100%;
3030
height: auto;
31+
overflow: hidden;
3132
&-placeholder {
3233
background-color: @background-color;
3334
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjhweCIgaGVpZ2h0PSIyMnB4IiB2aWV3Qm94PSIwIDAgMjggMjIiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDU1LjIgKDc4MTgxKSAtIGh0dHBzOi8vc2tldGNoYXBwLmNvbSAtLT4KICAgIDx0aXRsZT5pbWFnZS1maWxs5aSH5Lu9PC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGcgaWQ9Iuafpeeci+WbvueJh+S8mOWMljQuMCIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9IuWKoOi9veWbvueJhyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTU3Mi4wMDAwMDAsIC01MDYuMDAwMDAwKSI+CiAgICAgICAgICAgIDxnIGlkPSJpbWFnZS1maWxs5aSH5Lu9IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1NzAuMDAwMDAwLCA1MDEuMDAwMDAwKSI+CiAgICAgICAgICAgICAgICA8cmVjdCBpZD0iUmVjdGFuZ2xlIiBmaWxsPSIjMDAwMDAwIiBvcGFjaXR5PSIwIiB4PSIwIiB5PSIwIiB3aWR0aD0iMzIiIGhlaWdodD0iMzIiPjwvcmVjdD4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0yOSw1IEwzLDUgQzIuNDQ2ODc1LDUgMiw1LjQ0Njg3NSAyLDYgTDIsMjYgQzIsMjYuNTUzMTI1IDIuNDQ2ODc1LDI3IDMsMjcgTDI5LDI3IEMyOS41NTMxMjUsMjcgMzAsMjYuNTUzMTI1IDMwLDI2IEwzMCw2IEMzMCw1LjQ0Njg3NSAyOS41NTMxMjUsNSAyOSw1IFogTTEwLjU2MjUsOS41IEMxMS42NjU2MjUsOS41IDEyLjU2MjUsMTAuMzk2ODc1IDEyLjU2MjUsMTEuNSBDMTIuNTYyNSwxMi42MDMxMjUgMTEuNjY1NjI1LDEzLjUgMTAuNTYyNSwxMy41IEM5LjQ1OTM3NSwxMy41IDguNTYyNSwxMi42MDMxMjUgOC41NjI1LDExLjUgQzguNTYyNSwxMC4zOTY4NzUgOS40NTkzNzUsOS41IDEwLjU2MjUsOS41IFogTTI2LjYyMTg3NSwyMy4xNTkzNzUgQzI2LjU3ODEyNSwyMy4xOTY4NzUgMjYuNTE4NzUsMjMuMjE4NzUgMjYuNDU5Mzc1LDIzLjIxODc1IEw1LjUzNzUsMjMuMjE4NzUgQzUuNCwyMy4yMTg3NSA1LjI4NzUsMjMuMTA2MjUgNS4yODc1LDIyLjk2ODc1IEM1LjI4NzUsMjIuOTA5Mzc1IDUuMzA5Mzc1LDIyLjg1MzEyNSA1LjM0Njg3NSwyMi44MDYyNSBMMTAuNjY4NzUsMTYuNDkzNzUgQzEwLjc1NjI1LDE2LjM4NzUgMTAuOTE1NjI1LDE2LjM3NSAxMS4wMjE4NzUsMTYuNDYyNSBDMTEuMDMxMjUsMTYuNDcxODc1IDExLjA0Mzc1LDE2LjQ4MTI1IDExLjA1MzEyNSwxNi40OTM3NSBMMTQuMTU5Mzc1LDIwLjE4MTI1IEwxOS4xLDE0LjMyMTg3NSBDMTkuMTg3NSwxNC4yMTU2MjUgMTkuMzQ2ODc1LDE0LjIwMzEyNSAxOS40NTMxMjUsMTQuMjkwNjI1IEMxOS40NjI1LDE0LjMgMTkuNDc1LDE0LjMwOTM3NSAxOS40ODQzNzUsMTQuMzIxODc1IEwyNi42NTkzNzUsMjIuODA5Mzc1IEMyNi43NDA2MjUsMjIuOTEyNSAyNi43MjgxMjUsMjMuMDcxODc1IDI2LjYyMTg3NSwyMy4xNTkzNzUgWiIgaWQ9IlNoYXBlIiBmaWxsPSIjRThFOEU4Ij48L3BhdGg+CiAgICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==);
@@ -36,6 +37,46 @@
3637
}
3738
}
3839

40+
&-cover {
41+
position: absolute;
42+
right: 0;
43+
bottom: 0;
44+
left: 0;
45+
display: flex;
46+
flex-direction: column;
47+
align-items: center;
48+
justify-content: center;
49+
color: #fff;
50+
background-color: rgba(0, 0, 0, 0.5);
51+
transition: background-color 0.3s;
52+
53+
&-top {
54+
top: 0;
55+
left: 0;
56+
right: 0;
57+
height: max-content;
58+
justify-content: flex-start;
59+
padding: 5px;
60+
}
61+
&-bottom {
62+
bottom: 0;
63+
left: 0;
64+
right: 0;
65+
height: max-content;
66+
justify-content: flex-end;
67+
padding: 5px;
68+
}
69+
&-center {
70+
top: 50%;
71+
left: 0;
72+
right: 0;
73+
transform: translateY(-50%);
74+
height: 100%;
75+
align-items: center;
76+
justify-content: center;
77+
}
78+
}
79+
3980
&-placeholder {
4081
.box;
4182
}

docs/demo/coverPlacement.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: coverPlacement
3+
nav:
4+
title: Demo
5+
path: /demo
6+
---
7+
8+
<code src="../examples/coverPlacement.tsx"></code>

docs/examples/coverPlacement.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import type { CoverConfig } from '@rc-component/image';
2+
import Image from '@rc-component/image';
3+
import * as React from 'react';
4+
import '../../assets/index.less';
5+
import { defaultIcons } from './common';
6+
7+
export default function Base() {
8+
const [placement, setPlacement] = React.useState<CoverConfig["placement"]>('center');
9+
return (
10+
<div>
11+
<div>
12+
<label htmlFor="placement">
13+
<span>placement:</span>
14+
</label>
15+
<select id="placement" onChange={e => setPlacement(e.target.value as CoverConfig["placement"])} value={placement}>
16+
<option value="top">top</option>
17+
<option value="bottom">bottom</option>
18+
<option value="center">center</option>
19+
</select>
20+
</div>
21+
<br />
22+
<Image
23+
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
24+
width={200}
25+
onClick={() => {
26+
console.log('click');
27+
}}
28+
preview={{
29+
icons: defaultIcons,
30+
onOpenChange: open => {
31+
console.log('open', open);
32+
},
33+
zIndex: 9999,
34+
cover: {
35+
coverNode: 'Click to Preview',
36+
placement,
37+
},
38+
}}
39+
/>
40+
</div>
41+
);
42+
}

package.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rc-component/image",
3-
"version": "1.4.0",
3+
"version": "1.5.1",
44
"description": "React easy to use image component",
55
"keywords": [
66
"react",
@@ -36,22 +36,23 @@
3636
"prettier": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
3737
"start": "dumi dev",
3838
"test": "rc-test",
39+
"test:update": "rc-test -u",
3940
"tsc": "bunx tsc --noEmit"
4041
},
4142
"dependencies": {
42-
"@rc-component/portal": "^2.0.0",
4343
"@rc-component/motion": "^1.0.0",
44-
"@rc-component/util": "^1.0.0",
45-
"classnames": "^2.2.6"
44+
"@rc-component/portal": "^2.0.0",
45+
"@rc-component/util": "^1.3.0",
46+
"clsx": "^2.1.1"
4647
},
4748
"devDependencies": {
4849
"@ant-design/icons": "^5.0.1",
4950
"@rc-component/father-plugin": "^2.0.2",
5051
"@rc-component/np": "^1.0.0",
5152
"@testing-library/jest-dom": "^6.4.0",
5253
"@testing-library/react": "^15.0.6",
53-
"@types/classnames": "^2.2.10",
5454
"@types/jest": "^30.0.0",
55+
"@types/node": "^24.5.2",
5556
"@types/react": "^18.0.0",
5657
"@types/react-dom": "^18.0.0",
5758
"@umijs/fabric": "^4.0.1",

src/Image.tsx

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import useMergedState from '@rc-component/util/lib/hooks/useMergedState';
2-
import classnames from 'classnames';
1+
import useControlledState from '@rc-component/util/lib/hooks/useControlledState';
2+
import { clsx } from 'clsx';
33
import * as React from 'react';
44
import { useContext, useMemo, useState } from 'react';
55
import type { InternalPreviewConfig, PreviewSemanticName, ToolbarRenderInfoType } from './Preview';
@@ -19,8 +19,12 @@ export interface ImgInfo {
1919
height: string | number;
2020
}
2121

22+
export interface CoverConfig {
23+
coverNode?: React.ReactNode;
24+
placement?: 'top' | 'bottom' | 'center';
25+
}
2226
export interface PreviewConfig extends Omit<InternalPreviewConfig, 'countRender'> {
23-
cover?: React.ReactNode;
27+
cover?: React.ReactNode | CoverConfig;
2428

2529
// Similar to InternalPreviewConfig but not have `current`
2630
imageRender?: (
@@ -121,10 +125,18 @@ const ImageInternal: CompoundedComponent<ImageProps> = props => {
121125
...restProps
122126
}: PreviewConfig = preview && typeof preview === 'object' ? preview : {};
123127

128+
const coverPlacement =
129+
typeof cover === 'object' && (cover as CoverConfig).placement
130+
? (cover as CoverConfig).placement || 'center'
131+
: 'center';
132+
133+
const coverNode =
134+
typeof cover === 'object' && (cover as CoverConfig).coverNode
135+
? (cover as CoverConfig).coverNode
136+
: (cover as React.ReactNode);
137+
124138
// ============================ Open ============================
125-
const [isShowPreview, setShowPreview] = useMergedState(!!previewOpen, {
126-
value: previewOpen,
127-
});
139+
const [isShowPreview, setShowPreview] = useControlledState(!!previewOpen, previewOpen);
128140

129141
const [mousePosition, setMousePosition] = useState<null | { x: number; y: number }>(null);
130142

@@ -196,7 +208,7 @@ const ImageInternal: CompoundedComponent<ImageProps> = props => {
196208
<>
197209
<div
198210
{...otherProps}
199-
className={classnames(prefixCls, rootClassName, classNames.root, {
211+
className={clsx(prefixCls, rootClassName, classNames.root, {
200212
[`${prefixCls}-error`]: status === 'error',
201213
})}
202214
onClick={canPreview ? onPreview : onClick}
@@ -208,7 +220,7 @@ const ImageInternal: CompoundedComponent<ImageProps> = props => {
208220
>
209221
<img
210222
{...imgCommonProps}
211-
className={classnames(
223+
className={clsx(
212224
`${prefixCls}-img`,
213225
{
214226
[`${prefixCls}-img-placeholder`]: placeholder === true,
@@ -237,13 +249,17 @@ const ImageInternal: CompoundedComponent<ImageProps> = props => {
237249
{/* Preview Click Mask */}
238250
{cover !== false && canPreview && (
239251
<div
240-
className={classnames(`${prefixCls}-cover`, classNames.cover)}
252+
className={clsx(
253+
`${prefixCls}-cover`,
254+
classNames.cover,
255+
`${prefixCls}-cover-${coverPlacement}`,
256+
)}
241257
style={{
242258
display: style?.display === 'none' ? 'none' : undefined,
243259
...styles.cover,
244260
}}
245261
>
246-
{cover}
262+
{coverNode}
247263
</div>
248264
)}
249265
</div>
@@ -262,7 +278,7 @@ const ImageInternal: CompoundedComponent<ImageProps> = props => {
262278
{...restProps}
263279
classNames={classNames?.popup}
264280
styles={styles?.popup}
265-
rootClassName={classnames(previewRootClassName, rootClassName)}
281+
rootClassName={clsx(previewRootClassName, rootClassName)}
266282
/>
267283
)}
268284
</>

src/Preview/Footer.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import classnames from 'classnames';
1+
import { clsx } from 'clsx';
22
import * as React from 'react';
33
import type { Actions, PreviewProps } from '.';
44
import type { ImgInfo } from '../Image';
@@ -97,7 +97,7 @@ export default function Footer(props: FooterProps) {
9797
return (
9898
<div
9999
key={type}
100-
className={classnames(actionCls, `${actionCls}-${type}`, {
100+
className={clsx(actionCls, `${actionCls}-${type}`, {
101101
[`${actionCls}-disabled`]: !!disabled,
102102
})}
103103
onClick={onClick}
@@ -164,7 +164,7 @@ export default function Footer(props: FooterProps) {
164164
});
165165

166166
const actionsNode = (
167-
<div className={classnames(`${prefixCls}-actions`, classNames.actions)} style={styles.actions}>
167+
<div className={clsx(`${prefixCls}-actions`, classNames.actions)} style={styles.actions}>
168168
{flipYNode}
169169
{flipXNode}
170170
{rotateLeftNode}
@@ -176,7 +176,7 @@ export default function Footer(props: FooterProps) {
176176

177177
// >>>>> Render
178178
return (
179-
<div className={classnames(`${prefixCls}-footer`, classNames.footer)} style={styles.footer}>
179+
<div className={clsx(`${prefixCls}-footer`, classNames.footer)} style={styles.footer}>
180180
{progressNode}
181181
{actionsRender
182182
? actionsRender(actionsNode, {

src/Preview/PrevNext.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import classNames from 'classnames';
1+
import { clsx } from 'clsx';
22
import * as React from 'react';
33
import type { OperationIcons } from '.';
44

@@ -24,15 +24,15 @@ export default function PrevNext(props: PrevNextProps) {
2424
return (
2525
<>
2626
<div
27-
className={classNames(switchCls, `${switchCls}-prev`, {
27+
className={clsx(switchCls, `${switchCls}-prev`, {
2828
[`${switchCls}-disabled`]: current === 0,
2929
})}
3030
onClick={() => onActive(-1)}
3131
>
3232
{prev ?? left}
3333
</div>
3434
<div
35-
className={classNames(switchCls, `${switchCls}-next`, {
35+
className={clsx(switchCls, `${switchCls}-next`, {
3636
[`${switchCls}-disabled`]: current === count - 1,
3737
})}
3838
onClick={() => onActive(1)}

src/Preview/index.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Portal, { type PortalProps } from '@rc-component/portal';
33
import { useEvent } from '@rc-component/util';
44
import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect';
55
import KeyCode from '@rc-component/util/lib/KeyCode';
6-
import classnames from 'classnames';
6+
import { clsx } from 'clsx';
77
import React, { useContext, useEffect, useRef, useState } from 'react';
88
import { PreviewGroupContext } from '../context';
99
import type { TransformAction, TransformType } from '../hooks/useImageTransform';
@@ -410,27 +410,23 @@ const Preview: React.FC<PreviewProps> = props => {
410410

411411
return (
412412
<div
413-
className={classnames(prefixCls, rootClassName, classNames.root, motionClassName, {
413+
className={clsx(prefixCls, rootClassName, classNames.root, motionClassName, {
414414
[`${prefixCls}-moving`]: isMoving,
415415
})}
416416
style={mergedStyle}
417417
>
418418
{/* Mask */}
419419
<div
420-
className={classnames(`${prefixCls}-mask`, classNames.mask)}
420+
className={clsx(`${prefixCls}-mask`, classNames.mask)}
421421
style={styles.mask}
422422
onClick={onClose}
423423
/>
424424

425425
{/* Body */}
426-
<div className={classnames(`${prefixCls}-body`, classNames.body)} style={bodyStyle}>
426+
<div className={clsx(`${prefixCls}-body`, classNames.body)} style={bodyStyle}>
427427
{/* Preview Image */}
428428
{imageRender
429-
? imageRender(imgNode, {
430-
transform,
431-
image,
432-
...(groupContext ? { current } : {}),
433-
})
429+
? imageRender(imgNode, { transform, image, ...(groupContext ? { current } : {}) })
434430
: imgNode}
435431
</div>
436432

src/PreviewGroup.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import useMergedState from '@rc-component/util/lib/hooks/useMergedState';
1+
import useControlledState from '@rc-component/util/lib/hooks/useControlledState';
2+
import useEvent from '@rc-component/util/lib/hooks/useEvent';
23
import * as React from 'react';
34
import { useState } from 'react';
45
import type { ImgInfo } from './Image';
@@ -60,20 +61,19 @@ const Group: React.FC<PreviewGroupProps> = ({
6061

6162
// ========================= Preview ==========================
6263
// >>> Index
63-
const [current, setCurrent] = useMergedState(0, {
64-
value: currentIndex,
65-
});
64+
const [current, setCurrent] = useControlledState(0, currentIndex);
6665

6766
const [keepOpenIndex, setKeepOpenIndex] = useState(false);
6867

6968
// >>> Image
7069
const { src, ...imgCommonProps } = mergedItems[current]?.data || {};
7170
// >>> Visible
72-
const [isShowPreview, setShowPreview] = useMergedState(!!previewOpen, {
73-
value: previewOpen,
74-
onChange: val => {
75-
onOpenChange?.(val, { current });
76-
},
71+
const [isShowPreview, setShowPreview] = useControlledState(!!previewOpen, previewOpen);
72+
const triggerShowPreview = useEvent((next: boolean) => {
73+
setShowPreview(next);
74+
if (next !== isShowPreview) {
75+
onOpenChange?.(next, { current });
76+
}
7777
});
7878

7979
// >>> Position
@@ -87,7 +87,7 @@ const Group: React.FC<PreviewGroupProps> = ({
8787

8888
setCurrent(index < 0 ? 0 : index);
8989

90-
setShowPreview(true);
90+
triggerShowPreview(true);
9191
setMousePosition({ x: mouseX, y: mouseY });
9292

9393
setKeepOpenIndex(true);
@@ -114,7 +114,7 @@ const Group: React.FC<PreviewGroupProps> = ({
114114
};
115115

116116
const onPreviewClose = () => {
117-
setShowPreview(false);
117+
triggerShowPreview(false);
118118
setMousePosition(null);
119119
};
120120

tests/__snapshots__/basic.test.tsx.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ exports[`Basic snapshot 1`] = `
1111
width="200"
1212
/>
1313
<div
14-
class="rc-image-cover"
14+
class="rc-image-cover rc-image-cover-center"
1515
/>
1616
</div>
1717
`;

0 commit comments

Comments
 (0)