diff --git a/.changeset/afraid-pumpkins-visit.md b/.changeset/afraid-pumpkins-visit.md new file mode 100644 index 0000000..0d47fc4 --- /dev/null +++ b/.changeset/afraid-pumpkins-visit.md @@ -0,0 +1,5 @@ +--- +"svelte-maplibre": minor +--- + +Add on:open and on:close events to Popup diff --git a/src/lib/DeckGlLayer.svelte b/src/lib/DeckGlLayer.svelte index 2183813..60c959e 100644 --- a/src/lib/DeckGlLayer.svelte +++ b/src/lib/DeckGlLayer.svelte @@ -28,7 +28,11 @@ export let type: any; export let data: DATA[]; - const dispatch = createEventDispatcher(); + const dispatch = createEventDispatcher<{ + click: DeckGlMouseEvent; + mousemove: DeckGlMouseEvent; + mouseleave: DeckGlMouseEvent; + }>(); const context = mapContext(); const { map, minzoom: minZoomContext, maxzoom: maxZoomContext } = context; diff --git a/src/lib/DefaultMarker.svelte b/src/lib/DefaultMarker.svelte index 72b97ad..6deb674 100644 --- a/src/lib/DefaultMarker.svelte +++ b/src/lib/DefaultMarker.svelte @@ -21,7 +21,11 @@ /** The opacity of the marker */ export let opacity: number = 1; - const dispatch = createEventDispatcher(); + const dispatch = createEventDispatcher<{ + drag: MarkerClickInfo; + dragstart: MarkerClickInfo; + dragend: MarkerClickInfo; + }>(); const { map, layerEvent, self: marker } = updatedMarkerContext(); const dragStartListener = () => sendEvent('dragstart'); @@ -74,7 +78,7 @@ } } - function sendEvent(eventName: string) { + function sendEvent(eventName: Parameters[0]) { let loc = $marker?.getLngLat(); if (!loc) { return; diff --git a/src/lib/Marker.svelte b/src/lib/Marker.svelte index 01265cc..bf499a1 100644 --- a/src/lib/Marker.svelte +++ b/src/lib/Marker.svelte @@ -25,7 +25,12 @@ /** The opacity of the marker */ export let opacity: number = 1; - const dispatch = createEventDispatcher(); + const dispatch = createEventDispatcher<{ + drag: MarkerClickInfo; + dragstart: MarkerClickInfo; + dragend: MarkerClickInfo; + click: MarkerClickInfo; + }>(); const { map, layerEvent, self: marker } = updatedMarkerContext(); function addMarker(node: HTMLDivElement) { @@ -115,7 +120,7 @@ } } - function sendEvent(eventName: string) { + function sendEvent(eventName: Parameters[0]) { if (!interactive) { return; } diff --git a/src/lib/MarkerLayer.svelte b/src/lib/MarkerLayer.svelte index 5fc1a82..7afe243 100644 --- a/src/lib/MarkerLayer.svelte +++ b/src/lib/MarkerLayer.svelte @@ -7,9 +7,22 @@ import Marker from './Marker.svelte'; import FillLayer from './FillLayer.svelte'; import type { MapLibreZoomEvent } from 'maplibre-gl'; + import type { MarkerClickInfo } from './types'; + + interface ExtendedMarkerClickInfo extends MarkerClickInfo { + source: string | null; + feature: GeoJSON.Feature; + } const { map, source, minzoom: minZoomContext, maxzoom: maxZoomContext } = mapContext(); - const dispatch = createEventDispatcher(); + const dispatch = createEventDispatcher<{ + click: ExtendedMarkerClickInfo; + dblclick: ExtendedMarkerClickInfo; + contextmenu: ExtendedMarkerClickInfo; + drag: ExtendedMarkerClickInfo; + dragstart: ExtendedMarkerClickInfo; + dragend: ExtendedMarkerClickInfo; + }>(); export let applyToClusters: boolean | undefined = undefined; export let filter: maplibregl.ExpressionSpecification | undefined = undefined; diff --git a/src/lib/Popup.svelte b/src/lib/Popup.svelte index 76839aa..92e4e2f 100644 --- a/src/lib/Popup.svelte +++ b/src/lib/Popup.svelte @@ -5,13 +5,8 @@ type MapLayerMouseEvent, type MapLayerTouchEvent, } from 'maplibre-gl'; - import { onDestroy, onMount, tick } from 'svelte'; - import { - mapContext, - type DeckGlMouseEvent, - type LayerEvent, - isDeckGlMouseEvent, - } from './context.js'; + import { onDestroy, onMount, createEventDispatcher } from 'svelte'; + import { mapContext, type LayerEvent, isDeckGlMouseEvent } from './context.js'; /** Show the built-in close button. By default the close button will be shown * only if closeOnClickOutside and closeOnClickInside are not set. */ @@ -45,6 +40,12 @@ /** Whether the popup is open or not. Can be set to manualy open the popup at `lngLat`. */ export let open = false; + const dispatch = createEventDispatcher<{ + open: maplibregl.Popup; + close: maplibregl.Popup; + hover: maplibregl.Popup; + }>(); + const { map, popupTarget, layerEvent, layer, eventTopMost } = mapContext(); const clickEvents = ['click', 'dblclick', 'contextmenu']; @@ -70,10 +71,16 @@ popup.on('open', () => { open = true; setPopupClickHandler(); + dispatch('open', popup); }); - popup.on('close', (e) => { + popup.on('close', () => { open = false; + dispatch('close', popup); + }); + + popup.on('hover', () => { + dispatch('hover', popup); }); } diff --git a/src/routes/NavBar.svelte b/src/routes/NavBar.svelte index f8bbec5..14e3a4e 100644 --- a/src/routes/NavBar.svelte +++ b/src/routes/NavBar.svelte @@ -15,6 +15,7 @@ { href: '/examples/marker', title: `Default Markers` }, { href: '/examples/custom_marker', title: `Custom Markers` }, { href: '/examples/draggable_custom_marker', title: `Custom draggable Markers` }, + { href: '/examples/popup_remote', title: `Remote Popup Data` }, { href: '/examples/geojson_polygon', title: `GeoJSON Filled Polygon` }, { href: '/examples/geojson_line_layer', title: `Styled Line` }, { href: '/examples/heatmap', title: `Heatmap` }, diff --git a/src/routes/examples/popup_remote/+page.svelte b/src/routes/examples/popup_remote/+page.svelte new file mode 100644 index 0000000..9fee240 --- /dev/null +++ b/src/routes/examples/popup_remote/+page.svelte @@ -0,0 +1,87 @@ + + + + {#each markers as { lngLat, name }} + + + { + if (!(name in cache)) { + const resp = await fetch(`/examples/popup_remote/${name}`); + const result = await resp.json(); + cache[name] = result; + // trigger reactivity + cache = cache; + } + }} + > + {#if name in cache} + {@const result = cache[name]} +
{name}
+ kitten + {:else} +
Loading...
+ {/if} +
+
+ {/each} +
+ + + diff --git a/src/routes/examples/popup_remote/+page.ts b/src/routes/examples/popup_remote/+page.ts new file mode 100644 index 0000000..29591b7 --- /dev/null +++ b/src/routes/examples/popup_remote/+page.ts @@ -0,0 +1,7 @@ +import type { PageLoad } from './$types'; + +export const load: PageLoad = () => { + return { + title: 'Remote Popup Data', + }; +}; diff --git a/src/routes/examples/popup_remote/[id]/+server.ts b/src/routes/examples/popup_remote/[id]/+server.ts new file mode 100644 index 0000000..72ca497 --- /dev/null +++ b/src/routes/examples/popup_remote/[id]/+server.ts @@ -0,0 +1,18 @@ +import { json } from '@sveltejs/kit'; + +const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); + +/** + * This is a very contrived example. + */ +export async function GET({ params }) { + const { id } = params; + // make this request take one second + await sleep(1000); + // return some meaningless data + return json({ + id, + width: 10 * id.length, + height: 12 * id.length, + }); +}