import { ref, watch, nextTick, PropType, reactive, onMounted, CSSProperties, defineComponent, } from 'vue'; // Utils import { pick, truthProp, unknownProp, createNamespace, ComponentInstance, } from '../utils'; import { callInterceptor, Interceptor } from '../utils/interceptor'; // Composables import { useWindowSize } from '@vant/use'; import { useExpose } from '../composables/use-expose'; // Components import { Icon } from '../icon'; import { Swipe, SwipeToOptions } from '../swipe'; import { Popup, PopupCloseIconPosition } from '../popup'; import ImagePreviewItem from './ImagePreviewItem'; const [name, bem] = createNamespace('image-preview'); export type ScaleEventParams = { scale: number; index: number; }; export default defineComponent({ name, props: { show: Boolean, loop: truthProp, overlay: truthProp, closeable: Boolean, showIndex: truthProp, className: unknownProp, transition: String, beforeClose: Function as PropType, overlayStyle: Object as PropType, showIndicators: Boolean, closeOnPopstate: truthProp, images: { type: Array as PropType, default: () => [], }, minZoom: { type: [Number, String], default: 1 / 3, }, maxZoom: { type: [Number, String], default: 3, }, swipeDuration: { type: [Number, String], default: 300, }, startPosition: { type: [Number, String], default: 0, }, closeIcon: { type: String, default: 'clear', }, closeIconPosition: { type: String as PropType, default: 'top-right', }, }, emits: ['scale', 'close', 'closed', 'change', 'update:show'], setup(props, { emit, slots }) { const swipeRef = ref(); const windowSize = useWindowSize(); const state = reactive({ active: 0, rootWidth: 0, rootHeight: 0, }); const resize = () => { if (swipeRef.value) { const rect = swipeRef.value.$el.getBoundingClientRect(); state.rootWidth = rect.width; state.rootHeight = rect.height; swipeRef.value.resize(); } }; const emitScale = (args: ScaleEventParams) => emit('scale', args); const updateShow = (show: boolean) => emit('update:show', show); const emitClose = () => { callInterceptor({ interceptor: props.beforeClose, args: [state.active], done: () => updateShow(false), }); }; const setActive = (active: number) => { if (active !== state.active) { state.active = active; emit('change', active); } }; const renderIndex = () => { if (props.showIndex) { return (
{slots.index ? slots.index({ index: state.active }) : `${state.active + 1} / ${props.images.length}`}
); } }; const renderCover = () => { if (slots.cover) { return
{slots.cover()}
; } }; const renderImages = () => ( {props.images.map((image) => ( ))} ); const renderClose = () => { if (props.closeable) { return ( ); } }; const onClosed = () => emit('closed'); const swipeTo = (index: number, options?: SwipeToOptions) => swipeRef.value?.swipeTo(index, options); useExpose({ swipeTo }); onMounted(resize); watch([windowSize.width, windowSize.height], resize); watch( () => props.startPosition, (value) => setActive(+value) ); watch( () => props.show, (value) => { const { images, startPosition } = props; if (value) { setActive(+startPosition); nextTick(() => { resize(); swipeTo(+startPosition, { immediate: true }); }); } else { emit('close', { index: state.active, url: images[state.active], }); } } ); return () => ( {renderClose()} {renderImages()} {renderIndex()} {renderCover()} ); }, });