Skip to content

Commit 651c080

Browse files
kiner-tangtangwenhui1
andauthored
feat: support custom cover placement (#433)
Co-authored-by: tangwenhui1 <[email protected]>
1 parent e5a5685 commit 651c080

File tree

7 files changed

+166
-6
lines changed

7 files changed

+166
-6
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
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": {

src/Image.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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,6 +125,14 @@ const ImageInternal: CompoundedComponent<ImageProps> = props => {
121125
...restProps
122126
}: PreviewConfig = preview && typeof preview === 'object' ? preview : {};
123127

128+
const coverPlacement = typeof cover === 'object' && (cover as CoverConfig).placement ?
129+
(cover as CoverConfig).placement || 'center' :
130+
'center';
131+
132+
const coverNode = typeof cover === 'object' && (cover as CoverConfig).coverNode ?
133+
(cover as CoverConfig).coverNode :
134+
cover as React.ReactNode;
135+
124136
// ============================ Open ============================
125137
const [isShowPreview, setShowPreview] = useMergedState(!!previewOpen, {
126138
value: previewOpen,
@@ -237,13 +249,13 @@ const ImageInternal: CompoundedComponent<ImageProps> = props => {
237249
{/* Preview Click Mask */}
238250
{cover !== false && canPreview && (
239251
<div
240-
className={classnames(`${prefixCls}-cover`, classNames.cover)}
252+
className={classnames(`${prefixCls}-cover`, classNames.cover, `${prefixCls}-cover-${coverPlacement}`)}
241253
style={{
242254
display: style?.display === 'none' ? 'none' : undefined,
243255
...styles.cover,
244256
}}
245257
>
246-
{cover}
258+
{coverNode}
247259
</div>
248260
)}
249261
</div>

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
`;

tests/basic.test.tsx

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
import { fireEvent, render } from '@testing-library/react';
1+
import { act, fireEvent, render } from '@testing-library/react';
22
import React from 'react';
3-
import Image from '../src';
3+
import Image, { CoverConfig } from '../src';
44

55
describe('Basic', () => {
6+
beforeEach(() => {
7+
jest.useFakeTimers();
8+
});
9+
10+
afterEach(() => {
11+
jest.useRealTimers();
12+
});
13+
614
it('snapshot', () => {
715
const { asFragment } = render(
816
<Image
@@ -101,4 +109,52 @@ describe('Basic', () => {
101109
const operationsElement = baseElement.querySelector('.rc-image-preview');
102110
expect(operationsElement).toHaveStyle({ zIndex: 9999 });
103111
});
112+
it('cover placement should work', () => {
113+
const App = () => {
114+
const [placement, setPlacement] = React.useState<'top' | 'bottom' | 'center'>('center');
115+
return (
116+
<>
117+
<select
118+
id="placement"
119+
onChange={e => setPlacement(e.target.value as CoverConfig['placement'])}
120+
value={placement}
121+
>
122+
<option value="top">top</option>
123+
<option value="bottom">bottom</option>
124+
<option value="center">center</option>
125+
</select>
126+
<Image
127+
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
128+
preview={{
129+
cover: {
130+
coverNode: 'Click to Preview',
131+
placement: placement as CoverConfig['placement'],
132+
},
133+
}}
134+
/>
135+
</>
136+
);
137+
};
138+
const { container } = render(<App />);
139+
const coverElement = container.querySelector('.rc-image-cover');
140+
expect(coverElement).toHaveClass('rc-image-cover-center');
141+
142+
fireEvent.change(container.querySelector('#placement'), {
143+
target: { value: 'top' },
144+
});
145+
// Wait for the state update to take effect
146+
act(() => {
147+
jest.runAllTimers();
148+
});
149+
expect(coverElement).toHaveClass('rc-image-cover-top');
150+
151+
fireEvent.change(container.querySelector('#placement'), {
152+
target: { value: 'bottom' },
153+
});
154+
// Wait for the state update to take effect
155+
act(() => {
156+
jest.runAllTimers();
157+
});
158+
expect(coverElement).toHaveClass('rc-image-cover-bottom');
159+
});
104160
});

0 commit comments

Comments
 (0)