@beyonk/svelte-mapbox 11.0.0 → 12.1.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.
@@ -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' "${NODE_AUTH_TOKEN}"
19
+ pnpm config set '//registry.npmjs.org/:_authToken' "${{secrets.NPM_TOKEN}}"
28
20
  pnpm i
29
21
 
30
22
  - name: publish
package/README.md CHANGED
@@ -30,17 +30,17 @@ npm install --save-dev @beyonk/svelte-mapbox
30
30
 
31
31
  The container component is the map, and there are a variety of components which go on the map.
32
32
 
33
- ```jsx
33
+ ```svelte
34
34
  <Map
35
35
  accessToken="<your api key>" // add your api key here
36
36
  bind:this={mapComponent} // Get reference to your map component to use methods
37
- on:recentre={e => console.log(e.detail.center.lat, e.detail.center.lng) } // recentre events
37
+ onrecentre={e => console.log(e.detail.center.lat, e.detail.center.lng) } // recentre events
38
38
  options={{ scrollZoom: false }} // // add arbitrary options to the map from the mapbox api
39
39
  >
40
40
  <Earthquakes /> // Any custom component you create or want here - see marker example
41
41
  <Marker lat={someLat} lng={someLng} color="rgb(255,255,255)" label="some marker label" popupClassName="class-name" /> // built in Marker component
42
42
  <NavigationControl />
43
- <GeolocateControl options={{ some: 'control-option' }} on:eventname={eventHandler} />
43
+ <GeolocateControl options={{ some: 'control-option' }} oneventname={eventHandler} />
44
44
  <ScaleControl />
45
45
  </Map>
46
46
 
@@ -73,13 +73,13 @@ The container component is the map, and there are a variety of components which
73
73
 
74
74
  By default, markers are typical map pins to which you can pass a color property.
75
75
 
76
- ```jsx
76
+ ```svelte
77
77
  <Marker color={brandColour} />
78
78
  ```
79
79
 
80
80
  You may also create a custom pin with the default slot.
81
81
 
82
- ```jsx
82
+ ```svelte
83
83
  <Marker
84
84
  lat={waypoint.geo.lat}
85
85
  lng={waypoint.geo.lng}
@@ -98,13 +98,13 @@ lng={waypoint.geo.lng}
98
98
  ### Marker Popups
99
99
  By default a popup is revealed when you click a pin. It is populated with text provided in the label property.
100
100
 
101
- ```jsx
101
+ ```svelte
102
102
  <Marker label={markerText} />
103
103
  ```
104
104
 
105
105
  To indicate interactivity, you may target the marker with some custom CSS:
106
106
 
107
- ```jsx
107
+ ```svelte
108
108
  <style>
109
109
  :global(.mapboxgl-marker){
110
110
  cursor: pointer;
@@ -114,13 +114,13 @@ To indicate interactivity, you may target the marker with some custom CSS:
114
114
 
115
115
  Optionally, disable the popup with the `popup={false}` property:
116
116
 
117
- ```jsx
117
+ ```svelte
118
118
  <Marker popup={false} />
119
119
  ```
120
120
 
121
121
  You may alternatively pass a custom DOM element to the marker to use as a popup.
122
122
 
123
- ```jsx
123
+ ```svelte
124
124
  <Marker lat={pin.coordinates.latitude} lng={pin.coordinates.longitude}>
125
125
  <div class="content" slot="popup">
126
126
  <h3>{pin.name}</h3>
@@ -139,7 +139,7 @@ This also means that if you bind these properties to a variable, that variable w
139
139
 
140
140
  This is often easier than waiting for events such as `recentre` or `zoom` to be fired, to update markers and similar:
141
141
 
142
- ```jsx
142
+ ```svelte
143
143
  <Map accessToken="<your api key>" bind:center bind:zoom>
144
144
  <Marker bind:lat bind:lng />
145
145
  </Map>
@@ -165,19 +165,19 @@ map is ready in your browser when you call it this way.
165
165
 
166
166
  The Geocoder is an autocompleting place lookup, which returns a lat and lng for a place.
167
167
 
168
- ```jsx
169
- <Geocoder accessToken="<your api key>" on:result={somePlaceChangeFunction} />
168
+ ```svelte
169
+ <Geocoder accessToken="<your api key>" onresult={somePlaceChangeFunction} />
170
170
 
171
171
  <script>
172
172
  import { Geocoder } from '@beyonk/svelte-mapbox'
173
173
  </script>
174
174
  ```
175
175
 
176
- The geocoder has five events you can subscribe to: `on:loading`, `on:result`, `on:results`, `on:clear`, and `on:error` which are [documented here](https://github.com/mapbox/mapbox-gl-geocoder/blob/master/API.md#on)
176
+ The geocoder has five events you can subscribe to: `onloading`, `onresult`, `onresults`, `onclear`, and `onerror` which are [documented here](https://github.com/mapbox/mapbox-gl-geocoder/blob/master/API.md#on)
177
177
 
178
- The most important event is `on:result` which is fired when a user selects an autocomplete result.
178
+ The most important event is `onresult` which is fired when a user selects an autocomplete result.
179
179
 
180
- There is a sixth event specific to this library, which is `on:ready`, which is fired when the component is ready for use. You can likely ignore it.
180
+ There is a sixth event specific to this library, which is `onready`, which is fired when the component is ready for use. You can likely ignore it.
181
181
 
182
182
  ## Custom CSS
183
183
 
File without changes
package/jsconfig.json CHANGED
@@ -1,11 +1,4 @@
1
1
  {
2
2
  "extends": "./.svelte-kit/tsconfig.json",
3
- "compilerOptions": {
4
- "baseUrl": ".",
5
- "paths": {
6
- "$lib": ["src/lib"],
7
- "$lib/*": ["src/lib/*"]
8
- }
9
- },
10
3
  "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
11
4
  }
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@beyonk/svelte-mapbox",
3
- "version": "11.0.0",
3
+ "version": "12.1.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.8.0",
9
- "@sveltejs/vite-plugin-svelte": "^4.0.0",
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.1.14",
12
+ "svelte": "^5.55.0",
13
13
  "svelte2tsx": "^0.7.23",
14
14
  "typescript": "^5.6.3",
15
- "vite": "^5.4.11"
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,49 @@
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 { createEventDispatcher } from 'svelte'
15
- import action from './geocoder-action.js'
2
+ import geocoderAttachment from './geocoder-attachment.js'
16
3
 
17
- export let accessToken
18
- export let options = {}
19
- export let version = 'v5.0.3'
20
- export let types = [ 'country', 'region', 'postcode', 'district', 'place', 'locality', 'neighborhood', 'address' ]
21
- export let placeholder = 'Search'
22
- export let value = null
23
- export let customStylesheetUrl = false
24
- export let geocoder
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
+ onresults,
14
+ onresult,
15
+ onloading,
16
+ onerror,
17
+ onclear,
18
+ onload,
19
+ ...rest
20
+ } = $props()
25
21
 
26
- const dispatch = createEventDispatcher()
27
22
  const fieldId = 'bsm-' + Math.random().toString(36).substring(6)
28
23
 
29
- const optionsWithDefaults = Object.assign({
24
+ const optionsWithDefaults = $derived.by(() => Object.assign({
30
25
  version,
31
26
  accessToken,
32
27
  types: types.join(','),
33
28
  placeholder,
34
29
  customStylesheetUrl,
35
30
  value
36
- }, options)
31
+ }, options))
37
32
 
38
- function init ({ detail }) {
33
+ function init (detail) {
39
34
  geocoder = detail.geocoder
40
- dispatch('ready')
41
35
  }
42
36
  </script>
43
37
 
38
+ <div
39
+ id={fieldId}
40
+ {@attach geocoderAttachment(optionsWithDefaults, { onresults, onresult, onloading, onerror, onclear, onload })}
41
+ onready={init}
42
+ {...rest}
43
+ ></div>
44
+
44
45
  <style>
45
46
  div {
46
47
  padding: 0;
47
48
  }
48
- </style>
49
+ </style>
@@ -0,0 +1,50 @@
1
+ import { load } from '../asset-loader.js'
2
+
3
+ export default function geocoderAttachment (options, { onresults, onresult, onloading, onerror, onclear, onready }) {
4
+ return (element) => {
5
+ let geocoderInstance
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
+ load(resources, () => {
19
+ geocoderInstance = new window.MapboxGeocoder(options)
20
+ geocoderInstance.addTo(`#${element.id}`)
21
+ if (options.value) {
22
+ geocoderInstance.setInput(options.value)
23
+ }
24
+
25
+ geocoderInstance.on('results', (ev) => {
26
+ onresults?.(ev)
27
+ })
28
+ geocoderInstance.on('result', (ev) => {
29
+ console.log('result', onresult, ev)
30
+ onresult?.(ev)
31
+ })
32
+ geocoderInstance.on('loading', (ev) => {
33
+ onloading?.(ev)
34
+ })
35
+ geocoderInstance.on('error', (ev) => {
36
+ onerror?.(ev)
37
+ })
38
+ geocoderInstance.on('clear', (ev) => {
39
+ onclear?.(ev)
40
+ })
41
+ geocoderInstance.on('load', (ev) => {
42
+ onready?.({ ...ev, geocoder: geocoderInstance })
43
+ })
44
+ })
45
+
46
+ return () => {
47
+ geocoderInstance && geocoderInstance.remove && geocoderInstance.remove()
48
+ }
49
+ }
50
+ }
@@ -1,60 +1,40 @@
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, onDestroy, createEventDispatcher } from 'svelte'
2
+ import { setContext, untrack } from 'svelte'
30
3
  import { contextKey } from '../mapbox.js'
31
- import action from './map-action.js'
32
- import { EventQueue } from '../queue.js'
33
-
34
- export let map = null
35
- export let version = 'v3.7.0'
36
- export let center = [ 0, 0 ]
37
- export let zoom = 9
38
- export let zoomRate = 1
39
- export let wheelZoomRate = 1
40
- export let options = {}
41
- export let accessToken
42
- export let customStylesheetUrl = false
43
- export let style = 'mapbox://styles/mapbox/streets-v11'
44
-
45
- const dispatch = createEventDispatcher()
4
+ import mapAttachment from './map-attachment.js'
5
+ import { EventQueue } from '../queue.svelte.js'
6
+
7
+ let {
8
+ version = 'v3.20.0',
9
+ center = [ 0, 0 ],
10
+ zoom = $bindable(9),
11
+ zoomRate = 1,
12
+ wheelZoomRate = 1,
13
+ options = {},
14
+ accessToken,
15
+ customStylesheetUrl = false,
16
+ style = 'mapbox://styles/mapbox/streets-v11',
17
+ children,
18
+ onready,
19
+ ondragend,
20
+ ondrag,
21
+ onmoveend,
22
+ onzoomstart,
23
+ onzoom,
24
+ onzoomend,
25
+ ...rest
26
+ } = $props()
27
+
28
+ let map = $state()
29
+ let mapbox = $state()
46
30
 
47
31
  setContext(contextKey, {
48
32
  getMap: () => map,
49
33
  getMapbox: () => mapbox
50
34
  })
51
35
 
52
- let container
53
- let mapbox
54
-
55
- const optionsWithDefaults = Object.assign({
36
+ const optionsWithDefaults = $derived.by(() => Object.assign({
56
37
  accessToken,
57
- container,
58
38
  style,
59
39
  center,
60
40
  zoom,
@@ -62,21 +42,32 @@
62
42
  wheelZoomRate,
63
43
  version,
64
44
  customStylesheetUrl,
65
- map
66
- }, options)
45
+ ...options
46
+ }))
67
47
 
68
48
  const queue = new EventQueue()
69
49
 
70
- function init ({ detail }) {
50
+ function init (detail) {
71
51
  map = detail.map
72
52
  mapbox = detail.mapbox
73
53
  queue.start(map)
74
- dispatch('ready')
54
+
55
+ map.on('zoomend', (e) => {
56
+ zoom = map.getZoom()
57
+ })
58
+
59
+ onready?.({ map, mapbox })
75
60
  }
76
61
 
77
- onDestroy(() => {
78
- queue.stop()
79
- map = undefined
62
+ $effect(() => {
63
+ return () => {
64
+ queue.stop()
65
+ map = undefined
66
+ }
67
+ })
68
+
69
+ $effect(() => {
70
+ zoom && setZoom(zoom)
80
71
  })
81
72
 
82
73
  export function fitBounds (bbox, data = {}) {
@@ -110,6 +101,24 @@
110
101
  export function getMapbox () {
111
102
  return mapbox
112
103
  }
113
-
114
- $: zoom && setZoom(zoom)
115
104
  </script>
105
+
106
+ <div
107
+ {@attach mapAttachment(
108
+ optionsWithDefaults,
109
+ { onready: init, ondragend, ondrag, onmoveend, onzoomstart, onzoom, onzoomend }
110
+ )}
111
+ {...rest}
112
+ role="presentation"
113
+ >
114
+ {#if map}
115
+ {@render children?.()}
116
+ {/if}
117
+ </div>
118
+
119
+ <style>
120
+ div {
121
+ width: 100%;
122
+ height: 100%;
123
+ }
124
+ </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
- export let lat
18
- export let lng
19
- export let label = 'Marker'
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
- <slot></slot>
73
+ {@render children?.()}
71
74
  </div>
72
75
 
73
76
  <div class='popup' bind:this={elementPopup}>
74
- <slot name="popup"></slot>
77
+ {@render popupContent?.()}
75
78
  </div>
@@ -1,56 +1,33 @@
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
- import { getContext, onMount } from 'svelte'
12
- import { contextKey } from '../../mapbox.js'
13
- import { bindEvents } from '../../event-bindings.js'
14
-
15
- const { getMap, getMapbox } = getContext(contextKey)
16
- const map = getMap()
17
- const mapbox = getMapbox()
18
-
19
- export let position = 'top-left'
20
- export let options = {}
21
-
22
- let dispatcher
23
-
24
- const handlers = {
25
- error: (el, ev) => {
26
- return [ 'error', ev ]
27
- },
28
- geolocate: (el, ev) => {
29
- return [ 'geolocate', ev ]
30
- },
31
- outofmaxbounds: (el, ev) => {
32
- return [ 'outofmaxbounds', ev ]
33
- },
34
- trackuserlocationend: (el, ev) => {
35
- return [ 'trackuserlocationend', ev ]
36
- },
37
- trackuserlocationstart: (el, ev) => {
38
- return [ 'trackuserlocationstart', ev ]
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
+
5
+ const { getMap, getMapbox } = getContext(contextKey)
6
+ const map = getMap()
7
+ const mapbox = getMapbox()
8
+
9
+ let { position = 'top-left', options = {}, onerror, ongeolocate, onoutofmaxbounds, ontrackuserlocationend, ontrackuserlocationstart, ...rest } = $props()
10
+
11
+ let dispatcher = $state()
12
+
13
+ const geolocate = new mapbox.GeolocateControl(untrack(() => options))
14
+ map.addControl(geolocate, untrack(() => position))
15
+
16
+ onMount(() => {
17
+ geolocate.on('error', onerror)
18
+ geolocate.on('geolocate', ongeolocate)
19
+ geolocate.on('outofmaxbounds', onoutofmaxbounds)
20
+ geolocate.on('trackuserlocationend', ontrackuserlocationend)
21
+ geolocate.on('trackuserlocationstart', ontrackuserlocationstart)
22
+ })
23
+
24
+ export function trigger () {
25
+ geolocate.trigger()
26
+ }
52
27
  </script>
53
28
 
29
+ <div bind:this={dispatcher} {...rest}></div>
30
+
54
31
  <style>
55
- div { display: none; }
32
+ div { display: none; }
56
33
  </style>
@@ -1,14 +1,13 @@
1
1
  <script>
2
- import { getContext } from 'svelte'
3
- import { contextKey } from '../../mapbox.js'
2
+ import { getContext, untrack } from 'svelte'
3
+ import { contextKey } from '../../mapbox.js'
4
4
 
5
- const { getMap, getMapbox } = getContext(contextKey)
6
- const map = getMap()
7
- const mapbox = getMapbox()
5
+ const { getMap, getMapbox } = getContext(contextKey)
6
+ const map = getMap()
7
+ const mapbox = getMapbox()
8
8
 
9
- export let position = 'top-right'
10
- export let options = {}
9
+ let { position = 'top-right', options = {} } = $props()
11
10
 
12
- const nav = new mapbox.NavigationControl(options)
13
- map.addControl(nav, position)
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
- import { getContext } from 'svelte'
3
- import { contextKey } from '../../mapbox.js'
2
+ import { getContext, untrack } from 'svelte'
3
+ import { contextKey } from '../../mapbox.js'
4
4
 
5
- const { getMap, getMapbox } = getContext(contextKey)
6
- const map = getMap()
7
- const mapbox = getMapbox()
5
+ const { getMap, getMapbox } = getContext(contextKey)
6
+ const map = getMap()
7
+ const mapbox = getMapbox()
8
8
 
9
- export let position = 'bottom-right'
10
- export let options = {}
9
+ let { position = 'bottom-right', options = {} } = $props()
11
10
 
12
- const optionsWithDefaults = Object.assign({
13
- maxWidth: 80,
14
- unit: 'metric'
15
- }, options)
11
+ const optionsWithDefaults = untrack(() => Object.assign({
12
+ maxWidth: 80,
13
+ unit: 'metric'
14
+ }, options))
16
15
 
17
- const scale = new mapbox.ScaleControl(optionsWithDefaults)
18
- map.addControl(scale, position)
19
- </script>
16
+ const scale = new mapbox.ScaleControl(optionsWithDefaults)
17
+ map.addControl(scale, untrack(() => position))
18
+ </script>
@@ -0,0 +1,56 @@
1
+ import { load } from '../asset-loader.js'
2
+
3
+ export default function mapAttachment (options = {}, { ondragend, ondrag, onmoveend, onzoomstart, onzoom, onzoomend, onready }) {
4
+ return (element) => {
5
+ let map
6
+
7
+ function init (options) {
8
+ window.mapboxgl.accessToken = options.accessToken
9
+ const el = new window.mapboxgl.Map(options)
10
+
11
+ el.on('dragend', () => {
12
+ ondragend?.({ center: el.getCenter() })
13
+ })
14
+ el.on('drag', () => {
15
+ ondrag?.({ center: el.getCenter() })
16
+ })
17
+ el.on('moveend', () => {
18
+ onmoveend?.({ center: el.getCenter() })
19
+ })
20
+ el.on('zoomstart', () => {
21
+ onzoomstart?.({ zoom: el.getZoom() })
22
+ })
23
+ el.on('zoom', () => {
24
+ onzoom?.({ zoom: el.getZoom() })
25
+ })
26
+ el.on('zoomend', () => {
27
+ onzoomend?.({ zoom: el.getZoom() })
28
+ })
29
+ el.on('load', () => {
30
+ onready?.({ map: el, mapbox: window.mapboxgl })
31
+ })
32
+ }
33
+
34
+ const resources = [
35
+ { type: 'script', attr: 'src', value: `//api.mapbox.com/mapbox-gl-js/${options.version}/mapbox-gl.js`, id: 'byk-gl-js' },
36
+ { type: 'link', attr: 'href', value: `//api.mapbox.com/mapbox-gl-js/${options.version}/mapbox-gl.css`, id: 'byk-gl-css' }
37
+ ]
38
+
39
+ const customStylesheetUrl = options.customStylesheetUrl
40
+ if (customStylesheetUrl) {
41
+ resources.push({ type: 'link', attr: 'href', value: customStylesheetUrl, id: 'byk-mcsu-css' })
42
+ }
43
+
44
+ let unbind = () => {}
45
+ load(resources, () => {
46
+ unbind = init({ ...options, container: element })
47
+ })
48
+
49
+ return {
50
+ destroy () {
51
+ unbind()
52
+ map && map.remove && map.remove()
53
+ }
54
+ }
55
+ }
56
+ }
@@ -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
+ }
@@ -1,3 +1,47 @@
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
+ import { untrack } from 'svelte'
6
+
7
+ const { GeolocateControl, NavigationControl } = controls
8
+
9
+ let place = $state()
10
+ let placeName = $derived(untrack(() => place?.place_name || '(Near London)'))
11
+ let page = $state('about')
12
+ let center = $state({ lat: 53.3358627, lng: -2.8572362 })
13
+ let marker = $derived(center)
14
+ let zoom = $state(11.15)
15
+ let mapComponent = $state()
16
+
17
+ function navigate (next) {
18
+ page = next
19
+ }
20
+
21
+ function randomLng () {
22
+ return 77 + (Math.random() - 0.5) * 30
23
+ }
24
+
25
+ function randomLat () {
26
+ return 13 + (Math.random() - 0.5) * 30
27
+ }
28
+
29
+ function flyToRandomPlace () {
30
+ mapComponent.flyTo({
31
+ center: [ randomLng(), randomLat() ],
32
+ essential: true
33
+ })
34
+ }
35
+
36
+ function recentre (result) {
37
+ center = result.center
38
+ }
39
+
40
+ function drag (result) {
41
+ marker = result.center
42
+ }
43
+ </script>
44
+
1
45
  <header>
2
46
  <div class="container">
3
47
  <div class="row">
@@ -31,7 +75,7 @@
31
75
  </div>
32
76
  </div>
33
77
  <div class="col-lg-2 col-md-3 col-xs-12 right">
34
- <a class="btn" href="http://www.github.com/beyonk-adventures/svelte-mapbox">Github</a>
78
+ <a class="btn" href="http://www.github.com/beyonk/svelte-mapbox">Github</a>
35
79
  </div>
36
80
  </div>
37
81
  </div>
@@ -42,39 +86,43 @@
42
86
  <div class="row">
43
87
  <aside>
44
88
  <div class="menu-box">
45
- <h4>Navigation</h4>
89
+ <h4>Navigation</h4>
46
90
  <nav>
47
91
  <ul>
48
- <li><a href="#geocoder" on:click={() => { navigate('geocoder') } } class:current={page === 'geocoder'}>Geocoder</a></li>
49
- <li><a href="#map" on:click={() => { navigate('map') }} class:current={page === 'map'}>Map</a></li>
50
- </ul>
92
+ <li><a href="#geocoder" onclick={() => navigate('geocoder')} class:current={page === 'geocoder'}>Geocoder</a></li>
93
+ <li><a href="#map" onclick={() => navigate('map')} class:current={page === 'map'}>Map</a></li>
94
+ </ul>
51
95
  </nav>
52
96
  </div>
53
97
  </aside>
54
98
  <div class="content-info">
55
99
  <div class="action-buttons">
56
- <button id="fly-to" on:click={flyToRandomPlace}
57
- >Fly to random location</button
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>
100
+ <button id="fly-to" onclick={flyToRandomPlace}>Fly to random location</button>
101
+ <button id="change-zoom" onclick={() => (zoom = Math.floor(Math.random() * 10))}>Change Zoom Level</button>
102
+ </div>
66
103
 
67
104
  <div class="section-txt" id="geocoder">
68
105
  <form>
69
- <Geocoder value="(Near London)" accessToken={PUBLIC_MAPBOX_TOKEN} on:result={placeChanged} on:clear={() => mapComponent.setCenter({ lng: 0, lat: 0 })} />
70
- {#if place}
71
- <dl>
72
- <dt>Name:</dt>
73
- <dd>{place.label}</dd>
74
- <dt>Geolocation:</dt>
75
- <dd>lat: {place.geometry.lat}, lng: {place.geometry.lng}</dd>
76
- </dl>
77
- {/if}
106
+ <Geocoder
107
+ bind:value={placeName}
108
+ accessToken={PUBLIC_MAPBOX_TOKEN}
109
+ onresult={({ result }) => {
110
+ console.log('sets center', result.center)
111
+ mapComponent.setCenter(result.center, 14)
112
+ place = result
113
+ }}
114
+ onclear={() => {
115
+ mapComponent.setCenter({ lng: 0, lat: 0 })
116
+ }}
117
+ />
118
+ {#if place}
119
+ <dl>
120
+ <dt>Name:</dt>
121
+ <dd>{place.place_name}</dd>
122
+ <dt>Geolocation:</dt>
123
+ <dd>lat: {place.geometry.coordinates[1]}, lng: {place.geometry.coordinates[0]}</dd>
124
+ </dl>
125
+ {/if}
78
126
  </form>
79
127
  </div>
80
128
  <div class="section-txt" id="map">
@@ -82,14 +130,14 @@
82
130
  <Map
83
131
  bind:this={mapComponent}
84
132
  accessToken={PUBLIC_MAPBOX_TOKEN}
85
- on:recentre={recentre}
86
- on:drag={drag}
133
+ onrecentre={recentre}
134
+ ondrag={drag}
87
135
  {center}
88
136
  bind:zoom
89
137
  >
90
138
  <Earthquakes />
91
139
  <NavigationControl />
92
- <GeolocateControl on:geolocate={e => console.log('geolocated', e.detail)} />
140
+ <GeolocateControl ongeolocate={({ result }) => console.log('geolocated', result)} />
93
141
  <Marker lat={marker.lat} lng={marker.lng} />
94
142
  </Map>
95
143
  </div>
@@ -117,7 +165,7 @@
117
165
  <div class="container">
118
166
  <div class="row">
119
167
  <div class="col-lg-12 center">
120
- © 2019 Beyonk. All rights reserved.
168
+ © 2026 Beyonk. All rights reserved.
121
169
  </div>
122
170
  </div>
123
171
  </div>
@@ -157,55 +205,4 @@
157
205
  color: #fff;
158
206
  background: #ee8a65;
159
207
  }
160
-
161
208
  </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,5 +1,9 @@
1
1
 
2
2
  /** @type {import('@sveltejs/kit').Config} */
3
- const config = {}
3
+ const config = {
4
+ vitePlugin: {
5
+ inspector: true
6
+ }
7
+ }
4
8
 
5
9
  export default config
@@ -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,24 +0,0 @@
1
- function bindEvents (el, handlers, mapbox, node) {
2
- const unbindings = []
3
-
4
- for (const [ handler, fn ] of Object.entries(handlers)) {
5
- const cmd = ev => {
6
- const [ eventName, detail ] = fn(el, ev, mapbox)
7
- node.dispatchEvent(
8
- new CustomEvent(eventName, { detail })
9
- )
10
- }
11
- el.on(handler, cmd)
12
- unbindings.push([ handler, cmd ])
13
- }
14
-
15
- return () => {
16
- for (const [ handler, cmd ] of unbindings) {
17
- el.off(handler, cmd)
18
- }
19
- }
20
- }
21
-
22
- export {
23
- bindEvents
24
- }
@@ -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
- }