Skip to content
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
f609911
refactor: remove unused package @polar/lib-idx
dopenguin Jun 9, 2026
5e594a8
refactor(routing): add basic plugin structure
dopenguin Jun 9, 2026
c8f8aed
refactor(routing): migrate configuration options
dopenguin Jun 9, 2026
15e8fff
refactor(routing): add basic ui skeleton
dopenguin Jun 10, 2026
440ace0
refactor(routing): remove extra style end tag
dopenguin Jun 11, 2026
04e6e98
Merge branch 'vue3/feat-context-menu' into vue3/migrate-plugin-routing
dopenguin Jun 11, 2026
d6565c3
refactor(routing): add missing classes to add and remove buttons
dopenguin Jun 11, 2026
54c3cab
Merge branch 'vue3/feat-context-menu' into vue3/migrate-plugin-routing
dopenguin Jun 11, 2026
02fe673
fix: add name prop so radio inputs are properly grouped together as o…
dopenguin Jun 12, 2026
38370cf
Merge branch 'vue3/fix-polar-input' into vue3/migrate-plugin-routing
dopenguin Jun 12, 2026
3887afb
Merge branch 'next' into vue3/migrate-plugin-routing
dopenguin Jun 15, 2026
b23841e
feat: add reusable toggle button component
dopenguin Jun 15, 2026
366b4e0
refactor(routing): add toggle buttons to switch the selected travel mode
dopenguin Jun 15, 2026
8b5a531
refactor: add routing to MapConfiguration
dopenguin Jun 15, 2026
b1d1268
refactor: add routing configuration to snowbox
dopenguin Jun 15, 2026
d6372bf
refactor(routing): move travelModes to the store
dopenguin Jun 16, 2026
dba4e6e
refactor(routing): update default values and configurability of selec…
dopenguin Jun 16, 2026
3db5c27
refactor(routing): add possibility to select a preference for a route…
dopenguin Jun 16, 2026
58e43d6
refactor(routing): reset plugin on teardown
dopenguin Jun 16, 2026
28a7dd5
refactor(routing): add possibility to select route types to avoid
dopenguin Jun 16, 2026
06e894f
refactor(routing): add possibility to sent routing requests
dopenguin Jun 22, 2026
4146efb
Merge branch 'next' into vue3/migrate-plugin-routing
dopenguin Jun 22, 2026
30c9d79
refactor(routing): add reset and details buttons
dopenguin Jun 22, 2026
ca62527
refactor(routing): add possibility to view route details
dopenguin Jun 23, 2026
76a60df
refactor(routing): update draw interaction to only be set as long as …
dopenguin Jun 23, 2026
502fbad
Merge branch 'vue3/refactor-polar-select' into vue3/migrate-plugin-ro…
dopenguin Jun 23, 2026
cd3e6be
Merge branch 'next' into vue3/migrate-plugin-routing
dopenguin Jun 24, 2026
e50c504
refactor(routing): improve ux and accessibility of routing details
dopenguin Jun 24, 2026
09f2b47
refactor(routing): always display routing details when available
dopenguin Jun 24, 2026
9224521
refactor(routing): remove last kern-description classes
dopenguin Jun 25, 2026
c005a7c
refactor: improve accessibility and styling of PolarToggleButton
dopenguin Jun 25, 2026
e7c57ca
fix(routing): correctly associate label with the input
dopenguin Jun 25, 2026
d10b6be
fix(routing): remove unused import
dopenguin Jun 25, 2026
9f18c73
refactor(routing): further improve accessibility of routing details
dopenguin Jun 25, 2026
7fcc370
refactor(routing): correctly place aria live region for routing details
dopenguin Jun 26, 2026
366adcf
refactor(routing): add TSDoc and reorder exported parameters and func…
dopenguin Jun 26, 2026
8cbb1a7
refactor(routing): add comment regarding the structure of routingResp…
dopenguin Jun 26, 2026
7b21bc9
refactor(routing): add information when a route gets requested
dopenguin Jun 26, 2026
21567a2
refactor(routing): update fetchRoute to only set the Authorization he…
dopenguin Jun 26, 2026
cd89787
refactor(routing): add a return type for the fetch response
dopenguin Jun 26, 2026
03f7769
refactor(routing): add an AbortController to abort ongoing routing re…
dopenguin Jun 26, 2026
f125fc1
Merge branch 'vue3/feat-context-menu' into vue3/migrate-plugin-routing
dopenguin Jul 2, 2026
85c8c49
fix: various linting issues (mostly import order)
dopenguin Jul 2, 2026
f462539
Merge branch 'vue3/feat-context-menu' into vue3/migrate-plugin-routing
dopenguin Jul 2, 2026
8bde552
feat: add reusable toggle button component
dopenguin Jul 3, 2026
8dfea9a
fix(routing): use non-deprecated css parameter
dopenguin Jul 3, 2026
a5f3c31
fix(routing): add newly required prop
dopenguin Jul 6, 2026
d1be3c4
fix(routing): remove draw interaction if a user loses focus through k…
dopenguin Jul 6, 2026
16c98ad
refactor(routing): use consistent naming for start and end
dopenguin Jul 6, 2026
bda5ec3
Merge branch 'vue3/feat-context-menu' into vue3/migrate-plugin-routing
dopenguin Jul 6, 2026
df712a9
refactor: add TSDoc for routing in MapConfiguration
dopenguin Jul 6, 2026
8f829c4
Merge branch 'vue3/feat-context-menu' into vue3/migrate-plugin-routing
dopenguin Jul 7, 2026
5088e5d
Merge branch 'next' into vue3/migrate-plugin-routing
dopenguin Jul 9, 2026
4eb47ac
fix(routing): remove disallowed file extensions from imports
dopenguin Jul 9, 2026
d1b8e95
refactor: add routing to the generic client
dopenguin Jul 9, 2026
9f1bf80
refactor(routing): add default icon
dopenguin Jul 10, 2026
e0e7b67
refactor(routing): update tsdoc to reflect current implementation
dopenguin Jul 10, 2026
fdefe8e
refactor(iconMenu): add hint for routing
dopenguin Jul 10, 2026
cf55a71
fix: use correct plugin id
dopenguin Jul 10, 2026
96d49c4
refactor(routing): shorten getter
dopenguin Jul 10, 2026
95e974c
refactor(routing): move watcher definitions to store scope
dopenguin Jul 10, 2026
8c04226
refactor(routing): remove redundant url encoding
dopenguin Jul 10, 2026
35d0bac
refactor(routing): add composable for the route layer
dopenguin Jul 10, 2026
bb8948e
feat: allow composables in plugins
dopenguin Jul 10, 2026
d5702fd
refactor(routing): format distance and duration numbers based on lang…
dopenguin Jul 13, 2026
239a12a
refactor(routing): hoist layer composable to store scope
dopenguin Jul 14, 2026
569ba75
Merge branch 'next' into vue3/migrate-plugin-routing
dopenguin Jul 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions examples/snowbox/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import pluginPins from '@polar/polar/plugins/pins'
import pluginPointerPosition from '@polar/polar/plugins/pointerPosition'
import pluginReverseGeocoder from '@polar/polar/plugins/reverseGeocoder'
import pluginRouting from '@polar/polar/plugins/routing'
import pluginScale from '@polar/polar/plugins/scale'
import pluginToast from '@polar/polar/plugins/toast'
import pluginZoom from '@polar/polar/plugins/zoom'
Expand Down Expand Up @@ -92,7 +93,7 @@
colorScheme,
startCenter: [565874, 5934140],
layers: [
// TODO: Add internalization to snowbox

Check warning on line 96 in examples/snowbox/index.js

View workflow job for this annotation

GitHub Actions / Linting

Unexpected 'todo' comment: 'TODO: Add internalization to snowbox'
{
id: basemapId,
visibility: true,
Expand Down Expand Up @@ -488,6 +489,16 @@
}, */
}),
},
{
plugin: pluginRouting({
type: 'ors',
url: 'https://api.openrouteservice.org/v2/directions/',
apiKey: '',
Comment thread
dopenguin marked this conversation as resolved.
displayPreferences: true,
displayRouteTypesToAvoid: true,
}),
icon: 'kern-icon-fill--assistant-direction',
},
],
[
{
Expand Down
4 changes: 3 additions & 1 deletion src/components/PolarSelect.ce.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@
</template>

<script setup lang="ts">
import type { Ref } from 'vue'

import { computed, useId } from 'vue'

const props = defineProps<{
options: {
value: string | number
label: string
label: string | Ref<string>
ariaLabel?: string
[key: string]: unknown
}[]
Expand Down
4 changes: 4 additions & 0 deletions src/core/types/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import type { PinsPluginOptions } from '@/plugins/pins'
import type { PointerPositionPluginOptions } from '@/plugins/pointerPosition'
import type { ReverseGeocoderPluginOptions } from '@/plugins/reverseGeocoder'
import type { RoutingPluginOptions } from '@/plugins/routing'
import type { ScalePluginOptions } from '@/plugins/scale'
import type { ToastPluginOptions } from '@/plugins/toast'
import type defaults from '../utils/defaults'
Expand Down Expand Up @@ -304,7 +305,7 @@

// Plugins are not sorted alphabetical, but listed last.
// Remember to sort them alphabetical inside their space.
// TODO: Generate this section via types/plugin.ts

Check warning on line 308 in src/core/types/main.ts

View workflow job for this annotation

GitHub Actions / Linting

Unexpected 'todo' comment: 'TODO: Generate this section via...'
/* eslint-disable perfectionist/sort-interfaces */

/** Configuration for addressSearch plugin. */
Expand Down Expand Up @@ -343,6 +344,9 @@
/** Configuration for reverseGeocoder plugin. */
reverseGeocoder?: ReverseGeocoderPluginOptions

/** Configuration for routing plugin. */
routing?: RoutingPluginOptions

/** Configuration for scale plugin. */
scale?: ScalePluginOptions

Expand Down
6 changes: 6 additions & 0 deletions src/core/types/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
import type { usePointerPositionStore as PointerPositionStore } from '@/plugins/pointerPosition/store'
import type { PluginId as ReverseGeocoderPluginId } from '@/plugins/reverseGeocoder'
import type { useReverseGeocoderStore as ReverseGeocoderStore } from '@/plugins/reverseGeocoder/store'
import type { PluginId as RoutingPluginId } from '@/plugins/routing'
import type { resourcesEn as RoutingResources } from '@/plugins/routing/locales'
import type { useRoutingStore as RoutingStore } from '@/plugins/routing/store'
import type { PluginId as ScalePluginId } from '@/plugins/scale'
import type { resourcesEn as ScaleResources } from '@/plugins/scale/locales'
import type { useScaleStore as ScaleStore } from '@/plugins/scale/store'
Expand Down Expand Up @@ -151,6 +154,7 @@
| typeof PinsPluginId
| typeof PointerPositionPluginId
| typeof ReverseGeocoderPluginId
| typeof RoutingPluginId
| typeof ScalePluginId
| typeof ToastPluginId
| typeof ZoomPluginId
Expand All @@ -158,7 +162,7 @@
type GetPluginStore<
T extends BundledPluginId,
I extends BundledPluginId,
// TODO: This fixes the type error, but relaxes type-checking for the plugin store too much.

Check warning on line 165 in src/core/types/plugin.ts

View workflow job for this annotation

GitHub Actions / Linting

Unexpected 'todo' comment: 'TODO: This fixes the type error, but...'
// However, it is not clear if Pinia's type system allows for stronger checks at the moment.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
S extends PolarPluginStore<any>,
Expand Down Expand Up @@ -187,6 +191,7 @@
typeof ReverseGeocoderPluginId,
typeof ReverseGeocoderStore
>
| GetPluginStore<T, typeof RoutingPluginId, typeof RoutingStore>
| GetPluginStore<T, typeof ScalePluginId, typeof ScaleStore>
| GetPluginStore<T, typeof ToastPluginId, typeof ToastStore>
| GetPluginStore<T, typeof ZoomPluginId, typeof ZoomStore>
Expand Down Expand Up @@ -230,6 +235,7 @@
typeof PointerPositionPluginId,
typeof PointerPositionResources
>
| GetPluginResources<T, typeof RoutingPluginId, typeof RoutingResources>
| GetPluginResources<T, typeof ScalePluginId, typeof ScaleResources>
| GetPluginResources<T, typeof ToastPluginId, typeof ToastResources>
| GetPluginResources<T, typeof ZoomPluginId, typeof ZoomResources>
Expand Down
193 changes: 193 additions & 0 deletions src/plugins/routing/components/RoutingDetails.ce.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
<template>
<div class="routing-live-region">
<hr class="kern-divider" aria-hidden="true" />
<span role="status" aria-live="polite">
{{
$t(($) => $.ariaLive, {
ns: PluginId,
steps: steps.length,
duration: formatDuration(duration),
distance: formatDistance(distance),
})
}}
</span>
</div>
<section v-show="showDetails">
<div class="routing-details-header">
<h3>{{ $t(($) => $.label.details, { ns: PluginId }) }}</h3>
<span aria-hidden="true">
{{
$t(($) => $.duration, {
ns: PluginId,
duration: formatDuration(duration),
})
}}
</span>
<span aria-hidden="true">
{{
$t(($) => $.distance, {
ns: PluginId,
distance: formatDistance(distance),
})
}}
</span>
</div>
<ol :aria-label="$t(($) => $.label.steps, { ns: PluginId })">
<li v-for="(step, i) in steps" :key="i">
<span class="routing-instruction">
{{ step.instruction }}
</span>
<div class="routing-badges">
<span
class="kern-badge"
:aria-label="
$t(($) => $.duration, {
ns: PluginId,
duration: formatDuration(step.duration),
})
"
>
<span class="kern-icon kern-icon--pace" aria-hidden="true" />
<span class="kern-label" aria-hidden="true">
{{ formatDuration(step.duration) }}
</span>
</span>
<span
class="kern-badge"
:aria-label="
$t(($) => $.distance, {
ns: PluginId,
distance: formatDistance(step.distance),
})
"
>
<span class="kern-icon kern-icon--arrow-range" aria-hidden="true" />
<span class="kern-label" aria-hidden="true">
{{ formatDistance(step.distance) }}
</span>
</span>
</div>
</li>
</ol>
</section>
</template>

<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { computed } from 'vue'

import { useRoutingStore } from '../store.ts'

Check failure on line 79 in src/plugins/routing/components/RoutingDetails.ce.vue

View workflow job for this annotation

GitHub Actions / Linting

Import path "../store.ts" should be "../store"
import { PluginId } from '../types'

const routingStore = useRoutingStore()
const { showDetails } = storeToRefs(routingStore)

const segments = computed(
() => routingStore.routeFeature?.properties.segments ?? []
)
const distance = computed(() =>
segments.value.reduce((acc, segment) => acc + segment.distance, 0)
)
const duration = computed(() =>
segments.value.reduce((acc, segment) => acc + segment.duration, 0)
)
const steps = computed(() => segments.value.flatMap((segment) => segment.steps))

function formatDistance(distance: number) {
if (distance >= 1000) {
return `${(distance / 1000).toFixed(1)} km`
}
return `${distance} m`
Comment thread
oeninghe-dataport marked this conversation as resolved.
Outdated
}
function formatDuration(duration: number) {
if (duration >= 3600) {
return `${(duration / 3600).toFixed(2)} h`
} else if (duration >= 60) {
return `${(duration / 60).toFixed(1)} min`
}
return `${duration} sec`
Comment thread
dopenguin marked this conversation as resolved.
}
</script>

<style scoped>
.routing-live-region {
position: relative;
width: 100%;

hr {
width: 100%;
}

span {
position: absolute;
inset: 0;
opacity: 0;
pointer-events: none;
margin: calc(-1 * var(--kern-metric-space-small)) 0;
}
}

section {
margin-top: var(--kern-metric-space-small);

.routing-details-header {
position: relative;
display: flex;
justify-content: center;
gap: var(--kern-metric-space-small);
margin-bottom: var(--kern-metric-space-small);

h3 {
position: absolute;
inset: 0;
opacity: 0;
pointer-events: none;
margin: 0;
}

span {
font-size: var(--kern-typography-font-size-medium-static);
}
}

ol {
display: flex;
flex-direction: column;
gap: var(--kern-metric-space-small);
padding: 0;

li {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--kern-metric-space-small);
padding: var(--kern-metric-space-small);
padding-left: var(--kern-metric-space-default);
border-radius: var(--kern-metric-border-radius-default);
background-color: var(--kern-color-layout-background-hued);
white-space: break-spaces;
overflow-wrap: break-word;

.routing-instruction {
font-weight: var(--kern-typography-font-weight-label-default);
color: var(--kern-color-layout-text-default);
}

.routing-badges {
display: flex;
gap: var(--kern-metric-space-small);
width: 100%;
color: var(--kern-color-layout-text-default);

.kern-badge {
box-sizing: content-box;
justify-content: center;
width: var(--kern-metric-dimension-5x-large);
border: var(--kern-metric-border-width-light) solid
var(--kern-color-decorative-border-contextual);
}
}
}
}
}
</style>
92 changes: 92 additions & 0 deletions src/plugins/routing/components/RoutingInput.ce.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<template>
<div class="polar-plugin-routing-route-wrapper">
<div class="kern-form-input">
<label class="kern-label" :for="`polar-plugin-routing-input-${index}`">
{{ $t(($) => $.label[getRouteLabel(index)], { ns: PluginId }) }}
</label>
<input
:id="`polar-plugin-routing-input-${index}`"
Comment thread
oeninghe-dataport marked this conversation as resolved.
v-model="route[index]"
class="kern-form-input__input"
:aria-label="
$t(($) => $.label.aria, {
ns: PluginId,
position: $t(($) => $.label[getRouteLabel(index)], {
ns: PluginId,
}),
})
"
@focus="currentlyFocusedInput = index"
/>
</div>
<div class="polar-plugin-routing-waypoint-button-wrapper">
<KernButton
icon="kern-icon--add"
:label-sr-only="true"
class="kern-btn--tertiary"
:disabled="addWaypointButtonDisabled"
@click="routeStore.setRoute(index)"
>
{{ $t(($) => $.label.add, { ns: PluginId }) }}
</KernButton>
<KernButton
icon="kern-icon--remove"
:label-sr-only="true"
class="kern-btn--tertiary"
:disabled="route.length === 2"
@click="routeStore.setRoute(index, true)"
>
{{ $t(($) => $.label.remove, { ns: PluginId }) }}
</KernButton>
</div>
</div>
</template>

<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { computed } from 'vue'

import KernButton from '@/components/kern/KernButton.ce.vue'

import { useRoutingStore } from '../store.ts'

Check failure on line 51 in src/plugins/routing/components/RoutingInput.ce.vue

View workflow job for this annotation

GitHub Actions / Linting

Import path "../store.ts" should be "../store"
import { PluginId } from '../types'

defineProps<{
index: number
}>()

const routeStore = useRoutingStore()

const { currentlyFocusedInput, route } = storeToRefs(routeStore)

/**
* This makes sure that there are always two fillable input fields at max.
*/
const addWaypointButtonDisabled = computed(() => {
return (
route.value.filter((part) => Boolean(part.length)).length <
route.value.length - 1
)
})
Comment thread
oeninghe-dataport marked this conversation as resolved.
Outdated

function getRouteLabel(index: number) {
return index === 0
? 'start'
: index === route.value.length - 1
? 'end'
: 'middle'
}
</script>

<style scoped>
.polar-plugin-routing-route-wrapper {
display: flex;
align-items: end;
gap: var(--kern-metric-space-default);

.polar-plugin-routing-waypoint-button-wrapper {
display: flex;
gap: var(--kern-metric-space-small);
}
}
</style>
Loading
Loading