|
|
@ -1,5 +1,6 @@ |
|
|
|
<script setup> |
|
|
|
<script setup> |
|
|
|
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue' |
|
|
|
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue' |
|
|
|
|
|
|
|
import { useMotion } from '@vueuse/motion' |
|
|
|
|
|
|
|
|
|
|
|
const props = defineProps({ |
|
|
|
const props = defineProps({ |
|
|
|
modelValue: { |
|
|
|
modelValue: { |
|
|
@ -31,10 +32,6 @@ const props = defineProps({ |
|
|
|
type: Boolean, |
|
|
|
type: Boolean, |
|
|
|
default: true, |
|
|
|
default: true, |
|
|
|
}, |
|
|
|
}, |
|
|
|
transition: { |
|
|
|
|
|
|
|
type: String, |
|
|
|
|
|
|
|
default: 'fade', |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits(['update:modelValue']) |
|
|
|
const emit = defineEmits(['update:modelValue']) |
|
|
@ -49,6 +46,31 @@ const popoverStyle = computed(() => ({ |
|
|
|
width: props.width, |
|
|
|
width: props.width, |
|
|
|
})) |
|
|
|
})) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const { variant } = useMotion(popoverRef, { |
|
|
|
|
|
|
|
initial: { |
|
|
|
|
|
|
|
opacity: 0, |
|
|
|
|
|
|
|
scale: 0.1, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
enter: { |
|
|
|
|
|
|
|
opacity: 1, |
|
|
|
|
|
|
|
scale: 1, |
|
|
|
|
|
|
|
transition: { |
|
|
|
|
|
|
|
type: 'spring', |
|
|
|
|
|
|
|
stiffness: 300, |
|
|
|
|
|
|
|
damping: 20, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
leave: { |
|
|
|
|
|
|
|
opacity: 0, |
|
|
|
|
|
|
|
scale: 0.1, |
|
|
|
|
|
|
|
transition: { |
|
|
|
|
|
|
|
type: 'spring', |
|
|
|
|
|
|
|
stiffness: 300, |
|
|
|
|
|
|
|
damping: 20, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
const updatePopoverPosition = () => { |
|
|
|
const updatePopoverPosition = () => { |
|
|
|
if (!triggerRef.value || !popoverRef.value) return |
|
|
|
if (!triggerRef.value || !popoverRef.value) return |
|
|
|
|
|
|
|
|
|
|
@ -96,12 +118,18 @@ const updatePopoverPosition = () => { |
|
|
|
const openPopover = () => { |
|
|
|
const openPopover = () => { |
|
|
|
isOpen.value = true |
|
|
|
isOpen.value = true |
|
|
|
emit('update:modelValue', true) |
|
|
|
emit('update:modelValue', true) |
|
|
|
nextTick(updatePopoverPosition) |
|
|
|
nextTick(() => { |
|
|
|
|
|
|
|
updatePopoverPosition() |
|
|
|
|
|
|
|
variant.value = 'enter' |
|
|
|
|
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const closePopover = () => { |
|
|
|
const closePopover = () => { |
|
|
|
|
|
|
|
variant.value = 'leave' |
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
|
isOpen.value = false |
|
|
|
isOpen.value = false |
|
|
|
emit('update:modelValue', false) |
|
|
|
emit('update:modelValue', false) |
|
|
|
|
|
|
|
}, 300) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const handleClickOutside = (event) => { |
|
|
|
const handleClickOutside = (event) => { |
|
|
@ -139,7 +167,14 @@ watch( |
|
|
|
() => props.modelValue, |
|
|
|
() => props.modelValue, |
|
|
|
(newValue) => { |
|
|
|
(newValue) => { |
|
|
|
isOpen.value = newValue |
|
|
|
isOpen.value = newValue |
|
|
|
if (newValue) nextTick(updatePopoverPosition) |
|
|
|
if (newValue) { |
|
|
|
|
|
|
|
nextTick(() => { |
|
|
|
|
|
|
|
updatePopoverPosition() |
|
|
|
|
|
|
|
variant.value = 'enter' |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
variant.value = 'leave' |
|
|
|
|
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
) |
|
|
|
) |
|
|
|
</script> |
|
|
|
</script> |
|
|
@ -153,8 +188,7 @@ watch( |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<Teleport to="body"> |
|
|
|
<Teleport to="body"> |
|
|
|
<Transition :name="transition"> |
|
|
|
<div v-if="isOpen" ref="popoverRef" v-motion :style="popoverStyle" class="popover-content"> |
|
|
|
<div v-if="isOpen" ref="popoverRef" :style="popoverStyle" class="popover-content"> |
|
|
|
|
|
|
|
<slot name="content" :close="closePopover"> |
|
|
|
<slot name="content" :close="closePopover"> |
|
|
|
<div class="p-4"> |
|
|
|
<div class="p-4"> |
|
|
|
<p>Default popover content</p> |
|
|
|
<p>Default popover content</p> |
|
|
@ -162,22 +196,16 @@ watch( |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</slot> |
|
|
|
</slot> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</Transition> |
|
|
|
|
|
|
|
</Teleport> |
|
|
|
</Teleport> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
</template> |
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss"> |
|
|
|
<style scoped> |
|
|
|
.popover-content { |
|
|
|
.popover-content { |
|
|
|
} |
|
|
|
background-color: white; |
|
|
|
|
|
|
|
border: 1px solid #ccc; |
|
|
|
.fade-enter-active, |
|
|
|
border-radius: 4px; |
|
|
|
.fade-leave-active { |
|
|
|
padding: 10px; |
|
|
|
transition: opacity 0.3s ease; |
|
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.fade-enter-from, |
|
|
|
|
|
|
|
.fade-leave-to { |
|
|
|
|
|
|
|
opacity: 0; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
</style> |
|
|
|
</style> |
|
|
|