Skip to content

Commit a50f583

Browse files
authored
Merge pull request #235 from Geode-solutions/fix_draggable_contextmenu
fix(ContextMenu): The contextmenu is now draggable
2 parents 31af4b8 + d8c31cb commit a50f583

File tree

1 file changed

+120
-31
lines changed

1 file changed

+120
-31
lines changed

components/Viewer/ContextMenu.vue

Lines changed: 120 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,28 @@
33
v-model="show_menu"
44
content-class="circular-menu"
55
:style="getMenuStyle()"
6+
:close-on-content-click="false"
7+
:close-delay="100"
68
>
7-
<div
8-
class="circular-menu-items"
9-
:style="{ width: `${radius * 2}px`, height: `${radius * 2}px` }"
10-
>
11-
<component
12-
v-for="(item, index) in menu_items"
13-
:is="item"
14-
:key="index"
15-
:itemProps="{
16-
id: props.id,
17-
tooltip_location: getTooltipLocation(index),
18-
tooltip_origin: getTooltipOrigin(index),
19-
}"
20-
class="menu-item-wrapper"
21-
:style="getItemStyle(index)"
22-
/>
9+
<div class="circular-menu-drag-handle" @mousedown.stop="startDrag">
10+
<div
11+
class="circular-menu-items"
12+
:style="{ width: `${radius * 2}px`, height: `${radius * 2}px` }"
13+
>
14+
<component
15+
v-for="(item, index) in menu_items"
16+
:is="item"
17+
:key="index"
18+
:itemProps="{
19+
id: props.id,
20+
tooltip_location: getTooltipLocation(index),
21+
tooltip_origin: getTooltipOrigin(index),
22+
}"
23+
class="menu-item-wrapper"
24+
:style="getItemStyle(index)"
25+
@mousedown.stop
26+
/>
27+
</div>
2328
</div>
2429
</v-menu>
2530
</template>
@@ -40,12 +45,60 @@
4045
const itemId = props.id || menuStore.current_id
4146
return itemId ? dataBaseStore.itemMetaDatas(itemId) : {}
4247
})
48+
4349
const radius = 80
4450
const show_menu = ref(true)
51+
const isDragging = ref(false)
52+
const dragStartX = ref(0)
53+
const dragStartY = ref(0)
54+
const menuX = ref(props.x || menuStore.menuX)
55+
const menuY = ref(props.y || menuStore.menuY)
56+
57+
const { pause: pauseDragListeners, resume: resumeDragListeners } =
58+
useEventListener(
59+
"mousemove",
60+
(e) => {
61+
if (!isDragging.value) return
62+
handleDrag(e)
63+
},
64+
{ passive: true },
65+
)
66+
67+
const { pause: pauseStopListeners, resume: resumeStopListeners } =
68+
useEventListener("mouseup", (e) => {
69+
if (!isDragging.value) return
70+
stopDrag(e)
71+
})
4572
46-
watch(show_menu, (value) => {
47-
if (!value) {
48-
menuStore.closeMenu()
73+
useEventListener(
74+
"touchstart",
75+
(e) => {
76+
startDrag(e.touches[0])
77+
e.preventDefault()
78+
},
79+
{ passive: false },
80+
)
81+
82+
useEventListener(
83+
"touchmove",
84+
(e) => {
85+
if (!isDragging.value) return
86+
handleDrag(e.touches[0])
87+
e.preventDefault()
88+
},
89+
{ passive: false },
90+
)
91+
92+
useEventListener("touchend", (e) => {
93+
if (!isDragging.value) return
94+
stopDrag(e.changedTouches[0])
95+
})
96+
97+
watch(show_menu, (newVal) => {
98+
if (!newVal && isDragging.value) {
99+
setTimeout(() => {
100+
show_menu.value = true
101+
}, 10)
49102
}
50103
})
51104
@@ -58,18 +111,42 @@
58111
59112
const menuItemCount = computed(() => menu_items.value.length)
60113
61-
function getMenuStyle() {
62-
const x = props.x || menuStore.menuX
63-
const y = props.y || menuStore.menuY
64-
const width = props.containerWidth || menuStore.containerWidth
65-
const height = props.containerHeight || menuStore.containerHeight
114+
function startDrag(e) {
115+
isDragging.value = true
116+
dragStartX.value = e.clientX - menuX.value
117+
dragStartY.value = e.clientY - menuY.value
118+
resumeDragListeners()
119+
resumeStopListeners()
120+
e.preventDefault()
121+
}
66122
67-
const adjustedX = Math.min(Math.max(x, radius), width - radius)
68-
const adjustedY = Math.min(Math.max(y, radius), height - radius)
123+
function handleDrag(e) {
124+
if (!isDragging.value) return
125+
menuX.value = e.clientX - dragStartX.value
126+
menuY.value = e.clientY - dragStartY.value
69127
128+
menuX.value = Math.min(
129+
Math.max(menuX.value, radius),
130+
props.containerWidth - radius,
131+
)
132+
menuY.value = Math.min(
133+
Math.max(menuY.value, radius),
134+
props.containerHeight - radius,
135+
)
136+
}
137+
138+
function stopDrag(e) {
139+
isDragging.value = false
140+
pauseDragListeners()
141+
pauseStopListeners()
142+
e.stopPropagation()
143+
menuStore.setMenuPosition(menuX.value, menuY.value)
144+
}
145+
146+
function getMenuStyle() {
70147
return {
71-
left: `${adjustedX - radius}px`,
72-
top: `${adjustedY - radius}px`,
148+
left: `${menuX.value - radius}px`,
149+
top: `${menuY.value - radius}px`,
73150
}
74151
}
75152
@@ -92,9 +169,7 @@
92169
function getItemStyle(index) {
93170
const angle = (index / menuItemCount.value) * 2 * Math.PI
94171
return {
95-
transform: `translate(${Math.cos(angle) * radius}px, ${
96-
Math.sin(angle) * radius
97-
}px)`,
172+
transform: `translate(${Math.cos(angle) * radius}px, ${Math.sin(angle) * radius}px)`,
98173
transition: "opacity 0.1s ease, transform 0.1s ease",
99174
position: "absolute",
100175
}
@@ -106,6 +181,20 @@
106181
position: absolute;
107182
border-radius: 50%;
108183
background-color: rgba(0, 0, 0, 0.8);
184+
user-select: none;
185+
cursor: grab;
186+
z-index: 1000;
187+
}
188+
189+
.circular-menu-drag-handle {
190+
width: 100%;
191+
height: 100%;
192+
border-radius: 50%;
193+
cursor: grab;
194+
}
195+
196+
.circular-menu-drag-handle:active {
197+
cursor: grabbing;
109198
}
110199
111200
.circular-menu-items {

0 commit comments

Comments
 (0)