196 lines
3.9 KiB
Vue
196 lines
3.9 KiB
Vue
<template>
|
|
<v-dialog
|
|
v-model="dialog"
|
|
fullscreen
|
|
:scrim="true"
|
|
transition="dialog-bottom-transition"
|
|
@click:outside="closeViewer"
|
|
>
|
|
<div class="album-viewer" @click.self="closeViewer">
|
|
<!-- Main image container -->
|
|
<div class="image-container">
|
|
<img
|
|
:src="currentImage"
|
|
:alt="t('viewer.imageAlt', { index: currentIndex + 1 })"
|
|
class="main-image"
|
|
/>
|
|
|
|
<!-- Navigation buttons -->
|
|
<button
|
|
class="nav-btn left-btn"
|
|
@click.stop="previousImage"
|
|
:disabled="currentIndex === 0"
|
|
:title="t('viewer.previous')"
|
|
>
|
|
<v-icon size="large" color="white">mdi-chevron-left</v-icon>
|
|
</button>
|
|
|
|
<button
|
|
class="nav-btn right-btn"
|
|
@click.stop="nextImage"
|
|
:disabled="currentIndex === images.length - 1"
|
|
:title="t('viewer.next')"
|
|
>
|
|
<v-icon size="large" color="white">mdi-chevron-right</v-icon>
|
|
</button>
|
|
|
|
<!-- Close button -->
|
|
<button
|
|
class="close-btn"
|
|
@click.stop="closeViewer"
|
|
:title="t('viewer.close')"
|
|
>
|
|
<v-icon size="large" color="white">mdi-close</v-icon>
|
|
</button>
|
|
|
|
<!-- Image counter -->
|
|
<div class="image-counter">
|
|
{{ currentIndex + 1 }} / {{ images.length }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</v-dialog>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, watch, computed } from 'vue';
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
const { t } = useI18n();
|
|
|
|
const props = defineProps({
|
|
modelValue: {
|
|
type: Boolean,
|
|
required: true
|
|
},
|
|
images: {
|
|
type: Array,
|
|
required: true
|
|
},
|
|
startIndex: {
|
|
type: Number,
|
|
default: 0
|
|
}
|
|
});
|
|
|
|
const emit = defineEmits(['update:modelValue']);
|
|
|
|
const dialog = ref(false);
|
|
const currentIndex = ref(0);
|
|
|
|
const currentImage = computed(() => props.images[currentIndex.value]);
|
|
|
|
watch(() => props.modelValue, (newVal) => {
|
|
dialog.value = newVal;
|
|
if (newVal) {
|
|
currentIndex.value = props.startIndex;
|
|
}
|
|
});
|
|
|
|
watch(() => dialog.value, (newVal) => {
|
|
emit('update:modelValue', newVal);
|
|
});
|
|
|
|
function nextImage() {
|
|
if (currentIndex.value < props.images.length - 1) {
|
|
currentIndex.value++;
|
|
}
|
|
}
|
|
|
|
function previousImage() {
|
|
if (currentIndex.value > 0) {
|
|
currentIndex.value--;
|
|
}
|
|
}
|
|
|
|
function closeViewer() {
|
|
dialog.value = false;
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.album-viewer {
|
|
@apply fixed inset-0;
|
|
@apply flex items-center justify-center;
|
|
@apply bg-black bg-opacity-90;
|
|
@apply z-50;
|
|
}
|
|
|
|
.image-container {
|
|
@apply relative;
|
|
@apply max-w-[90vw];
|
|
@apply max-h-[90vh];
|
|
}
|
|
|
|
.main-image {
|
|
@apply max-w-full;
|
|
@apply max-h-[90vh];
|
|
@apply object-contain;
|
|
}
|
|
|
|
.nav-btn {
|
|
@apply absolute top-1/2 -translate-y-1/2;
|
|
@apply p-4;
|
|
@apply rounded-full;
|
|
@apply bg-black bg-opacity-50;
|
|
@apply transition-all duration-200;
|
|
@apply hover:bg-opacity-75;
|
|
@apply disabled:opacity-30 disabled:cursor-not-allowed;
|
|
}
|
|
|
|
.left-btn {
|
|
@apply left-4;
|
|
}
|
|
|
|
.right-btn {
|
|
@apply right-4;
|
|
}
|
|
|
|
.close-btn {
|
|
@apply absolute top-4 right-4;
|
|
@apply p-2;
|
|
@apply rounded-full;
|
|
@apply bg-black bg-opacity-50;
|
|
@apply transition-all duration-200;
|
|
@apply hover:bg-opacity-75;
|
|
}
|
|
|
|
.image-counter {
|
|
@apply absolute bottom-4 left-1/2 -translate-x-1/2;
|
|
@apply px-4 py-2;
|
|
@apply bg-black bg-opacity-50;
|
|
@apply text-white;
|
|
@apply rounded-full;
|
|
@apply text-sm;
|
|
}
|
|
</style>
|
|
|
|
<i18n>
|
|
{
|
|
"en": {
|
|
"viewer": {
|
|
"previous": "Previous image",
|
|
"next": "Next image",
|
|
"close": "Close viewer",
|
|
"imageAlt": "Image {index}"
|
|
}
|
|
},
|
|
"fr": {
|
|
"viewer": {
|
|
"previous": "Image précédente",
|
|
"next": "Image suivante",
|
|
"close": "Fermer",
|
|
"imageAlt": "Image {index}"
|
|
}
|
|
},
|
|
"es": {
|
|
"viewer": {
|
|
"previous": "Imagen anterior",
|
|
"next": "Imagen siguiente",
|
|
"close": "Cerrar",
|
|
"imageAlt": "Imagen {index}"
|
|
}
|
|
}
|
|
}
|
|
</i18n>
|