115 lines
3.0 KiB
Vue
115 lines
3.0 KiB
Vue
<template>
|
|
<TransitionRoot as="template" :show="open">
|
|
<Dialog as="div" class="relative z-40" @close="open = false">
|
|
<TransitionChild
|
|
as="template"
|
|
enter="ease-out duration-300"
|
|
enter-from="opacity-0"
|
|
enter-to="opacity-100"
|
|
leave="ease-in duration-200"
|
|
leave-from="opacity-100"
|
|
leave-to="opacity-0"
|
|
>
|
|
<div
|
|
class="fixed inset-0 bg-neutral-100/70 dark:bg-neutral-900/70 transition-opacity"
|
|
/>
|
|
</TransitionChild>
|
|
|
|
<div class="fixed inset-0 z-10 overflow-y-auto">
|
|
<div class="flex min-h-full justify-center p-4 text-center items-center sm:p-0">
|
|
<TransitionChild
|
|
as="template"
|
|
enter="ease-out duration-300"
|
|
enter-from="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
enter-to="opacity-100 translate-y-0 sm:scale-100"
|
|
leave="ease-in duration-200"
|
|
leave-from="opacity-100 translate-y-0 sm:scale-100"
|
|
leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
@after-leave="$emit('fully-closed')"
|
|
>
|
|
<DialogPanel
|
|
:class="[
|
|
'transform rounded-lg bg-foundation text-left shadow-xl transition-all',
|
|
widthClasses
|
|
]"
|
|
>
|
|
<div class="relative">
|
|
<slot>Put your content here!</slot>
|
|
<button
|
|
v-if="!hideCloser"
|
|
class="absolute top-0 right-0 text-foreground"
|
|
@click="open = false"
|
|
>
|
|
<XMarkIcon class="h-6 w-6" />
|
|
</button>
|
|
</div>
|
|
</DialogPanel>
|
|
</TransitionChild>
|
|
</div>
|
|
</div>
|
|
</Dialog>
|
|
</TransitionRoot>
|
|
</template>
|
|
<script setup lang="ts">
|
|
import { Dialog, DialogPanel, TransitionChild, TransitionRoot } from '@headlessui/vue'
|
|
import { XMarkIcon } from '@heroicons/vue/24/outline'
|
|
|
|
type MaxWidthValue = 'sm' | 'md' | 'lg' | 'xl'
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'update:open', v: boolean): void
|
|
(e: 'fully-closed'): void
|
|
}>()
|
|
|
|
const props = defineProps<{
|
|
open: boolean
|
|
maxWidth?: MaxWidthValue
|
|
hideCloser?: boolean
|
|
}>()
|
|
|
|
const open = computed({
|
|
get: () => props.open,
|
|
set: (newVal) => emit('update:open', newVal)
|
|
})
|
|
|
|
const maxWidthWeight = computed(() => {
|
|
switch (props.maxWidth) {
|
|
case 'sm':
|
|
return 0
|
|
case 'md':
|
|
return 1
|
|
case 'lg':
|
|
return 2
|
|
case 'xl':
|
|
return 3
|
|
default:
|
|
return 10000
|
|
}
|
|
})
|
|
|
|
const widthClasses = computed(() => {
|
|
const classParts: string[] = [
|
|
'px-4 pt-4 pb-4 w-full',
|
|
'sm:my-8 sm:w-full sm:max-w-xl sm:p-6'
|
|
]
|
|
|
|
if (maxWidthWeight.value >= 1) {
|
|
classParts.push('md:max-w-2xl')
|
|
}
|
|
|
|
if (maxWidthWeight.value >= 2) {
|
|
classParts.push('lg:max-w-4xl')
|
|
}
|
|
|
|
if (maxWidthWeight.value >= 3) {
|
|
classParts.push('xl:max-w-6xl')
|
|
}
|
|
|
|
if (maxWidthWeight.value >= 4) {
|
|
classParts.push('2xl:max-w-7xl')
|
|
}
|
|
|
|
return classParts.join(' ')
|
|
})
|
|
</script>
|