77 lines
1.7 KiB
Svelte
77 lines
1.7 KiB
Svelte
<script lang="ts">
|
|
import { onMount, onDestroy, type Snippet } from 'svelte';
|
|
import type { IMap } from './core';
|
|
import type { LngLatTuple } from '$domain';
|
|
import { createMapLibreMap } from './maplibre';
|
|
import { setMapContext } from './context';
|
|
|
|
interface Props {
|
|
center?: LngLatTuple;
|
|
zoom?: number;
|
|
baseLayer?: 'osm' | 'satellite';
|
|
showNavigationControl?: boolean;
|
|
showScaleControl?: boolean;
|
|
children?: Snippet;
|
|
onReady?: (map: IMap) => void;
|
|
}
|
|
|
|
let {
|
|
center = [129.1234, 62.1234],
|
|
zoom = 4,
|
|
baseLayer = 'osm',
|
|
showNavigationControl = true,
|
|
showScaleControl = true,
|
|
children,
|
|
onReady,
|
|
}: Props = $props();
|
|
|
|
let container: HTMLDivElement;
|
|
let map: IMap | null = $state(null);
|
|
/**
|
|
* Children must not render until the map's first `load` event. MapLibre
|
|
* throws if addSource/addLayer is called on an unloaded style, and this
|
|
* component is the natural gate for that invariant.
|
|
*/
|
|
let ready = $state(false);
|
|
|
|
setMapContext(() => map);
|
|
|
|
export function getInstance(): IMap | null {
|
|
return map;
|
|
}
|
|
|
|
onMount(() => {
|
|
map = createMapLibreMap({
|
|
container,
|
|
center,
|
|
zoom,
|
|
baseLayer,
|
|
showNavigationControl,
|
|
showScaleControl,
|
|
});
|
|
map.ready.then(() => {
|
|
ready = true;
|
|
if (map) {
|
|
onReady?.(map);
|
|
if (import.meta.env.DEV) {
|
|
// Debug handle for e2e tests and console inspection. Only exposed
|
|
// in dev builds; trimmed from production bundles.
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
(window as any)._lsvMap = map.getRawInstance();
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
onDestroy(() => {
|
|
map?.dispose();
|
|
map = null;
|
|
ready = false;
|
|
});
|
|
</script>
|
|
|
|
<div class="map-container" bind:this={container}>
|
|
{#if ready && map}
|
|
{@render children?.()}
|
|
{/if}
|
|
</div>
|