Create simple custom modal component

This commit is contained in:
Michael Winter 2026-03-08 11:42:09 +01:00
parent 5347bf0023
commit f2666ce12c
2 changed files with 48 additions and 5 deletions

View file

@ -0,0 +1,43 @@
<template>
<Teleport to="body">
<div v-if="modelValue" class="fixed inset-0 z-50" @click.self="close">
<!-- Backdrop -->
<div class="fixed inset-0 bg-black/50" @click="close"></div>
<!-- Modal Panel -->
<div class="fixed inset-0 flex items-center justify-center p-4">
<div
class="bg-white rounded-lg shadow-xl max-w-[85vw] max-h-[85vh] overflow-auto relative"
:style="{ maxHeight }"
>
<!-- Close button -->
<button
@click="close"
class="absolute top-4 right-4 z-10 text-gray-500 hover:text-gray-700 text-2xl leading-none"
>
&times;
</button>
<!-- Content -->
<slot />
</div>
</div>
</div>
</Teleport>
</template>
<script setup>
const props = defineProps({
modelValue: Boolean,
maxHeight: {
type: String,
default: '85vh'
}
})
const emit = defineEmits(['update:modelValue'])
const close = () => {
emit('update:modelValue', false)
}
</script>

View file

@ -67,8 +67,8 @@
</ClientOnly> </ClientOnly>
</footer> </footer>
<Modal v-model="modalStore.isOpen" :maxHeight="modalStore.type === 'image' && modalStore.soundcloudUrl ? 'calc(85vh + 60px)' : '85vh'"> <SimpleModal v-model="modalStore.isOpen" :maxHeight="modalStore.type === 'image' && modalStore.soundcloudUrl ? 'calc(85vh + 60px)' : '85vh'">
<div class="flex flex-col h-full overflow-hidden relative"> <div class="flex flex-col h-full overflow-hidden relative p-4">
<!-- Navigation buttons for images - fixed to modal --> <!-- Navigation buttons for images - fixed to modal -->
<template v-if="modalStore.type === 'image' && modalStore.gallery && modalStore.gallery.length > 1"> <template v-if="modalStore.type === 'image' && modalStore.gallery && modalStore.gallery.length > 1">
<button @click="prevImage" class="absolute left-2 top-1/2 -translate-y-1/2 z-20 text-gray-600 hover:text-gray-900"> <button @click="prevImage" class="absolute left-2 top-1/2 -translate-y-1/2 z-20 text-gray-600 hover:text-gray-900">
@ -87,7 +87,7 @@
<iframe :src="modalStore.soundcloudUrl" class="w-64" height="20px" scrolling="no" frameborder="no" allow="autoplay"></iframe> <iframe :src="modalStore.soundcloudUrl" class="w-64" height="20px" scrolling="no" frameborder="no" allow="autoplay"></iframe>
</ClientOnly> </ClientOnly>
</div> </div>
<div v-if="modalStore.type === 'video'" class="w-full flex items-center justify-center p-4"> <div v-if="modalStore.type === 'video'" class="w-full flex items-center justify-center">
<div class="w-full aspect-video"> <div class="w-full aspect-video">
<ClientOnly> <ClientOnly>
<iframe <iframe
@ -118,13 +118,13 @@
</ClientOnly> </ClientOnly>
</div> </div>
</div> </div>
<div v-if="modalStore.type === 'pdf' || modalStore.type === 'image' || modalStore.type === 'document'" class="absolute bottom-0 right-0 p-4 z-10"> <div v-if="modalStore.type === 'pdf' || modalStore.type === 'image' || modalStore.type === 'document'" class="absolute bottom-4 right-4 z-10">
<a :href="modalStore.type === 'pdf' ? modalStore.pdfUrl : modalStore.type === 'image' ? '/' + modalStore.bucket + '/' + modalStore.gallery[0]?.image : modalStore.type === 'document' ? modalStore.iframeUrl : undefined" target="_blank" rel="noopener noreferrer" class="p-2 bg-gray-600 rounded-lg inline-flex items-center justify-center pointer-events-auto"> <a :href="modalStore.type === 'pdf' ? modalStore.pdfUrl : modalStore.type === 'image' ? '/' + modalStore.bucket + '/' + modalStore.gallery[0]?.image : modalStore.type === 'document' ? modalStore.iframeUrl : undefined" target="_blank" rel="noopener noreferrer" class="p-2 bg-gray-600 rounded-lg inline-flex items-center justify-center pointer-events-auto">
<Icon name="mdi:open-in-new" class="w-5 h-5 text-white" /> <Icon name="mdi:open-in-new" class="w-5 h-5 text-white" />
</a> </a>
</div> </div>
</div> </div>
</Modal> </SimpleModal>
</div> </div>
</template> </template>