PluginProbe
The Innovative Form Builder – IvyForms / 0.8.2
The Innovative Form Builder – IvyForms v0.8.2
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.8.2, at frontend/src/views/_components/context-menu/IvyContextMenu.vue

129 lines 3.7 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 :selection-type="contextMenuStore.contextMenuProps.selectionType"
18 @action-executed="handleActionExecuted"
19 @close-menu="contextMenuStore.closeContextMenu"
20 />
21 </IvyPopover>
22 </template>
23
24 <script setup lang="ts">
25 import { ref, computed, type ComponentPublicInstance } from 'vue'
26 import { storeToRefs } from 'pinia'
27 import { useContextMenuStore } from '@/stores/contextMenuStore.ts'
28 import { watch } from 'vue'
29 import { onClickOutside } from '@vueuse/core'
30 import IvyPopover from '@/views/_components/popover/IvyPopover.vue'
31
32 interface Props {
33 width?: number
34 offset?: number
35 }
36
37 const props = withDefaults(defineProps<Props>(), {
38 width: 205,
39 offset: undefined,
40 })
41
42 const contextMenuStore = useContextMenuStore()
43 const { isOpen } = storeToRefs(contextMenuStore)
44
45 const stopClickOutsideListener = ref<null | (() => void)>(null)
46
47 const handleActionExecuted = () => {
48 if (!contextMenuStore.contextMenuProps.selectionType) {
49 contextMenuStore.closeContextMenu()
50 }
51 }
52
53 const menuProps = computed(() => contextMenuStore.contextMenuProps)
54
55 type MaybeHTMLElement = HTMLElement | (ComponentPublicInstance & { $el: HTMLElement })
56
57 watch(
58 () => menuProps.value.contextMenuButtonRef,
59 (newRef) => {
60 if (stopClickOutsideListener.value) {
61 // Cleanup old listener
62 stopClickOutsideListener.value()
63 }
64 if (newRef instanceof HTMLElement) {
65 stopClickOutsideListener.value = onClickOutside(newRef, (event) => {
66 // Don't close if selectionType is present and click is on a menu item
67 if (contextMenuStore.contextMenuProps.selectionType) {
68 const target = event.target as HTMLElement
69 if (target.closest('.ivyforms-popper-context-menu')) {
70 return
71 }
72 }
73 contextMenuStore.closeContextMenu()
74 })
75 } else if (newRef && newRef.$el && newRef.$el instanceof HTMLElement) {
76 stopClickOutsideListener.value = onClickOutside(newRef.$el, (event) => {
77 // Don't close if selectionType is present and click is on a menu item
78 if (contextMenuStore.contextMenuProps.selectionType) {
79 const target = event.target as HTMLElement
80 if (target.closest('.ivyforms-popper-context-menu')) {
81 return
82 }
83 }
84 contextMenuStore.closeContextMenu()
85 })
86 } else {
87 stopClickOutsideListener.value = null
88 }
89 },
90 )
91
92 const resolveContextMenuButtonRef = computed(() => {
93 const ref = contextMenuStore.contextMenuProps.contextMenuButtonRef as
94 | MaybeHTMLElement
95 | null
96 | undefined
97 if (ref && '$el' in ref && ref.$el instanceof HTMLElement) {
98 return ref.$el
99 }
100 if (ref instanceof HTMLElement) {
101 return ref
102 }
103 return null
104 })
105
106 // Watch for when the context menu closes and remove the click outside listener
107 watch(isOpen, (newVal) => {
108 if (!newVal && stopClickOutsideListener.value) {
109 stopClickOutsideListener.value()
110 stopClickOutsideListener.value = null
111 }
112 })
113 </script>
114
115 <style lang="scss">
116 // Popper Context Menu
117 .ivyforms-popper-context-menu {
118 // Element Popper
119 &.el-popper.el-popover {
120 padding: 0;
121 border-radius: 2px;
122 background: var(--map-ground-level-2-foreground);
123 box-shadow:
124 0 1px 2px 0 rgba(18, 26, 38, 0.3),
125 0 1px 3px 1px rgba(18, 26, 38, 0.15);
126 }
127 }
128 </style>
129