Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions assets/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
&-img {
width: 100%;
height: auto;
overflow: hidden;
&-placeholder {
background-color: @background-color;
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjhweCIgaGVpZ2h0PSIyMnB4IiB2aWV3Qm94PSIwIDAgMjggMjIiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDU1LjIgKDc4MTgxKSAtIGh0dHBzOi8vc2tldGNoYXBwLmNvbSAtLT4KICAgIDx0aXRsZT5pbWFnZS1maWxs5aSH5Lu9PC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGcgaWQ9Iuafpeeci+WbvueJh+S8mOWMljQuMCIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9IuWKoOi9veWbvueJhyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTU3Mi4wMDAwMDAsIC01MDYuMDAwMDAwKSI+CiAgICAgICAgICAgIDxnIGlkPSJpbWFnZS1maWxs5aSH5Lu9IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1NzAuMDAwMDAwLCA1MDEuMDAwMDAwKSI+CiAgICAgICAgICAgICAgICA8cmVjdCBpZD0iUmVjdGFuZ2xlIiBmaWxsPSIjMDAwMDAwIiBvcGFjaXR5PSIwIiB4PSIwIiB5PSIwIiB3aWR0aD0iMzIiIGhlaWdodD0iMzIiPjwvcmVjdD4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0yOSw1IEwzLDUgQzIuNDQ2ODc1LDUgMiw1LjQ0Njg3NSAyLDYgTDIsMjYgQzIsMjYuNTUzMTI1IDIuNDQ2ODc1LDI3IDMsMjcgTDI5LDI3IEMyOS41NTMxMjUsMjcgMzAsMjYuNTUzMTI1IDMwLDI2IEwzMCw2IEMzMCw1LjQ0Njg3NSAyOS41NTMxMjUsNSAyOSw1IFogTTEwLjU2MjUsOS41IEMxMS42NjU2MjUsOS41IDEyLjU2MjUsMTAuMzk2ODc1IDEyLjU2MjUsMTEuNSBDMTIuNTYyNSwxMi42MDMxMjUgMTEuNjY1NjI1LDEzLjUgMTAuNTYyNSwxMy41IEM5LjQ1OTM3NSwxMy41IDguNTYyNSwxMi42MDMxMjUgOC41NjI1LDExLjUgQzguNTYyNSwxMC4zOTY4NzUgOS40NTkzNzUsOS41IDEwLjU2MjUsOS41IFogTTI2LjYyMTg3NSwyMy4xNTkzNzUgQzI2LjU3ODEyNSwyMy4xOTY4NzUgMjYuNTE4NzUsMjMuMjE4NzUgMjYuNDU5Mzc1LDIzLjIxODc1IEw1LjUzNzUsMjMuMjE4NzUgQzUuNCwyMy4yMTg3NSA1LjI4NzUsMjMuMTA2MjUgNS4yODc1LDIyLjk2ODc1IEM1LjI4NzUsMjIuOTA5Mzc1IDUuMzA5Mzc1LDIyLjg1MzEyNSA1LjM0Njg3NSwyMi44MDYyNSBMMTAuNjY4NzUsMTYuNDkzNzUgQzEwLjc1NjI1LDE2LjM4NzUgMTAuOTE1NjI1LDE2LjM3NSAxMS4wMjE4NzUsMTYuNDYyNSBDMTEuMDMxMjUsMTYuNDcxODc1IDExLjA0Mzc1LDE2LjQ4MTI1IDExLjA1MzEyNSwxNi40OTM3NSBMMTQuMTU5Mzc1LDIwLjE4MTI1IEwxOS4xLDE0LjMyMTg3NSBDMTkuMTg3NSwxNC4yMTU2MjUgMTkuMzQ2ODc1LDE0LjIwMzEyNSAxOS40NTMxMjUsMTQuMjkwNjI1IEMxOS40NjI1LDE0LjMgMTkuNDc1LDE0LjMwOTM3NSAxOS40ODQzNzUsMTQuMzIxODc1IEwyNi42NTkzNzUsMjIuODA5Mzc1IEMyNi43NDA2MjUsMjIuOTEyNSAyNi43MjgxMjUsMjMuMDcxODc1IDI2LjYyMTg3NSwyMy4xNTkzNzUgWiIgaWQ9IlNoYXBlIiBmaWxsPSIjRThFOEU4Ij48L3BhdGg+CiAgICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==);
Expand All @@ -36,6 +37,46 @@
}
}

&-cover {
position: absolute;
right: 0;
bottom: 0;
left: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #fff;
background-color: rgba(0, 0, 0, 0.5);
transition: background-color 0.3s;

&-top {
top: 0;
left: 0;
right: 0;
height: max-content;
justify-content: flex-start;
padding: 5px;
}
&-bottom {
bottom: 0;
left: 0;
right: 0;
height: max-content;
justify-content: flex-end;
padding: 5px;
}
&-center {
top: 50%;
left: 0;
right: 0;
transform: translateY(-50%);
height: 100%;
align-items: center;
justify-content: center;
}
}

&-placeholder {
.box;
}
Expand Down
8 changes: 8 additions & 0 deletions docs/demo/coverPlacement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: coverPlacement
nav:
title: Demo
path: /demo
---

<code src="../examples/coverPlacement.tsx"></code>
42 changes: 42 additions & 0 deletions docs/examples/coverPlacement.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { CoverConfig } from '@rc-component/image';
import Image from '@rc-component/image';
import * as React from 'react';
import '../../assets/index.less';
import { defaultIcons } from './common';

export default function Base() {
const [placement, setPlacement] = React.useState<CoverConfig["placement"]>('center');
return (
<div>
<div>
<label htmlFor="placement">
<span>placement:</span>
</label>
<select id="placement" onChange={e => setPlacement(e.target.value as CoverConfig["placement"])} value={placement}>
<option value="top">top</option>
<option value="bottom">bottom</option>
<option value="center">center</option>
</select>
</div>
<br />
<Image
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
width={200}
onClick={() => {
console.log('click');
}}
preview={{
icons: defaultIcons,
onOpenChange: open => {
console.log('open', open);
},
zIndex: 9999,
cover: {
coverNode: 'Click to Preview',
placement,
},
}}
/>
</div>
);
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"prettier": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
"start": "dumi dev",
"test": "rc-test",
"test:update": "rc-test -u",
"tsc": "bunx tsc --noEmit"
},
"dependencies": {
Expand Down
18 changes: 15 additions & 3 deletions src/Image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ export interface ImgInfo {
height: string | number;
}

export interface CoverConfig {
coverNode?: React.ReactNode;
placement?: 'top' | 'bottom' | 'center';
}
export interface PreviewConfig extends Omit<InternalPreviewConfig, 'countRender'> {
cover?: React.ReactNode;
cover?: React.ReactNode | CoverConfig;

// Similar to InternalPreviewConfig but not have `current`
imageRender?: (
Expand Down Expand Up @@ -121,6 +125,14 @@ const ImageInternal: CompoundedComponent<ImageProps> = props => {
...restProps
}: PreviewConfig = preview && typeof preview === 'object' ? preview : {};

const coverPlacement = typeof cover === 'object' && (cover as CoverConfig).placement ?
(cover as CoverConfig).placement || 'center' :
'center';

const coverNode = typeof cover === 'object' && (cover as CoverConfig).coverNode ?
(cover as CoverConfig).coverNode :
cover as React.ReactNode;

// ============================ Open ============================
const [isShowPreview, setShowPreview] = useMergedState(!!previewOpen, {
value: previewOpen,
Expand Down Expand Up @@ -237,13 +249,13 @@ const ImageInternal: CompoundedComponent<ImageProps> = props => {
{/* Preview Click Mask */}
{cover !== false && canPreview && (
<div
className={classnames(`${prefixCls}-cover`, classNames.cover)}
className={classnames(`${prefixCls}-cover`, classNames.cover, `${prefixCls}-cover-${coverPlacement}`)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里有支持语义化结构,可以直接在 antd 里通过 classNames.cover 来扩展

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不需要在rc支持placement么

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不需要再 rc 组件支持 placement api 的话,我周末直接在 antd 上搞吧

style={{
display: style?.display === 'none' ? 'none' : undefined,
...styles.cover,
}}
>
{cover}
{coverNode}
</div>
)}
</div>
Expand Down
2 changes: 1 addition & 1 deletion tests/__snapshots__/basic.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ exports[`Basic snapshot 1`] = `
width="200"
/>
<div
class="rc-image-cover"
class="rc-image-cover rc-image-cover-center"
/>
</div>
`;
60 changes: 58 additions & 2 deletions tests/basic.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import { fireEvent, render } from '@testing-library/react';
import { act, fireEvent, render } from '@testing-library/react';
import React from 'react';
import Image from '../src';
import Image, { CoverConfig } from '../src';

describe('Basic', () => {
beforeEach(() => {
jest.useFakeTimers();
});

afterEach(() => {
jest.useRealTimers();
});

it('snapshot', () => {
const { asFragment } = render(
<Image
Expand Down Expand Up @@ -101,4 +109,52 @@ describe('Basic', () => {
const operationsElement = baseElement.querySelector('.rc-image-preview');
expect(operationsElement).toHaveStyle({ zIndex: 9999 });
});
it('cover placement should work', () => {
const App = () => {
const [placement, setPlacement] = React.useState<'top' | 'bottom' | 'center'>('center');
return (
<>
<select
id="placement"
onChange={e => setPlacement(e.target.value as CoverConfig['placement'])}
value={placement}
>
<option value="top">top</option>
<option value="bottom">bottom</option>
<option value="center">center</option>
</select>
<Image
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
preview={{
cover: {
coverNode: 'Click to Preview',
placement: placement as CoverConfig['placement'],
},
}}
/>
</>
);
};
const { container } = render(<App />);
const coverElement = container.querySelector('.rc-image-cover');
expect(coverElement).toHaveClass('rc-image-cover-center');

fireEvent.change(container.querySelector('#placement'), {
target: { value: 'top' },
});
// Wait for the state update to take effect
act(() => {
jest.runAllTimers();
});
expect(coverElement).toHaveClass('rc-image-cover-top');

fireEvent.change(container.querySelector('#placement'), {
target: { value: 'bottom' },
});
// Wait for the state update to take effect
act(() => {
jest.runAllTimers();
});
expect(coverElement).toHaveClass('rc-image-cover-bottom');
});
});