Files
ai-stylegallery/resources/js/Components/GalleryGrid.vue

57 lines
2.1 KiB
Vue

<template>
<div class="space-y-4">
<p
v-if="!images.length"
class="rounded-3xl border border-dashed border-slate-300 bg-white px-4 py-10 text-center text-sm text-slate-500 dark:border-white/20 dark:bg-white/5 dark:text-slate-300"
>
{{ __('api.gallery.empty') }}
</p>
<div
v-else
class="grid grid-cols-2 gap-3 sm:grid-cols-3 sm:gap-5 xl:grid-cols-4 2xl:grid-cols-5"
>
<button
v-for="image in images"
:key="image.id ?? image.name"
type="button"
class="group relative aspect-[4/3] overflow-hidden rounded-[1.75rem] border border-slate-200 bg-white shadow-2xl ring-1 ring-slate-100 transition-all hover:border-emerald-300 focus:outline-none focus-visible:ring-4 focus-visible:ring-emerald-400 dark:border-white/10 dark:bg-slate-900/30 dark:ring-white/5"
@click="$emit('imageTapped', image, $event)"
:aria-label="fallbackLabel(image)"
>
<img
:src="image.path"
:alt="fallbackLabel(image)"
loading="lazy"
class="h-full w-full object-cover transition duration-700 group-hover:scale-105"
/>
<span
v-if="image.is_new"
class="absolute left-4 top-4 inline-flex items-center gap-2 rounded-full bg-emerald-300/90 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-emerald-900 shadow-lg"
>
<span class="h-1.5 w-1.5 rounded-full bg-emerald-800"></span>
{{ __('new') }}
</span>
<div class="pointer-events-none absolute inset-x-0 bottom-0 bg-gradient-to-t from-slate-900/80 via-slate-900/10 to-transparent p-4 text-left text-white">
<p class="text-base font-semibold drop-shadow">{{ fallbackLabel(image) }}</p>
<p class="text-xs uppercase tracking-[0.3em] text-white/70">{{ __('api.gallery.tap_to_open') }}</p>
</div>
</button>
</div>
</div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue';
const props = defineProps({
images: {
type: Array,
required: true,
},
});
const emits = defineEmits(['imageTapped']);
const fallbackLabel = (image) => image?.name || 'Bild';
</script>