Skip to content

Commit 95fe4b7

Browse files
committed
wip: Provide a selection manager component (#1098)
1 parent 84ab15f commit 95fe4b7

File tree

3 files changed

+78
-17
lines changed

3 files changed

+78
-17
lines changed

core/client/composables/selection.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import _ from 'lodash'
2+
import { onBeforeUnmount } from 'vue'
23
import { useStore } from './store.js'
34

45
export function useSelection (name, options = {}) {
@@ -8,7 +9,7 @@ export function useSelection (name, options = {}) {
89
// selection store
910
const { store, set, get, has } = useStore(`selections.${name}`)
1011

11-
// functions
12+
// Functions
1213
// Single selection will rely on the lastly selected item only
1314
// Multiple selection mode will rely on all items
1415
function clearSelection () {
@@ -78,7 +79,13 @@ export function useSelection (name, options = {}) {
7879
set('enabled', true)
7980
}
8081

81-
// expose
82+
// Cleanup on destroy
83+
onBeforeUnmount(() => {
84+
setSelectionEnabled()
85+
clearSelection()
86+
})
87+
88+
// Expose
8289
return {
8390
selection: store,
8491
clearSelection,

map/client/components/selection/KSelectedLayerFeatures.vue

+7-5
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ function isFeaturePropertiesNode (node) {
181181
}
182182
function getIcon (node) {
183183
if (isLayerNode(node)) return (editedFeatures.value.length > 0 ? 'las la-edit' : '')
184-
if (isFeatureNode(node)) return (editedFeatures.value.contains(node) ? 'las la-edit' : '')
184+
if (isFeatureNode(node)) return (editedFeatures.value.contains(getFeatureId(node, props.item.layer)) ? 'las la-edit' : '')
185185
if (isFeaturePropertiesNode(node)) return 'las la-address-card'
186186
return ''
187187
}
@@ -200,8 +200,9 @@ function zoomToSelectedFeature (feature) {
200200
function editSelectedFeatures () {
201201
// Zoom to then edit
202202
zoomToSelectedFeatures()
203+
editedFeatures.value = props.item.features.map(feature => getFeatureId(feature, props.item.layer))
203204
CurrentActivity.value.startEditLayer(props.item.layer, {
204-
features: props.item.features.map(feature => getFeatureId(feature, props.item.layer)),
205+
features: editedFeatures.value,
205206
editMode: 'edit-geometry',
206207
allowedEditModes: [
207208
'edit-properties',
@@ -210,15 +211,16 @@ function editSelectedFeatures () {
210211
'rotate'
211212
],
212213
callback: (event) => {
213-
editedFeatures.value = (event.status === 'edit-start' ? props.item.features : [])
214+
if (event.status === 'accept') editedFeatures.value = []
214215
}
215216
})
216217
}
217218
function editSelectedFeature (feature) {
218219
// Zoom to then edit
219220
zoomToSelectedFeature(feature)
221+
editedFeatures.value = [getFeatureId(feature, props.item.layer)]
220222
CurrentActivity.value.startEditLayer(props.item.layer, {
221-
features: [getFeatureId(feature, props.item.layer)],
223+
features: editedFeatures.value,
222224
editMode: 'edit-geometry',
223225
allowedEditModes: [
224226
'edit-properties',
@@ -227,7 +229,7 @@ function editSelectedFeature (feature) {
227229
'rotate'
228230
],
229231
callback: (event) => {
230-
editedFeatures.value = (event.status === 'edit-start' ? [feature] : [])
232+
if (event.status === 'accept') editedFeatures.value = []
231233
}
232234
})
233235
}

map/client/composables/selection.js

+62-10
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import circle from '@turf/circle'
77
import bboxPolygon from '@turf/bbox-polygon'
88
import intersects from '@turf/boolean-intersects'
99
import { featureEach } from '@turf/meta'
10-
import { unref } from 'vue'
10+
import { unref, onBeforeMount, onBeforeUnmount } from 'vue'
1111
import * as composables from '../../../core/client/composables/index.js'
12-
import { getFeatureId } from '../utils.js'
12+
import * as utils from '../utils.js'
1313
import { convertPolygonStyleToLeafletPath } from '../leaflet/utils/index.js'
1414

1515
export function useSelection (name, options = {}) {
16+
// Data
17+
let layerServiceEventListeners = {}
1618
// Retrieve core selection
1719
const selection = composables.useSelection(name, options)
1820
// Selection store, as we store options inside check if already initialized
@@ -29,8 +31,8 @@ export function useSelection (name, options = {}) {
2931
if (layer1 !== layer2) return false
3032
// Then compare features
3133
if (item1.feature && item2.feature) {
32-
const id1 = getFeatureId(item1.feature, item1.layer)
33-
const id2 = getFeatureId(item2.feature, item2.layer)
34+
const id1 = utils.getFeatureId(item1.feature, item1.layer)
35+
const id2 = utils.getFeatureId(item2.feature, item2.layer)
3436
return id1 === id2
3537
} else {
3638
// If only one has a feature then it cannot be the same
@@ -70,13 +72,17 @@ export function useSelection (name, options = {}) {
7072
activity.$engineEvents.off('click', onClicked)
7173
if (options.boxSelection) activity.$engineEvents.off('boxselectionend', onBoxSelection)
7274
if (options.clusterSelection) activity.$engineEvents.off('spiderfied', onClusterSelection)
75+
activity.$engineEvents.off('layer-added', listenToFeaturesServiceEventsForLayer)
76+
activity.$engineEvents.off('layer-removed', unlistenToFeaturesServiceEventsForLayer)
7377
activity.$engineEvents.off('layer-hidden', onSelectedLayerHidden)
7478
}
7579
activity = newActivity
7680
if (newActivity && newActivity.$engineEvents) {
7781
newActivity.$engineEvents.on('click', onClicked)
7882
if (options.boxSelection) newActivity.$engineEvents.on('boxselectionend', onBoxSelection)
7983
if (options.clusterSelection) newActivity.$engineEvents.on('spiderfied', onClusterSelection)
84+
newActivity.$engineEvents.on('layer-added', listenToFeaturesServiceEventsForLayer)
85+
newActivity.$engineEvents.on('layer-removed', unlistenToFeaturesServiceEventsForLayer)
8086
newActivity.$engineEvents.on('layer-hidden', onSelectedLayerHidden)
8187
}
8288
}
@@ -143,17 +149,20 @@ export function useSelection (name, options = {}) {
143149
function getSelectedLocation () {
144150
return selection.getSelectedItem().location
145151
}
146-
function isFeatureSelected (feature, layer) {
152+
function findSelectedFeature (feature, layer) {
147153
const items = selection.getSelectedItems()
148154
for (let i = 0; i < items.length; i++) {
149155
const item = items[i]
150156
if (item.feature && item.layer && (item.layer.name === layer.name)) {
151-
const selectedId = getFeatureId(item.feature, item.layer)
152-
const featureId = getFeatureId(feature, layer)
153-
if (featureId === selectedId) return true
157+
const selectedId = utils.getFeatureId(item.feature, item.layer)
158+
const featureId = utils.getFeatureId(feature, layer)
159+
if (featureId === selectedId) return item
154160
}
155161
}
156-
return false
162+
return null
163+
}
164+
function isFeatureSelected (feature, layer) {
165+
return findSelectedFeature(feature, layer) !== null
157166
}
158167
function getWidgetForSelection () {
159168
let widget
@@ -341,8 +350,51 @@ export function useSelection (name, options = {}) {
341350
const hiddenFeatures = selection.getSelectedItems().filter(item => layer.name === _.get(item, 'layer.name'))
342351
hiddenFeatures.forEach((item) => selection.unselectItem(item))
343352
}
353+
function listenToFeaturesServiceEventsForLayer (layer) {
354+
const listeners = utils.listenToFeaturesServiceEventsForLayer(layer, {
355+
all: onFeatureUpdated, removed: onFeatureRemoved
356+
}, layerServiceEventListeners[layer._id])
357+
if (listeners) layerServiceEventListeners[layer._id] = listeners
358+
}
359+
function unlistenToFeaturesServiceEventsForLayer (layer) {
360+
utils.unlistenToFeaturesServiceEventsForLayer(layer, layerServiceEventListeners[layer._id])
361+
delete layerServiceEventListeners[layer._id]
362+
}
363+
function listenToFeaturesServiceEventsForLayers () {
364+
layerServiceEventListeners = {}
365+
_.forEach(activity.getLayers(), listenToFeaturesServiceEventsForLayer)
366+
}
367+
function unlistenToFeaturesServiceEventsForLayers () {
368+
_.forOwn(layerServiceEventListeners, unlistenToFeaturesServiceEventsForLayer)
369+
layerServiceEventListeners = {}
370+
}
371+
function onFeatureUpdated (feature, layer) {
372+
// Find related layer, either directly given in feature if coming from user-defined features service
373+
// otherwise bound to the listener for features services attached to a built-in layer
374+
if (!layer && feature.layer) layer = activity.getLayerById(feature.layer)
375+
if (!layer) return
376+
const item = findSelectedFeature(feature, layer)
377+
if (item) Object.assign(item.feature, feature)
378+
}
379+
function onFeatureRemoved (feature, layer) {
380+
// Find related layer, either directly given in feature if coming from user-defined features service
381+
// otherwise bound to the listener for features services attached to a built-in layer
382+
if (!layer && feature.layer) layer = activity.getLayerById(feature.layer)
383+
if (!layer) return
384+
const item = findSelectedFeature(feature, layer)
385+
if (item) selection.unselectItem(item)
386+
}
344387

345-
// expose
388+
// Here we need to listen to service events for all realtime layers
389+
onBeforeMount(() => {
390+
listenToFeaturesServiceEventsForLayers()
391+
})
392+
// Cleanup on destroy
393+
onBeforeUnmount(() => {
394+
unlistenToFeaturesServiceEventsForLayers()
395+
})
396+
397+
// Expose
346398
return {
347399
...selection,
348400
getSelectionOptions: () => get('options'),

0 commit comments

Comments
 (0)