@beyonk/svelte-mapbox
Advanced tools
| import { load } from '../asset-loader.js' | ||
| import { bindEvents } from '../event-bindings.js' | ||
| export default function geocoderAttachment (options) { | ||
| return (element) => { | ||
| let geocoderInstance | ||
| let unbind = () => {} | ||
| const resources = [ | ||
| { type: 'script', value: `//api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/${options.version}/mapbox-gl-geocoder.min.js`, id: 'byk-gc-js' } | ||
| ] | ||
| const customStylesheetUrl = options.customStylesheetUrl | ||
| if (customStylesheetUrl) { | ||
| resources.push({ type: 'link', value: customStylesheetUrl, id: 'byk-gcsu-css' }) | ||
| } else { | ||
| resources.push({ type: 'link', value: `//api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/${options.version}/mapbox-gl-geocoder.css`, id: 'byk-gc-css' }) | ||
| } | ||
| load(resources, () => { | ||
| geocoderInstance = new window.MapboxGeocoder(options) | ||
| geocoderInstance.addTo(`#${element.id}`) | ||
| if (options.value) { | ||
| geocoderInstance.setInput(options.value) | ||
| } | ||
| unbind = bindEvents(geocoderInstance, handlers, false, element) | ||
| }) | ||
| return () => { | ||
| unbind() | ||
| geocoderInstance && geocoderInstance.remove && geocoderInstance.remove() | ||
| } | ||
| } | ||
| } | ||
| const handlers = { | ||
| results: (el, ev) => { | ||
| return [ 'results', ev ] | ||
| }, | ||
| result: (el, ev) => { | ||
| return [ 'result', ev ] | ||
| }, | ||
| loading: (el, ev) => { | ||
| return [ 'loading', ev ] | ||
| }, | ||
| error: (el, ev) => { | ||
| return [ 'error', ev ] | ||
| }, | ||
| clear: (el, ev) => { | ||
| return [ 'clear', ev ] | ||
| }, | ||
| load: el => { | ||
| return [ 'ready', { geocoder: el } ] | ||
| } | ||
| } |
| import { load } from '../asset-loader.js' | ||
| import { bindEvents } from '../event-bindings.js' | ||
| export default function mapAttachment (options = {}) { | ||
| return (element) => { | ||
| console.log('attach', element, options) | ||
| let map | ||
| function init (options) { | ||
| window.mapboxgl.accessToken = options.accessToken | ||
| const el = new window.mapboxgl.Map(options) | ||
| return bindEvents(el, handlers, window.mapboxgl, element) | ||
| } | ||
| const resources = [ | ||
| { type: 'script', attr: 'src', value: `//api.mapbox.com/mapbox-gl-js/${options.version}/mapbox-gl.js`, id: 'byk-gl-js' }, | ||
| { type: 'link', attr: 'href', value: `//api.mapbox.com/mapbox-gl-js/${options.version}/mapbox-gl.css`, id: 'byk-gl-css' } | ||
| ] | ||
| const customStylesheetUrl = options.customStylesheetUrl | ||
| if (customStylesheetUrl) { | ||
| resources.push({ type: 'link', attr: 'href', value: customStylesheetUrl, id: 'byk-mcsu-css' }) | ||
| } | ||
| let unbind = () => {} | ||
| load(resources, () => { | ||
| unbind = init({ ...options, container: element }) | ||
| }) | ||
| return { | ||
| destroy () { | ||
| unbind() | ||
| map && map.remove && map.remove() | ||
| } | ||
| } | ||
| } | ||
| } | ||
| const handlers = { | ||
| dragend: el => { | ||
| return [ 'dragend', { center: el.getCenter() } ] | ||
| }, | ||
| drag: el => { | ||
| return [ 'drag', { center: el.getCenter() } ] | ||
| }, | ||
| moveend: el => { | ||
| return [ 'recentre', { center: el.getCenter() } ] | ||
| }, | ||
| click: (el, { lngLat }) => { | ||
| return [ 'click', { lng: lngLat.lng, lat: lngLat.lat } ] | ||
| }, | ||
| zoomstart: el => { | ||
| return [ 'zoomstart', { zoom: el.getZoom() } ] | ||
| }, | ||
| zoom: el => { | ||
| return [ 'zoom', { zoom: el.getZoom() } ] | ||
| }, | ||
| zoomend: el => { | ||
| return [ 'zoomend', { zoom: el.getZoom() } ] | ||
| }, | ||
| load: (el, ev, mapbox) => { | ||
| return [ 'ready', { map: el, mapbox } ] | ||
| } | ||
| } |
| export class EventQueue { | ||
| queue = [] | ||
| started = false | ||
| map = null | ||
| send (command, params = []) { | ||
| if (!command) { return } | ||
| this.queue.push([ command, params ]) | ||
| this.#process() | ||
| } | ||
| start (map) { | ||
| this.started = true | ||
| this.map = map | ||
| } | ||
| #process () { | ||
| if (!this.started) { return } | ||
| while (this.queue.length) { | ||
| const [ command, params ] = this.queue.shift() | ||
| this.map[command].apply(this.map, params) | ||
| } | ||
| } | ||
| stop () { | ||
| if (!this.started) { return } | ||
| this.queue = [] | ||
| this.map = null | ||
| this.started = false | ||
| } | ||
| } |
@@ -5,13 +5,5 @@ name: publish | ||
| push: | ||
| branches: | ||
| - '*' | ||
| tags: | ||
| - 'v*' | ||
| pull_request: | ||
| branches: | ||
| - '*' | ||
| env: | ||
| NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} | ||
| jobs: | ||
@@ -28,3 +20,3 @@ publish-npm: | ||
| corepack enable | ||
| pnpm config set '//registry.npmjs.org/:_authToken' "${NODE_AUTH_TOKEN}" | ||
| pnpm config set '//registry.npmjs.org/:_authToken' "${{secrets.NPM_TOKEN}}" | ||
| pnpm i | ||
@@ -31,0 +23,0 @@ |
+0
-7
| { | ||
| "extends": "./.svelte-kit/tsconfig.json", | ||
| "compilerOptions": { | ||
| "baseUrl": ".", | ||
| "paths": { | ||
| "$lib": ["src/lib"], | ||
| "$lib/*": ["src/lib/*"] | ||
| } | ||
| }, | ||
| "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] | ||
| } |
+5
-6
| { | ||
| "name": "@beyonk/svelte-mapbox", | ||
| "version": "11.0.0", | ||
| "version": "12.0.0", | ||
| "devDependencies": { | ||
@@ -8,10 +8,10 @@ "@beyonk/eslint-config": "^8.0.2", | ||
| "@eslint/js": "^9.14.0", | ||
| "@sveltejs/kit": "^2.8.0", | ||
| "@sveltejs/vite-plugin-svelte": "^4.0.0", | ||
| "@sveltejs/kit": "^2.55.0", | ||
| "@sveltejs/vite-plugin-svelte": "^7.0.0", | ||
| "eslint": "^9.14.0", | ||
| "eslint-plugin-svelte": "^2.46.0", | ||
| "svelte": "^5.1.14", | ||
| "svelte": "^5.55.0", | ||
| "svelte2tsx": "^0.7.23", | ||
| "typescript": "^5.6.3", | ||
| "vite": "^5.4.11" | ||
| "vite": "^8.0.2" | ||
| }, | ||
@@ -26,3 +26,2 @@ "peerDependencies": { | ||
| "type": "module", | ||
| "packageManager": "pnpm@8.15.7", | ||
| "scripts": { | ||
@@ -29,0 +28,0 @@ "dev": "vite dev --port 3030", |
@@ -1,30 +0,19 @@ | ||
| <div | ||
| id={fieldId} | ||
| use:action={optionsWithDefaults} | ||
| on:ready={init} | ||
| on:results | ||
| on:result | ||
| on:loading | ||
| on:error | ||
| on:clear | ||
| on:load | ||
| ></div> | ||
| <script> | ||
| import { createEventDispatcher } from 'svelte' | ||
| import action from './geocoder-action.js' | ||
| import geocoderAttachment from './geocoder-attachment.js' | ||
| export let accessToken | ||
| export let options = {} | ||
| export let version = 'v5.0.3' | ||
| export let types = [ 'country', 'region', 'postcode', 'district', 'place', 'locality', 'neighborhood', 'address' ] | ||
| export let placeholder = 'Search' | ||
| export let value = null | ||
| export let customStylesheetUrl = false | ||
| export let geocoder | ||
| let { | ||
| accessToken, | ||
| options = {}, | ||
| version = 'v5.1.0', | ||
| types = [ 'country', 'region', 'postcode', 'district', 'place', 'locality', 'neighborhood', 'address' ], | ||
| placeholder = 'Search', | ||
| value = null, | ||
| customStylesheetUrl = false, | ||
| geocoder = $bindable(), | ||
| ...rest | ||
| } = $props() | ||
| const dispatch = createEventDispatcher() | ||
| const fieldId = 'bsm-' + Math.random().toString(36).substring(6) | ||
| const optionsWithDefaults = Object.assign({ | ||
| const optionsWithDefaults = $derived.by(() => Object.assign({ | ||
| version, | ||
@@ -36,10 +25,17 @@ accessToken, | ||
| value | ||
| }, options) | ||
| }, options)) | ||
| function init ({ detail }) { | ||
| geocoder = detail.geocoder | ||
| dispatch('ready') | ||
| function init (e) { | ||
| geocoder = e.detail.geocoder | ||
| onready?.() | ||
| } | ||
| </script> | ||
| <div | ||
| id={fieldId} | ||
| {@attach geocoderAttachment(optionsWithDefaults)} | ||
| onready={init} | ||
| {...rest} | ||
| ></div> | ||
| <style> | ||
@@ -49,2 +45,2 @@ div { | ||
| } | ||
| </style> | ||
| </style> |
@@ -1,56 +0,38 @@ | ||
| <div | ||
| bind:this={dispatcher} | ||
| on:error | ||
| on:geolocate | ||
| on:outofmaxbounds | ||
| on:trackuserlocationend | ||
| on:trackuserlocationstart | ||
| /> | ||
| <script> | ||
| import { getContext, onMount } from 'svelte' | ||
| import { contextKey } from '../../mapbox.js' | ||
| import { bindEvents } from '../../event-bindings.js' | ||
| import { getContext, onMount, untrack } from 'svelte' | ||
| import { contextKey } from '../../mapbox.js' | ||
| import { bindEvents } from '../../event-bindings.js' | ||
| const { getMap, getMapbox } = getContext(contextKey) | ||
| const map = getMap() | ||
| const mapbox = getMapbox() | ||
| const { getMap, getMapbox } = getContext(contextKey) | ||
| const map = getMap() | ||
| const mapbox = getMapbox() | ||
| export let position = 'top-left' | ||
| export let options = {} | ||
| let { position = 'top-left', options = {}, ...rest } = $props() | ||
| let dispatcher | ||
| let dispatcher = $state() | ||
| const handlers = { | ||
| error: (el, ev) => { | ||
| return [ 'error', ev ] | ||
| }, | ||
| geolocate: (el, ev) => { | ||
| return [ 'geolocate', ev ] | ||
| }, | ||
| outofmaxbounds: (el, ev) => { | ||
| return [ 'outofmaxbounds', ev ] | ||
| }, | ||
| trackuserlocationend: (el, ev) => { | ||
| return [ 'trackuserlocationend', ev ] | ||
| }, | ||
| trackuserlocationstart: (el, ev) => { | ||
| return [ 'trackuserlocationstart', ev ] | ||
| } | ||
| } | ||
| const handlers = { | ||
| error: (el, ev) => [ 'error', ev ], | ||
| geolocate: (el, ev) => [ 'geolocate', ev ], | ||
| outofmaxbounds: (el, ev) => [ 'outofmaxbounds', ev ], | ||
| trackuserlocationend: (el, ev) => [ 'trackuserlocationend', ev ], | ||
| trackuserlocationstart: (el, ev) => [ 'trackuserlocationstart', ev ] | ||
| } | ||
| const geolocate = new mapbox.GeolocateControl(options) | ||
| map.addControl(geolocate, position) | ||
| const geolocate = new mapbox.GeolocateControl(untrack(() => options)) | ||
| map.addControl(geolocate, untrack(() => position)) | ||
| onMount(() => { | ||
| return bindEvents(geolocate, handlers, mapbox, dispatcher) | ||
| }) | ||
| onMount(() => { | ||
| return bindEvents(geolocate, handlers, mapbox, dispatcher) | ||
| }) | ||
| export function trigger () { | ||
| geolocate.trigger() | ||
| } | ||
| export function trigger () { | ||
| geolocate.trigger() | ||
| } | ||
| </script> | ||
| <div bind:this={dispatcher} {...rest}></div> | ||
| <style> | ||
| div { display: none; } | ||
| div { display: none; } | ||
| </style> |
| <script> | ||
| import { getContext } from 'svelte' | ||
| import { contextKey } from '../../mapbox.js' | ||
| import { getContext, untrack } from 'svelte' | ||
| import { contextKey } from '../../mapbox.js' | ||
| const { getMap, getMapbox } = getContext(contextKey) | ||
| const map = getMap() | ||
| const mapbox = getMapbox() | ||
| const { getMap, getMapbox } = getContext(contextKey) | ||
| const map = getMap() | ||
| const mapbox = getMapbox() | ||
| export let position = 'top-right' | ||
| export let options = {} | ||
| let { position = 'top-right', options = {} } = $props() | ||
| const nav = new mapbox.NavigationControl(options) | ||
| map.addControl(nav, position) | ||
| </script> | ||
| const nav = new mapbox.NavigationControl(untrack(() => options)) | ||
| map.addControl(nav, untrack(() => position)) | ||
| </script> |
| <script> | ||
| import { getContext } from 'svelte' | ||
| import { contextKey } from '../../mapbox.js' | ||
| import { getContext, untrack } from 'svelte' | ||
| import { contextKey } from '../../mapbox.js' | ||
| const { getMap, getMapbox } = getContext(contextKey) | ||
| const map = getMap() | ||
| const mapbox = getMapbox() | ||
| const { getMap, getMapbox } = getContext(contextKey) | ||
| const map = getMap() | ||
| const mapbox = getMapbox() | ||
| export let position = 'bottom-right' | ||
| export let options = {} | ||
| let { position = 'bottom-right', options = {} } = $props() | ||
| const optionsWithDefaults = Object.assign({ | ||
| maxWidth: 80, | ||
| unit: 'metric' | ||
| }, options) | ||
| const optionsWithDefaults = untrack(() => Object.assign({ | ||
| maxWidth: 80, | ||
| unit: 'metric' | ||
| }, options)) | ||
| const scale = new mapbox.ScaleControl(optionsWithDefaults) | ||
| map.addControl(scale, position) | ||
| </script> | ||
| const scale = new mapbox.ScaleControl(optionsWithDefaults) | ||
| map.addControl(scale, untrack(() => position)) | ||
| </script> |
+63
-61
@@ -1,58 +0,28 @@ | ||
| <!-- svelte-ignore a11y_no_noninteractive_element_interactions --> | ||
| <div | ||
| use:action={optionsWithDefaults} | ||
| on:ready={init} | ||
| on:recentre | ||
| on:dragend | ||
| on:click | ||
| on:zoomstart | ||
| on:zoom | ||
| on:zoomend | ||
| on:drag | ||
| on:keydown | ||
| on:keyup | ||
| role="application" | ||
| > | ||
| {#if map} | ||
| <slot></slot> | ||
| {/if} | ||
| </div> | ||
| <style> | ||
| div { | ||
| width: 100%; | ||
| height: 100%; | ||
| } | ||
| </style> | ||
| <script> | ||
| import { setContext, onDestroy, createEventDispatcher } from 'svelte' | ||
| import { setContext, untrack } from 'svelte' | ||
| import { contextKey } from '../mapbox.js' | ||
| import action from './map-action.js' | ||
| import { EventQueue } from '../queue.js' | ||
| import mapAttachment from './map-attachment.js' | ||
| import { EventQueue } from '../queue.svelte.js' | ||
| export let map = null | ||
| export let version = 'v3.7.0' | ||
| export let center = [ 0, 0 ] | ||
| export let zoom = 9 | ||
| export let zoomRate = 1 | ||
| export let wheelZoomRate = 1 | ||
| export let options = {} | ||
| export let accessToken | ||
| export let customStylesheetUrl = false | ||
| export let style = 'mapbox://styles/mapbox/streets-v11' | ||
| let { | ||
| map = $bindable(null), | ||
| version = 'v3.20.0', | ||
| center = [ 0, 0 ], | ||
| zoom = $bindable(9), | ||
| zoomRate = 1, | ||
| wheelZoomRate = 1, | ||
| options = {}, | ||
| accessToken, | ||
| customStylesheetUrl = false, | ||
| style = 'mapbox://styles/mapbox/streets-v11', | ||
| children, | ||
| onready, | ||
| ...rest | ||
| } = $props() | ||
| const dispatch = createEventDispatcher() | ||
| let mapbox = $state() | ||
| setContext(contextKey, { | ||
| getMap: () => map, | ||
| getMapbox: () => mapbox | ||
| }) | ||
| let container | ||
| let mapbox | ||
| const optionsWithDefaults = Object.assign({ | ||
| const optionsWithDefaults = { | ||
| accessToken, | ||
| container, | ||
| style, | ||
@@ -65,19 +35,35 @@ center, | ||
| customStylesheetUrl, | ||
| map | ||
| }, options) | ||
| map, | ||
| ...options | ||
| } | ||
| setContext(contextKey, { | ||
| getMap: () => map, | ||
| getMapbox: () => mapbox | ||
| }) | ||
| const queue = new EventQueue() | ||
| function init ({ detail }) { | ||
| map = detail.map | ||
| mapbox = detail.mapbox | ||
| function init (e) { | ||
| map = e.detail.map | ||
| mapbox = e.detail.mapbox | ||
| queue.start(map) | ||
| dispatch('ready') | ||
| onready?.() | ||
| map.on('zoomend', (e) => { | ||
| zoom = map.getZoom() | ||
| }) | ||
| } | ||
| onDestroy(() => { | ||
| queue.stop() | ||
| map = undefined | ||
| $effect(() => { | ||
| return () => { | ||
| queue.stop() | ||
| map = undefined | ||
| } | ||
| }) | ||
| $effect(() => { | ||
| zoom && setZoom(zoom) | ||
| }) | ||
| export function fitBounds (bbox, data = {}) { | ||
@@ -114,4 +100,20 @@ queue.send('fitBounds', [ bbox, data ]) | ||
| } | ||
| </script> | ||
| $: zoom && setZoom(zoom) | ||
| </script> | ||
| <div | ||
| {@attach mapAttachment(optionsWithDefaults)} | ||
| onready={init} | ||
| {...rest} | ||
| role="presentation" | ||
| > | ||
| {#if map} | ||
| {@render children?.()} | ||
| {/if} | ||
| </div> | ||
| <style> | ||
| div { | ||
| width: 100%; | ||
| height: 100%; | ||
| } | ||
| </style> |
@@ -13,2 +13,21 @@ <script> | ||
| let { | ||
| lat, | ||
| lng, | ||
| label = 'Marker', | ||
| popupClassName = 'beyonk-mapbox-popup', | ||
| markerOffset = [ 0, 0 ], | ||
| popupOffset = 10, | ||
| color = randomColour(), | ||
| popup = true, | ||
| popupOptions = {}, | ||
| markerOptions = {}, | ||
| children, | ||
| popupContent | ||
| } = $props() | ||
| let marker = $state() | ||
| let element = $state() | ||
| let elementPopup = $state() | ||
| function move (lng, lat) { | ||
@@ -18,28 +37,13 @@ marker.setLngLat({ lng, lat }) | ||
| export let lat | ||
| export let lng | ||
| export let label = 'Marker' | ||
| export let popupClassName = 'beyonk-mapbox-popup' | ||
| export let markerOffset = [ 0, 0 ] | ||
| export let popupOffset = 10 | ||
| export let color = randomColour() | ||
| export let popup = true | ||
| export let popupOptions = {} | ||
| export let markerOptions = {} | ||
| $effect(() => { | ||
| if (marker) move(lng, lat) | ||
| }) | ||
| let marker | ||
| let element | ||
| let elementPopup | ||
| $: marker && move(lng, lat) | ||
| onMount(() => { | ||
| const namedParams = Object.assign( | ||
| { | ||
| offset: markerOffset | ||
| }, | ||
| { offset: markerOffset }, | ||
| element.hasChildNodes() ? { element } : { color } | ||
| ) | ||
| marker = new mapbox.Marker(Object.assign(namedParams, markerOptions)) | ||
| if (popup) { | ||
@@ -53,3 +57,2 @@ const namedPopupParams = { offset: popupOffset, className: popupClassName } | ||
| } | ||
| marker.setPopup(popupEl) | ||
@@ -73,7 +76,7 @@ } | ||
| <div bind:this={element}> | ||
| <slot></slot> | ||
| {@render children?.()} | ||
| </div> | ||
| <div class='popup' bind:this={elementPopup}> | ||
| <slot name="popup"></slot> | ||
| {@render popupContent?.()} | ||
| </div> |
+67
-77
@@ -0,1 +1,49 @@ | ||
| <script> | ||
| import { PUBLIC_MAPBOX_TOKEN } from '$env/static/public' | ||
| import { Map, Geocoder, Marker, controls } from '$lib/components.js' | ||
| import Earthquakes from './_Earthquakes.svelte' | ||
| const { GeolocateControl, NavigationControl } = controls | ||
| let place = $state(null) | ||
| let page = $state('about') | ||
| let center = $state({ lat: 53.3358627, lng: -2.8572362 }) | ||
| let marker = $derived(center) | ||
| let zoom = $state(11.15) | ||
| let mapComponent = $state() | ||
| function navigate (next) { | ||
| page = next | ||
| } | ||
| function placeChanged (e) { | ||
| const { result } = e.detail | ||
| mapComponent.setCenter(result.center, 14) | ||
| place = result | ||
| } | ||
| function randomLng () { | ||
| return 77 + (Math.random() - 0.5) * 30 | ||
| } | ||
| function randomLat () { | ||
| return 13 + (Math.random() - 0.5) * 30 | ||
| } | ||
| function flyToRandomPlace () { | ||
| mapComponent.flyTo({ | ||
| center: [ randomLng(), randomLat() ], | ||
| essential: true | ||
| }) | ||
| } | ||
| function recentre (e) { | ||
| center = e.detail.center | ||
| } | ||
| function drag (e) { | ||
| marker = e.detail.center | ||
| } | ||
| </script> | ||
| <header> | ||
@@ -45,8 +93,8 @@ <div class="container"> | ||
| <div class="menu-box"> | ||
| <h4>Navigation</h4> | ||
| <h4>Navigation</h4> | ||
| <nav> | ||
| <ul> | ||
| <li><a href="#geocoder" on:click={() => { navigate('geocoder') } } class:current={page === 'geocoder'}>Geocoder</a></li> | ||
| <li><a href="#map" on:click={() => { navigate('map') }} class:current={page === 'map'}>Map</a></li> | ||
| </ul> | ||
| <li><a href="#geocoder" onclick={() => navigate('geocoder')} class:current={page === 'geocoder'}>Geocoder</a></li> | ||
| <li><a href="#map" onclick={() => navigate('map')} class:current={page === 'map'}>Map</a></li> | ||
| </ul> | ||
| </nav> | ||
@@ -57,24 +105,17 @@ </div> | ||
| <div class="action-buttons"> | ||
| <button id="fly-to" on:click={flyToRandomPlace} | ||
| >Fly to random location</button | ||
| > | ||
| <button | ||
| id="change-zoom" | ||
| on:click={() => (zoom = Math.floor(Math.random() * 10))} | ||
| >Change Zoom Level</button | ||
| > | ||
| </div> | ||
| <button id="fly-to" onclick={flyToRandomPlace}>Fly to random location</button> | ||
| <button id="change-zoom" onclick={() => (zoom = Math.floor(Math.random() * 10))}>Change Zoom Level</button> | ||
| </div> | ||
| <div class="section-txt" id="geocoder"> | ||
| <form> | ||
| <Geocoder value="(Near London)" accessToken={PUBLIC_MAPBOX_TOKEN} on:result={placeChanged} on:clear={() => mapComponent.setCenter({ lng: 0, lat: 0 })} /> | ||
| {#if place} | ||
| <dl> | ||
| <dt>Name:</dt> | ||
| <dd>{place.label}</dd> | ||
| <dt>Geolocation:</dt> | ||
| <dd>lat: {place.geometry.lat}, lng: {place.geometry.lng}</dd> | ||
| </dl> | ||
| {/if} | ||
| <Geocoder value="(Near London)" accessToken={PUBLIC_MAPBOX_TOKEN} onresult={placeChanged} onclear={() => mapComponent.setCenter({ lng: 0, lat: 0 })} /> | ||
| {#if place} | ||
| <dl> | ||
| <dt>Name:</dt> | ||
| <dd>{place.place_name}</dd> | ||
| <dt>Geolocation:</dt> | ||
| <dd>lat: {place.geometry.coordinates[1]}, lng: {place.geometry.coordinates[0]}</dd> | ||
| </dl> | ||
| {/if} | ||
| </form> | ||
@@ -87,4 +128,4 @@ </div> | ||
| accessToken={PUBLIC_MAPBOX_TOKEN} | ||
| on:recentre={recentre} | ||
| on:drag={drag} | ||
| onrecentre={recentre} | ||
| ondrag={drag} | ||
| {center} | ||
@@ -95,3 +136,3 @@ bind:zoom | ||
| <NavigationControl /> | ||
| <GeolocateControl on:geolocate={e => console.log('geolocated', e.detail)} /> | ||
| <GeolocateControl ongeolocate={e => console.log('geolocated', e.detail)} /> | ||
| <Marker lat={marker.lat} lng={marker.lng} /> | ||
@@ -164,53 +205,2 @@ </Map> | ||
| } | ||
| </style> | ||
| <script> | ||
| import { PUBLIC_MAPBOX_TOKEN } from '$env/static/public' | ||
| import { Map, Geocoder, Marker, controls } from '$lib/components.js' | ||
| import Earthquakes from './_Earthquakes.svelte' | ||
| const { GeolocateControl, NavigationControl } = controls | ||
| const place = null | ||
| let page = 'about' | ||
| let center = { lat: 53.3358627, lng: -2.8572362 } | ||
| let marker = center | ||
| let zoom = 11.15 | ||
| let mapComponent | ||
| function navigate (next) { | ||
| page = next | ||
| } | ||
| function placeChanged (e) { | ||
| const { result } = e.detail | ||
| mapComponent.setCenter(result.center, 14) | ||
| } | ||
| function randomLng () { | ||
| return 77 + (Math.random() - 0.5) * 30 | ||
| } | ||
| function randomLat () { | ||
| return 13 + (Math.random() - 0.5) * 30 | ||
| } | ||
| function flyToRandomPlace () { | ||
| mapComponent.flyTo({ | ||
| center: [ | ||
| randomLng(), | ||
| randomLat() | ||
| ], | ||
| essential: true | ||
| }) | ||
| } | ||
| function recentre ({ detail }) { | ||
| center = detail.center | ||
| } | ||
| function drag ({ detail }) { | ||
| marker = detail.center | ||
| } | ||
| </script> |
+5
-1
| /** @type {import('@sveltejs/kit').Config} */ | ||
| const config = {} | ||
| const config = { | ||
| vitePlugin: { | ||
| inspector: true | ||
| } | ||
| } | ||
| export default config |
Sorry, the diff of this file is not supported yet
| import { load } from '../asset-loader.js' | ||
| import { bindEvents } from '../event-bindings.js' | ||
| export default function action (node, options = {}) { | ||
| let map | ||
| const resources = [ | ||
| { type: 'script', value: `//api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/${options.version}/mapbox-gl-geocoder.min.js`, id: 'byk-gc-js' } | ||
| ] | ||
| const customStylesheetUrl = options.customStylesheetUrl | ||
| if (customStylesheetUrl) { | ||
| resources.push({ type: 'link', value: customStylesheetUrl, id: 'byk-gcsu-css' }) | ||
| } else { | ||
| resources.push({ type: 'link', value: `//api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/${options.version}/mapbox-gl-geocoder.css`, id: 'byk-gc-css' }) | ||
| } | ||
| let unbind = () => {} | ||
| load(resources, () => { | ||
| unbind = init(options, node) | ||
| }) | ||
| return { | ||
| destroy () { | ||
| unbind() | ||
| map && map.remove && map.remove() | ||
| } | ||
| } | ||
| } | ||
| function init (options, node) { | ||
| const geocoder = new window.MapboxGeocoder(options) | ||
| geocoder.addTo(`#${node.id}`) | ||
| if (options.value) { | ||
| geocoder.setInput(options.value) | ||
| } | ||
| return bindEvents(geocoder, handlers, false, node) | ||
| } | ||
| const handlers = { | ||
| results: (el, ev) => { | ||
| return [ 'results', ev ] | ||
| }, | ||
| result: (el, ev) => { | ||
| return [ 'result', ev ] | ||
| }, | ||
| loading: (el, ev) => { | ||
| return [ 'loading', ev ] | ||
| }, | ||
| error: (el, ev) => { | ||
| return [ 'error', ev ] | ||
| }, | ||
| clear: (el, ev) => { | ||
| return [ 'clear', ev ] | ||
| }, | ||
| load: el => { | ||
| return [ 'ready', { geocoder: el } ] | ||
| } | ||
| } |
| import { load } from '../asset-loader.js' | ||
| import { bindEvents } from '../event-bindings.js' | ||
| export default function action (node, options = {}) { | ||
| let map | ||
| const resources = [ | ||
| { type: 'script', attr: 'src', value: `//api.mapbox.com/mapbox-gl-js/${options.version}/mapbox-gl.js`, id: 'byk-gl-js' }, | ||
| { type: 'link', attr: 'href', value: `//api.mapbox.com/mapbox-gl-js/${options.version}/mapbox-gl.css`, id: 'byk-gl-css' } | ||
| ] | ||
| const customStylesheetUrl = options.customStylesheetUrl | ||
| if (customStylesheetUrl) { | ||
| resources.push({ type: 'link', attr: 'href', value: customStylesheetUrl, id: 'byk-mcsu-css' }) | ||
| } | ||
| let unbind = () => {} | ||
| load(resources, () => { | ||
| unbind = init({ ...options, container: node }, node) | ||
| }) | ||
| return { | ||
| destroy () { | ||
| unbind() | ||
| map && map.remove && map.remove() | ||
| } | ||
| } | ||
| } | ||
| function init (options, node) { | ||
| window.mapboxgl.accessToken = options.accessToken | ||
| const el = new window.mapboxgl.Map(options) | ||
| return bindEvents(el, handlers, window.mapboxgl, node) | ||
| } | ||
| const handlers = { | ||
| dragend: el => { | ||
| return [ 'dragend', { center: el.getCenter() } ] | ||
| }, | ||
| drag: el => { | ||
| return [ 'drag', { center: el.getCenter() } ] | ||
| }, | ||
| moveend: el => { | ||
| return [ 'recentre', { center: el.getCenter() } ] | ||
| }, | ||
| click: (el, { lngLat }) => { | ||
| return [ 'click', { lng: lngLat.lng, lat: lngLat.lat } ] | ||
| }, | ||
| zoomstart: el => { | ||
| return [ 'zoomstart', { zoom: el.getZoom() } ] | ||
| }, | ||
| zoom: el => { | ||
| return [ 'zoom', { zoom: el.getZoom() } ] | ||
| }, | ||
| zoomend: el => { | ||
| return [ 'zoomend', { zoom: el.getZoom() } ] | ||
| }, | ||
| load: (el, ev, mapbox) => { | ||
| return [ 'ready', { map: el, mapbox } ] | ||
| } | ||
| } |
| import { writable } from 'svelte/store' | ||
| export class EventQueue { | ||
| constructor () { | ||
| this.queue = writable([]) | ||
| this.unsubscribe = null | ||
| this.started = false | ||
| } | ||
| send (command, params = []) { | ||
| if (!command) { return } | ||
| this.queue.update(q => ([ ...q, [ command, params ] ])) | ||
| } | ||
| start (map) { | ||
| this.unsubscribe = this.queue.subscribe(queue => { | ||
| while (queue.length) { | ||
| const [ command, params ] = queue.shift() | ||
| map[command].apply(map, params) | ||
| } | ||
| }) | ||
| this.started = true | ||
| } | ||
| stop () { | ||
| if (!this.started) { return } | ||
| this.unsubscribe() | ||
| this.queue = writable([]) | ||
| this.started = false | ||
| } | ||
| } |
46738
-2.02%871
-0.34%