Files
social-media/frontend/src/views/creators/AlbumViewer.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>