@beyonk/svelte-mapbox 11.0.0 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +1 -9
- package/eslint.config.js +0 -0
- package/jsconfig.json +0 -7
- package/package.json +5 -6
- package/src/lib/geocoder/Geocoder.svelte +25 -29
- package/src/lib/geocoder/geocoder-attachment.js +55 -0
- package/src/lib/map/Map.svelte +64 -62
- package/src/lib/map/Marker.svelte +26 -23
- package/src/lib/map/controls/GeolocateControl.svelte +33 -51
- package/src/lib/map/controls/NavigationControl.svelte +9 -10
- package/src/lib/map/controls/ScaleControl.svelte +13 -14
- package/src/lib/map/map-attachment.js +65 -0
- package/src/lib/queue.svelte.js +31 -0
- package/src/routes/+page.svelte +67 -77
- package/svelte.config.js +5 -1
- package/eslint.config.mjs.disabled +0 -21
- package/src/lib/geocoder/geocoder-action.js +0 -60
- package/src/lib/map/map-action.js +0 -62
- package/src/lib/queue.js +0 -31
|
@@ -2,16 +2,8 @@ name: publish
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
-
branches:
|
|
6
|
-
- '*'
|
|
7
5
|
tags:
|
|
8
6
|
- 'v*'
|
|
9
|
-
pull_request:
|
|
10
|
-
branches:
|
|
11
|
-
- '*'
|
|
12
|
-
|
|
13
|
-
env:
|
|
14
|
-
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
|
15
7
|
|
|
16
8
|
jobs:
|
|
17
9
|
publish-npm:
|
|
@@ -24,7 +16,7 @@ jobs:
|
|
|
24
16
|
- name: set up node and pnpm
|
|
25
17
|
run: |
|
|
26
18
|
corepack enable
|
|
27
|
-
pnpm config set '//registry.npmjs.org/:_authToken' "${
|
|
19
|
+
pnpm config set '//registry.npmjs.org/:_authToken' "${{secrets.NPM_TOKEN}}"
|
|
28
20
|
pnpm i
|
|
29
21
|
|
|
30
22
|
- name: publish
|
package/eslint.config.js
ADDED
|
File without changes
|
package/jsconfig.json
CHANGED
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beyonk/svelte-mapbox",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "12.0.0",
|
|
4
4
|
"devDependencies": {
|
|
5
5
|
"@beyonk/eslint-config": "^8.0.2",
|
|
6
6
|
"@eslint/eslintrc": "^3.1.0",
|
|
7
7
|
"@eslint/js": "^9.14.0",
|
|
8
|
-
"@sveltejs/kit": "^2.
|
|
9
|
-
"@sveltejs/vite-plugin-svelte": "^
|
|
8
|
+
"@sveltejs/kit": "^2.55.0",
|
|
9
|
+
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
10
10
|
"eslint": "^9.14.0",
|
|
11
11
|
"eslint-plugin-svelte": "^2.46.0",
|
|
12
|
-
"svelte": "^5.
|
|
12
|
+
"svelte": "^5.55.0",
|
|
13
13
|
"svelte2tsx": "^0.7.23",
|
|
14
14
|
"typescript": "^5.6.3",
|
|
15
|
-
"vite": "^
|
|
15
|
+
"vite": "^8.0.2"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
18
|
"svelte": ">=4.0.0"
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
},
|
|
23
23
|
"types": "./components.d.ts",
|
|
24
24
|
"type": "module",
|
|
25
|
-
"packageManager": "pnpm@8.15.7",
|
|
26
25
|
"scripts": {
|
|
27
26
|
"dev": "vite dev --port 3030",
|
|
28
27
|
"build": "vite build",
|
|
@@ -1,48 +1,44 @@
|
|
|
1
|
-
<div
|
|
2
|
-
id={fieldId}
|
|
3
|
-
use:action={optionsWithDefaults}
|
|
4
|
-
on:ready={init}
|
|
5
|
-
on:results
|
|
6
|
-
on:result
|
|
7
|
-
on:loading
|
|
8
|
-
on:error
|
|
9
|
-
on:clear
|
|
10
|
-
on:load
|
|
11
|
-
></div>
|
|
12
|
-
|
|
13
1
|
<script>
|
|
14
|
-
import
|
|
15
|
-
import action from './geocoder-action.js'
|
|
2
|
+
import geocoderAttachment from './geocoder-attachment.js'
|
|
16
3
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
4
|
+
let {
|
|
5
|
+
accessToken,
|
|
6
|
+
options = {},
|
|
7
|
+
version = 'v5.1.0',
|
|
8
|
+
types = [ 'country', 'region', 'postcode', 'district', 'place', 'locality', 'neighborhood', 'address' ],
|
|
9
|
+
placeholder = 'Search',
|
|
10
|
+
value = null,
|
|
11
|
+
customStylesheetUrl = false,
|
|
12
|
+
geocoder = $bindable(),
|
|
13
|
+
...rest
|
|
14
|
+
} = $props()
|
|
25
15
|
|
|
26
|
-
const dispatch = createEventDispatcher()
|
|
27
16
|
const fieldId = 'bsm-' + Math.random().toString(36).substring(6)
|
|
28
17
|
|
|
29
|
-
const optionsWithDefaults = Object.assign({
|
|
18
|
+
const optionsWithDefaults = $derived.by(() => Object.assign({
|
|
30
19
|
version,
|
|
31
20
|
accessToken,
|
|
32
21
|
types: types.join(','),
|
|
33
22
|
placeholder,
|
|
34
23
|
customStylesheetUrl,
|
|
35
24
|
value
|
|
36
|
-
}, options)
|
|
25
|
+
}, options))
|
|
37
26
|
|
|
38
|
-
function init (
|
|
39
|
-
geocoder = detail.geocoder
|
|
40
|
-
|
|
27
|
+
function init (e) {
|
|
28
|
+
geocoder = e.detail.geocoder
|
|
29
|
+
onready?.()
|
|
41
30
|
}
|
|
42
31
|
</script>
|
|
43
32
|
|
|
33
|
+
<div
|
|
34
|
+
id={fieldId}
|
|
35
|
+
{@attach geocoderAttachment(optionsWithDefaults)}
|
|
36
|
+
onready={init}
|
|
37
|
+
{...rest}
|
|
38
|
+
></div>
|
|
39
|
+
|
|
44
40
|
<style>
|
|
45
41
|
div {
|
|
46
42
|
padding: 0;
|
|
47
43
|
}
|
|
48
|
-
</style>
|
|
44
|
+
</style>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { load } from '../asset-loader.js'
|
|
2
|
+
import { bindEvents } from '../event-bindings.js'
|
|
3
|
+
|
|
4
|
+
export default function geocoderAttachment (options) {
|
|
5
|
+
return (element) => {
|
|
6
|
+
let geocoderInstance
|
|
7
|
+
let unbind = () => {}
|
|
8
|
+
|
|
9
|
+
const resources = [
|
|
10
|
+
{ type: 'script', value: `//api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/${options.version}/mapbox-gl-geocoder.min.js`, id: 'byk-gc-js' }
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
const customStylesheetUrl = options.customStylesheetUrl
|
|
14
|
+
if (customStylesheetUrl) {
|
|
15
|
+
resources.push({ type: 'link', value: customStylesheetUrl, id: 'byk-gcsu-css' })
|
|
16
|
+
} else {
|
|
17
|
+
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' })
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
load(resources, () => {
|
|
21
|
+
geocoderInstance = new window.MapboxGeocoder(options)
|
|
22
|
+
geocoderInstance.addTo(`#${element.id}`)
|
|
23
|
+
if (options.value) {
|
|
24
|
+
geocoderInstance.setInput(options.value)
|
|
25
|
+
}
|
|
26
|
+
unbind = bindEvents(geocoderInstance, handlers, false, element)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
return () => {
|
|
30
|
+
unbind()
|
|
31
|
+
geocoderInstance && geocoderInstance.remove && geocoderInstance.remove()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const handlers = {
|
|
37
|
+
results: (el, ev) => {
|
|
38
|
+
return [ 'results', ev ]
|
|
39
|
+
},
|
|
40
|
+
result: (el, ev) => {
|
|
41
|
+
return [ 'result', ev ]
|
|
42
|
+
},
|
|
43
|
+
loading: (el, ev) => {
|
|
44
|
+
return [ 'loading', ev ]
|
|
45
|
+
},
|
|
46
|
+
error: (el, ev) => {
|
|
47
|
+
return [ 'error', ev ]
|
|
48
|
+
},
|
|
49
|
+
clear: (el, ev) => {
|
|
50
|
+
return [ 'clear', ev ]
|
|
51
|
+
},
|
|
52
|
+
load: el => {
|
|
53
|
+
return [ 'ready', { geocoder: el } ]
|
|
54
|
+
}
|
|
55
|
+
}
|
package/src/lib/map/Map.svelte
CHANGED
|
@@ -1,60 +1,30 @@
|
|
|
1
|
-
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
2
|
-
<div
|
|
3
|
-
use:action={optionsWithDefaults}
|
|
4
|
-
on:ready={init}
|
|
5
|
-
on:recentre
|
|
6
|
-
on:dragend
|
|
7
|
-
on:click
|
|
8
|
-
on:zoomstart
|
|
9
|
-
on:zoom
|
|
10
|
-
on:zoomend
|
|
11
|
-
on:drag
|
|
12
|
-
on:keydown
|
|
13
|
-
on:keyup
|
|
14
|
-
role="application"
|
|
15
|
-
>
|
|
16
|
-
{#if map}
|
|
17
|
-
<slot></slot>
|
|
18
|
-
{/if}
|
|
19
|
-
</div>
|
|
20
|
-
|
|
21
|
-
<style>
|
|
22
|
-
div {
|
|
23
|
-
width: 100%;
|
|
24
|
-
height: 100%;
|
|
25
|
-
}
|
|
26
|
-
</style>
|
|
27
|
-
|
|
28
1
|
<script>
|
|
29
|
-
import { setContext,
|
|
2
|
+
import { setContext, untrack } from 'svelte'
|
|
30
3
|
import { contextKey } from '../mapbox.js'
|
|
31
|
-
import
|
|
32
|
-
import { EventQueue } from '../queue.js'
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
4
|
+
import mapAttachment from './map-attachment.js'
|
|
5
|
+
import { EventQueue } from '../queue.svelte.js'
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
map = $bindable(null),
|
|
9
|
+
version = 'v3.20.0',
|
|
10
|
+
center = [ 0, 0 ],
|
|
11
|
+
zoom = $bindable(9),
|
|
12
|
+
zoomRate = 1,
|
|
13
|
+
wheelZoomRate = 1,
|
|
14
|
+
options = {},
|
|
15
|
+
accessToken,
|
|
16
|
+
customStylesheetUrl = false,
|
|
17
|
+
style = 'mapbox://styles/mapbox/streets-v11',
|
|
18
|
+
children,
|
|
19
|
+
onready,
|
|
20
|
+
...rest
|
|
21
|
+
} = $props()
|
|
46
22
|
|
|
47
|
-
|
|
48
|
-
getMap: () => map,
|
|
49
|
-
getMapbox: () => mapbox
|
|
50
|
-
})
|
|
23
|
+
let mapbox = $state()
|
|
51
24
|
|
|
52
|
-
let container
|
|
53
|
-
let mapbox
|
|
54
25
|
|
|
55
|
-
const optionsWithDefaults =
|
|
26
|
+
const optionsWithDefaults = {
|
|
56
27
|
accessToken,
|
|
57
|
-
container,
|
|
58
28
|
style,
|
|
59
29
|
center,
|
|
60
30
|
zoom,
|
|
@@ -62,21 +32,37 @@
|
|
|
62
32
|
wheelZoomRate,
|
|
63
33
|
version,
|
|
64
34
|
customStylesheetUrl,
|
|
65
|
-
map
|
|
66
|
-
|
|
35
|
+
map,
|
|
36
|
+
...options
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setContext(contextKey, {
|
|
40
|
+
getMap: () => map,
|
|
41
|
+
getMapbox: () => mapbox
|
|
42
|
+
})
|
|
67
43
|
|
|
68
44
|
const queue = new EventQueue()
|
|
69
45
|
|
|
70
|
-
function init (
|
|
71
|
-
map = detail.map
|
|
72
|
-
mapbox = detail.mapbox
|
|
46
|
+
function init (e) {
|
|
47
|
+
map = e.detail.map
|
|
48
|
+
mapbox = e.detail.mapbox
|
|
73
49
|
queue.start(map)
|
|
74
|
-
|
|
50
|
+
onready?.()
|
|
51
|
+
|
|
52
|
+
map.on('zoomend', (e) => {
|
|
53
|
+
zoom = map.getZoom()
|
|
54
|
+
})
|
|
75
55
|
}
|
|
76
56
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
57
|
+
$effect(() => {
|
|
58
|
+
return () => {
|
|
59
|
+
queue.stop()
|
|
60
|
+
map = undefined
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
$effect(() => {
|
|
65
|
+
zoom && setZoom(zoom)
|
|
80
66
|
})
|
|
81
67
|
|
|
82
68
|
export function fitBounds (bbox, data = {}) {
|
|
@@ -110,6 +96,22 @@
|
|
|
110
96
|
export function getMapbox () {
|
|
111
97
|
return mapbox
|
|
112
98
|
}
|
|
113
|
-
|
|
114
|
-
$: zoom && setZoom(zoom)
|
|
115
99
|
</script>
|
|
100
|
+
|
|
101
|
+
<div
|
|
102
|
+
{@attach mapAttachment(optionsWithDefaults)}
|
|
103
|
+
onready={init}
|
|
104
|
+
{...rest}
|
|
105
|
+
role="presentation"
|
|
106
|
+
>
|
|
107
|
+
{#if map}
|
|
108
|
+
{@render children?.()}
|
|
109
|
+
{/if}
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<style>
|
|
113
|
+
div {
|
|
114
|
+
width: 100%;
|
|
115
|
+
height: 100%;
|
|
116
|
+
}
|
|
117
|
+
</style>
|
|
@@ -10,36 +10,40 @@
|
|
|
10
10
|
return Math.round(Math.random() * 255)
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
let {
|
|
14
|
+
lat,
|
|
15
|
+
lng,
|
|
16
|
+
label = 'Marker',
|
|
17
|
+
popupClassName = 'beyonk-mapbox-popup',
|
|
18
|
+
markerOffset = [ 0, 0 ],
|
|
19
|
+
popupOffset = 10,
|
|
20
|
+
color = randomColour(),
|
|
21
|
+
popup = true,
|
|
22
|
+
popupOptions = {},
|
|
23
|
+
markerOptions = {},
|
|
24
|
+
children,
|
|
25
|
+
popupContent
|
|
26
|
+
} = $props()
|
|
27
|
+
|
|
28
|
+
let marker = $state()
|
|
29
|
+
let element = $state()
|
|
30
|
+
let elementPopup = $state()
|
|
31
|
+
|
|
13
32
|
function move (lng, lat) {
|
|
14
33
|
marker.setLngLat({ lng, lat })
|
|
15
34
|
}
|
|
16
35
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export let popupClassName = 'beyonk-mapbox-popup'
|
|
21
|
-
export let markerOffset = [ 0, 0 ]
|
|
22
|
-
export let popupOffset = 10
|
|
23
|
-
export let color = randomColour()
|
|
24
|
-
export let popup = true
|
|
25
|
-
export let popupOptions = {}
|
|
26
|
-
export let markerOptions = {}
|
|
27
|
-
|
|
28
|
-
let marker
|
|
29
|
-
let element
|
|
30
|
-
let elementPopup
|
|
31
|
-
|
|
32
|
-
$: marker && move(lng, lat)
|
|
36
|
+
$effect(() => {
|
|
37
|
+
if (marker) move(lng, lat)
|
|
38
|
+
})
|
|
33
39
|
|
|
34
40
|
onMount(() => {
|
|
35
41
|
const namedParams = Object.assign(
|
|
36
|
-
{
|
|
37
|
-
offset: markerOffset
|
|
38
|
-
},
|
|
42
|
+
{ offset: markerOffset },
|
|
39
43
|
element.hasChildNodes() ? { element } : { color }
|
|
40
44
|
)
|
|
41
45
|
marker = new mapbox.Marker(Object.assign(namedParams, markerOptions))
|
|
42
|
-
|
|
46
|
+
|
|
43
47
|
if (popup) {
|
|
44
48
|
const namedPopupParams = { offset: popupOffset, className: popupClassName }
|
|
45
49
|
const popupEl = new mapbox.Popup(Object.assign(namedPopupParams, popupOptions))
|
|
@@ -48,7 +52,6 @@
|
|
|
48
52
|
} else {
|
|
49
53
|
popupEl.setText(label)
|
|
50
54
|
}
|
|
51
|
-
|
|
52
55
|
marker.setPopup(popupEl)
|
|
53
56
|
}
|
|
54
57
|
|
|
@@ -67,9 +70,9 @@
|
|
|
67
70
|
</script>
|
|
68
71
|
|
|
69
72
|
<div bind:this={element}>
|
|
70
|
-
|
|
73
|
+
{@render children?.()}
|
|
71
74
|
</div>
|
|
72
75
|
|
|
73
76
|
<div class='popup' bind:this={elementPopup}>
|
|
74
|
-
|
|
77
|
+
{@render popupContent?.()}
|
|
75
78
|
</div>
|
|
@@ -1,56 +1,38 @@
|
|
|
1
|
-
<div
|
|
2
|
-
bind:this={dispatcher}
|
|
3
|
-
on:error
|
|
4
|
-
on:geolocate
|
|
5
|
-
on:outofmaxbounds
|
|
6
|
-
on:trackuserlocationend
|
|
7
|
-
on:trackuserlocationstart
|
|
8
|
-
/>
|
|
9
|
-
|
|
10
1
|
<script>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const geolocate = new mapbox.GeolocateControl(options)
|
|
43
|
-
map.addControl(geolocate, position)
|
|
44
|
-
|
|
45
|
-
onMount(() => {
|
|
46
|
-
return bindEvents(geolocate, handlers, mapbox, dispatcher)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
export function trigger () {
|
|
50
|
-
geolocate.trigger()
|
|
51
|
-
}
|
|
2
|
+
import { getContext, onMount, untrack } from 'svelte'
|
|
3
|
+
import { contextKey } from '../../mapbox.js'
|
|
4
|
+
import { bindEvents } from '../../event-bindings.js'
|
|
5
|
+
|
|
6
|
+
const { getMap, getMapbox } = getContext(contextKey)
|
|
7
|
+
const map = getMap()
|
|
8
|
+
const mapbox = getMapbox()
|
|
9
|
+
|
|
10
|
+
let { position = 'top-left', options = {}, ...rest } = $props()
|
|
11
|
+
|
|
12
|
+
let dispatcher = $state()
|
|
13
|
+
|
|
14
|
+
const handlers = {
|
|
15
|
+
error: (el, ev) => [ 'error', ev ],
|
|
16
|
+
geolocate: (el, ev) => [ 'geolocate', ev ],
|
|
17
|
+
outofmaxbounds: (el, ev) => [ 'outofmaxbounds', ev ],
|
|
18
|
+
trackuserlocationend: (el, ev) => [ 'trackuserlocationend', ev ],
|
|
19
|
+
trackuserlocationstart: (el, ev) => [ 'trackuserlocationstart', ev ]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const geolocate = new mapbox.GeolocateControl(untrack(() => options))
|
|
23
|
+
map.addControl(geolocate, untrack(() => position))
|
|
24
|
+
|
|
25
|
+
onMount(() => {
|
|
26
|
+
return bindEvents(geolocate, handlers, mapbox, dispatcher)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
export function trigger () {
|
|
30
|
+
geolocate.trigger()
|
|
31
|
+
}
|
|
52
32
|
</script>
|
|
53
33
|
|
|
34
|
+
<div bind:this={dispatcher} {...rest}></div>
|
|
35
|
+
|
|
54
36
|
<style>
|
|
55
|
-
|
|
37
|
+
div { display: none; }
|
|
56
38
|
</style>
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { getContext, untrack } from 'svelte'
|
|
3
|
+
import { contextKey } from '../../mapbox.js'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
const { getMap, getMapbox } = getContext(contextKey)
|
|
6
|
+
const map = getMap()
|
|
7
|
+
const mapbox = getMapbox()
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
export let options = {}
|
|
9
|
+
let { position = 'top-right', options = {} } = $props()
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
</script>
|
|
11
|
+
const nav = new mapbox.NavigationControl(untrack(() => options))
|
|
12
|
+
map.addControl(nav, untrack(() => position))
|
|
13
|
+
</script>
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { getContext, untrack } from 'svelte'
|
|
3
|
+
import { contextKey } from '../../mapbox.js'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
const { getMap, getMapbox } = getContext(contextKey)
|
|
6
|
+
const map = getMap()
|
|
7
|
+
const mapbox = getMapbox()
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
export let options = {}
|
|
9
|
+
let { position = 'bottom-right', options = {} } = $props()
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
const optionsWithDefaults = untrack(() => Object.assign({
|
|
12
|
+
maxWidth: 80,
|
|
13
|
+
unit: 'metric'
|
|
14
|
+
}, options))
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
</script>
|
|
16
|
+
const scale = new mapbox.ScaleControl(optionsWithDefaults)
|
|
17
|
+
map.addControl(scale, untrack(() => position))
|
|
18
|
+
</script>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { load } from '../asset-loader.js'
|
|
2
|
+
import { bindEvents } from '../event-bindings.js'
|
|
3
|
+
|
|
4
|
+
export default function mapAttachment (options = {}) {
|
|
5
|
+
return (element) => {
|
|
6
|
+
console.log('attach', element, options)
|
|
7
|
+
let map
|
|
8
|
+
|
|
9
|
+
function init (options) {
|
|
10
|
+
window.mapboxgl.accessToken = options.accessToken
|
|
11
|
+
const el = new window.mapboxgl.Map(options)
|
|
12
|
+
|
|
13
|
+
return bindEvents(el, handlers, window.mapboxgl, element)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const resources = [
|
|
17
|
+
{ type: 'script', attr: 'src', value: `//api.mapbox.com/mapbox-gl-js/${options.version}/mapbox-gl.js`, id: 'byk-gl-js' },
|
|
18
|
+
{ type: 'link', attr: 'href', value: `//api.mapbox.com/mapbox-gl-js/${options.version}/mapbox-gl.css`, id: 'byk-gl-css' }
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
const customStylesheetUrl = options.customStylesheetUrl
|
|
22
|
+
if (customStylesheetUrl) {
|
|
23
|
+
resources.push({ type: 'link', attr: 'href', value: customStylesheetUrl, id: 'byk-mcsu-css' })
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let unbind = () => {}
|
|
27
|
+
load(resources, () => {
|
|
28
|
+
unbind = init({ ...options, container: element })
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
destroy () {
|
|
33
|
+
unbind()
|
|
34
|
+
map && map.remove && map.remove()
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const handlers = {
|
|
41
|
+
dragend: el => {
|
|
42
|
+
return [ 'dragend', { center: el.getCenter() } ]
|
|
43
|
+
},
|
|
44
|
+
drag: el => {
|
|
45
|
+
return [ 'drag', { center: el.getCenter() } ]
|
|
46
|
+
},
|
|
47
|
+
moveend: el => {
|
|
48
|
+
return [ 'recentre', { center: el.getCenter() } ]
|
|
49
|
+
},
|
|
50
|
+
click: (el, { lngLat }) => {
|
|
51
|
+
return [ 'click', { lng: lngLat.lng, lat: lngLat.lat } ]
|
|
52
|
+
},
|
|
53
|
+
zoomstart: el => {
|
|
54
|
+
return [ 'zoomstart', { zoom: el.getZoom() } ]
|
|
55
|
+
},
|
|
56
|
+
zoom: el => {
|
|
57
|
+
return [ 'zoom', { zoom: el.getZoom() } ]
|
|
58
|
+
},
|
|
59
|
+
zoomend: el => {
|
|
60
|
+
return [ 'zoomend', { zoom: el.getZoom() } ]
|
|
61
|
+
},
|
|
62
|
+
load: (el, ev, mapbox) => {
|
|
63
|
+
return [ 'ready', { map: el, mapbox } ]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export class EventQueue {
|
|
2
|
+
queue = []
|
|
3
|
+
started = false
|
|
4
|
+
map = null
|
|
5
|
+
|
|
6
|
+
send (command, params = []) {
|
|
7
|
+
if (!command) { return }
|
|
8
|
+
this.queue.push([ command, params ])
|
|
9
|
+
this.#process()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
start (map) {
|
|
13
|
+
this.started = true
|
|
14
|
+
this.map = map
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#process () {
|
|
18
|
+
if (!this.started) { return }
|
|
19
|
+
while (this.queue.length) {
|
|
20
|
+
const [ command, params ] = this.queue.shift()
|
|
21
|
+
this.map[command].apply(this.map, params)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
stop () {
|
|
26
|
+
if (!this.started) { return }
|
|
27
|
+
this.queue = []
|
|
28
|
+
this.map = null
|
|
29
|
+
this.started = false
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/routes/+page.svelte
CHANGED
|
@@ -1,3 +1,51 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { PUBLIC_MAPBOX_TOKEN } from '$env/static/public'
|
|
3
|
+
import { Map, Geocoder, Marker, controls } from '$lib/components.js'
|
|
4
|
+
import Earthquakes from './_Earthquakes.svelte'
|
|
5
|
+
|
|
6
|
+
const { GeolocateControl, NavigationControl } = controls
|
|
7
|
+
|
|
8
|
+
let place = $state(null)
|
|
9
|
+
let page = $state('about')
|
|
10
|
+
let center = $state({ lat: 53.3358627, lng: -2.8572362 })
|
|
11
|
+
let marker = $derived(center)
|
|
12
|
+
let zoom = $state(11.15)
|
|
13
|
+
let mapComponent = $state()
|
|
14
|
+
|
|
15
|
+
function navigate (next) {
|
|
16
|
+
page = next
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function placeChanged (e) {
|
|
20
|
+
const { result } = e.detail
|
|
21
|
+
mapComponent.setCenter(result.center, 14)
|
|
22
|
+
place = result
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function randomLng () {
|
|
26
|
+
return 77 + (Math.random() - 0.5) * 30
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function randomLat () {
|
|
30
|
+
return 13 + (Math.random() - 0.5) * 30
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function flyToRandomPlace () {
|
|
34
|
+
mapComponent.flyTo({
|
|
35
|
+
center: [ randomLng(), randomLat() ],
|
|
36
|
+
essential: true
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function recentre (e) {
|
|
41
|
+
center = e.detail.center
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function drag (e) {
|
|
45
|
+
marker = e.detail.center
|
|
46
|
+
}
|
|
47
|
+
</script>
|
|
48
|
+
|
|
1
49
|
<header>
|
|
2
50
|
<div class="container">
|
|
3
51
|
<div class="row">
|
|
@@ -42,39 +90,32 @@
|
|
|
42
90
|
<div class="row">
|
|
43
91
|
<aside>
|
|
44
92
|
<div class="menu-box">
|
|
45
|
-
<h4>Navigation</h4>
|
|
93
|
+
<h4>Navigation</h4>
|
|
46
94
|
<nav>
|
|
47
95
|
<ul>
|
|
48
|
-
<li><a href="#geocoder"
|
|
49
|
-
<li><a href="#map"
|
|
50
|
-
</ul>
|
|
96
|
+
<li><a href="#geocoder" onclick={() => navigate('geocoder')} class:current={page === 'geocoder'}>Geocoder</a></li>
|
|
97
|
+
<li><a href="#map" onclick={() => navigate('map')} class:current={page === 'map'}>Map</a></li>
|
|
98
|
+
</ul>
|
|
51
99
|
</nav>
|
|
52
100
|
</div>
|
|
53
101
|
</aside>
|
|
54
102
|
<div class="content-info">
|
|
55
103
|
<div class="action-buttons">
|
|
56
|
-
<button id="fly-to"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<button
|
|
61
|
-
id="change-zoom"
|
|
62
|
-
on:click={() => (zoom = Math.floor(Math.random() * 10))}
|
|
63
|
-
>Change Zoom Level</button
|
|
64
|
-
>
|
|
65
|
-
</div>
|
|
104
|
+
<button id="fly-to" onclick={flyToRandomPlace}>Fly to random location</button>
|
|
105
|
+
<button id="change-zoom" onclick={() => (zoom = Math.floor(Math.random() * 10))}>Change Zoom Level</button>
|
|
106
|
+
</div>
|
|
66
107
|
|
|
67
108
|
<div class="section-txt" id="geocoder">
|
|
68
109
|
<form>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
110
|
+
<Geocoder value="(Near London)" accessToken={PUBLIC_MAPBOX_TOKEN} onresult={placeChanged} onclear={() => mapComponent.setCenter({ lng: 0, lat: 0 })} />
|
|
111
|
+
{#if place}
|
|
112
|
+
<dl>
|
|
113
|
+
<dt>Name:</dt>
|
|
114
|
+
<dd>{place.place_name}</dd>
|
|
115
|
+
<dt>Geolocation:</dt>
|
|
116
|
+
<dd>lat: {place.geometry.coordinates[1]}, lng: {place.geometry.coordinates[0]}</dd>
|
|
117
|
+
</dl>
|
|
118
|
+
{/if}
|
|
78
119
|
</form>
|
|
79
120
|
</div>
|
|
80
121
|
<div class="section-txt" id="map">
|
|
@@ -82,14 +123,14 @@
|
|
|
82
123
|
<Map
|
|
83
124
|
bind:this={mapComponent}
|
|
84
125
|
accessToken={PUBLIC_MAPBOX_TOKEN}
|
|
85
|
-
|
|
86
|
-
|
|
126
|
+
onrecentre={recentre}
|
|
127
|
+
ondrag={drag}
|
|
87
128
|
{center}
|
|
88
129
|
bind:zoom
|
|
89
130
|
>
|
|
90
131
|
<Earthquakes />
|
|
91
132
|
<NavigationControl />
|
|
92
|
-
<GeolocateControl
|
|
133
|
+
<GeolocateControl ongeolocate={e => console.log('geolocated', e.detail)} />
|
|
93
134
|
<Marker lat={marker.lat} lng={marker.lng} />
|
|
94
135
|
</Map>
|
|
95
136
|
</div>
|
|
@@ -157,55 +198,4 @@
|
|
|
157
198
|
color: #fff;
|
|
158
199
|
background: #ee8a65;
|
|
159
200
|
}
|
|
160
|
-
|
|
161
201
|
</style>
|
|
162
|
-
|
|
163
|
-
<script>
|
|
164
|
-
import { PUBLIC_MAPBOX_TOKEN } from '$env/static/public'
|
|
165
|
-
import { Map, Geocoder, Marker, controls } from '$lib/components.js'
|
|
166
|
-
import Earthquakes from './_Earthquakes.svelte'
|
|
167
|
-
|
|
168
|
-
const { GeolocateControl, NavigationControl } = controls
|
|
169
|
-
const place = null
|
|
170
|
-
|
|
171
|
-
let page = 'about'
|
|
172
|
-
let center = { lat: 53.3358627, lng: -2.8572362 }
|
|
173
|
-
let marker = center
|
|
174
|
-
let zoom = 11.15
|
|
175
|
-
let mapComponent
|
|
176
|
-
|
|
177
|
-
function navigate (next) {
|
|
178
|
-
page = next
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function placeChanged (e) {
|
|
182
|
-
const { result } = e.detail
|
|
183
|
-
mapComponent.setCenter(result.center, 14)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function randomLng () {
|
|
187
|
-
return 77 + (Math.random() - 0.5) * 30
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function randomLat () {
|
|
191
|
-
return 13 + (Math.random() - 0.5) * 30
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function flyToRandomPlace () {
|
|
195
|
-
mapComponent.flyTo({
|
|
196
|
-
center: [
|
|
197
|
-
randomLng(),
|
|
198
|
-
randomLat()
|
|
199
|
-
],
|
|
200
|
-
essential: true
|
|
201
|
-
})
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function recentre ({ detail }) {
|
|
205
|
-
center = detail.center
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function drag ({ detail }) {
|
|
209
|
-
marker = detail.center
|
|
210
|
-
}
|
|
211
|
-
</script>
|
package/svelte.config.js
CHANGED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
import js from "@eslint/js";
|
|
4
|
-
import { FlatCompat } from "@eslint/eslintrc";
|
|
5
|
-
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
-
const compat = new FlatCompat({
|
|
9
|
-
baseDirectory: __dirname,
|
|
10
|
-
recommendedConfig: js.configs.recommended,
|
|
11
|
-
allConfig: js.configs.all
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
export default [{
|
|
15
|
-
ignores: ["**/package", "**/.svelte-kit"],
|
|
16
|
-
}, ...compat.extends("@beyonk/eslint-config/svelte"), {
|
|
17
|
-
languageOptions: {
|
|
18
|
-
ecmaVersion: 2020,
|
|
19
|
-
sourceType: "module",
|
|
20
|
-
},
|
|
21
|
-
}];
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { load } from '../asset-loader.js'
|
|
2
|
-
import { bindEvents } from '../event-bindings.js'
|
|
3
|
-
|
|
4
|
-
export default function action (node, options = {}) {
|
|
5
|
-
let map
|
|
6
|
-
|
|
7
|
-
const resources = [
|
|
8
|
-
{ type: 'script', value: `//api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/${options.version}/mapbox-gl-geocoder.min.js`, id: 'byk-gc-js' }
|
|
9
|
-
]
|
|
10
|
-
|
|
11
|
-
const customStylesheetUrl = options.customStylesheetUrl
|
|
12
|
-
if (customStylesheetUrl) {
|
|
13
|
-
resources.push({ type: 'link', value: customStylesheetUrl, id: 'byk-gcsu-css' })
|
|
14
|
-
} else {
|
|
15
|
-
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' })
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let unbind = () => {}
|
|
19
|
-
load(resources, () => {
|
|
20
|
-
unbind = init(options, node)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
destroy () {
|
|
25
|
-
unbind()
|
|
26
|
-
map && map.remove && map.remove()
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function init (options, node) {
|
|
32
|
-
const geocoder = new window.MapboxGeocoder(options)
|
|
33
|
-
geocoder.addTo(`#${node.id}`)
|
|
34
|
-
if (options.value) {
|
|
35
|
-
geocoder.setInput(options.value)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return bindEvents(geocoder, handlers, false, node)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const handlers = {
|
|
42
|
-
results: (el, ev) => {
|
|
43
|
-
return [ 'results', ev ]
|
|
44
|
-
},
|
|
45
|
-
result: (el, ev) => {
|
|
46
|
-
return [ 'result', ev ]
|
|
47
|
-
},
|
|
48
|
-
loading: (el, ev) => {
|
|
49
|
-
return [ 'loading', ev ]
|
|
50
|
-
},
|
|
51
|
-
error: (el, ev) => {
|
|
52
|
-
return [ 'error', ev ]
|
|
53
|
-
},
|
|
54
|
-
clear: (el, ev) => {
|
|
55
|
-
return [ 'clear', ev ]
|
|
56
|
-
},
|
|
57
|
-
load: el => {
|
|
58
|
-
return [ 'ready', { geocoder: el } ]
|
|
59
|
-
}
|
|
60
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { load } from '../asset-loader.js'
|
|
2
|
-
import { bindEvents } from '../event-bindings.js'
|
|
3
|
-
|
|
4
|
-
export default function action (node, options = {}) {
|
|
5
|
-
let map
|
|
6
|
-
|
|
7
|
-
const resources = [
|
|
8
|
-
{ type: 'script', attr: 'src', value: `//api.mapbox.com/mapbox-gl-js/${options.version}/mapbox-gl.js`, id: 'byk-gl-js' },
|
|
9
|
-
{ type: 'link', attr: 'href', value: `//api.mapbox.com/mapbox-gl-js/${options.version}/mapbox-gl.css`, id: 'byk-gl-css' }
|
|
10
|
-
]
|
|
11
|
-
|
|
12
|
-
const customStylesheetUrl = options.customStylesheetUrl
|
|
13
|
-
if (customStylesheetUrl) {
|
|
14
|
-
resources.push({ type: 'link', attr: 'href', value: customStylesheetUrl, id: 'byk-mcsu-css' })
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
let unbind = () => {}
|
|
18
|
-
load(resources, () => {
|
|
19
|
-
unbind = init({ ...options, container: node }, node)
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
return {
|
|
23
|
-
destroy () {
|
|
24
|
-
unbind()
|
|
25
|
-
map && map.remove && map.remove()
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function init (options, node) {
|
|
31
|
-
window.mapboxgl.accessToken = options.accessToken
|
|
32
|
-
const el = new window.mapboxgl.Map(options)
|
|
33
|
-
|
|
34
|
-
return bindEvents(el, handlers, window.mapboxgl, node)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const handlers = {
|
|
38
|
-
dragend: el => {
|
|
39
|
-
return [ 'dragend', { center: el.getCenter() } ]
|
|
40
|
-
},
|
|
41
|
-
drag: el => {
|
|
42
|
-
return [ 'drag', { center: el.getCenter() } ]
|
|
43
|
-
},
|
|
44
|
-
moveend: el => {
|
|
45
|
-
return [ 'recentre', { center: el.getCenter() } ]
|
|
46
|
-
},
|
|
47
|
-
click: (el, { lngLat }) => {
|
|
48
|
-
return [ 'click', { lng: lngLat.lng, lat: lngLat.lat } ]
|
|
49
|
-
},
|
|
50
|
-
zoomstart: el => {
|
|
51
|
-
return [ 'zoomstart', { zoom: el.getZoom() } ]
|
|
52
|
-
},
|
|
53
|
-
zoom: el => {
|
|
54
|
-
return [ 'zoom', { zoom: el.getZoom() } ]
|
|
55
|
-
},
|
|
56
|
-
zoomend: el => {
|
|
57
|
-
return [ 'zoomend', { zoom: el.getZoom() } ]
|
|
58
|
-
},
|
|
59
|
-
load: (el, ev, mapbox) => {
|
|
60
|
-
return [ 'ready', { map: el, mapbox } ]
|
|
61
|
-
}
|
|
62
|
-
}
|
package/src/lib/queue.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { writable } from 'svelte/store'
|
|
2
|
-
|
|
3
|
-
export class EventQueue {
|
|
4
|
-
constructor () {
|
|
5
|
-
this.queue = writable([])
|
|
6
|
-
this.unsubscribe = null
|
|
7
|
-
this.started = false
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
send (command, params = []) {
|
|
11
|
-
if (!command) { return }
|
|
12
|
-
this.queue.update(q => ([ ...q, [ command, params ] ]))
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
start (map) {
|
|
16
|
-
this.unsubscribe = this.queue.subscribe(queue => {
|
|
17
|
-
while (queue.length) {
|
|
18
|
-
const [ command, params ] = queue.shift()
|
|
19
|
-
map[command].apply(map, params)
|
|
20
|
-
}
|
|
21
|
-
})
|
|
22
|
-
this.started = true
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
stop () {
|
|
26
|
-
if (!this.started) { return }
|
|
27
|
-
this.unsubscribe()
|
|
28
|
-
this.queue = writable([])
|
|
29
|
-
this.started = false
|
|
30
|
-
}
|
|
31
|
-
}
|