store/resources/js/components/media/MediaThumbnailCell.vue

127 lines
3.5 KiB
Vue

<script setup lang="ts">
import { computed, ref } from 'vue';
import MediaPreviewDialog from '@/components/media/MediaPreviewDialog.vue';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import type { MediaItem } from '@/types/media';
const props = defineProps<{
items: MediaItem[];
maxVisible?: number;
title?: string;
allUrls?: string[];
allTitles?: string[];
allStocks?: string[];
}>();
const previewOpen = ref(false);
const previewUrl = ref<string | null>(null);
const galleryOpen = ref(false);
const cameFromGallery = ref(false);
const visibleItems = computed(() => props.items.slice(0, props.maxVisible ?? 3));
const hiddenCount = computed(() => Math.max(props.items.length - visibleItems.value.length, 0));
const galleryWidthClass = computed(() => {
const count = props.items.length;
if (count <= 1) {
return 'sm:max-w-xs';
}
if (count <= 2) {
return 'sm:max-w-sm';
}
if (count <= 4) {
return 'sm:max-w-md';
}
if (count <= 8) {
return 'sm:max-w-lg';
}
return 'sm:max-w-xl';
});
const galleryGridClass = computed(() => {
const count = props.items.length;
if (count === 1) {
return 'grid-cols-1';
}
if (count === 2) {
return 'grid-cols-2';
}
if (count === 3) {
return 'grid-cols-3';
}
return 'grid-cols-4';
});
function openPreview(url: string) {
previewUrl.value = url;
previewOpen.value = true;
}
function openPreviewFromGallery(url: string) {
cameFromGallery.value = true;
galleryOpen.value = false;
setTimeout(() => {
previewUrl.value = url;
previewOpen.value = true;
}, 200);
}
function onPreviewClose() {
if (cameFromGallery.value) {
cameFromGallery.value = false;
setTimeout(() => {
galleryOpen.value = true;
}, 200);
}
}
function openGallery() {
galleryOpen.value = true;
}
</script>
<template>
<div v-if="items.length" class="flex items-center gap-1">
<button v-for="item in visibleItems" :key="item.id" type="button"
class="size-8 overflow-hidden rounded border bg-muted/30" @click="openPreview(item.url)">
<img :src="item.thumb_url" alt="Foto" class="size-full object-cover">
</button>
<button v-if="hiddenCount > 0" type="button"
class="flex size-8 items-center justify-center rounded border bg-muted/30 text-muted-foreground text-xs font-medium hover:bg-muted/50 transition-colors"
@click="openGallery">
+{{ hiddenCount }}
</button>
</div>
<Dialog v-model:open="galleryOpen">
<DialogContent :class="galleryWidthClass">
<DialogHeader>
<DialogTitle>Semua Foto ({{ items.length }})</DialogTitle>
</DialogHeader>
<div class="grid gap-2 max-h-[60vh] overflow-y-auto p-1" :class="galleryGridClass">
<button v-for="item in items" :key="item.id" type="button"
class="aspect-square overflow-hidden rounded border bg-muted/30"
@click="openPreviewFromGallery(item.url)">
<img :src="item.thumb_url" alt="Foto" class="size-full object-cover">
</button>
</div>
</DialogContent>
</Dialog>
<MediaPreviewDialog v-model:open="previewOpen" v-model:url="previewUrl" :urls="allUrls ?? items.map(item => item.url)" :title="title" :titles="allTitles" :stocks="allStocks" @close="onPreviewClose" />
</template>