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

210 lines
9.3 KiB
Vue

<template>
<div class="fixed inset-0 z-[110] flex items-center justify-center bg-slate-950/80 px-4 backdrop-blur-lg" @click.self="$emit('close')">
<div class="w-full max-w-6xl overflow-hidden rounded-[2.5rem] border border-white/10 bg-white/95 text-slate-900 shadow-[0_45px_85px_rgba(15,23,42,0.45)] ring-1 ring-slate-200 transition dark:bg-slate-900/95 dark:text-white">
<div class="flex flex-col gap-6 transition-all duration-300 lg:flex-row">
<figure
class="relative w-full overflow-hidden bg-slate-900/80"
:class="showStyleSelectorView ? 'lg:w-[12%]' : 'lg:flex-1'"
>
<img
v-if="image?.path"
:src="image.path"
:alt="imageLabel"
class="h-full w-full object-contain"
/>
<div v-else class="flex h-full min-h-[320px] items-center justify-center text-slate-500">
Kein Bild geladen
</div>
<div class="pointer-events-none absolute inset-x-0 top-0 flex items-center justify-between p-6 text-xs uppercase tracking-[0.4em] text-white/70">
<span>{{ image?.name || 'Ausgewähltes Bild' }}</span>
<span>{{ image?.id }}</span>
</div>
</figure>
<div
class="flex w-full flex-col gap-6 p-6"
:class="showStyleSelectorView ? 'lg:w-[88%]' : 'lg:w-[360px]'"
>
<div class="flex items-start justify-between gap-4">
<div>
<p class="text-xs uppercase tracking-[0.4em] text-slate-400">Menü</p>
<h2 class="mt-1 text-2xl font-semibold leading-tight">{{ imageLabel }}</h2>
<p class="text-sm text-slate-500 dark:text-slate-400 truncate">
{{ image?.path }}
</p>
</div>
<button
type="button"
class="rounded-full border border-white/20 bg-white/10 p-2 text-slate-900 shadow-sm transition hover:border-rose-400 hover:text-rose-400 focus:outline-none focus-visible:ring-2 focus-visible:ring-rose-400 dark:text-white"
@click="$emit('close')"
aria-label="Kontextmenü schließen"
>
<font-awesome-icon :icon="['fas', 'xmark']" class="h-5 w-5" />
</button>
</div>
<div v-if="!showStyleSelectorView" class="space-y-3">
<button
v-if="props.allowAiStyles"
type="button"
class="flex w-full items-center justify-between gap-3 rounded-2xl border px-4 py-3 text-left font-semibold text-slate-900 transition focus:outline-none focus-visible:ring-2 dark:text-white"
:class="[effectiveAiAvailable ? 'border-white/20 bg-white/40 hover:border-emerald-400 hover:bg-white/70 dark:border-white/10 dark:bg-white/5' : 'border-rose-200 bg-rose-50 cursor-not-allowed opacity-70']"
:disabled="!effectiveAiAvailable"
@click="showStyleSelectorView = true"
>
<div class="flex items-center gap-3">
<span class="h-2 w-2 rounded-full"
:class="effectiveAiAvailable ? 'bg-emerald-500' : 'bg-rose-500'"></span>
<div>
<p class="text-base">Stile anzeigen</p>
<p class="text-sm font-normal text-slate-500 dark:text-slate-400">
{{ effectiveAiAvailable ? 'Lass die KI dein Motiv verzaubern' : 'AI-Dienste derzeit nicht verfügbar' }}
</p>
</div>
</div>
<span class="flex h-12 w-12 items-center justify-center rounded-full bg-white/60 text-slate-900 shadow-md dark:bg-slate-800/70 dark:text-white">
<font-awesome-icon :icon="['fas', 'magic-wand-sparkles']" class="h-5 w-5" />
</span>
</button>
<button
v-if="showPrintButton"
type="button"
class="flex w-full items-center justify-between gap-3 rounded-2xl border border-white/10 bg-slate-900/5 px-4 py-3 text-left font-semibold text-slate-900 transition hover:border-cyan-300 hover:bg-cyan-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300 dark:border-white/10 dark:bg-white/5 dark:text-white"
@click="$emit('print', image)"
>
<div>
<p class="text-base">Drucken</p>
<p class="text-sm font-normal text-slate-500 dark:text-slate-400">Sofort den Druckdialog öffnen</p>
</div>
<span class="flex h-12 w-12 items-center justify-center rounded-full bg-cyan-100 text-cyan-900 shadow-md dark:bg-cyan-400/20 dark:text-cyan-200">
<font-awesome-icon :icon="['fas', 'print']" class="h-5 w-5" />
</span>
</button>
<button
v-if="shouldShowDownload"
type="button"
class="flex w-full items-center justify-between gap-3 rounded-2xl border border-white/10 bg-slate-900/5 px-4 py-3 text-left font-semibold text-slate-900 transition hover:border-indigo-300 hover:bg-indigo-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-300 dark:border-white/10 dark:bg-white/5 dark:text-white"
@click="$emit('download', image)"
>
<div>
<p class="text-base">Herunterladen</p>
<p class="text-sm font-normal text-slate-500 dark:text-slate-400">Datei lokal speichern</p>
</div>
<span class="flex h-12 w-12 items-center justify-center rounded-full bg-indigo-100 text-indigo-900 shadow-md dark:bg-indigo-400/20 dark:text-indigo-200">
<font-awesome-icon :icon="['fas', 'download']" class="h-5 w-5" />
</span>
</button>
<button
type="button"
class="flex w-full items-center justify-between gap-3 rounded-2xl border border-white/10 bg-white/0 px-4 py-3 text-left font-semibold text-slate-900 transition hover:border-rose-300 hover:bg-rose-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-rose-300 dark:border-white/10 dark:bg-white/5 dark:text-white"
@click="$emit('close')"
>
<div>
<p class="text-base">Schließen</p>
<p class="text-sm font-normal text-slate-500 dark:text-slate-400">Zurück zur Galerie</p>
</div>
<span class="flex h-12 w-12 items-center justify-center rounded-full bg-rose-100 text-rose-900 shadow-md dark:bg-rose-400/20 dark:text-rose-200">
<font-awesome-icon :icon="['fas', 'xmark']" class="h-5 w-5" />
</span>
</button>
</div>
<div v-else class="space-y-4">
<div class="flex items-center justify-between text-sm text-slate-400">
<button
type="button"
class="inline-flex items-center gap-2 rounded-full border border-slate-200 px-3 py-1 text-xs uppercase tracking-[0.3em] text-slate-600 transition hover:border-emerald-300 hover:text-emerald-600 dark:border-white/20 dark:text-white/80"
@click="showStyleSelectorView = false"
>
<font-awesome-icon :icon="['fas', 'arrow-left']" class="h-3.5 w-3.5" />
Zurück
</button>
<span>Stilauswahl</span>
</div>
<StyleSelector
class="max-h-[520px] flex-1"
:image_id="image?.id ?? image?.image_id"
:allow-ai-styles="props.allowAiStyles"
:gallery-slug="props.gallerySlug"
:ai-available-override="props.aiAvailable"
@styleSelected="handleStyleSelected"
@close="$emit('close')"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { computed, ref, watch, onMounted } from 'vue';
import { usePage } from '@inertiajs/vue3';
import StyleSelector from './StyleSelector.vue';
import axios from 'axios';
const page = usePage();
const props = defineProps({
image: {
type: Object,
required: true,
},
allowAiStyles: {
type: Boolean,
default: true,
},
showPrintButton: {
type: Boolean,
default: true,
},
gallerySlug: {
type: String,
default: 'default',
},
aiAvailable: {
type: Boolean,
default: true,
},
});
const emits = defineEmits(['close', 'print', 'styleSelected', 'download']);
const settings = computed(() => page.props.settings || {});
const showPrintButton = computed(() => {
const fromSettings = settings.value.show_print_button ?? true;
return fromSettings && props.showPrintButton;
});
const shouldShowDownload = computed(() => {
const hostname = window.location.hostname;
return hostname !== 'localhost' &&
hostname !== '127.0.0.1' &&
!hostname.startsWith('192.168.') &&
!hostname.startsWith('10.') &&
!hostname.startsWith('172.');
});
const showStyleSelectorView = ref(false);
const effectiveAiAvailable = computed(() => props.allowAiStyles && props.aiAvailable);
onMounted(() => {});
watch(
() => props.image,
() => {
showStyleSelectorView.value = false;
}
);
const imageLabel = computed(() => props.image?.name || 'Ausgewähltes Bild');
const handleStyleSelected = (style, imageId) => {
showStyleSelectorView.value = false;
emits('styleSelected', style, imageId);
};
</script>