Files
interview-demo-code/frontend/spa/src/components/SingleProductImageSwiper.vue
Nikita Kiselev 3cc82e45f0
Some checks are pending
Telegram Mini App Shop Builder / Compute version metadata (push) Waiting to run
Telegram Mini App Shop Builder / Run Frontend tests (push) Waiting to run
Telegram Mini App Shop Builder / Run Backend tests (push) Waiting to run
Telegram Mini App Shop Builder / Run PHP_CodeSniffer (push) Waiting to run
Telegram Mini App Shop Builder / Build module. (push) Blocked by required conditions
Telegram Mini App Shop Builder / release (push) Blocked by required conditions
Squashed commit message
2026-03-11 23:02:54 +03:00

113 lines
2.4 KiB
Vue

<template>
<div class="product-image-swiper">
<Swiper
:lazy="true"
:modules="modules"
:pagination="pagination"
@sliderMove="hapticScroll"
>
<SwiperSlide
v-for="(image, index) in images"
:key="image.url"
>
<img
:src="image.thumbnailURL"
:alt="image.alt"
loading="lazy"
@click="showFullScreen(index)"
/>
</SwiperSlide>
</Swiper>
<FullScreenImageViewer
v-if="isFullScreen"
:images="images"
:activeIndex="initialFullScreenIndex"
@close="closeFullScreen"
/>
</div>
</template>
<script setup>
import {Swiper, SwiperSlide} from 'swiper/vue';
import FullScreenImageViewer from "@/components/FullScreenImageViewer.vue";
import {useHapticFeedback} from "@/composables/useHapticFeedback.js";
import {onMounted, onUnmounted, ref} from "vue";
import {useHapticScroll} from "@/composables/useHapticScroll.js";
import {useRouter} from "vue-router";
const emit = defineEmits(['onLoad']);
const props = defineProps({
images: {
type: Array,
default: [],
}
});
const router = useRouter();
const haptic = useHapticFeedback();
const hapticScroll = useHapticScroll();
const pagination = {
clickable: true,
};
const modules = [];
const isFullScreen = ref(false);
const initialFullScreenIndex = ref(0);
function showFullScreen(index) {
haptic.selectionChanged();
isFullScreen.value = true;
initialFullScreenIndex.value = index;
document.body.style.overflow = 'hidden';
history.pushState({fullscreen: true}, '');
}
function closeFullScreen() {
isFullScreen.value = false;
document.body.style.overflow = '';
}
function onPopState() {
if (isFullScreen.value) {
closeFullScreen();
} else {
// пусть Vue Router сам обработает
router.back();
}
}
onMounted(async () => {
window.addEventListener('popstate', onPopState);
});
onUnmounted(() => {
window.removeEventListener('popstate', onPopState);
});
</script>
<style scoped>
.swiper-slide {
display: flex;
justify-content: center;
align-items: center;
}
.swiper {
height: 500px;
border-radius: var(--radius-box, 0.5rem);
overflow: hidden;
}
.swiper-slide img {
width: 100%;
height: 500px;
object-fit: contain;
cursor: pointer;
transition: transform 0.2s ease;
}
.swiper-slide img:active {
transform: scale(0.98);
}
</style>