Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 83 additions & 14 deletions src/components/VMapbox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<script>
import mapboxgl from 'mapbox-gl'
import { propsBinder, propsDefaults } from '../utils/propsBinder.js'
import { uniqueInArrayById } from '../utils/helpers'
import { bindMapEvents } from '../utils/eventsBinder.js'

const MAP_EVENTS = [
Expand Down Expand Up @@ -146,31 +147,88 @@ export const props = {
export default {
name: 'v-mapbox',

// @QUESTION :: Why is this back? Now the whole map is reactive again..
data () {
return {
map: null
map: null, // @QUESTION :: Why is this back? Now the whole map is reactive again..
mapInitialized: false,
mapLayers: []
}
},

props,

provide () {
// Allows us to use inject: ['getMap'] in child components
return {
getMap: () => this.map
getMap: () => this.map,
getMapMethods: () => ({
addLayer: this.addLayerToArray,
removeLayer: this.removeLayerFromArray,
updateLayer: this.updateLayer
}),
}
},

methods: {
addLayers () {
// @TODO :: consider sorting or using slots if we run into render order problems
// let [...children] = this.$children
// children.sort(child => {
// return child.key
// })
addLayerToArray(layerOptions, beforeId) {
// If we get a `beforeId`, we insert layer at that index
if(beforeId && beforeId !== layerOptions.id) {
const existingLayerIndex = this.mapLayers.findIndex(({ id }) => id === beforeId)
if(existingLayerIndex !== -1) {
this.mapLayers = Object.freeze([
...this.mapLayers.slice(0, existingLayerIndex),
layerOptions,
...this.mapLayers.slice(existingLayerIndex),
])
return
}
}
// Otherwise we just add it at the tail
this.mapLayers = Object.freeze([ ...this.mapLayers, layerOptions ])
},

addLayerToMap(layerOptions) {
// We use the position in the array to predictably match
// the layer order in the mapbox instance
const thisIndex = this.mapLayers.findIndex(({ id }) => id === layerOptions.id)
const nextItem = this.mapLayers[thisIndex + 1]
const maybeBefore = nextItem ? nextItem.id : undefined
const existingLayer = maybeBefore && this.map.getLayer(maybeBefore)
const before = existingLayer ? maybeBefore : undefined
this.map.addLayer(layerOptions, before)
},

removeLayerFromArray(layerId) {
this.mapLayers = this.mapLayers.filter(({ id }) => id !== layerId)
},

removeLayerFromMap(layerId) {
if(!this.map) return
const layer = this.map.getLayer(layerId)
if(!layer) return
const layerSource = layer.source
this.map.removeLayer(layerId)
if(layerSource && !this.map.getStyle().layers.some(({ source }) => source === layerSource)) {
this.map.removeSource(layerSource)
}
},

updateLayer(newLayerOptions) {
const index = this.mapLayers.findIndex(({ id }) => id === newLayerOptions.id)
if(index === -1) return
this.mapLayers = Object.freeze(
Object.assign([], this.mapLayers, { [index]: newLayerOptions })
)
this.removeLayerFromMap(newLayerOptions.id)
this.addLayerToMap(newLayerOptions)
},

addAllChildren () {
// Add layers
this.mapLayers.forEach(this.addLayerToMap)
// Add other children
this.$children.forEach(child => {
child.deferredMountedTo(this.map)
if(child.deferredMountedTo)
child.deferredMountedTo(this.map)
})
},

Expand Down Expand Up @@ -205,13 +263,14 @@ export default {

// Once the map is loaded, add all layers that were present during mount time
this.$on('mb-load', () => {
this.addLayers()
this.mapInitialized = true
this.addAllChildren()
})

// If the style was changed, wait for the styledata to be loaded and re-add all the layers
this.$on('style:update', () => {
this.$once('mb-styledata', () => {
this.addLayers()
this.addAllChildren()
})
})

Expand All @@ -221,6 +280,16 @@ export default {
let observer = new ResizeObserver(this.resize)
observer.observe(this.$el)
this.resizeObserver = observer
}
},

watch: {
mapLayers(newArr, oldArr) {
if(!this.mapInitialized) return
const addedLayers = uniqueInArrayById(newArr, oldArr)
const removedLayers = uniqueInArrayById(oldArr, newArr)
addedLayers.forEach(this.addLayerToMap)
removedLayers.forEach(({ id }) => { this.removeLayerFromMap(id) })
}
},
}
</script>
57 changes: 15 additions & 42 deletions src/components/v-mapbox-layer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export default {
name: 'v-mapbox-layer',

inject: ['getMap'],
inject: [ 'getMapMethods' ],

render: () => null,

Expand All @@ -10,71 +10,44 @@ export default {
type: Object,
default: () => ({})
},

// Allows to place a layer before another
before: {
type: String,
default: undefined
}
},

data: () => ({
isInitialized: false
}),

methods: {
deferredMountedTo() {
if(!this.isInitialized) {
this.renderLayer();
this.isInitialized = true;
}
},

renderLayer() {
this.removeLayer();
this.addLayer();
},

addLayer() {
const map = this.getMap();
map.addLayer(this.options, this.before);
const mapMethods = this.getMapMethods()
mapMethods.addLayer(this.options, this.before)
},

removeLayer() {
const map = this.getMap();
if(map) {
const layerId = this.options.id;
const layer = map.getLayer(layerId);
if(layer) {
const layerSource = layer.source;
map.removeLayer(layerId);
if(layerSource && !map.getStyle().layers.some(({ source }) => source === layerSource)) {
map.removeSource(layerSource);
}
}
}
const mapMethods = this.getMapMethods()
const layerId = this.options.id
mapMethods.removeLayer(layerId)
},

updateLayer() {
const mapMethods = this.getMapMethods()
mapMethods.updateLayer(this.options)
},
},

mounted() {
const map = this.getMap();
// We can immediately initialize if we have the map ready
if(map && map.isStyleLoaded()) {
this.renderLayer();
this.isInitialized = true;
}
this.addLayer()
},

destroyed() {
this.removeLayer();
this.removeLayer()
},

watch: {
options: {
deep: true,
handler() {
this.renderLayer();
this.updateLayer()
}
}
}
};
}
4 changes: 2 additions & 2 deletions src/stories/index.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,10 @@ const sortingTemplate = `
style="height: 300px;"
:center="[0, 0]"
>
<!-- green (we want this on top) -->
<v-mapbox-layer :options="layerA"></v-mapbox-layer>
<!-- red -->
<v-mapbox-layer :options="layerB"></v-mapbox-layer>
<!-- green (we want this on top) -->
<v-mapbox-layer :options="layerA" before="b"></v-mapbox-layer>
</v-mapbox>
`

Expand Down
1 change: 1 addition & 0 deletions src/utils/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const uniqueInArrayById = (arr1, arr2) => arr1.filter(({ id }) => !arr2.some(el => el.id === id))