Skip to content

Vue Package API

The @route-optimization/vue package provides Vue 3 composables and components for route visualization.

Installation

bash
npm install @route-optimization/vue @route-optimization/core

Peer Dependencies:

  • vue ^3.3.0

Components

RouteMapView

Main component for displaying routes on a map.

vue
<template>
  <RouteMapView :api-key="apiKey" :route="route" auto-fit-bounds />
</template>

Props

PropTypeRequiredDefaultDescription
apiKeystringYes-Google Maps API key
routeRoute | nullNonullRoute to display
centerLatLngNoBangkokInitial map center
zoomnumberNo12Initial zoom level
heightstringNo'600px'Map container height
widthstringNo'100%'Map container width
autoFitBoundsbooleanNofalseAuto-fit to route bounds

Events

EventPayloadDescription
map-readygoogle.maps.MapEmitted when map is initialized
route-renderedRouteEmitted when route is rendered
errorErrorEmitted on error

Slots

SlotPropsDescription
loading-Custom loading state
error{ error: Error }Custom error display

Example

vue
<script setup lang="ts">
import { RouteMapView } from '@route-optimization/vue';
import type { Route } from '@route-optimization/core';
import { ref } from 'vue';

const apiKey = import.meta.env.VITE_GOOGLE_MAPS_API_KEY;

const route = ref<Route>({
  id: 'route-1',
  vehicleId: 'vehicle-1',
  stops: [
    {
      id: 'start',
      location: { lat: 13.7563, lng: 100.5018 },
      type: 'START',
      sequence: 0,
    },
    {
      id: 'delivery',
      location: { lat: 13.7467, lng: 100.5342 },
      type: 'DELIVERY',
      label: 'Customer #1',
      sequence: 1,
    },
  ],
});

const handleMapReady = (map: google.maps.Map) => {
  console.log('Map ready', map);
};

const handleError = (error: Error) => {
  console.error('Map error', error);
};
</script>

<template>
  <RouteMapView
    :api-key="apiKey"
    :route="route"
    auto-fit-bounds
    height="600px"
    @map-ready="handleMapReady"
    @error="handleError"
  >
    <template #loading>
      <div class="loading">Loading map...</div>
    </template>

    <template #error="{ error }">
      <div class="error">Error: {{ error.message }}</div>
    </template>
  </RouteMapView>
</template>

MapControls

Map control buttons for zoom and navigation.

vue
<template>
  <MapControls
    :map-instance="mapInstance"
    :show-zoom-level="true"
    @zoom-change="handleZoomChange"
  />
</template>

Props

PropTypeRequiredDefaultDescription
mapInstancegoogle.maps.Map | nullYes-Map instance
initialZoomnumberNo12Initial zoom level
minZoomnumberNo5Minimum zoom level
maxZoomnumberNo20Maximum zoom level
showZoomLevelbooleanNotrueShow zoom level display
showResetButtonbooleanNotrueShow reset button

Events

EventPayloadDescription
zoom-changenumberZoom level changed
zoom-in-Zoom in clicked
zoom-out-Zoom out clicked
reset-Reset clicked

Slots

SlotPropsDescription
before-controls-Content before controls
after-controls-Content after controls
zoom-in-icon-Custom zoom in icon
zoom-out-icon-Custom zoom out icon
reset-icon-Custom reset icon

Composables

useRouteMap

Composable for managing a route map instance.

typescript
const { mapInstance, isLoading, error, renderRoute, clearRoute, fitBounds } = useRouteMap(options);

Parameters

typescript
interface UseRouteMapOptions {
  apiKey: string;
  container: Ref<HTMLElement | null>;
  center?: LatLng;
  zoom?: number;
  onMapReady?: (map: google.maps.Map) => void;
  onError?: (error: Error) => void;
}

Returns

typescript
{
  mapInstance: Ref<google.maps.Map | null>;
  isLoading: Ref<boolean>;
  error: Ref<Error | null>;
  renderRoute: (route: Route, options?: RouteRenderOptions) => void;
  clearRoute: () => void;
  fitBounds: () => void;
}

Example

vue
<script setup lang="ts">
import { useRouteMap } from '@route-optimization/vue';
import { ref, watch } from 'vue';

const mapContainer = ref<HTMLElement | null>(null);
const route = ref<Route | null>(null);

const { mapInstance, isLoading, error, renderRoute, clearRoute, fitBounds } = useRouteMap({
  apiKey: import.meta.env.VITE_GOOGLE_MAPS_API_KEY,
  container: mapContainer,
  center: { lat: 13.7563, lng: 100.5018 },
  zoom: 12,
  onMapReady: (map) => {
    console.log('Map ready', map);
  },
});

watch(route, (newRoute) => {
  if (newRoute && mapInstance.value) {
    renderRoute(newRoute);
  }
});

const handleClearRoute = () => {
  clearRoute();
  route.value = null;
};
</script>

<template>
  <div>
    <div v-if="isLoading">Loading map...</div>
    <div v-if="error">Error: {{ error.message }}</div>
    <div ref="mapContainer" style="width: 100%; height: 600px" />
    <button @click="handleClearRoute">Clear Route</button>
  </div>
</template>

useMapControls

Composable for map control functionality.

typescript
const { zoom, canZoomIn, canZoomOut, zoomIn, zoomOut, setZoom, resetZoom } = useMapControls(
  mapInstance,
  options
);

Parameters

typescript
interface UseMapControlsOptions {
  initialZoom?: number;
  minZoom?: number;
  maxZoom?: number;
  onZoomChange?: (zoom: number) => void;
}

Returns

typescript
{
  zoom: Ref<number>;
  canZoomIn: ComputedRef<boolean>;
  canZoomOut: ComputedRef<boolean>;
  zoomIn: () => void;
  zoomOut: () => void;
  setZoom: (zoom: number) => void;
  resetZoom: () => void;
}

Example

vue
<script setup lang="ts">
import { useMapControls } from '@route-optimization/vue';
import { ref } from 'vue';

const mapInstance = ref<google.maps.Map | null>(null);

const { zoom, canZoomIn, canZoomOut, zoomIn, zoomOut, resetZoom } = useMapControls(mapInstance, {
  initialZoom: 12,
  minZoom: 5,
  maxZoom: 20,
  onZoomChange: (zoom) => {
    console.log('Zoom changed to:', zoom);
  },
});
</script>

<template>
  <div class="map-controls">
    <button @click="zoomIn" :disabled="!canZoomIn">+</button>
    <span>Zoom: {{ zoom }}</span>
    <button @click="zoomOut" :disabled="!canZoomOut">-</button>
    <button @click="resetZoom">Reset</button>
  </div>
</template>

Advanced Examples

Reactive Route Updates

vue
<script setup lang="ts">
import { RouteMapView } from '@route-optimization/vue';
import { ref, watch } from 'vue';

const route = ref<Route>({
  id: 'route-1',
  vehicleId: 'vehicle-1',
  stops: [],
});

// Add stops dynamically
const addStop = (location: LatLng) => {
  route.value = {
    ...route.value,
    stops: [
      ...route.value.stops,
      {
        id: `stop-${route.value.stops.length}`,
        location,
        type: 'DELIVERY',
        sequence: route.value.stops.length,
      },
    ],
  };
};

// Component automatically re-renders when route changes
</script>

<template>
  <RouteMapView :api-key="apiKey" :route="route" auto-fit-bounds />
</template>

Custom Styling with Composable

vue
<script setup lang="ts">
import { useRouteMap } from '@route-optimization/vue';
import { ref, watchEffect } from 'vue';

const mapContainer = ref<HTMLElement | null>(null);
const route = ref<Route | null>(null);
const color = ref('#4CAF50');

const { mapInstance, renderRoute } = useRouteMap({
  apiKey: import.meta.env.VITE_GOOGLE_MAPS_API_KEY,
  container: mapContainer,
});

watchEffect(() => {
  if (route.value && mapInstance.value) {
    renderRoute(route.value, {
      color: color.value,
      polyline: {
        strokeWeight: 6,
        strokeOpacity: 0.9,
      },
    });
  }
});
</script>

<template>
  <div>
    <input v-model="color" type="color" />
    <div ref="mapContainer" style="width: 100%; height: 600px" />
  </div>
</template>

Multiple Routes

vue
<script setup lang="ts">
import { useRouteMap } from '@route-optimization/vue';
import { ref, watch } from 'vue';

const mapContainer = ref<HTMLElement | null>(null);
const routes = ref<Route[]>([]);

const { mapInstance, renderRoute, clearRoute } = useRouteMap({
  apiKey: import.meta.env.VITE_GOOGLE_MAPS_API_KEY,
  container: mapContainer,
});

const colors = ['#FF5722', '#2196F3', '#4CAF50'];

watch([routes, mapInstance], ([newRoutes, map]) => {
  if (map && newRoutes.length > 0) {
    clearRoute();
    newRoutes.forEach((route, index) => {
      renderRoute(route, {
        color: colors[index % colors.length],
      });
    });
  }
});
</script>

<template>
  <div ref="mapContainer" style="width: 100%; height: 600px" />
</template>

TypeScript Support

All composables and components are fully typed:

typescript
import type { Route, RouteRenderOptions } from '@route-optimization/core';
import type { UseRouteMapOptions } from '@route-optimization/vue';

const options: UseRouteMapOptions = {
  apiKey: 'YOUR_API_KEY',
  container: mapContainer,
  center: { lat: 13.7563, lng: 100.5018 },
  zoom: 12,
};

CSS Customization

Components include scoped styles that can be customized:

vue
<style scoped>
.route-map-view {
  /* Custom styles */
}

.map-controls {
  /* Custom control styles */
}
</style>

Next Steps

Released under the MIT License.