feat(components): [el-image] support append preview to body (#5883)

* feat(components): [el-image] support append preview to body

* chore: add deprecated warning

* chore: compatible with  append-to-body and get zIndex from PopupManager

* fix: apply transition on the initial render

* fix: PopupManager import

* fix: modify default value

* chore: rename to teleported

* chore: rename to teleported

* fix: resolve import issue

* fix: resolve import issue

* fix: resolve import issue
This commit is contained in:
msidolphin 2022-02-11 15:16:45 +08:00 committed by GitHub
parent b6be179a2c
commit dcb7365365
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 137 additions and 98 deletions

View File

@ -8,7 +8,6 @@ export const imageViewerProps = buildProps({
},
zIndex: {
type: Number,
default: 2000,
},
initialIndex: {
type: Number,
@ -22,6 +21,10 @@ export const imageViewerProps = buildProps({
type: Boolean,
default: false,
},
teleported: {
type: Boolean,
default: false,
},
} as const)
export type ImageViewerProps = ExtractPropTypes<typeof imageViewerProps>

View File

@ -1,81 +1,83 @@
<template>
<transition name="viewer-fade">
<div
ref="wrapper"
:tabindex="-1"
:class="ns.e('wrapper')"
:style="{ zIndex }"
>
<div :class="ns.e('mask')" @click.self="hideOnClickModal && hide()" />
<teleport to="body" :disabled="!teleported">
<transition name="viewer-fade" appear>
<div
ref="wrapper"
:tabindex="-1"
:class="ns.e('wrapper')"
:style="{ zIndex: computedZIndex }"
>
<div :class="ns.e('mask')" @click.self="hideOnClickModal && hide()" />
<!-- CLOSE -->
<span :class="[ns.e('btn'), ns.e('close')]" @click="hide">
<el-icon><close /></el-icon>
</span>
<!-- CLOSE -->
<span :class="[ns.e('btn'), ns.e('close')]" @click="hide">
<el-icon><close /></el-icon>
</span>
<!-- ARROW -->
<template v-if="!isSingle">
<span
:class="[
ns.e('btn'),
ns.e('prev'),
ns.is('disabled', !infinite && isFirst),
]"
@click="prev"
>
<el-icon><arrow-left /></el-icon>
</span>
<span
:class="[
ns.e('btn'),
ns.e('next'),
ns.is('disabled', !infinite && isLast),
]"
@click="next"
>
<el-icon><arrow-right /></el-icon>
</span>
</template>
<!-- ACTIONS -->
<div :class="[ns.e('btn'), ns.e('actions')]">
<div :class="ns.e('actions__inner')">
<el-icon @click="handleActions('zoomOut')">
<zoom-out />
</el-icon>
<el-icon @click="handleActions('zoomIn')">
<zoom-in />
</el-icon>
<i :class="ns.e('actions__divider')"></i>
<el-icon @click="toggleMode">
<component :is="mode.icon" />
</el-icon>
<i :class="ns.e('actions__divider')"></i>
<el-icon @click="handleActions('anticlockwise')">
<refresh-left />
</el-icon>
<el-icon @click="handleActions('clockwise')">
<refresh-right />
</el-icon>
<!-- ARROW -->
<template v-if="!isSingle">
<span
:class="[
ns.e('btn'),
ns.e('prev'),
ns.is('disabled', !infinite && isFirst),
]"
@click="prev"
>
<el-icon><arrow-left /></el-icon>
</span>
<span
:class="[
ns.e('btn'),
ns.e('next'),
ns.is('disabled', !infinite && isLast),
]"
@click="next"
>
<el-icon><arrow-right /></el-icon>
</span>
</template>
<!-- ACTIONS -->
<div :class="[ns.e('btn'), ns.e('actions')]">
<div :class="ns.e('actions__inner')">
<el-icon @click="handleActions('zoomOut')">
<zoom-out />
</el-icon>
<el-icon @click="handleActions('zoomIn')">
<zoom-in />
</el-icon>
<i :class="ns.e('actions__divider')"></i>
<el-icon @click="toggleMode">
<component :is="mode.icon" />
</el-icon>
<i :class="ns.e('actions__divider')"></i>
<el-icon @click="handleActions('anticlockwise')">
<refresh-left />
</el-icon>
<el-icon @click="handleActions('clockwise')">
<refresh-right />
</el-icon>
</div>
</div>
<!-- CANVAS -->
<div :class="ns.e('canvas')">
<img
v-for="(url, i) in urlList"
v-show="i === index"
:ref="(el) => (imgRefs[i] = el)"
:key="url"
:src="url"
:style="imgStyle"
:class="ns.e('img')"
@load="handleImgLoad"
@error="handleImgError"
@mousedown="handleMouseDown"
/>
</div>
<slot />
</div>
<!-- CANVAS -->
<div :class="ns.e('canvas')">
<img
v-for="(url, i) in urlList"
v-show="i === index"
:ref="(el) => (imgRefs[i] = el)"
:key="url"
:src="url"
:style="imgStyle"
:class="ns.e('img')"
@load="handleImgLoad"
@error="handleImgError"
@mousedown="handleMouseDown"
/>
</div>
<slot />
</div>
</transition>
</transition>
</teleport>
</template>
<script lang="ts">
@ -89,12 +91,12 @@ import {
effectScope,
markRaw,
} from 'vue'
import { useEventListener } from '@vueuse/core'
import { useEventListener, isNumber } from '@vueuse/core'
import { throttle } from 'lodash-unified'
import ElIcon from '@element-plus/components/icon'
import { useLocale, useNamespace } from '@element-plus/hooks'
import { isFirefox } from '@element-plus/utils'
import { useLocale, useNamespace, useZIndex } from '@element-plus/hooks'
import { EVENT_CODE } from '@element-plus/constants'
import { isFirefox } from '@element-plus/utils'
import {
Close,
ArrowLeft,
@ -146,6 +148,7 @@ export default defineComponent({
setup(props, { emit }) {
const { t } = useLocale()
const ns = useNamespace('image-viewer')
const { nextZIndex } = useZIndex()
const wrapper = ref<HTMLDivElement>()
const imgRefs = ref<any[]>([])
@ -209,6 +212,10 @@ export default defineComponent({
return style
})
const computedZIndex = computed(() => {
return isNumber(props.zIndex) ? props.zIndex : nextZIndex()
})
function hide() {
unregisterEventListener()
emit('close')
@ -404,6 +411,7 @@ export default defineComponent({
currentImg,
imgStyle,
mode,
computedZIndex,
handleActions,
prev,
next,

View File

@ -10,7 +10,7 @@ import type { ExtractPropTypes } from 'vue'
export const imageProps = buildProps({
appendToBody: {
type: Boolean,
default: false,
default: undefined,
},
hideOnClickModal: {
type: Boolean,
@ -36,9 +36,12 @@ export const imageProps = buildProps({
type: definePropType<string[]>(Array),
default: () => mutable([] as const),
},
previewTeleported: {
type: Boolean,
default: false,
},
zIndex: {
type: Number,
default: 2000,
},
initialIndex: {
type: Number,

View File

@ -14,31 +14,40 @@
:class="[ns.e('inner'), preview ? ns.e('preview') : '']"
@click="clickHandler"
/>
<teleport to="body" :disabled="!appendToBody">
<template v-if="preview">
<image-viewer
v-if="showViewer"
:z-index="zIndex"
:initial-index="imageIndex"
:url-list="previewSrcList"
:hide-on-click-modal="hideOnClickModal"
@close="closeViewer"
@switch="switchViewer"
>
<div v-if="$slots.viewer">
<slot name="viewer" />
</div>
</image-viewer>
</template>
</teleport>
<template v-if="preview">
<image-viewer
v-if="showViewer"
:z-index="zIndex"
:initial-index="imageIndex"
:url-list="previewSrcList"
:hide-on-click-modal="hideOnClickModal"
:teleported="teleported"
@close="closeViewer"
@switch="switchViewer"
>
<div v-if="$slots.viewer">
<slot name="viewer" />
</div>
</image-viewer>
</template>
</div>
</template>
<script lang="ts">
import { defineComponent, computed, ref, onMounted, watch, nextTick } from 'vue'
import { isString } from '@vue/shared'
import { useEventListener, useThrottleFn, isClient } from '@vueuse/core'
import { useAttrs, useLocale, useNamespace } from '@element-plus/hooks'
import {
useEventListener,
useThrottleFn,
isClient,
isBoolean,
} from '@vueuse/core'
import {
useAttrs,
useLocale,
useNamespace,
useDeprecated,
} from '@element-plus/hooks'
import ImageViewer from '@element-plus/components/image-viewer'
import { getScrollContainer, isInContainer } from '@element-plus/utils'
import { imageEmits, imageProps } from './image'
@ -61,6 +70,17 @@ export default defineComponent({
emits: imageEmits,
setup(props, { emit, attrs: rawAttrs }) {
useDeprecated(
{
scope: 'el-image',
from: 'append-to-body',
replacement: 'preview-teleported',
version: '2.2.0',
ref: 'https://element-plus.org/en-US/component/image.html#image-attributess',
},
computed(() => isBoolean(props.appendToBody))
)
const { t } = useLocale()
const ns = useNamespace('image')
@ -91,6 +111,10 @@ export default defineComponent({
return Array.isArray(previewSrcList) && previewSrcList.length > 0
})
const teleported = computed(() => {
return props.appendToBody || props.previewTeleported
})
const imageIndex = computed(() => {
const { previewSrcList, initialIndex } = props
let previewIndex = initialIndex
@ -259,6 +283,7 @@ export default defineComponent({
imageIndex,
container,
ns,
teleported,
clickHandler,
closeViewer,