Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ImagePreview): add close-on-click-image prop #12566

Merged
merged 1 commit into from
Jan 13, 2024
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
2 changes: 2 additions & 0 deletions packages/vant/src/image-preview/ImagePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export const imagePreviewProps = {
startPosition: makeNumericProp(0),
showIndicators: Boolean,
closeOnPopstate: truthProp,
closeOnClickImage: truthProp,
closeOnClickOverlay: truthProp,
closeIconPosition: makeStringProp<PopupCloseIconPosition>('top-right'),
teleport: [String, Object] as PropType<TeleportProps['to']>,
Expand Down Expand Up @@ -185,6 +186,7 @@ export default defineComponent({
rootHeight={state.rootHeight}
disableZoom={state.disableZoom}
doubleScale={props.doubleScale}
closeOnClickImage={props.closeOnClickImage}
closeOnClickOverlay={props.closeOnClickOverlay}
onScale={emitScale}
onClose={emitClose}
Expand Down
7 changes: 6 additions & 1 deletion packages/vant/src/image-preview/ImagePreviewItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const imagePreviewItemProps = {
rootHeight: makeRequiredProp(Number),
disableZoom: Boolean,
doubleScale: Boolean,
closeOnClickImage: Boolean,
closeOnClickOverlay: Boolean,
};

Expand Down Expand Up @@ -251,8 +252,12 @@ export default defineComponent({
};

const checkClose = (event: TouchEvent) => {
const isClickOverlay = event.target === swipeItem.value?.$el;
const swipeItemEl: HTMLElement = swipeItem.value?.$el;
const imageEl = swipeItemEl.firstElementChild;
Copy link
Member

Choose a reason for hiding this comment

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

When using slots, it's also possible that the image element is not the first element, should we consider that case

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fortunately, the parent element of the image slot is always fixed, so we don't have to consider this situation.

<div class={bem('image-wrap')}>
  {slots.image({ src: props.src })}
</div>

Copy link
Member

Choose a reason for hiding this comment

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

nice 😄

const isClickOverlay = event.target === swipeItemEl;
const isClickImage = imageEl?.contains(event.target as HTMLElement);

if (!props.closeOnClickImage && isClickImage) return;
if (!props.closeOnClickOverlay && isClickOverlay) return;

emit('close');
Expand Down
10 changes: 8 additions & 2 deletions packages/vant/src/image-preview/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,14 @@ export default {

### Use image slot

When using ImagePreview component, you can custom the image through the `image` slot, such as render a video content.
When using ImagePreview component, you can custom the image through the `image` slot, such as render a video content. In this example, you can also set `close-on-click-image` prop to `false`, so that the preview won't be accidentally closed when you click on the video.

```html
<van-image-preview v-model:show="show" :images="images">
<van-image-preview
v-model:show="show"
:images="images"
:close-on-click-image="false"
>
<template #image="{ src }">
<video style="width: 100%;" controls>
<source :src="src" />
Expand Down Expand Up @@ -206,6 +210,7 @@ Vant exports following ImagePreview utility functions:
| onScale | Emitted when scaling current image | _Function_ | - |
| closeOnPopstate | Whether to close when popstate | _boolean_ | `true` |
| doubleScale `v4.7.2` | Whether to enable double tap zoom gesture. When disabled, the image preview will be closed immediately upon clicking | _boolean_ | `true` |
| closeOnClickImage `v4.8.3` | Whether to close when image is clicked | _boolean_ | `true` |
| closeOnClickOverlay `v4.6.4` | Whether to close when overlay is clicked | _boolean_ | `true` |
| beforeClose | Callback function before close | _(action) => boolean \| Promise_ | - |
| className | Custom className | _string \| Array \| object_ | - |
Expand Down Expand Up @@ -233,6 +238,7 @@ Vant exports following ImagePreview utility functions:
| double-scale | Whether to enable double tap zoom gesture. When disabled, the image preview will be closed immediately upon clicking | _boolean_ | `true` |
| before-close | Callback function before close | _(action: number) => boolean \| Promise\<boolean\>_ | - |
| close-on-popstate | Whether to close when popstate | _boolean_ | `true` |
| close-on-click-image `v4.8.3` | Whether to close when image is clicked | _boolean_ | `true` |
| close-on-click-overlay `v4.6.4` | Whether to close when overlay is clicked | _boolean_ | `true` |
| class-name | Custom className (apply to Popup in image preview) | _string \| Array \| object_ | - |
| max-zoom | Max zoom | _number \| string_ | `3` |
Expand Down
10 changes: 8 additions & 2 deletions packages/vant/src/image-preview/README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,14 @@ export default {

### 使用 image 插槽

当以组件调用的方式使用 ImagePreview 时,可以通过 `image` 插槽来插入自定义的内容,比如展示一个视频内容。
当以组件调用的方式使用 ImagePreview 时,可以通过 `image` 插槽来插入自定义的内容,比如展示一个视频内容。在这个例子中,你可以将 `close-on-click-image` 属性设置为 `false`,这样当你点击视频时就不会意外关闭预览了。

```html
<van-image-preview v-model:show="show" :images="images">
<van-image-preview
v-model:show="show"
:images="images"
:close-on-click-image="false"
>
<template #image="{ src }">
<video style="width: 100%;" controls>
<source :src="src" />
Expand Down Expand Up @@ -210,6 +214,7 @@ Vant 中导出了以下 ImagePreview 相关的辅助函数:
| onScale | 缩放图片时的回调函数,回调参数为当前索引和当前缩放值组成的对象 | _Function_ | - |
| beforeClose | 关闭前的回调函数,返回 `false` 可阻止关闭,支持返回 Promise | _(active: number) => boolean \| Promise\<boolean\>_ | - |
| closeOnPopstate | 是否在页面回退时自动关闭 | _boolean_ | `true` |
| closeOnClickImage `v4.8.3` | 是否在点击图片后关闭图片预览 | _boolean_ | `true` |
| closeOnClickOverlay `v4.6.4` | 是否在点击遮罩层后关闭图片预览 | _boolean_ | `true` |
| className | 自定义类名 (应用在图片预览的弹出层) | _string \| Array \| object_ | - |
| maxZoom | 手势缩放时,最大缩放比例 | _number \| string_ | `3` |
Expand Down Expand Up @@ -238,6 +243,7 @@ Vant 中导出了以下 ImagePreview 相关的辅助函数:
| double-scale `v4.7.2` | 是否启用双击缩放手势,禁用后,点击时会立即关闭图片预览 | _boolean_ | `true` |
| before-close | 关闭前的回调函数,返回 `false` 可阻止关闭,支持返回 Promise | _(active: number) => boolean \| Promise\<boolean\>_ | - |
| close-on-popstate | 是否在页面回退时自动关闭 | _boolean_ | `true` |
| close-on-click-image `v4.8.3` | 是否在点击图片后关闭图片预览 | _boolean_ | `true` |
| close-on-click-overlay `v4.6.4` | 是否在点击遮罩层后关闭图片预览 | _boolean_ | `true` |
| class-name | 自定义类名 | _string \| Array \| object_ | - |
| max-zoom | 手势缩放时,最大缩放比例 | _number \| string_ | `3` |
Expand Down
6 changes: 5 additions & 1 deletion packages/vant/src/image-preview/demo/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,11 @@ const showFunctionCall = (options: Partial<ImagePreviewOptions> = {}) => {
:title="t('useImageSlot')"
@click="showComponentCallSlot"
/>
<van-image-preview v-model:show="showSlot" :images="imagesSlot">
<van-image-preview
v-model:show="showSlot"
:images="imagesSlot"
:close-on-click-image="false"
>
<template #image="{ src }">
<video style="width: 100%" controls>
<source :src="src" />
Expand Down
39 changes: 39 additions & 0 deletions packages/vant/src/image-preview/test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,25 @@ test('before close prop', async () => {
expect(wrapper.emitted('close')![0]).toBeTruthy();
});

test('should close when image is clicked', async () => {
const wrapper = mount(ImagePreview, {
props: {
images,
show: true,
'onUpdate:show': (show) => {
wrapper.setProps({ show });
},
},
});

await later();
const image = wrapper.find('.van-image');

await triggerDrag(image, 0, 0);
await later(300);
expect(wrapper.emitted('close')).toBeTruthy();
});

test('should close when overlay is clicked', async () => {
const wrapper = mount(ImagePreview, {
props: {
Expand All @@ -199,6 +218,26 @@ test('should close when overlay is clicked', async () => {
expect(wrapper.emitted('close')).toBeTruthy();
});

test('should not close when image is clicked and closeOnClickImage is false', async () => {
const wrapper = mount(ImagePreview, {
props: {
images,
show: true,
closeOnClickImage: false,
'onUpdate:show': (show) => {
wrapper.setProps({ show });
},
},
});

await later();
const image = wrapper.find('.van-image');

triggerDrag(image, 0, 0);
await later(300);
expect(wrapper.emitted('close')).toBeFalsy();
});

test('should not close when overlay is clicked and closeOnClickOverlay is false', async () => {
const wrapper = mount(ImagePreview, {
props: {
Expand Down
1 change: 1 addition & 0 deletions packages/vant/src/image-preview/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export type ImagePreviewOptions = {
showIndicators?: boolean;
closeOnPopstate?: boolean;
closeIconPosition?: PopupCloseIconPosition;
closeOnClickImage?: boolean;
closeOnClickOverlay?: boolean;
onClose?(): void;
onScale?(args: { scale: number; index: number }): void;
Expand Down