@defra/interactive-map 0.0.37-alpha → 0.0.39-alpha
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/assets/templates/draw-tools.njk +4 -3
- package/dist/css/index.css +1 -1
- package/dist/esm/im-core.js +1 -1
- package/dist/esm/im-shell.js +1 -1
- package/dist/umd/im-core.js +1 -1
- package/dist/umd/index.js +1 -1
- package/docs/api.md +32 -0
- package/docs/examples/draw-tools.mdx +0 -4
- package/docs/plugins/draw.md +482 -0
- package/docs/plugins.md +2 -6
- package/govuk-prototype-kit.config.json +3 -2
- package/package.json +2 -4
- package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
- package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ol/dist/esm/im-draw-ol-plugin.js +1 -1
- package/plugins/beta/map-styles/dist/css/index.css +1 -97
- package/plugins/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/datasets/dist/umd/im-datasets-plugin.js +1 -1
- package/plugins/datasets/src/registry/dataset.js +3 -1
- package/plugins/datasets/src/registry/datasetRegistry.js +30 -24
- package/plugins/datasets/src/registry/datasetRegistry.test.js +25 -18
- package/plugins/draw/dist/esm/im-draw-ml-adapter.js +1 -0
- package/plugins/draw/dist/esm/im-draw-ol-adapter.js +1 -0
- package/plugins/draw/dist/esm/im-draw-plugin.js +1 -0
- package/plugins/draw/dist/esm/index.js +1 -0
- package/plugins/draw/dist/umd/im-draw-ml-adapter.js +1 -0
- package/plugins/draw/dist/umd/im-draw-ol-adapter.js +2 -0
- package/plugins/draw/dist/umd/im-draw-ol-adapter.js.LICENSE.txt +1 -0
- package/plugins/draw/dist/umd/im-draw-plugin.js +2 -0
- package/plugins/draw/dist/umd/im-draw-plugin.js.LICENSE.txt +1 -0
- package/plugins/draw/dist/umd/index.js +2 -0
- package/plugins/draw/dist/umd/index.js.LICENSE.txt +1 -0
- package/plugins/draw/src/DrawInit.jsx +89 -0
- package/plugins/draw/src/DrawInit.test.jsx +184 -0
- package/plugins/draw/src/adapterEvents.js +127 -0
- package/plugins/draw/src/adapterEvents.test.js +20 -0
- package/plugins/draw/src/adapters/adapterContract.test.js +48 -0
- package/plugins/draw/src/adapters/loadDrawAdapter.js +14 -0
- package/plugins/draw/src/adapters/loadDrawAdapter.test.js +53 -0
- package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.js +367 -0
- package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.test.js +799 -0
- package/plugins/draw/src/adapters/maplibre/defaults.js +1 -0
- package/plugins/draw/src/adapters/maplibre/drawEvents.js +35 -0
- package/plugins/draw/src/adapters/maplibre/mapboxDraw.js +126 -0
- package/plugins/draw/src/adapters/maplibre/mapboxDraw.test.js +268 -0
- package/plugins/draw/src/adapters/maplibre/mapboxSnap.js +39 -0
- package/plugins/draw/src/adapters/maplibre/mapboxSnap.test.js +91 -0
- package/plugins/draw/src/adapters/maplibre/modes/createDrawMode.js +58 -0
- package/plugins/draw/src/adapters/maplibre/modes/createDrawMode.test.js +53 -0
- package/plugins/draw/src/adapters/maplibre/modes/disabledMode.js +23 -0
- package/plugins/draw/src/adapters/maplibre/modes/disabledMode.test.js +21 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawLineMode.js +15 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawLineMode.test.js +37 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/__helpers__/harness.js +115 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/clickHandlers.js +193 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/clickHandlers.test.js +241 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/keyboardHandlers.js +78 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/keyboardHandlers.test.js +75 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.js +78 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.test.js +48 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/pointerHandlers.js +103 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/pointerHandlers.test.js +127 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/renderHelpers.js +59 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/renderHelpers.test.js +34 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/undoHandlers.js +193 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/undoHandlers.test.js +195 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawPolygonMode.js +16 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawPolygonMode.test.js +27 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/__helpers__/harness.js +107 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/geometryHelpers.js +139 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/geometryHelpers.test.js +46 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/helpers.js +2 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/helpers.test.js +17 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.js +151 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.test.js +123 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/pointerHandlers.js +161 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/pointerHandlers.test.js +98 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.js +133 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.test.js +105 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.js +166 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.test.js +133 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.js +210 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.test.js +167 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexQueries.js +121 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexQueries.test.js +73 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.js +249 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.test.js +153 -0
- package/plugins/draw/src/adapters/maplibre/snap/constants.js +2 -0
- package/plugins/draw/src/adapters/maplibre/snap/mapHandlers.js +39 -0
- package/plugins/draw/src/adapters/maplibre/snap/mapHandlers.test.js +106 -0
- package/plugins/draw/src/adapters/maplibre/snap/prototypePatches.js +148 -0
- package/plugins/draw/src/adapters/maplibre/snap/prototypePatches.test.js +238 -0
- package/plugins/draw/src/adapters/maplibre/snap/snapInstance.js +115 -0
- package/plugins/draw/src/adapters/maplibre/snap/snapInstance.test.js +151 -0
- package/plugins/draw/src/adapters/maplibre/snap/sourceData.js +34 -0
- package/plugins/draw/src/adapters/maplibre/snap/sourceData.test.js +62 -0
- package/plugins/draw/src/adapters/maplibre/styles.js +207 -0
- package/plugins/draw/src/adapters/maplibre/styles.test.js +155 -0
- package/plugins/draw/src/adapters/maplibre/utils/snapHelpers.js +202 -0
- package/plugins/draw/src/adapters/maplibre/utils/snapHelpers.test.js +253 -0
- package/plugins/draw/src/adapters/maplibre/utils/touchClickWorkaround.js +57 -0
- package/plugins/draw/src/adapters/maplibre/utils/touchClickWorkaround.test.js +106 -0
- package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.js +117 -0
- package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.test.js +159 -0
- package/plugins/draw/src/adapters/openlayers/__helpers__/harness.js +90 -0
- package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.js +178 -0
- package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.test.js +153 -0
- package/plugins/draw/src/adapters/openlayers/core/featureStore.js +69 -0
- package/plugins/draw/src/adapters/openlayers/core/featureStore.test.js +58 -0
- package/plugins/draw/src/adapters/openlayers/core/internalEvents.js +8 -0
- package/plugins/draw/src/adapters/openlayers/core/styles.js +155 -0
- package/plugins/draw/src/adapters/openlayers/core/styles.test.js +143 -0
- package/plugins/draw/src/adapters/openlayers/defaults.js +1 -0
- package/plugins/draw/src/adapters/openlayers/draw/DrawMode.js +272 -0
- package/plugins/draw/src/adapters/openlayers/draw/DrawMode.test.js +431 -0
- package/plugins/draw/src/adapters/openlayers/draw/drawInput.js +139 -0
- package/plugins/draw/src/adapters/openlayers/draw/drawInput.test.js +140 -0
- package/plugins/draw/src/adapters/openlayers/draw/vertexPlacement.js +144 -0
- package/plugins/draw/src/adapters/openlayers/draw/vertexPlacement.test.js +196 -0
- package/plugins/draw/src/adapters/openlayers/edit/EditMode.js +385 -0
- package/plugins/draw/src/adapters/openlayers/edit/EditMode.test.js +303 -0
- package/plugins/draw/src/adapters/openlayers/edit/activeVertexLayer.js +55 -0
- package/plugins/draw/src/adapters/openlayers/edit/activeVertexLayer.test.js +53 -0
- package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.js +157 -0
- package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.test.js +195 -0
- package/plugins/draw/src/adapters/openlayers/edit/midpointLayer.js +57 -0
- package/plugins/draw/src/adapters/openlayers/edit/midpointLayer.test.js +42 -0
- package/plugins/draw/src/adapters/openlayers/edit/modifyInteraction.js +83 -0
- package/plugins/draw/src/adapters/openlayers/edit/modifyInteraction.test.js +80 -0
- package/plugins/draw/src/adapters/openlayers/edit/nudge.js +118 -0
- package/plugins/draw/src/adapters/openlayers/edit/nudge.test.js +165 -0
- package/plugins/draw/src/adapters/openlayers/edit/pointerHandlers.js +79 -0
- package/plugins/draw/src/adapters/openlayers/edit/pointerHandlers.test.js +78 -0
- package/plugins/draw/src/adapters/openlayers/edit/selectionState.js +116 -0
- package/plugins/draw/src/adapters/openlayers/edit/selectionState.test.js +109 -0
- package/plugins/draw/src/adapters/openlayers/edit/touchHandler.js +184 -0
- package/plugins/draw/src/adapters/openlayers/edit/touchHandler.test.js +133 -0
- package/plugins/draw/src/adapters/openlayers/edit/undoOps.js +94 -0
- package/plugins/draw/src/adapters/openlayers/edit/undoOps.test.js +64 -0
- package/plugins/draw/src/adapters/openlayers/edit/vertexHitTest.js +74 -0
- package/plugins/draw/src/adapters/openlayers/edit/vertexHitTest.test.js +28 -0
- package/plugins/draw/src/adapters/openlayers/edit/vertexLayer.js +49 -0
- package/plugins/draw/src/adapters/openlayers/edit/vertexLayer.test.js +42 -0
- package/plugins/draw/src/adapters/openlayers/edit/vertexOps.js +100 -0
- package/plugins/draw/src/adapters/openlayers/edit/vertexOps.test.js +80 -0
- package/plugins/draw/src/adapters/openlayers/olDraw.js +40 -0
- package/plugins/draw/src/adapters/openlayers/olDraw.test.js +70 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapEngine.js +220 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapEngine.test.js +232 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapGeometry.js +198 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapGeometry.test.js +136 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapIndicator.js +81 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapIndicator.test.js +72 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapInteraction.js +57 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapInteraction.test.js +68 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapManager.js +99 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapManager.test.js +101 -0
- package/plugins/draw/src/adapters/openlayers/utils/geometryHelpers.js +103 -0
- package/plugins/draw/src/adapters/openlayers/utils/geometryHelpers.test.js +76 -0
- package/plugins/draw/src/adapters/openlayers/utils/olCoords.js +35 -0
- package/plugins/draw/src/adapters/openlayers/utils/olCoords.test.js +22 -0
- package/plugins/draw/src/adapters/openlayers/utils/sketchHelpers.js +22 -0
- package/plugins/draw/src/adapters/openlayers/utils/sketchHelpers.test.js +18 -0
- package/plugins/draw/src/adapters/openlayers/utils/touchTarget.js +8 -0
- package/plugins/draw/src/api/addFeature.js +22 -0
- package/plugins/draw/src/api/addFeature.test.js +41 -0
- package/plugins/draw/src/api/deleteFeature.js +11 -0
- package/plugins/draw/src/api/deleteFeature.test.js +24 -0
- package/plugins/draw/src/api/editFeature.js +54 -0
- package/plugins/draw/src/api/editFeature.test.js +134 -0
- package/plugins/draw/src/api/merge.js +30 -0
- package/plugins/draw/src/api/merge.test.js +57 -0
- package/plugins/draw/src/api/newLine.js +41 -0
- package/plugins/draw/src/api/newLine.test.js +93 -0
- package/plugins/draw/src/api/newPolygon.js +41 -0
- package/plugins/draw/src/api/newPolygon.test.js +93 -0
- package/plugins/draw/src/api/split.js +117 -0
- package/plugins/draw/src/api/split.test.js +204 -0
- package/plugins/draw/src/defaults.js +52 -0
- package/plugins/draw/src/defaults.test.js +18 -0
- package/plugins/draw/src/draw.scss +43 -0
- package/plugins/draw/src/events.js +237 -0
- package/plugins/draw/src/events.test.js +369 -0
- package/plugins/draw/src/index.js +10 -0
- package/plugins/draw/src/index.test.js +24 -0
- package/plugins/draw/src/manifest.js +170 -0
- package/plugins/draw/src/manifest.test.js +147 -0
- package/plugins/draw/src/reducer.js +69 -0
- package/plugins/draw/src/reducer.test.js +85 -0
- package/plugins/draw/src/utils/debounce.js +16 -0
- package/plugins/draw/src/utils/debounce.test.js +40 -0
- package/plugins/draw/src/utils/eventBus.js +36 -0
- package/plugins/draw/src/utils/eventBus.test.js +47 -0
- package/plugins/draw/src/utils/flattenStyleProperties.js +25 -0
- package/plugins/draw/src/utils/flattenStyleProperties.test.js +33 -0
- package/plugins/draw/src/utils/getValueForStyle.js +33 -0
- package/plugins/draw/src/utils/getValueForStyle.test.js +32 -0
- package/plugins/draw/src/utils/resolveColors.js +39 -0
- package/plugins/draw/src/utils/resolveColors.test.js +34 -0
- package/plugins/draw/src/utils/spatial.js +243 -0
- package/plugins/draw/src/utils/spatial.test.js +243 -0
- package/plugins/draw/src/utils/touchTarget.js +95 -0
- package/plugins/draw/src/utils/touchTarget.test.js +107 -0
- package/plugins/draw/src/utils/undoStack.js +31 -0
- package/plugins/draw/src/utils/undoStack.test.js +49 -0
- package/plugins/draw/src/validation/liveDrawChecks.js +120 -0
- package/plugins/draw/src/validation/liveDrawChecks.test.js +159 -0
- package/plugins/draw/src/validation/liveStroke.js +85 -0
- package/plugins/draw/src/validation/liveStroke.test.js +150 -0
- package/plugins/draw/src/validation/rules.areaError.test.js +12 -0
- package/plugins/draw/src/validation/rules.js +186 -0
- package/plugins/draw/src/validation/rules.test.js +120 -0
- package/plugins/draw/src/validation/validateGeometry.js +119 -0
- package/plugins/draw/src/validation/validateGeometry.test.js +200 -0
- package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
- package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
- package/plugins/interact/src/hooks/useInteractionHandlers.js +1 -1
- package/plugins/interact/src/hooks/useInteractionHandlers.test.js +1 -1
- package/plugins/interact/src/utils/buildStylesMap.js +1 -0
- package/plugins/search/dist/css/index.css +1 -317
- package/rollup.esm.mjs +35 -28
- package/sonar-project.properties +2 -2
- package/src/App/components/Attributions/Attributions.module.scss +1 -0
- package/src/App/components/Hints/Hints.module.scss +5 -4
- package/src/App/components/KeyboardHelp/KeyboardHelp.jsx +1 -1
- package/src/App/components/KeyboardHelp/KeyboardHelp.test.jsx +11 -0
- package/src/App/components/MoveControl/MoveControl.jsx +73 -9
- package/src/App/components/MoveControl/MoveControl.module.scss +12 -2
- package/src/App/components/MoveControl/MoveControl.test.jsx +136 -0
- package/src/App/hooks/useHintsAPI.js +67 -0
- package/src/App/hooks/useHintsAPI.test.js +145 -0
- package/src/App/renderer/PluginInits.jsx +4 -0
- package/src/App/renderer/PluginInits.test.jsx +7 -1
- package/src/InteractiveMap/InteractiveMap.js +20 -0
- package/src/InteractiveMap/InteractiveMap.test.js +7 -0
- package/src/config/events.js +4 -0
- package/src/scss/settings/_dimensions.scss +3 -0
- package/src/services/announcer.js +38 -3
- package/src/services/announcer.test.js +133 -1
- package/src/services/hints.js +3 -0
- package/src/services/hints.test.js +19 -0
- package/src/types.js +12 -0
- package/src/utils/detectInterfaceType.js +5 -1
- package/src/utils/isMac.js +15 -0
- package/src/utils/isMac.test.js +37 -0
- package/webpack.umd.mjs +1 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import MapboxDraw from '@mapbox/mapbox-gl-draw'
|
|
2
|
+
import { getSnapInstance, clearSnapIndicator } from '../utils/snapHelpers.js'
|
|
3
|
+
import { getCoords } from './editVertexMode/geometryHelpers.js'
|
|
4
|
+
import { scalePoint } from './editVertexMode/helpers.js'
|
|
5
|
+
import { undoHandlers } from './editVertexMode/undoHandlers.js'
|
|
6
|
+
import { touchHandlers } from './editVertexMode/touchHandlers.js'
|
|
7
|
+
import { vertexOperations } from './editVertexMode/vertexOperations.js'
|
|
8
|
+
import { vertexQueries } from './editVertexMode/vertexQueries.js'
|
|
9
|
+
import { keyboardHandlers } from './editVertexMode/keyboardHandlers.js'
|
|
10
|
+
import { pointerHandlers } from './editVertexMode/pointerHandlers.js'
|
|
11
|
+
|
|
12
|
+
const EVENT_VERTEX_SELECTION = 'draw.vertexselection'
|
|
13
|
+
const EVENT_NUDGE_VERTEX = 'draw.nudgevertex'
|
|
14
|
+
|
|
15
|
+
export const EditVertexMode = {
|
|
16
|
+
...MapboxDraw.modes.direct_select,
|
|
17
|
+
...undoHandlers,
|
|
18
|
+
...touchHandlers,
|
|
19
|
+
...vertexOperations,
|
|
20
|
+
...vertexQueries,
|
|
21
|
+
...keyboardHandlers,
|
|
22
|
+
...pointerHandlers,
|
|
23
|
+
|
|
24
|
+
onSetup (options) {
|
|
25
|
+
const state = MapboxDraw.modes.direct_select.onSetup.call(this, options)
|
|
26
|
+
Object.assign(state, {
|
|
27
|
+
container: options.container,
|
|
28
|
+
interfaceType: options.interfaceType,
|
|
29
|
+
deleteVertexButtonId: options.deleteVertexButtonId,
|
|
30
|
+
undoButtonId: options.undoButtonId,
|
|
31
|
+
isPanEnabled: options.isPanEnabled,
|
|
32
|
+
getSnapEnabled: options.getSnapEnabled,
|
|
33
|
+
featureId: state.featureId,
|
|
34
|
+
selectedVertexIndex: options.selectedVertexIndex ?? -1,
|
|
35
|
+
selectedVertexType: options.selectedVertexType,
|
|
36
|
+
coordPath: options.coordPath,
|
|
37
|
+
scale: options.scale ?? 1
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// Clear undo stack only on initial entry to edit mode for a feature
|
|
41
|
+
// Only clear if we're starting a new editing session (not already editing)
|
|
42
|
+
if (this.map._lastEditFeatureId !== state.featureId) {
|
|
43
|
+
this.map._undoStack?.clear()
|
|
44
|
+
this.map._lastEditFeatureId = state.featureId
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Get feature type for later reference
|
|
48
|
+
const feature = this.getFeature(state.featureId)
|
|
49
|
+
state.featureType = feature?.type
|
|
50
|
+
|
|
51
|
+
state.vertecies = this.getVerticies(state.featureId)
|
|
52
|
+
state.midpoints = this.getMidpoints(state.featureId)
|
|
53
|
+
this.setupEventListeners(state)
|
|
54
|
+
|
|
55
|
+
this.applyVertexSelection(state, options)
|
|
56
|
+
this.map._drawEditContainer = options.container
|
|
57
|
+
this.addTouchVertexTarget(state)
|
|
58
|
+
|
|
59
|
+
// Clear any snap indicator when entering edit mode
|
|
60
|
+
const snap = getSnapInstance(this.map)
|
|
61
|
+
if (snap) {
|
|
62
|
+
clearSnapIndicator(snap, this.map)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Show touch target if entering with a selected vertex on touch interface
|
|
66
|
+
if (state.interfaceType === 'touch' && state.selectedVertexIndex >= 0 && state.selectedVertexType === 'vertex') {
|
|
67
|
+
const vertex = state.vertecies[state.selectedVertexIndex]
|
|
68
|
+
if (vertex) {
|
|
69
|
+
setTimeout(() => {
|
|
70
|
+
this.updateTouchVertexTarget(state, scalePoint(this.map.project(vertex), state.scale))
|
|
71
|
+
}, 0)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Ignore pointermove deselection briefly after setup to let Safari settle
|
|
76
|
+
state._ignorePointermoveDeselect = true
|
|
77
|
+
setTimeout(() => { state._ignorePointermoveDeselect = false }, 100)
|
|
78
|
+
|
|
79
|
+
return state
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
setupEventListeners (state) {
|
|
83
|
+
const bind = (fn) => (e) => fn.call(this, state, e)
|
|
84
|
+
const h = this.handlers = {
|
|
85
|
+
keydown: bind(this.onKeydown),
|
|
86
|
+
keyup: bind(this.onKeyup),
|
|
87
|
+
pointerdown: bind(this.onPointerevent),
|
|
88
|
+
pointermove: bind(this.onPointerevent),
|
|
89
|
+
pointerup: bind(this.onPointerevent),
|
|
90
|
+
click: bind(this.onButtonClick),
|
|
91
|
+
touchstart: bind(this.onTouchstart),
|
|
92
|
+
touchmove: bind(this.onTouchmove),
|
|
93
|
+
touchend: bind(this.onTouchend),
|
|
94
|
+
selectionchange: bind(this.onSelectionChange),
|
|
95
|
+
scalechange: bind(this.onScaleChange),
|
|
96
|
+
update: bind(this.onUpdate),
|
|
97
|
+
move: bind(this.onMove),
|
|
98
|
+
interfacetypechange: bind(this.onInterfaceTypeChange),
|
|
99
|
+
nudgevertex: bind(this.onNudgeVertex)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
window.addEventListener('keydown', h.keydown, { capture: true })
|
|
103
|
+
window.addEventListener('keyup', h.keyup, { capture: true })
|
|
104
|
+
window.addEventListener('click', h.click)
|
|
105
|
+
state.container.addEventListener('pointerdown', h.pointerdown)
|
|
106
|
+
state.container.addEventListener('pointermove', h.pointermove)
|
|
107
|
+
state.container.addEventListener('pointerup', h.pointerup)
|
|
108
|
+
state.container.addEventListener('touchstart', h.touchstart, { passive: false })
|
|
109
|
+
state.container.addEventListener('touchmove', h.touchmove, { passive: false })
|
|
110
|
+
state.container.addEventListener('touchend', h.touchend, { passive: false })
|
|
111
|
+
this.map.on('draw.selectionchange', h.selectionchange)
|
|
112
|
+
this.map.on('draw.scalechange', h.scalechange)
|
|
113
|
+
this.map.on('draw.update', h.update)
|
|
114
|
+
this.map.on('move', h.move)
|
|
115
|
+
this.map.on('draw.interfacetypechange', h.interfacetypechange)
|
|
116
|
+
this.map.on(EVENT_NUDGE_VERTEX, h.nudgevertex)
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
applyVertexSelection (state, options) {
|
|
120
|
+
if (options.selectedVertexType === 'midpoint') {
|
|
121
|
+
state.selectedCoordPaths = []
|
|
122
|
+
this.clearSelectedCoordinates()
|
|
123
|
+
state.feature.changed()
|
|
124
|
+
this._ctx.store.render()
|
|
125
|
+
this.updateMidpoint(state.midpoints[options.selectedVertexIndex - state.vertecies.length])
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
if (options.selectedVertexIndex === -1) {
|
|
129
|
+
state.selectedCoordPaths = []
|
|
130
|
+
this.clearSelectedCoordinates()
|
|
131
|
+
state.feature.changed()
|
|
132
|
+
this._ctx.store.render()
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
onSelectionChange (state, e) {
|
|
137
|
+
// Refresh vertex list so numVertecies reflects the latest geometry (e.g. after midpoint insertion)
|
|
138
|
+
this.syncVertices(state)
|
|
139
|
+
|
|
140
|
+
const vertexCoord = e.points[e.points.length - 1]?.geometry.coordinates
|
|
141
|
+
|
|
142
|
+
// Only update selectedVertexIndex from event if not keyboard mode AND event has valid vertex
|
|
143
|
+
// For keyboard mode or when we have coordPath, trust the existing selectedVertexIndex
|
|
144
|
+
if (state.interfaceType !== 'keyboard' && vertexCoord && !state.coordPath) {
|
|
145
|
+
// No coordPath available - need to search for vertex by coordinates
|
|
146
|
+
const geom = e.features[0]?.geometry
|
|
147
|
+
const coords = getCoords(geom)
|
|
148
|
+
state.selectedVertexIndex = this.findVertexIndex(coords, vertexCoord, state.selectedVertexIndex)
|
|
149
|
+
}
|
|
150
|
+
// If we have coordPath, selectedVertexIndex is already correct from onTap/changeMode
|
|
151
|
+
|
|
152
|
+
state.selectedVertexType ??= state.selectedVertexIndex >= 0 ? 'vertex' : null
|
|
153
|
+
|
|
154
|
+
this.map.fire(EVENT_VERTEX_SELECTION, {
|
|
155
|
+
index: state.selectedVertexType === 'vertex' ? state.selectedVertexIndex : -1,
|
|
156
|
+
numVertecies: state.vertecies.length
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
// Use vertex from event if available, otherwise fall back to state
|
|
160
|
+
const vertex = vertexCoord || (state.selectedVertexIndex >= 0 ? state.vertecies[state.selectedVertexIndex] : null)
|
|
161
|
+
this.updateTouchVertexTarget(state, vertex ? scalePoint(this.map.project(vertex), state.scale) : null)
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
onScaleChange (state, e) {
|
|
165
|
+
state.scale = e.scale
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
onInterfaceTypeChange (state, e) {
|
|
169
|
+
state.interfaceType = e.interfaceType
|
|
170
|
+
const vertex = state.selectedVertexIndex >= 0 ? state.vertecies[state.selectedVertexIndex] : null
|
|
171
|
+
this.updateTouchVertexTarget(state, vertex ? scalePoint(this.map.project(vertex), state.scale) : null)
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
onUpdate (state) {
|
|
175
|
+
const prev = new Set(state.vertecies.map(c => JSON.stringify(c)))
|
|
176
|
+
if (prev.size === state.vertecies.length) {
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
// Duplicate coordinates exist (e.g. a self-touching ring). Comparing the list
|
|
180
|
+
// against itself cannot surface a distinct new vertex, so clear the selection.
|
|
181
|
+
state.selectedVertexIndex = -1
|
|
182
|
+
state.selectedVertexType ??= null
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
onMove (state) {
|
|
186
|
+
const vertex = state.vertecies[state.selectedVertexIndex]
|
|
187
|
+
if (vertex) {
|
|
188
|
+
this.updateTouchVertexTarget(state, scalePoint(this.map.project(vertex), state.scale))
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
// Inbound signal from MaplibreDrawAdapter.nudgeSelectedVertex — bridges into the
|
|
193
|
+
// running mode the same way onInterfaceTypeChange does, since the adapter has no
|
|
194
|
+
// direct reference to this mode's live state. Unlike OL (where setState's shared
|
|
195
|
+
// onUpdate hook repositions the touch target for any vertex move), moveVertex
|
|
196
|
+
// here has no equivalent hook, so the reposition has to happen explicitly —
|
|
197
|
+
// same as onMove/onSelectionChange/onInterfaceTypeChange already do.
|
|
198
|
+
onNudgeVertex (state, e) {
|
|
199
|
+
this.nudgeVertexByDelta(state, e.dx, e.dy, e.isLargeStep)
|
|
200
|
+
const vertex = state.vertecies[state.selectedVertexIndex]
|
|
201
|
+
if (vertex) {
|
|
202
|
+
this.updateTouchVertexTarget(state, scalePoint(this.map.project(vertex), state.scale))
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
onButtonClick (state, e) {
|
|
207
|
+
if (e.target.closest(`#${state.deleteVertexButtonId}`) && state.selectedVertexType === 'vertex') {
|
|
208
|
+
this.deleteVertex(state)
|
|
209
|
+
}
|
|
210
|
+
if (e.target.closest(`#${state.undoButtonId}`)) {
|
|
211
|
+
this.handleUndo(state)
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
clickNoTarget (state) {
|
|
216
|
+
this.changeMode(state, { selectedVertexIndex: -1, selectedVertexType: null, isPanEnabled: true })
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
// Prevent selecting other features
|
|
220
|
+
changeMode (state, updates) {
|
|
221
|
+
if (!state.featureId) {
|
|
222
|
+
return
|
|
223
|
+
}
|
|
224
|
+
this._ctx.api.changeMode('edit_vertex', { ...state, ...updates })
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
onStop (state) {
|
|
228
|
+
this.map._drawEditContainer = null
|
|
229
|
+
this.map._editingFeatureId = null
|
|
230
|
+
const h = this.handlers
|
|
231
|
+
state.container.removeEventListener('pointerdown', h.pointerdown)
|
|
232
|
+
state.container.removeEventListener('pointermove', h.pointermove)
|
|
233
|
+
state.container.removeEventListener('pointerup', h.pointerup)
|
|
234
|
+
state.container.removeEventListener('touchstart', h.touchstart)
|
|
235
|
+
state.container.removeEventListener('touchmove', h.touchmove)
|
|
236
|
+
state.container.removeEventListener('touchend', h.touchend)
|
|
237
|
+
this.map.off('draw.selectionchange', h.selectionchange)
|
|
238
|
+
this.map.off('draw.scalechange', h.scalechange)
|
|
239
|
+
this.map.off('draw.update', h.update)
|
|
240
|
+
this.map.off('move', h.move)
|
|
241
|
+
this.map.off('draw.interfacetypechange', h.interfacetypechange)
|
|
242
|
+
this.map.off(EVENT_NUDGE_VERTEX, h.nudgevertex)
|
|
243
|
+
this.map.dragPan.enable()
|
|
244
|
+
window.removeEventListener('click', h.click)
|
|
245
|
+
window.removeEventListener('keydown', h.keydown, { capture: true })
|
|
246
|
+
window.removeEventListener('keyup', h.keyup, { capture: true })
|
|
247
|
+
this.hideTouchVertexIndicator(state)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { createHarness, POLYGON } from './editVertexMode/__helpers__/harness.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tests for EditVertexMode's own methods: setup/teardown lifecycle, selection/scale/update
|
|
5
|
+
* events, and the small move/button/changeMode routing. The keyboard, pointer, touch, undo,
|
|
6
|
+
* vertex-query, vertex-operation and geometry helpers are covered in their own colocated
|
|
7
|
+
* test files under editVertexMode/.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
describe('onSetup / onStop lifecycle', () => {
|
|
11
|
+
test('populates state, clears the undo stack for a new feature and registers listeners', () => {
|
|
12
|
+
const { ctx, state, map } = createHarness()
|
|
13
|
+
expect(state.featureId).toBe('feat-1')
|
|
14
|
+
// Real PolygonFeature stores rings without the duplicate closing coordinate
|
|
15
|
+
expect(state.vertecies).toHaveLength(4)
|
|
16
|
+
expect(state.midpoints).toHaveLength(4)
|
|
17
|
+
expect(map._lastEditFeatureId).toBe('feat-1')
|
|
18
|
+
expect(map._drawEditContainer).toBe(state.container)
|
|
19
|
+
expect(ctx.map.on).toHaveBeenCalledWith('draw.update', expect.any(Function))
|
|
20
|
+
expect(ctx.map.on).toHaveBeenCalledWith('draw.nudgevertex', expect.any(Function))
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('does not clear the undo stack when re-entering the same feature', () => {
|
|
24
|
+
const { ctx, map, options } = createHarness()
|
|
25
|
+
map._undoStack.push({ type: 'move_vertex' })
|
|
26
|
+
// map._lastEditFeatureId is already 'feat-1' from the first setup
|
|
27
|
+
ctx.onSetup(options)
|
|
28
|
+
expect(map._undoStack.length).toBe(1)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('onStop removes listeners and clears editing container; DirectSelect touch stubs are inert', () => {
|
|
32
|
+
const { ctx, state, map } = createHarness()
|
|
33
|
+
expect(() => { ctx.onTouchStart(); ctx.onTouchMove(); ctx.onTouchEnd() }).not.toThrow()
|
|
34
|
+
ctx.onStop(state)
|
|
35
|
+
expect(map._drawEditContainer).toBeNull()
|
|
36
|
+
expect(map.off).toHaveBeenCalledWith('draw.update', expect.any(Function))
|
|
37
|
+
expect(map.off).toHaveBeenCalledWith('draw.nudgevertex', expect.any(Function))
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('onSetup edge cases: clears an active snap indicator, positions or skips the touch target, clears an explicit -1 selection', () => {
|
|
41
|
+
jest.useFakeTimers()
|
|
42
|
+
const { ctx, map, options } = createHarness()
|
|
43
|
+
map._snapInstance = { status: true, snapStatus: false, snapCoords: null }
|
|
44
|
+
ctx.onSetup(options)
|
|
45
|
+
expect(map.getLayer).toHaveBeenCalledWith('snap-helper-circle')
|
|
46
|
+
|
|
47
|
+
const touch = createHarness(POLYGON(), { interfaceType: 'touch', selectedVertexIndex: 0, selectedVertexType: 'vertex' })
|
|
48
|
+
jest.runAllTimers()
|
|
49
|
+
expect(touch.state.touchVertexTarget.style.display).toBe('block')
|
|
50
|
+
|
|
51
|
+
const outOfRange = createHarness(POLYGON(), { interfaceType: 'touch', selectedVertexIndex: 99, selectedVertexType: 'vertex' })
|
|
52
|
+
jest.runAllTimers()
|
|
53
|
+
expect(outOfRange.state.touchVertexTarget.style.display).toBe('none')
|
|
54
|
+
|
|
55
|
+
const cleared = createHarness(POLYGON(), { selectedVertexIndex: -1 })
|
|
56
|
+
expect(cleared.ctx.clearSelectedCoordinates).toHaveBeenCalled()
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('selection, scale and update events', () => {
|
|
61
|
+
test('applyVertexSelection handles midpoint entry and cleared selection', () => {
|
|
62
|
+
jest.useFakeTimers()
|
|
63
|
+
const midpoint = createHarness(POLYGON(), { selectedVertexType: 'midpoint', selectedVertexIndex: 4 })
|
|
64
|
+
jest.runAllTimers()
|
|
65
|
+
expect(midpoint.ctx.clearSelectedCoordinates).toHaveBeenCalled()
|
|
66
|
+
expect(midpoint.map.getSource).toHaveBeenCalledWith('mapbox-gl-draw-hot')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('onSelectionChange searches by coordinate for mouse, trusts coordPath/keyboard otherwise', () => {
|
|
70
|
+
const { ctx, state, map } = createHarness()
|
|
71
|
+
const geom = ctx.getFeature('feat-1').toGeoJSON().geometry
|
|
72
|
+
ctx.onSelectionChange(state, { points: [{ geometry: { coordinates: [10, 0] } }], features: [{ geometry: geom }] })
|
|
73
|
+
expect(state.selectedVertexIndex).toBe(1)
|
|
74
|
+
expect(map.fire).toHaveBeenCalledWith('draw.vertexselection', expect.objectContaining({ numVertecies: 4 }))
|
|
75
|
+
|
|
76
|
+
state.coordPath = '0.2'
|
|
77
|
+
state.selectedVertexIndex = 2
|
|
78
|
+
ctx.onSelectionChange(state, { points: [{ geometry: { coordinates: [10, 10] } }], features: [{ geometry: geom }] })
|
|
79
|
+
expect(state.selectedVertexIndex).toBe(2) // trusted, not re-searched
|
|
80
|
+
|
|
81
|
+
// Keyboard mode trusts the selection; with no event vertex it falls back to state, then null
|
|
82
|
+
const kb = createHarness()
|
|
83
|
+
const g2 = kb.ctx.getFeature('feat-1').toGeoJSON().geometry
|
|
84
|
+
kb.state.interfaceType = 'keyboard'
|
|
85
|
+
kb.state.selectedVertexIndex = 2
|
|
86
|
+
kb.ctx.onSelectionChange(kb.state, { points: [{ geometry: { coordinates: [10, 10] } }], features: [{ geometry: g2 }] })
|
|
87
|
+
expect(kb.state.selectedVertexType).toBe('vertex')
|
|
88
|
+
kb.ctx.onSelectionChange({ ...kb.state, interfaceType: 'mouse', selectedVertexType: 'vertex', selectedVertexIndex: 0 }, { points: [], features: [{ geometry: g2 }] })
|
|
89
|
+
kb.ctx.onSelectionChange({ ...kb.state, interfaceType: 'mouse', selectedVertexType: null, selectedVertexIndex: -1 }, { points: [], features: [{ geometry: g2 }] })
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('onScaleChange and onInterfaceTypeChange update state and the touch target', () => {
|
|
93
|
+
const { ctx, state } = createHarness()
|
|
94
|
+
ctx.onScaleChange(state, { scale: 2 })
|
|
95
|
+
expect(state.scale).toBe(2)
|
|
96
|
+
state.selectedVertexIndex = 0
|
|
97
|
+
ctx.onInterfaceTypeChange(state, { interfaceType: 'touch' })
|
|
98
|
+
expect(state.interfaceType).toBe('touch')
|
|
99
|
+
ctx.onInterfaceTypeChange({ ...state, selectedVertexIndex: -1 }, { interfaceType: 'mouse' })
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('draw.nudgevertex moves the selected vertex and repositions the touch target — the inbound bridge for MoveControl.mapProvider.activeMoveTarget', () => {
|
|
103
|
+
jest.useFakeTimers()
|
|
104
|
+
const { state, map } = createHarness(POLYGON(), { interfaceType: 'touch', selectedVertexIndex: 0, selectedVertexType: 'vertex' })
|
|
105
|
+
jest.runAllTimers() // flush onSetup's deferred initial touch-target positioning
|
|
106
|
+
const before = [...state.vertecies[0]]
|
|
107
|
+
const targetBefore = { top: state.touchVertexTarget.style.top, left: state.touchVertexTarget.style.left }
|
|
108
|
+
map.fire('draw.nudgevertex', { dx: 1, dy: 0, isLargeStep: true })
|
|
109
|
+
expect(state.vertecies[0]).not.toEqual(before)
|
|
110
|
+
expect({ top: state.touchVertexTarget.style.top, left: state.touchVertexTarget.style.left }).not.toEqual(targetBefore)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('onUpdate re-selects a changed vertex only when the vertex count is ambiguous', () => {
|
|
114
|
+
const { ctx, state } = createHarness()
|
|
115
|
+
ctx.onUpdate(state) // unique coords → no change
|
|
116
|
+
state.vertecies = [[0, 0], [0, 0], [1, 1]]
|
|
117
|
+
ctx.onUpdate(state)
|
|
118
|
+
expect(state.selectedVertexIndex).toBe(-1)
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
describe('move, button and changeMode routing', () => {
|
|
123
|
+
test('onMove keeps the touch target aligned with the selected vertex, ignoring an unselected vertex', () => {
|
|
124
|
+
const { ctx, state } = createHarness()
|
|
125
|
+
state.selectedVertexIndex = 0
|
|
126
|
+
state.interfaceType = 'touch'
|
|
127
|
+
ctx.onMove(state)
|
|
128
|
+
expect(state.touchVertexTarget.style.display).toBe('block')
|
|
129
|
+
expect(() => ctx.onMove({ ...state, selectedVertexIndex: -1 })).not.toThrow()
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test('onButtonClick deletes or undoes based on the clicked control', () => {
|
|
133
|
+
const { ctx, state, container } = createHarness()
|
|
134
|
+
const del = document.createElement('button')
|
|
135
|
+
del.id = 'delete-vertex'
|
|
136
|
+
const undo = document.createElement('button')
|
|
137
|
+
undo.id = 'undo-vertex'
|
|
138
|
+
container.append(del, undo)
|
|
139
|
+
const deleteSpy = jest.spyOn(ctx, 'deleteVertex').mockImplementation(() => {})
|
|
140
|
+
const undoSpy = jest.spyOn(ctx, 'handleUndo').mockImplementation(() => {})
|
|
141
|
+
|
|
142
|
+
ctx.onButtonClick({ ...state, selectedVertexType: 'vertex' }, { target: del })
|
|
143
|
+
expect(deleteSpy).toHaveBeenCalled()
|
|
144
|
+
ctx.onButtonClick(state, { target: undo })
|
|
145
|
+
expect(undoSpy).toHaveBeenCalled()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test('changeMode is a no-op without a feature id', () => {
|
|
149
|
+
const { ctx, api } = createHarness()
|
|
150
|
+
ctx.changeMode({ featureId: null }, { selectedVertexIndex: -1 })
|
|
151
|
+
expect(api.changeMode).not.toHaveBeenCalled()
|
|
152
|
+
})
|
|
153
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { pollUntil, patchSourceData } from './sourceData.js'
|
|
2
|
+
import { createSnapInstance, ensureSnapLayer } from './snapInstance.js'
|
|
3
|
+
import { SNAP_HELPER_LAYER, DRAW_HOT_SOURCE } from './constants.js'
|
|
4
|
+
|
|
5
|
+
/** Re-patch the source and restore the snap layer whenever the style reloads */
|
|
6
|
+
export function registerStyleLoadHandler (map, draw, config) {
|
|
7
|
+
map.on('style.load', () => {
|
|
8
|
+
pollUntil(
|
|
9
|
+
() => map._removed ? null : map.getSource(DRAW_HOT_SOURCE),
|
|
10
|
+
(source) => {
|
|
11
|
+
patchSourceData(source)
|
|
12
|
+
ensureSnapLayer(map)
|
|
13
|
+
if (!map._snapInstance) {
|
|
14
|
+
createSnapInstance(map, draw, source, config)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
)
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Suppress snap processing during zoom and reset the indicator afterwards */
|
|
22
|
+
export function registerZoomHandlers (map) {
|
|
23
|
+
// Suppress snap processing during zoom (indicator freezes in place)
|
|
24
|
+
map.on('zoomstart', () => {
|
|
25
|
+
map._isZooming = true
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
map.on('zoomend', () => {
|
|
29
|
+
map._isZooming = false
|
|
30
|
+
// Force hide then re-show to reset indicator at new zoom level (Safari fix)
|
|
31
|
+
if (map.getLayer(SNAP_HELPER_LAYER)) {
|
|
32
|
+
map.setLayoutProperty(SNAP_HELPER_LAYER, 'visibility', 'none')
|
|
33
|
+
const snap = map._snapInstance
|
|
34
|
+
if (snap?.status) {
|
|
35
|
+
map.setLayoutProperty(SNAP_HELPER_LAYER, 'visibility', 'visible')
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { registerStyleLoadHandler, registerZoomHandlers } from './mapHandlers.js'
|
|
2
|
+
import { createSnapInstance, ensureSnapLayer } from './snapInstance.js'
|
|
3
|
+
import { patchSourceData } from './sourceData.js'
|
|
4
|
+
|
|
5
|
+
const SNAP_LAYER = 'snap-helper-circle'
|
|
6
|
+
const DRAW_SOURCE = 'mapbox-gl-draw-hot'
|
|
7
|
+
|
|
8
|
+
jest.mock('./snapInstance.js', () => ({
|
|
9
|
+
createSnapInstance: jest.fn(),
|
|
10
|
+
ensureSnapLayer: jest.fn()
|
|
11
|
+
}))
|
|
12
|
+
|
|
13
|
+
jest.mock('./sourceData.js', () => ({
|
|
14
|
+
...jest.requireActual('./sourceData.js'),
|
|
15
|
+
patchSourceData: jest.fn()
|
|
16
|
+
}))
|
|
17
|
+
|
|
18
|
+
const config = { layers: [], radius: 12, rules: [], status: false, onSnapped: () => {} }
|
|
19
|
+
|
|
20
|
+
const handlerFor = (map, name) => map.on.mock.calls.find(([event]) => event === name)?.[1]
|
|
21
|
+
|
|
22
|
+
const makeMap = (overrides = {}) => ({
|
|
23
|
+
on: jest.fn(),
|
|
24
|
+
getSource: jest.fn((id) => (id === DRAW_SOURCE ? { id: 'hot' } : null)),
|
|
25
|
+
getLayer: jest.fn(() => null),
|
|
26
|
+
setLayoutProperty: jest.fn(),
|
|
27
|
+
...overrides
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
jest.clearAllMocks()
|
|
32
|
+
global.requestAnimationFrame = jest.fn()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe('registerStyleLoadHandler', () => {
|
|
36
|
+
test('re-patches the source, ensures the layer and creates a missing instance', () => {
|
|
37
|
+
const map = makeMap()
|
|
38
|
+
const draw = { id: 'draw' }
|
|
39
|
+
|
|
40
|
+
registerStyleLoadHandler(map, draw, config)
|
|
41
|
+
handlerFor(map, 'style.load')()
|
|
42
|
+
|
|
43
|
+
expect(patchSourceData).toHaveBeenCalledWith({ id: 'hot' })
|
|
44
|
+
expect(ensureSnapLayer).toHaveBeenCalledWith(map)
|
|
45
|
+
expect(createSnapInstance).toHaveBeenCalledWith(map, draw, { id: 'hot' }, config)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('does not recreate an existing instance', () => {
|
|
49
|
+
const map = makeMap({ _snapInstance: { id: 'exists' } })
|
|
50
|
+
|
|
51
|
+
registerStyleLoadHandler(map, {}, config)
|
|
52
|
+
handlerFor(map, 'style.load')()
|
|
53
|
+
|
|
54
|
+
expect(ensureSnapLayer).toHaveBeenCalledWith(map)
|
|
55
|
+
expect(createSnapInstance).not.toHaveBeenCalled()
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('stops when the map has been removed', () => {
|
|
59
|
+
const map = makeMap({ _removed: true })
|
|
60
|
+
|
|
61
|
+
registerStyleLoadHandler(map, {}, config)
|
|
62
|
+
handlerFor(map, 'style.load')()
|
|
63
|
+
|
|
64
|
+
expect(ensureSnapLayer).not.toHaveBeenCalled()
|
|
65
|
+
expect(createSnapInstance).not.toHaveBeenCalled()
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('registerZoomHandlers', () => {
|
|
70
|
+
test('zoomstart sets the zooming flag', () => {
|
|
71
|
+
const map = makeMap()
|
|
72
|
+
registerZoomHandlers(map)
|
|
73
|
+
handlerFor(map, 'zoomstart')()
|
|
74
|
+
expect(map._isZooming).toBe(true)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('zoomend clears the flag and re-shows the indicator when snapping is active', () => {
|
|
78
|
+
const map = makeMap({ getLayer: jest.fn(() => ({ id: 'layer' })), _snapInstance: { status: true } })
|
|
79
|
+
|
|
80
|
+
registerZoomHandlers(map)
|
|
81
|
+
handlerFor(map, 'zoomend')()
|
|
82
|
+
|
|
83
|
+
expect(map._isZooming).toBe(false)
|
|
84
|
+
expect(map.setLayoutProperty).toHaveBeenCalledWith(SNAP_LAYER, 'visibility', 'none')
|
|
85
|
+
expect(map.setLayoutProperty).toHaveBeenCalledWith(SNAP_LAYER, 'visibility', 'visible')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('zoomend hides but does not re-show when snapping is disabled', () => {
|
|
89
|
+
const map = makeMap({ getLayer: jest.fn(() => ({ id: 'layer' })), _snapInstance: { status: false } })
|
|
90
|
+
|
|
91
|
+
registerZoomHandlers(map)
|
|
92
|
+
handlerFor(map, 'zoomend')()
|
|
93
|
+
|
|
94
|
+
expect(map.setLayoutProperty).toHaveBeenCalledTimes(1)
|
|
95
|
+
expect(map.setLayoutProperty).toHaveBeenCalledWith(SNAP_LAYER, 'visibility', 'none')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('zoomend does nothing when the snap layer is absent', () => {
|
|
99
|
+
const map = makeMap({ getLayer: jest.fn(() => null) })
|
|
100
|
+
|
|
101
|
+
registerZoomHandlers(map)
|
|
102
|
+
handlerFor(map, 'zoomend')()
|
|
103
|
+
|
|
104
|
+
expect(map.setLayoutProperty).not.toHaveBeenCalled()
|
|
105
|
+
})
|
|
106
|
+
})
|