155 lines
3.4 KiB
Vue
155 lines
3.4 KiB
Vue
<script setup>
|
|
import { ref, watch, nextTick, onMounted, onUnmounted } from 'vue'
|
|
|
|
const props = defineProps({
|
|
visible: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
title: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
maximizable: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
size: {
|
|
type: String,
|
|
default: 'medium',
|
|
validator: (value) => ['small', 'medium', 'large', 'xlarge'].includes(value)
|
|
},
|
|
closable: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
dismissableMask: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
modal: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
class: {
|
|
type: String,
|
|
default: ''
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits(['update:visible', 'hide', 'show', 'fullscreen-change'])
|
|
|
|
const isVisible = ref(props.visible)
|
|
const isFullscreen = ref(false)
|
|
|
|
watch(() => props.visible, (newValue) => {
|
|
isVisible.value = newValue
|
|
})
|
|
|
|
watch(isVisible, (newValue) => {
|
|
emit('update:visible', newValue)
|
|
if (newValue) {
|
|
emit('show')
|
|
nextTick(() => {
|
|
checkFullscreenState()
|
|
})
|
|
} else {
|
|
emit('hide')
|
|
}
|
|
})
|
|
|
|
const checkFullscreenState = () => {
|
|
const dialog = document.querySelector('.p-dialog:not(.p-dialog-hidden)')
|
|
if (dialog) {
|
|
const rect = dialog.getBoundingClientRect()
|
|
const isMaximized = rect.width >= window.innerWidth * 0.95 && rect.height >= window.innerHeight * 0.95
|
|
|
|
if (isMaximized !== isFullscreen.value) {
|
|
isFullscreen.value = isMaximized
|
|
emit('fullscreen-change', isMaximized)
|
|
}
|
|
}
|
|
}
|
|
|
|
const handleResize = () => {
|
|
if (isVisible.value) {
|
|
checkFullscreenState()
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
window.addEventListener('resize', handleResize)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener('resize', handleResize)
|
|
})
|
|
|
|
const hide = () => {
|
|
isVisible.value = false
|
|
}
|
|
|
|
const show = () => {
|
|
isVisible.value = true
|
|
}
|
|
|
|
const handleMaximize = () => {
|
|
isFullscreen.value = true
|
|
emit('fullscreen-change', true)
|
|
}
|
|
|
|
const handleUnmaximize = () => {
|
|
isFullscreen.value = false
|
|
emit('fullscreen-change', false)
|
|
}
|
|
|
|
const sizeClasses = {
|
|
small: 'max-w-md',
|
|
medium: 'max-w-lg',
|
|
large: 'max-w-2xl',
|
|
xlarge: 'max-w-3xl'
|
|
}
|
|
|
|
defineExpose({
|
|
show,
|
|
hide
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<Dialog
|
|
v-model:visible="isVisible"
|
|
:modal="modal"
|
|
:closable="closable"
|
|
:dismissableMask="dismissableMask"
|
|
:maximizable="maximizable"
|
|
@hide="hide"
|
|
@show="show"
|
|
@maximize="handleMaximize"
|
|
@unmaximize="handleUnmaximize"
|
|
:class="[!maximizable ? sizeClasses[size] : '', props.class]"
|
|
:pt="{
|
|
root: 'backdrop-blur-sm',
|
|
header: 'border-b border-gray-200',
|
|
content: 'p-0',
|
|
footer: 'border-t border-gray-200',
|
|
}"
|
|
>
|
|
<template #header v-if="title">
|
|
<div class="flex items-center justify-between w-full">
|
|
<h3 class="text-lg font-semibold text-gray-900">{{ title }}</h3>
|
|
</div>
|
|
</template>
|
|
|
|
<div class="p-6">
|
|
<slot :isFullscreen="isFullscreen" />
|
|
</div>
|
|
|
|
<template #footer v-if="$slots.footer">
|
|
<div class="flex justify-end gap-3">
|
|
<slot name="footer" />
|
|
</div>
|
|
</template>
|
|
</Dialog>
|
|
</template>
|