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

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