From 8778850924a0c73ba840221abb611497bd571602 Mon Sep 17 00:00:00 2001 From: MokujinMap Date: Tue, 16 Jul 2024 19:06:25 +0300 Subject: [PATCH] Add the ability to drag the MMapRouteControl markers and fix minor bugs --- example/route-control/react/index.tsx | 35 +++++- example/route-control/vanilla/index.ts | 110 ++++++++++-------- example/route-control/vue/index.ts | 22 +++- .../MMapWaypointInput/index.ts | 28 +++-- src/controls/MMapRouteControl/index.ts | 18 ++- src/controls/MMapRouteControl/vue/index.ts | 27 +++++ 6 files changed, 172 insertions(+), 68 deletions(-) create mode 100644 src/controls/MMapRouteControl/vue/index.ts diff --git a/example/route-control/react/index.tsx b/example/route-control/react/index.tsx index a68d4ca..542c4c8 100644 --- a/example/route-control/react/index.tsx +++ b/example/route-control/react/index.tsx @@ -39,6 +39,7 @@ async function main() { const [fromCoords, setFromCoords] = React.useState(); const [toCoords, setToCoords] = React.useState(); const [previewCoords, setPreviewCoords] = React.useState(); + const [waypoints, setWaypoints] = React.useState<[LngLat, LngLat]>([LOCATION.center, null]); const onRouteResult = React.useCallback((result: BaseRouteResponse, type: RouteOptions['type']) => { setRouteType(type); @@ -69,6 +70,20 @@ async function main() { }, [] ); + + const onDragEndHandler = React.useCallback( + (coordinates: LngLat, type: 'from' | 'to') => { + if (type === 'from') { + setFromCoords(coordinates); + setWaypoints([coordinates, toCoords]); + } else { + setToCoords(coordinates); + setWaypoints([fromCoords, coordinates]); + } + }, + [fromCoords, toCoords] + ); + const features = React.useMemo(() => { if (!routeResult) { return null; @@ -96,7 +111,7 @@ async function main() { {showFeature && features} - {fromCoords !== undefined && } - {toCoords !== undefined && } + {fromCoords !== undefined && ( + onDragEndHandler(coordinates, 'from')} + {...FROM_POINT_STYLE} + /> + )} + {toCoords !== undefined && ( + onDragEndHandler(coordinates, 'to')} + {...TO_POINT_STYLE} + /> + )} {previewCoords !== undefined && ( )} diff --git a/example/route-control/vanilla/index.ts b/example/route-control/vanilla/index.ts index 84b0760..95b9fcf 100644 --- a/example/route-control/vanilla/index.ts +++ b/example/route-control/vanilla/index.ts @@ -1,4 +1,4 @@ -import type {BaseRouteResponse, LngLat, MMapFeature, RouteOptions, Stroke} from '@mappable-world/mappable-types'; +import type {BaseRouteResponse, LngLat, MMapFeature, RouteOptions} from '@mappable-world/mappable-types'; import type {MMapDefaultMarker} from '../../src'; import { FROM_POINT_STYLE, @@ -24,12 +24,25 @@ async function main() { new MMapDefaultFeaturesLayer({}) ]); + const dragEndHandler = () => { + routeControl.update({ + waypoints: [ + map.children.includes(fromPoint) ? fromPoint.coordinates : null, + map.children.includes(toPoint) ? toPoint.coordinates : null + ] + }); + }; + const fromPoint: MMapDefaultMarker = new MMapDefaultMarker({ coordinates: map.center as LngLat, + onDragEnd: dragEndHandler, + draggable: true, ...FROM_POINT_STYLE }); const toPoint: MMapDefaultMarker = new MMapDefaultMarker({ coordinates: map.center as LngLat, + onDragEnd: dragEndHandler, + draggable: true, ...TO_POINT_STYLE }); let previewPoint: MMapDefaultMarker = new MMapDefaultMarker({ @@ -39,59 +52,56 @@ async function main() { let featuresOnMap: MMapFeature[] = []; - map.addChild( - new MMapControls({position: 'top left'}).addChild( - new MMapRouteControl({ - truckParameters: TRUCK_PARAMS, - waypoints: [map.center as LngLat, null], - onBuildRouteError() { - featuresOnMap.forEach((f) => map.removeChild(f)); - featuresOnMap = []; - }, - onRouteResult(result, type) { - featuresOnMap.forEach((f) => map.removeChild(f)); - featuresOnMap = getFeatures(result, type); - featuresOnMap.forEach((f) => map.addChild(f)); + const routeControl = new MMapRouteControl({ + truckParameters: TRUCK_PARAMS, + waypoints: [map.center as LngLat, null], + onBuildRouteError() { + featuresOnMap.forEach((f) => map.removeChild(f)); + featuresOnMap = []; + }, + onRouteResult(result, type) { + featuresOnMap.forEach((f) => map.removeChild(f)); + featuresOnMap = getFeatures(result, type); + featuresOnMap.forEach((f) => map.addChild(f)); - const bounds = computeBoundsForPoints(result.toRoute().geometry.coordinates); - map.update({location: {bounds, duration: 500}}); - }, - onUpdateWaypoints(waypoints) { - const [from, to] = waypoints; - if (from) { - const {coordinates} = from.geometry; - fromPoint.update({coordinates}); - map.addChild(fromPoint); - } else { - map.removeChild(fromPoint); - } + const bounds = computeBoundsForPoints(result.toRoute().geometry.coordinates); + map.update({location: {bounds, duration: 500}}); + }, + onUpdateWaypoints(waypoints) { + const [from, to] = waypoints; + if (from) { + const {coordinates} = from.geometry; + fromPoint.update({coordinates}); + map.addChild(fromPoint); + } else { + map.removeChild(fromPoint); + } - if (to) { - const {coordinates} = to.geometry; - toPoint.update({coordinates}); - map.addChild(toPoint); - } else { - map.removeChild(toPoint); - } - if (!to && !from) { - featuresOnMap.forEach((f) => map.removeChild(f)); - featuresOnMap = []; - } - }, - onMouseMoveOnMap(coordinates, index, lastCall) { - if (!lastCall) { - previewPoint.update({coordinates}); + if (to) { + const {coordinates} = to.geometry; + toPoint.update({coordinates}); + map.addChild(toPoint); + } else { + map.removeChild(toPoint); + } + if (!to || !from) { + featuresOnMap.forEach((f) => map.removeChild(f)); + featuresOnMap = []; + } + }, + onMouseMoveOnMap(coordinates, index, lastCall) { + if (!lastCall) { + previewPoint.update({coordinates}); - if (!map.children.includes(previewPoint)) { - map.addChild(previewPoint); - } - } else { - map.removeChild(previewPoint); - } + if (!map.children.includes(previewPoint)) { + map.addChild(previewPoint); } - }) - ) - ); + } else { + map.removeChild(previewPoint); + } + } + }); + map.addChild(new MMapControls({position: 'top left'}).addChild(routeControl)); const getFeatures = (result: BaseRouteResponse, type: RouteOptions['type']): MMapFeature[] => { if (type !== 'transit') { diff --git a/example/route-control/vue/index.ts b/example/route-control/vue/index.ts index 5ddd683..3649340 100644 --- a/example/route-control/vue/index.ts +++ b/example/route-control/vue/index.ts @@ -42,6 +42,7 @@ async function main() { const fromCoords = Vue.ref(); const toCoords = Vue.ref(); const previewCoords = Vue.ref(); + const waypoints = Vue.ref<[LngLat, LngLat]>([LOCATION.center, null]); const refMap = (ref: any) => { window.map = ref?.entity; @@ -69,6 +70,13 @@ async function main() { const onMouseMoveOnMap: MMapRouteControlProps['onMouseMoveOnMap'] = (coordinates, index, lastCall) => { previewCoords.value = lastCall ? undefined : coordinates; }; + const onDragEndHandler = (coordinates: LngLat, type: 'from' | 'to') => { + if (type === 'from') { + waypoints.value = [coordinates, toCoords.value]; + } else { + waypoints.value = [fromCoords.value, coordinates]; + } + }; const features = Vue.computed(() => { if (!routeResult) { return null; @@ -105,11 +113,13 @@ async function main() { fromCoords, toCoords, previewCoords, + waypoints, features, onRouteResult, onUpdateWaypoints, onBuildRouteError, onMouseMoveOnMap, + onDragEndHandler, refMap }; }, @@ -120,7 +130,7 @@ async function main() { + :coordinates="fromCoords" + draggable + :onDragEnd="(coordinates) => onDragEndHandler(coordinates, 'from')" + v-bind="FROM_POINT_STYLE" /> + :coordinates="toCoords" + draggable + :onDragEnd="(coordinates) => onDragEndHandler(coordinates, 'to')" + v-bind="TO_POINT_STYLE" /> diff --git a/src/controls/MMapRouteControl/MMapWaypointInput/index.ts b/src/controls/MMapRouteControl/MMapWaypointInput/index.ts index 2e3c3af..7762b13 100644 --- a/src/controls/MMapRouteControl/MMapWaypointInput/index.ts +++ b/src/controls/MMapRouteControl/MMapWaypointInput/index.ts @@ -258,18 +258,26 @@ export class MMapWaypointInput extends mappable.MMapComplexEntity { diff --git a/src/controls/MMapRouteControl/index.ts b/src/controls/MMapRouteControl/index.ts index 2bbe5f5..a24ed86 100644 --- a/src/controls/MMapRouteControl/index.ts +++ b/src/controls/MMapRouteControl/index.ts @@ -1,3 +1,4 @@ +import debounce from 'lodash/debounce'; import { BaseRouteResponse, DomDetach, @@ -21,6 +22,7 @@ import { } from './helpers'; import './index.css'; import {formatDistance, formatDuration} from './utils'; +import {MMapRouteControlVuefyOptions} from './vue'; export type WaypointsArray = Array; @@ -59,6 +61,7 @@ type DefaultProps = typeof defaultProps; export class MMapRouteControl extends mappable.MMapComplexEntity { static defaultProps = defaultProps; + static [mappable.optionsKeyVuefy] = MMapRouteControlVuefyOptions; private _control: MMapControl; private _router: MMapCommonRouteControl; @@ -76,6 +79,10 @@ export class MMapRouteControl extends mappable.MMapComplexEntity): void { + this._router.update(props); + } + protected _onDetach(): void { this._removeDirectChild(this._control); } @@ -154,6 +161,13 @@ class MMapCommonRouteControl extends mappable.MMapComplexEntity): void { + if (diffProps.waypoints) { + this._waypointInputFromElement.update({waypoint: diffProps.waypoints[0]}); + this._waypointInputToElement.update({waypoint: diffProps.waypoints[1]}); + } + } + protected _onDetach(): void { this._detachDom?.(); this._detachDom = undefined; @@ -215,7 +229,7 @@ class MMapCommonRouteControl extends mappable.MMapComplexEntity { if (!this._waypoints.every((point) => point !== null)) { return; } @@ -245,7 +259,7 @@ class MMapCommonRouteControl extends mappable.MMapComplexEntity this._route())); } - } + }, 200); private _getRouteDetails(response: BaseRouteResponse): HTMLElement[] { if (!response.toSteps) { diff --git a/src/controls/MMapRouteControl/vue/index.ts b/src/controls/MMapRouteControl/vue/index.ts new file mode 100644 index 0000000..f899881 --- /dev/null +++ b/src/controls/MMapRouteControl/vue/index.ts @@ -0,0 +1,27 @@ +import {CustomVuefyOptions} from '@mappable-world/mappable-types/modules/vuefy'; +import {TruckParameters} from '@mappable-world/mappable-types/imperative/route'; +import {LngLat} from '@mappable-world/mappable-types'; +import {MMapRouteControl, MMapRouteControlProps, AvailableTypes} from '..'; +import type TVue from '@vue/runtime-core'; + +export const MMapRouteControlVuefyOptions: CustomVuefyOptions = { + props: { + geolocationTextInput: {type: String, default: 'My location'}, + clearFieldsText: {type: String, default: 'Clear all'}, + changeOrderText: {type: String, default: 'Change the order'}, + availableTypes: { + type: Array as TVue.PropType, + default: ['driving', 'truck', 'walking', 'transit'] + }, + truckParameters: Object as TVue.PropType, + waypoints: Array as unknown as TVue.PropType<[LngLat | null, LngLat | null]>, + waypointsPlaceholders: {type: Array as unknown as TVue.PropType<[string, string]>, default: ['From', 'To']}, + search: Function as TVue.PropType, + suggest: Function as TVue.PropType, + route: Function as TVue.PropType, + onMouseMoveOnMap: Function as TVue.PropType, + onUpdateWaypoints: Function as TVue.PropType, + onRouteResult: Function as TVue.PropType, + onBuildRouteError: Function as TVue.PropType + } +};