Skip to content

Commit 9bf4262

Browse files
committed
Fix route dragging after switching to a different collaborative map
1 parent edf3689 commit 9bf4262

File tree

2 files changed

+119
-90
lines changed

2 files changed

+119
-90
lines changed

frontend/src/lib/components/route-form/route-form.vue

Lines changed: 106 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import { computed, markRaw, nextTick, onBeforeUnmount, onMounted, reactive, ref, toRaw, watch, watchEffect } from "vue";
2+
import { computed, markRaw, nextTick, reactive, ref, toRaw, watch } from "vue";
33
import Icon from "../ui/icon.vue";
44
import { decodeRouteQuery, encodeRouteQuery, formatCoordinates, formatDistance, formatRouteMode, formatRouteTime, formatTypeName, isSearchId, normalizeMarkerName } from "facilmap-utils";
55
import { useToasts } from "../ui/toasts/toasts.vue";
@@ -23,7 +23,7 @@
2323
import AddToMapDropdown from "../ui/add-to-map-dropdown.vue";
2424
import ExportDropdown from "../ui/export-dropdown.vue";
2525
import { useI18n } from "../../utils/i18n";
26-
import { mapRef } from "../../utils/vue";
26+
import { mapRef, useIsMounted } from "../../utils/vue";
2727
2828
type SearchSuggestion = SearchResult;
2929
type MapSuggestion = FindOnMapResult & { kind: "marker" };
@@ -117,84 +117,113 @@
117117
const hoverInsertIdx = ref<number>();
118118
const suggestionMarker = ref<MarkerLayer>();
119119
120-
// TODO: Handle client.value change
121-
const routeLayer = new RouteLayer(client.value, props.routeId, { weight: 7, opacity: 1, raised: true });
122-
routeLayer.on("click", (e) => {
123-
if (!props.active && !(e.originalEvent as any).ctrlKey) {
124-
emit("activate");
125-
}
120+
const routeLayer = computed(() => {
121+
const layer = markRaw(new RouteLayer(client.value, props.routeId, { weight: 7, opacity: 1, raised: true }));
122+
layer.on("click", (e) => {
123+
if (!props.active && !(e.originalEvent as any).ctrlKey) {
124+
emit("activate");
125+
}
126+
});
127+
return layer;
126128
});
127129
128-
const draggable = new DraggableLines(mapContext.value.components.map, {
129-
enableForLayer: false,
130-
tempMarkerOptions: () => ({
131-
icon: getMarkerIcon(`#${dragMarkerColour}`, 35),
132-
pane: "fm-raised-marker"
133-
}),
134-
plusTempMarkerOptions: () => ({
135-
icon: getMarkerIcon(`#${dragMarkerColour}`, 35),
136-
pane: "fm-raised-marker"
137-
}),
138-
dragMarkerOptions: (layer, i, length) => ({
139-
icon: getIcon(i, length),
140-
pane: "fm-raised-marker"
141-
})
142-
});
143-
draggable.on({
144-
insert: (e: any) => {
145-
destinations.value.splice(e.idx, 0, makeCoordDestination(e.latlng));
146-
void reroute(false);
147-
},
148-
dragstart: (e: any) => {
149-
hoverDestinationIdx.value = e.idx;
150-
hoverInsertIdx.value = undefined;
151-
if (e.isNew)
152-
destinations.value.splice(e.idx, 0, makeCoordDestination(e.to));
153-
},
154-
drag: throttle((e: any) => {
155-
destinations.value[e.idx] = makeCoordDestination(e.to);
156-
}, 300),
157-
dragend: (e: any) => {
158-
destinations.value[e.idx] = makeCoordDestination(e.to);
159-
void reroute(false);
160-
},
161-
remove: (e: any) => {
162-
hoverDestinationIdx.value = undefined;
163-
destinations.value.splice(e.idx, 1);
164-
void reroute(false);
165-
},
166-
dragmouseover: (e: any) => {
167-
destinationMouseOver(e.idx);
168-
},
169-
dragmouseout: (e: any) => {
170-
destinationMouseOut(e.idx);
171-
},
172-
plusmouseover: (e: any) => {
173-
hoverInsertIdx.value = e.idx;
174-
},
175-
plusmouseout: (e: any) => {
176-
hoverInsertIdx.value = undefined;
177-
},
178-
tempmouseover: (e: any) => {
179-
hoverInsertIdx.value = e.idx;
180-
},
181-
tempmousemove: (e: any) => {
182-
if (e.idx != hoverInsertIdx.value)
130+
const draggable = computed(() => {
131+
const draggable = markRaw(new DraggableLines(mapContext.value.components.map, {
132+
enableForLayer: false,
133+
tempMarkerOptions: () => ({
134+
icon: getMarkerIcon(`#${dragMarkerColour}`, 35),
135+
pane: "fm-raised-marker"
136+
}),
137+
plusTempMarkerOptions: () => ({
138+
icon: getMarkerIcon(`#${dragMarkerColour}`, 35),
139+
pane: "fm-raised-marker"
140+
}),
141+
dragMarkerOptions: (layer, i, length) => ({
142+
icon: getIcon(i, length),
143+
pane: "fm-raised-marker"
144+
})
145+
}));
146+
147+
draggable.on({
148+
insert: (e: any) => {
149+
destinations.value.splice(e.idx, 0, makeCoordDestination(e.latlng));
150+
void reroute(false);
151+
},
152+
dragstart: (e: any) => {
153+
hoverDestinationIdx.value = e.idx;
154+
hoverInsertIdx.value = undefined;
155+
if (e.isNew)
156+
destinations.value.splice(e.idx, 0, makeCoordDestination(e.to));
157+
},
158+
drag: throttle((e: any) => {
159+
destinations.value[e.idx] = makeCoordDestination(e.to);
160+
}, 300),
161+
dragend: (e: any) => {
162+
destinations.value[e.idx] = makeCoordDestination(e.to);
163+
void reroute(false);
164+
},
165+
remove: (e: any) => {
166+
hoverDestinationIdx.value = undefined;
167+
destinations.value.splice(e.idx, 1);
168+
void reroute(false);
169+
},
170+
dragmouseover: (e: any) => {
171+
destinationMouseOver(e.idx);
172+
},
173+
dragmouseout: (e: any) => {
174+
destinationMouseOut(e.idx);
175+
},
176+
plusmouseover: (e: any) => {
177+
hoverInsertIdx.value = e.idx;
178+
},
179+
plusmouseout: (e: any) => {
180+
hoverInsertIdx.value = undefined;
181+
},
182+
tempmouseover: (e: any) => {
183183
hoverInsertIdx.value = e.idx;
184-
},
185-
tempmouseout: (e: any) => {
186-
hoverInsertIdx.value = undefined;
184+
},
185+
tempmousemove: (e: any) => {
186+
if (e.idx != hoverInsertIdx.value)
187+
hoverInsertIdx.value = e.idx;
188+
},
189+
tempmouseout: (e: any) => {
190+
hoverInsertIdx.value = undefined;
191+
}
192+
} as any);
193+
194+
return draggable;
195+
});
196+
197+
const isMounted = useIsMounted();
198+
watch([isMounted, draggable], (v, o, onCleanup) => {
199+
if (isMounted.value) {
200+
draggable.value.enable();
201+
202+
onCleanup(() => {
203+
draggable.value.disable();
204+
});
187205
}
188-
} as any);
206+
});
207+
watch([isMounted, routeLayer, () => mapContext.value.components.map], (v, o, onCleanup) => {
208+
if (isMounted.value) {
209+
routeLayer.value.addTo(mapContext.value.components.map);
189210
190-
onMounted(() => {
191-
routeLayer.addTo(mapContext.value.components.map);
192-
draggable.enable();
211+
onCleanup(() => {
212+
routeLayer.value.remove();
213+
});
214+
}
193215
});
194216
195-
onBeforeUnmount(() => {
196-
draggable.disable();
197-
routeLayer.remove();
217+
watch([hasRoute, () => props.active, draggable, routeLayer], () => {
218+
if (hasRoute.value)
219+
routeLayer.value.setStyle({ opacity: props.active ? 1 : 0.35, raised: props.active });
220+
221+
// Enable dragging after updating the style, since that might re-add the layer to the map
222+
if (props.active) {
223+
draggable.value.enableForLayer(routeLayer.value);
224+
} else {
225+
draggable.value.disableForLayer(routeLayer.value);
226+
}
198227
});
199228
200229
const zoomDestination = computed(() => routeObj.value && getZoomDestinationForRoute(routeObj.value));
@@ -222,17 +251,6 @@
222251
isInvalid: getValidationState(destination) === false
223252
})));
224253
225-
watchEffect(() => {
226-
if (hasRoute.value)
227-
routeLayer.setStyle({ opacity: props.active ? 1 : 0.35, raised: props.active });
228-
229-
// Enable dragging after updating the style, since that might re-add the layer to the map
230-
if (props.active)
231-
draggable.enableForLayer(routeLayer);
232-
else
233-
draggable.disableForLayer(routeLayer);
234-
});
235-
236254
watch(hashQuery, (hashQuery) => {
237255
emit("hash-query-change", hashQuery);
238256
});
@@ -386,23 +404,23 @@
386404
}
387405
388406
function destinationMouseOver(idx: number): void {
389-
const marker = routeLayer._draggableLines?.dragMarkers[idx];
407+
const marker = routeLayer.value._draggableLines?.dragMarkers[idx];
390408
391409
if (marker) {
392410
hoverDestinationIdx.value = idx;
393-
marker.setIcon(getIcon(idx, routeLayer._draggableLines!.dragMarkers.length, true));
411+
marker.setIcon(getIcon(idx, routeLayer.value._draggableLines!.dragMarkers.length, true));
394412
}
395413
}
396414
397415
function destinationMouseOut(idx: number): void {
398416
hoverDestinationIdx.value = undefined;
399417
400-
const marker = routeLayer._draggableLines?.dragMarkers[idx];
418+
const marker = routeLayer.value._draggableLines?.dragMarkers[idx];
401419
if (marker) {
402420
void Promise.resolve().then(() => {
403421
// If mouseout event is directly followed by a dragend event, the marker will be removed. Only update the icon if the marker is not removed.
404422
if (marker["_map"])
405-
marker.setIcon(getIcon(idx, routeLayer._draggableLines!.dragMarkers.length));
423+
marker.setIcon(getIcon(idx, routeLayer.value._draggableLines!.dragMarkers.length));
406424
});
407425
}
408426
}

frontend/src/lib/utils/vue.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type ComponentPublicInstance, type DeepReadonly, type Directive, type Ref, computed, onScopeDispose, readonly, ref, shallowReadonly, shallowRef, watch, type ComputedGetter, type Component, type VNodeProps, type AllowedComponentProps } from "vue";
1+
import { type ComponentPublicInstance, type DeepReadonly, type Directive, type Ref, computed, onScopeDispose, readonly, ref, shallowReadonly, shallowRef, watch, type ComputedGetter, type Component, type VNodeProps, type AllowedComponentProps, onBeforeUnmount, onMounted } from "vue";
22

33
// https://stackoverflow.com/a/73784241/242365
44
export type ComponentProps<C extends Component> = C extends new (...args: any) => any
@@ -113,4 +113,15 @@ export const vHtmlAsync: Directive<Element, Promise<string>> = (el, binding) =>
113113
watch(() => html.value, (val) => {
114114
el.innerHTML = val ?? "";
115115
});
116-
};
116+
};
117+
118+
export function useIsMounted(): Readonly<Ref<boolean>> {
119+
const isMounted = ref(false);
120+
onMounted(() => {
121+
isMounted.value = true;
122+
});
123+
onBeforeUnmount(() => {
124+
isMounted.value = false;
125+
});
126+
return readonly(isMounted);
127+
}

0 commit comments

Comments
 (0)