PluginProbe
The Innovative Form Builder – IvyForms / 0.7
The Innovative Form Builder – IvyForms v0.7
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / frontend / src / views / _components / context-menu / IvyContextMenu.vue

IvyContextMenu.vue in The Innovative Form Builder – IvyForms 0.7, at frontend/src/views/_components/context-menu/IvyContextMenu.vue

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