| 1 |
<template> |
| 2 |
<IvyPopover |
| 3 |
:visible="isOpen" |
| 4 |
popper-class="ivyforms-popper-context-menu" |
| 5 |
:popper-background="'level-1-foreground'" |
| 6 |
:width="205" |
| 7 |
placement="bottom-start" |
| 8 |
:virtual-ref="resolveContextMenuButtonRef" |
| 9 |
:show-arrow="false" |
| 10 |
virtual-triggering |
| 11 |
> |
| 12 |
<component |
| 13 |
:is="contextMenuStore.contextMenuContent" |
| 14 |
:actions="contextMenuStore.contextMenuProps.actions" |
| 15 |
:entity-id="contextMenuStore.contextMenuProps.entityId" |
| 16 |
@action-executed="handleActionExecuted" |
| 17 |
@close-menu="contextMenuStore.closeContextMenu" |
| 18 |
/> |
| 19 |
</IvyPopover> |
| 20 |
</template> |
| 21 |
|
| 22 |
<script setup lang="ts"> |
| 23 |
import { ref, computed, type ComponentPublicInstance } from 'vue' |
| 24 |
import { storeToRefs } from 'pinia' |
| 25 |
import { useContextMenuStore } from '@/stores/contextMenuStore.ts' |
| 26 |
import { watch } from 'vue' |
| 27 |
import { onClickOutside } from '@vueuse/core' |
| 28 |
import IvyPopover from '@/views/_components/popover/IvyPopover.vue' |
| 29 |
|
| 30 |
const contextMenuStore = useContextMenuStore() |
| 31 |
const { isOpen } = storeToRefs(contextMenuStore) |
| 32 |
|
| 33 |
const stopClickOutsideListener = ref<null | (() => void)>(null) |
| 34 |
|
| 35 |
const handleActionExecuted = () => { |
| 36 |
contextMenuStore.closeContextMenu() |
| 37 |
} |
| 38 |
|
| 39 |
const props = computed(() => contextMenuStore.contextMenuProps) |
| 40 |
|
| 41 |
type MaybeHTMLElement = HTMLElement | (ComponentPublicInstance & { $el: HTMLElement }) |
| 42 |
|
| 43 |
watch( |
| 44 |
() => props.value.contextMenuButtonRef, |
| 45 |
(newRef) => { |
| 46 |
if (stopClickOutsideListener.value) { |
| 47 |
// Cleanup old listener |
| 48 |
stopClickOutsideListener.value() |
| 49 |
} |
| 50 |
if (newRef instanceof HTMLElement) { |
| 51 |
stopClickOutsideListener.value = onClickOutside(newRef, () => |
| 52 |
contextMenuStore.closeContextMenu(), |
| 53 |
) |
| 54 |
} else if (newRef && newRef.$el && newRef.$el instanceof HTMLElement) { |
| 55 |
stopClickOutsideListener.value = onClickOutside(newRef.$el, () => |
| 56 |
contextMenuStore.closeContextMenu(), |
| 57 |
) |
| 58 |
} else { |
| 59 |
stopClickOutsideListener.value = null |
| 60 |
} |
| 61 |
}, |
| 62 |
) |
| 63 |
|
| 64 |
const resolveContextMenuButtonRef = computed(() => { |
| 65 |
const ref = contextMenuStore.contextMenuProps.contextMenuButtonRef as |
| 66 |
| MaybeHTMLElement |
| 67 |
| null |
| 68 |
| undefined |
| 69 |
if (ref && '$el' in ref && ref.$el instanceof HTMLElement) { |
| 70 |
return ref.$el |
| 71 |
} |
| 72 |
if (ref instanceof HTMLElement) { |
| 73 |
return ref |
| 74 |
} |
| 75 |
return null |
| 76 |
}) |
| 77 |
|
| 78 |
// Watch for when the context menu closes and remove the click outside listener |
| 79 |
watch(isOpen, (newVal) => { |
| 80 |
if (!newVal && stopClickOutsideListener.value) { |
| 81 |
stopClickOutsideListener.value() |
| 82 |
stopClickOutsideListener.value = null |
| 83 |
} |
| 84 |
}) |
| 85 |
</script> |
| 86 |
|
| 87 |
<style lang="scss"> |
| 88 |
// Popper Context Menu |
| 89 |
.ivyforms-popper-context-menu { |
| 90 |
// Element Popper |
| 91 |
&.el-popper.el-popover { |
| 92 |
padding: 0; |
| 93 |
border-radius: 2px; |
| 94 |
background: var(--map-ground-level-2-foreground); |
| 95 |
box-shadow: |
| 96 |
0 1px 2px 0 rgba(18, 26, 38, 0.3), |
| 97 |
0 1px 3px 1px rgba(18, 26, 38, 0.15); |
| 98 |
} |
| 99 |
} |
| 100 |
</style> |
| 101 |
|