diff --git a/packages/ui/organisms/MapboxMap/MapboxMap.ts b/packages/ui/organisms/MapboxMap/MapboxMap.ts new file mode 100644 index 00000000..6800c9c0 --- /dev/null +++ b/packages/ui/organisms/MapboxMap/MapboxMap.ts @@ -0,0 +1,76 @@ +import { Base, BaseProps } from '@studiometa/js-toolkit'; +import mapbox from 'mapbox-gl'; +import { MapboxMarker } from './MapboxMarker.js'; +import { MapboxPopup } from './MapboxPopup.js'; + +export interface MapboxMapProps extends BaseProps { + $children: { + MapboxMarker: MapboxMarker[]; + }; + $options: { + accessToken: string; + zoom: number; + center: [number, number]; + }; +} + +export class MapboxMap extends Base { + static config = { + name: 'MapboxMap', + emits: ['map-load'], + options: { + accessToken: String, + zoom: Number, + center: { type: Array, default: () => [0, 0] }, + }, + components: { + MapboxMarker: (mapboxMap) => { + return new Promise((resolve) => { + if (mapboxMap.isLoaded) { + resolve(MapboxMarker); + } else { + mapboxMap.$on('map-load', () => { + resolve(MapboxMarker); + }); + } + }); + }, + MapboxPopup: (mapboxMap) => { + return new Promise((resolve) => { + if (mapboxMap.isLoaded) { + resolve(MapboxPopup); + } else { + mapboxMap.$on('map-load', () => { + resolve(MapboxPopup); + }); + } + }); + }, + }, + }; + + map: mapbox.Map | null = null; + + isLoaded = false; + + get mapboxOptions() { + return { + container: this.$el, + center: this.$options.center, + accessToken: this.$options.accessToken, + zoom: this.$options.zoom, + }; + } + + async mounted() { + this.map = new mapbox.Map(this.mapboxOptions); + this.map.on('load', () => { + this.isLoaded = true; + this.$emit('map-load'); + }); + } + + destroyed() { + this.map?.remove(); + } +} diff --git a/packages/ui/organisms/MapboxMap/MapboxMarker.ts b/packages/ui/organisms/MapboxMap/MapboxMarker.ts new file mode 100644 index 00000000..73df3a05 --- /dev/null +++ b/packages/ui/organisms/MapboxMap/MapboxMarker.ts @@ -0,0 +1,41 @@ +import { Base, getClosestParent } from '@studiometa/js-toolkit'; +import mapbox from 'mapbox-gl'; +import { MapboxMap } from './MapboxMap.js'; + +export class MapboxMarker extends Base { + static config = { + name: 'MapboxMarker', + options: { + lng: Number, + lat: Number, + // Marker options. (https://docs.mapbox.com/mapbox-gl-js/api/markers#marker) + markerOptions: Object, + }, + }; + + marker: mapbox.Marker | null = null; + + get map() { + return getClosestParent(this, MapboxMap)?.map; + } + + get markerOptions() { + if (this.$options.markerOptions) { + return this.$options.markerOptions; + } + + return {}; + } + + get lngLat() { + return [this.$options.lng, this.$options.lat]; + } + + mounted() { + this.marker = new mapbox.Marker(this.markerOptions).setLngLat(this.lngLat).addTo(this.map); + } + + destroyed() { + this.marker?.remove(); + } +} diff --git a/packages/ui/organisms/MapboxMap/MapboxPopup.ts b/packages/ui/organisms/MapboxMap/MapboxPopup.ts new file mode 100644 index 00000000..17fe8a31 --- /dev/null +++ b/packages/ui/organisms/MapboxMap/MapboxPopup.ts @@ -0,0 +1,45 @@ +import { Base, getClosestParent } from '@studiometa/js-toolkit'; +import mapbox from 'mapbox-gl'; +import { MapboxMap } from './MapboxMap.js'; + +export class MapboxPopup extends Base { + static config = { + name: 'MapboxPopup', + options: { + lng: Number, + lat: Number, + // Marker options. (https://docs.mapbox.com/mapbox-gl-js/api/markers#popup) + popupOptions: Object, + }, + }; + + popup: mapbox.Popup | null = null; + + get map() { + return getClosestParent(this, MapboxMap)?.map; + } + + get popupOptions() { + if (this.$options.markerOptions) { + return this.$options.markerOptions; + } + + return {}; + } + + get lngLat() { + return [this.$options.lng, this.$options.lat]; + } + + mounted() { + this.popup = new mapbox.Popup() + .setLngLat(this.lngLat) + .setHTML('

Hello World!

') + .setMaxWidth('300px') + .addTo(this.map); + } + + destroyed() { + this.popup?.remove(); + } +} diff --git a/packages/ui/organisms/MapboxMap/index.ts b/packages/ui/organisms/MapboxMap/index.ts new file mode 100644 index 00000000..8be253a9 --- /dev/null +++ b/packages/ui/organisms/MapboxMap/index.ts @@ -0,0 +1,3 @@ +export * from './MapboxMap.js'; +export * from './MapboxMarker.js'; +export * from './MapboxPopup.js'; diff --git a/packages/ui/organisms/MapboxMap/package.json b/packages/ui/organisms/MapboxMap/package.json new file mode 100644 index 00000000..39d95024 --- /dev/null +++ b/packages/ui/organisms/MapboxMap/package.json @@ -0,0 +1,5 @@ +{ + "name": "@studiometa/ui-mapbox-map", + "type": "module", + "version": "0.0.1" +} diff --git a/packages/ui/organisms/index.ts b/packages/ui/organisms/index.ts index 6cc28cb0..d8b81ac1 100644 --- a/packages/ui/organisms/index.ts +++ b/packages/ui/organisms/index.ts @@ -1 +1,2 @@ export * from './Frame/index.js'; +export * from './MapboxMap/index.js'; diff --git a/packages/ui/package.json b/packages/ui/package.json index 90706dd2..23d8363f 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -30,6 +30,8 @@ "homepage": "https://github.com/studiometa/ui#readme", "dependencies": { "@studiometa/js-toolkit": "^2.10.2", - "deepmerge": "^4.2.2" + "@types/mapbox-gl": "^3.1.0", + "deepmerge": "^4.2.2", + "mapbox-gl": "^3.2.0" } }