mirror of
https://github.com/dfpc-coe/CloudTAK.git
synced 2025-12-22 05:37:16 +00:00
Setup RadialMenu Popup
This commit is contained in:
@@ -676,25 +676,7 @@ const noMenuShown = computed<boolean>(() => {
|
||||
&& (!route.name || !String(route.name).startsWith('home-menu'))
|
||||
});
|
||||
|
||||
watch(mapStore.radial, () => {
|
||||
if (mapStore.radial.cot) {
|
||||
mapStore.map.scrollZoom.disable();
|
||||
mapStore.map.touchZoomRotate.disableRotation();
|
||||
mapStore.map.dragRotate.disable();
|
||||
mapStore.map.dragPan.disable();
|
||||
|
||||
const id = mapStore.radial.cot.properties ? mapStore.radial.cot.properties.id : mapStore.radial.cot.id;
|
||||
if (!mapStore.locked.includes(id)) {
|
||||
mapStore.locked.push(mapStore.radial.cot.properties ? mapStore.radial.cot.properties.id : mapStore.radial.cot.id);
|
||||
}
|
||||
} else {
|
||||
mapStore.map.scrollZoom.enable();
|
||||
mapStore.map.touchZoomRotate.enableRotation();
|
||||
mapStore.map.dragRotate.enable();
|
||||
mapStore.map.dragPan.enable();
|
||||
mapStore.locked.pop();
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
// ensure uncaught errors in the stack are captured into vue context
|
||||
|
||||
@@ -73,3 +73,13 @@ svg.menu > g.center > use {
|
||||
cursor: pointer;
|
||||
fill: white;
|
||||
}
|
||||
|
||||
.radial-menu-popup .maplibregl-popup-content {
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.radial-menu-popup .maplibregl-popup-tip {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,4 @@
|
||||
<template>
|
||||
<div
|
||||
ref='radial-menu'
|
||||
class='position-absolute'
|
||||
style='pointer-events: none; z-index: 1000;'
|
||||
:style='{
|
||||
top: `${mapStore.radial.y - (size / 2)}px`,
|
||||
left: `${mapStore.radial.x - (size / 2)}px`,
|
||||
}'
|
||||
/>
|
||||
|
||||
<svg
|
||||
id='icons'
|
||||
class='d-none'
|
||||
@@ -125,12 +115,13 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick, useTemplateRef } from 'vue';
|
||||
import { ref, shallowRef, onMounted, onUnmounted, nextTick } from 'vue';
|
||||
import { OriginMode } from '../../../base/cot.ts';
|
||||
import Subscription from '../../../base/subscription.ts';
|
||||
import RadialMenu from './RadialMenu.js';
|
||||
import './RadialMenu.css';
|
||||
import { useMapStore } from '../../../stores/map.ts';
|
||||
import mapgl from 'maplibre-gl';
|
||||
|
||||
const mapStore = useMapStore();
|
||||
|
||||
@@ -143,29 +134,59 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(['close', 'click']);
|
||||
|
||||
const menuRef = useTemplateRef('radial-menu');
|
||||
const menuItems = ref([]);
|
||||
const menu = ref();
|
||||
const menu = shallowRef();
|
||||
const popup = shallowRef();
|
||||
|
||||
onUnmounted(() => {
|
||||
if (menu.value) {
|
||||
menu.value.close();
|
||||
}
|
||||
if (popup.value) {
|
||||
popup.value.remove();
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
await genMenuItems();
|
||||
|
||||
nextTick(() => {
|
||||
if (!menuRef.value) {
|
||||
console.warn('Warning: Could not mount Menu. menuRef is null.');
|
||||
return; // Skip menu initialization
|
||||
}
|
||||
const container = document.createElement('div');
|
||||
container.style.width = `${props.size}px`;
|
||||
container.style.height = `${props.size}px`;
|
||||
|
||||
menu.value = new RadialMenu({
|
||||
parent: menuRef.value,
|
||||
parent: container,
|
||||
size: props.size,
|
||||
closeOnClick: true,
|
||||
menuItems: menuItems.value,
|
||||
onClick: (item) => {
|
||||
emit('click', `${mapStore.radial.mode}:${item.id}`);
|
||||
},
|
||||
onClose: () => {
|
||||
emit('close');
|
||||
}
|
||||
});
|
||||
menu.value.open();
|
||||
|
||||
if (mapStore.radial.lngLat) {
|
||||
popup.value = new mapgl.Popup({
|
||||
closeButton: false,
|
||||
closeOnClick: true,
|
||||
maxWidth: 'none',
|
||||
anchor: 'center',
|
||||
className: 'radial-menu-popup'
|
||||
})
|
||||
.setLngLat(mapStore.radial.lngLat)
|
||||
.setDOMContent(container)
|
||||
.addTo(mapStore.map);
|
||||
|
||||
popup.value.on('close', () => {
|
||||
emit('close');
|
||||
});
|
||||
} else {
|
||||
emit('close');
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
import { ref, onMounted, onUnmounted, watch } from 'vue';
|
||||
import { ref, onMounted, onUnmounted, watch, markRaw } from 'vue';
|
||||
import Feature from './FeatureRow.vue'
|
||||
import { useMapStore } from '../../../stores/map.ts';
|
||||
import mapgl from 'maplibre-gl';
|
||||
@@ -33,40 +33,43 @@ const emit = defineEmits([ 'selected' ]);
|
||||
|
||||
const mapStore = useMapStore();
|
||||
const selectMenu = ref<HTMLElement | null>(null);
|
||||
let popup: mapgl.Popup | undefined;
|
||||
|
||||
onMounted(() => {
|
||||
if (!selectMenu.value) return;
|
||||
|
||||
if (!mapStore.select.popup) {
|
||||
mapStore.select.popup = markRaw(new mapgl.Popup({
|
||||
closeButton: false,
|
||||
closeOnClick: false,
|
||||
maxWidth: 'none',
|
||||
className: 'multiple-select-popup'
|
||||
}));
|
||||
|
||||
mapStore.select.popup.on('close', () => {
|
||||
if (mapStore.select.feats.length) {
|
||||
mapStore.select.feats = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const lngLat = mapStore.map.unproject([mapStore.select.x, mapStore.select.y]);
|
||||
|
||||
popup = new mapgl.Popup({
|
||||
closeButton: false,
|
||||
closeOnClick: false,
|
||||
maxWidth: 'none',
|
||||
className: 'multiple-select-popup'
|
||||
})
|
||||
mapStore.select.popup
|
||||
.setLngLat(lngLat)
|
||||
.setDOMContent(selectMenu.value)
|
||||
.addTo(mapStore.map);
|
||||
|
||||
popup.on('close', () => {
|
||||
if (mapStore.select.feats.length) {
|
||||
mapStore.select.feats = [];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
watch(() => [mapStore.select.x, mapStore.select.y], ([x, y]) => {
|
||||
if (popup) {
|
||||
if (mapStore.select.popup) {
|
||||
const lngLat = mapStore.map.unproject([x, y]);
|
||||
popup.setLngLat(lngLat);
|
||||
mapStore.select.popup.setLngLat(lngLat);
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (popup) {
|
||||
popup.remove();
|
||||
if (mapStore.select.popup) {
|
||||
mapStore.select.popup.remove();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -80,12 +80,14 @@ export const useMapStore = defineStore('cloudtak', {
|
||||
feats: Array<COT | MapGeoJSONFeature>;
|
||||
x: number;
|
||||
y: number;
|
||||
popup?: mapgl.Popup;
|
||||
},
|
||||
radial: {
|
||||
mode: string | undefined;
|
||||
cot: Feature | MapGeoJSONFeature | undefined;
|
||||
x: number;
|
||||
y: number;
|
||||
lngLat?: LngLat;
|
||||
},
|
||||
overlays: Array<Overlay>
|
||||
} => {
|
||||
@@ -140,6 +142,7 @@ export const useMapStore = defineStore('cloudtak', {
|
||||
mode: undefined,
|
||||
cot: undefined,
|
||||
x: 0, y: 0,
|
||||
lngLat: undefined
|
||||
},
|
||||
overlays: [],
|
||||
|
||||
@@ -332,10 +335,6 @@ export const useMapStore = defineStore('cloudtak', {
|
||||
};
|
||||
this.map.flyTo(flyTo);
|
||||
|
||||
if (this.radial.mode) {
|
||||
this.radial.x = this.container ? this.container.clientWidth / 2 : 0;
|
||||
this.radial.y = this.container ? this.container.clientHeight / 2 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -889,24 +888,14 @@ export const useMapStore = defineStore('cloudtak', {
|
||||
|
||||
if (!opts.mode) opts.mode = this.featureSource(feat) || 'feat';
|
||||
|
||||
if (opts.point.x < 150 || opts.point.y < 150) {
|
||||
const flyTo: mapgl.FlyToOptions = {
|
||||
speed: Infinity,
|
||||
center: [opts.lngLat.lng, opts.lngLat.lat]
|
||||
};
|
||||
|
||||
if (this.map.getZoom() < 3) flyTo.zoom = 4;
|
||||
this.map.flyTo(flyTo)
|
||||
|
||||
this.radial.x = this.container ? this.container.clientWidth / 2 : 0;
|
||||
this.radial.y = this.container ? this.container.clientHeight / 2 : 0;
|
||||
} else {
|
||||
this.radial.x = opts.point.x;
|
||||
this.radial.y = opts.point.y;
|
||||
}
|
||||
|
||||
this.radial.cot = feat;
|
||||
this.radial.mode = opts.mode;
|
||||
|
||||
if (feat.properties && feat.properties.center) {
|
||||
this.radial.lngLat = mapgl.LngLat.convert(feat.properties.center as LngLatLike);
|
||||
} else {
|
||||
this.radial.lngLat = opts.lngLat;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user