91 lines
2.1 KiB
Vue
91 lines
2.1 KiB
Vue
<script setup lang="ts">
|
|
import { onClickOutside } from '@vueuse/core'
|
|
import { vibrate } from '@/composables/useHaptics'
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
open?: boolean
|
|
persistent?: boolean
|
|
size?: 'sm' | 'md' | 'lg'
|
|
icon?: string
|
|
}>(),
|
|
{
|
|
open: false,
|
|
persistent: false,
|
|
size: 'md'
|
|
}
|
|
)
|
|
|
|
const show = ref<boolean>(false)
|
|
watch(
|
|
() => props.open,
|
|
() => {
|
|
if (show.value) vibrate()
|
|
|
|
show.value = props.open
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
|
|
const modal = ref<HTMLElement | null>(null)
|
|
const modalBox = ref(null)
|
|
|
|
const open = () => (show.value = true)
|
|
const close = (): Promise<boolean> => {
|
|
return new Promise((resolve) => {
|
|
modal.value?.addEventListener('transitionend', () => resolve(true))
|
|
show.value = false
|
|
})
|
|
}
|
|
|
|
const slotProps = { open, close }
|
|
|
|
if (!props.persistent) onClickOutside(modalBox, () => close())
|
|
|
|
const onEnter = (el: Element, done: () => void): void => {
|
|
setTimeout(() => {
|
|
el.classList.add('dui-modal-open')
|
|
done()
|
|
})
|
|
}
|
|
|
|
const onLeave = (el: Element, done: () => void): void => {
|
|
el.classList.remove('dui-modal-open')
|
|
el.addEventListener('transitionend', () => done())
|
|
}
|
|
|
|
const styleClass = computed(() => {
|
|
const sizeVariants = {
|
|
sm: 'max-w-xs',
|
|
md: 'max-w-md',
|
|
lg: 'max-w-2xl'
|
|
}
|
|
const sizeClass = sizeVariants[props.size]
|
|
return [sizeClass]
|
|
})
|
|
|
|
defineExpose({ open, close })
|
|
</script>
|
|
<template>
|
|
<slot name="activator" v-bind="slotProps"></slot>
|
|
<Teleport to="body">
|
|
<Transition @enter="onEnter" @leave="onLeave" appear>
|
|
<div class="dui-modal bg-neutral-800 bg-opacity-60" v-if="show" ref="modal">
|
|
<div class="dui-modal-box" :class="styleClass" ref="modalBox">
|
|
<h3 class="mb-4 flex items-center text-xl font-bold" v-if="$slots.title">
|
|
<slot name="title" />
|
|
</h3>
|
|
<div>
|
|
<slot v-bind="slotProps" />
|
|
</div>
|
|
<div class="dui-modal-action mt-4">
|
|
<slot name="actions" v-bind="slotProps">
|
|
<UIButton size="sm" @click="close">Close</UIButton>
|
|
</slot>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Transition>
|
|
</Teleport>
|
|
</template>
|