@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,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyboard handling for the shared draw mode: cmd/ctrl+z undo, arrow/Enter keyboard
|
|
3
|
+
* drawing, and Escape cancel/reinitialise. Part of createDrawMode.
|
|
4
|
+
*
|
|
5
|
+
* Note the two distinct handlers: onKeyup (lowercase) is a window listener; onKeyUp
|
|
6
|
+
* (capital U) is mapbox-gl-draw's own, registered on the container.
|
|
7
|
+
*/
|
|
8
|
+
export const createKeyboardHandlers = ({ ParentMode, getFeature, INTERFACE_KEYS }) => ({
|
|
9
|
+
onKeydown (state, e) {
|
|
10
|
+
if (e.key === 'z' && (e.metaKey || e.ctrlKey) && !e.shiftKey) {
|
|
11
|
+
this._handleUndoKeydown(state, e)
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
if (document.activeElement !== state.container) {
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
if (e.key === 'Escape') {
|
|
18
|
+
e.preventDefault()
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
if (e.key === 'Enter') {
|
|
22
|
+
state.isActive = true
|
|
23
|
+
}
|
|
24
|
+
if (!INTERFACE_KEYS.has(e.key)) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
this._setInterface(state, 'keyboard')
|
|
28
|
+
this.onMove(state, e)
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
onKeyup (state, e) {
|
|
32
|
+
if (e.key === 'Escape') {
|
|
33
|
+
if (state.interfaceType !== 'keyboard') {
|
|
34
|
+
// Mouse/touch: cancel drawing — onKeyUp (capital U) won't fire since container isn't focused
|
|
35
|
+
this.map.fire('draw.cancel')
|
|
36
|
+
}
|
|
37
|
+
// Keyboard: onKeyUp (capital U) handles reinitialize (container is focused, event reaches it)
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
if (document.activeElement !== state.container) {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
if (!INTERFACE_KEYS.has(e.key)) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
this._setInterface(state, 'keyboard')
|
|
47
|
+
this.onMove(state, e)
|
|
48
|
+
if (e.key === 'Enter' && state.isActive) {
|
|
49
|
+
this.doClick(state)
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
// Called by mapbox-gl-draw's event system (capital U — distinct from onKeyup above).
|
|
54
|
+
// Registered on ctx.container, so only fires when the viewport has focus (keyboard drawing).
|
|
55
|
+
// 1. A UI element inside the viewport has focus (e.g. popup menu) → ignore, let React handle
|
|
56
|
+
// 2. Keyboard drawing (container focused, interfaceType === 'keyboard') → Escape restarts
|
|
57
|
+
// 3. Non-keyboard with container focused → skip (already handled by window onKeyup via draw.cancel)
|
|
58
|
+
onKeyUp (state, e) {
|
|
59
|
+
const activeEl = document.activeElement
|
|
60
|
+
if (activeEl && activeEl !== state.container && state.container.contains(activeEl)) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
if (e.key === 'Escape') {
|
|
64
|
+
if (state.interfaceType === 'keyboard') {
|
|
65
|
+
const undoStack = this.map._undoStack
|
|
66
|
+
if (undoStack) {
|
|
67
|
+
undoStack.clear()
|
|
68
|
+
}
|
|
69
|
+
this._reinitializeFeature(state, getFeature(state))
|
|
70
|
+
}
|
|
71
|
+
// Non-keyboard already handled by onKeyup (window) via draw.cancel
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
if (activeEl !== state.container) {
|
|
75
|
+
ParentMode.onKeyUp.call(this, state, e)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
})
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { setup, clickAt, firedWith, DrawPolygonMode } from './__helpers__/harness.js'
|
|
2
|
+
|
|
3
|
+
describe('keyboard interface', () => {
|
|
4
|
+
test('arrow keys switch to keyboard interface, show the crosshair and move the rubber band to center', () => {
|
|
5
|
+
const { ctx, state, marker, container } = setup(DrawPolygonMode)
|
|
6
|
+
clickAt(ctx, state, 0, 0)
|
|
7
|
+
container.focus()
|
|
8
|
+
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
|
|
9
|
+
expect(state.interfaceType).toBe('keyboard')
|
|
10
|
+
expect(marker.style.display).toBe('block')
|
|
11
|
+
expect(state.polygon.coordinates[0].at(-1)).toEqual([5, 5])
|
|
12
|
+
expect(firedWith(ctx.map, 'draw.geometrychange').length).toBeGreaterThan(0)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('other keys, unfocused container and Escape do not switch interface', () => {
|
|
16
|
+
const { ctx, state, marker, container } = setup(DrawPolygonMode)
|
|
17
|
+
ctx.keydownHandler(new KeyboardEvent('keydown', { key: 'ArrowRight' })) // container not focused
|
|
18
|
+
container.focus()
|
|
19
|
+
ctx.keydownHandler(new KeyboardEvent('keydown', { key: 'a' }))
|
|
20
|
+
ctx.keydownHandler(new KeyboardEvent('keydown', { key: 'Escape' }))
|
|
21
|
+
ctx.keyupHandler(new KeyboardEvent('keyup', { key: 'a' }))
|
|
22
|
+
expect(state.interfaceType).toBe('mouse')
|
|
23
|
+
expect(marker.style.display).toBe('none')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('keyup without focus is ignored', () => {
|
|
27
|
+
const { ctx, state } = setup(DrawPolygonMode)
|
|
28
|
+
ctx.keyupHandler(new KeyboardEvent('keyup', { key: 'ArrowRight' }))
|
|
29
|
+
expect(state.interfaceType).toBe('mouse')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('Enter down then up places a vertex at center', () => {
|
|
33
|
+
const { ctx, state, container } = setup(DrawPolygonMode)
|
|
34
|
+
container.focus()
|
|
35
|
+
ctx.keydownHandler(new KeyboardEvent('keydown', { key: 'Enter' }))
|
|
36
|
+
ctx.keyupHandler(new KeyboardEvent('keyup', { key: 'Enter' }))
|
|
37
|
+
expect(state.isActive).toBe(true)
|
|
38
|
+
expect(state.polygon.coordinates[0][0]).toEqual([5, 5])
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('Escape keyup cancels drawing for mouse/touch but not keyboard', () => {
|
|
42
|
+
const { ctx, state } = setup(DrawPolygonMode)
|
|
43
|
+
ctx.keyupHandler(new KeyboardEvent('keyup', { key: 'Escape' }))
|
|
44
|
+
expect(firedWith(ctx.map, 'draw.cancel')).toHaveLength(1)
|
|
45
|
+
|
|
46
|
+
state.interfaceType = 'keyboard'
|
|
47
|
+
ctx.keyupHandler(new KeyboardEvent('keyup', { key: 'Escape' }))
|
|
48
|
+
expect(firedWith(ctx.map, 'draw.cancel')).toHaveLength(1)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('keyup of a non-Enter interface key while focused switches interface without committing a vertex', () => {
|
|
52
|
+
const { ctx, state, container } = setup(DrawPolygonMode)
|
|
53
|
+
container.focus()
|
|
54
|
+
ctx.keyupHandler(new KeyboardEvent('keyup', { key: 'ArrowRight' }))
|
|
55
|
+
expect(state.interfaceType).toBe('keyboard')
|
|
56
|
+
expect(ctx.map._undoStack.length).toBe(0)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('container keyUp with the container itself focused and a non-Escape key does not defer to the parent', () => {
|
|
60
|
+
const { ctx, state, container } = setup(DrawPolygonMode)
|
|
61
|
+
container.focus()
|
|
62
|
+
ctx.onKeyUp(state, { key: 'a' })
|
|
63
|
+
expect(ctx.changeMode).not.toHaveBeenCalled()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('Escape during keyboard drawing with no undo stack still reinitialises the feature', () => {
|
|
67
|
+
const { ctx, state, container } = setup(DrawPolygonMode, { interfaceType: 'keyboard' })
|
|
68
|
+
clickAt(ctx, state, 0, 0)
|
|
69
|
+
container.focus()
|
|
70
|
+
const id = state.polygon.id
|
|
71
|
+
ctx.map._undoStack = null
|
|
72
|
+
ctx.onKeyUp(state, { key: 'Escape' })
|
|
73
|
+
expect(ctx._ctx.store.delete).toHaveBeenCalledWith([id])
|
|
74
|
+
})
|
|
75
|
+
})
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup / teardown for the shared draw mode: binds the window/container/map event
|
|
3
|
+
* handlers on entry and removes them on exit. Part of createDrawMode.
|
|
4
|
+
*/
|
|
5
|
+
export const createLifecycle = ({ ParentMode, featureProp, excludeFeatureIdFromSetup }) => ({
|
|
6
|
+
onSetup (options) {
|
|
7
|
+
const { map } = this
|
|
8
|
+
|
|
9
|
+
// Some parent modes (DrawLineString) interpret featureId as "continue existing"
|
|
10
|
+
// rather than "use this ID for new feature"
|
|
11
|
+
const parentOptions = excludeFeatureIdFromSetup
|
|
12
|
+
? { ...options, featureId: null }
|
|
13
|
+
: options
|
|
14
|
+
|
|
15
|
+
const state = {
|
|
16
|
+
...ParentMode.onSetup.call(this, parentOptions),
|
|
17
|
+
...options
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Add initial props
|
|
21
|
+
state[featureProp].properties = options.properties
|
|
22
|
+
|
|
23
|
+
const { container, vertexMarkerId, getInterfaceType } = state
|
|
24
|
+
const currentInterfaceType = getInterfaceType ? getInterfaceType() : state.interfaceType
|
|
25
|
+
state.interfaceType = currentInterfaceType
|
|
26
|
+
const vertexMarker = container.querySelector(`#${vertexMarkerId}`)
|
|
27
|
+
state.vertexMarker = vertexMarker
|
|
28
|
+
if (['touch', 'keyboard'].includes(currentInterfaceType)) {
|
|
29
|
+
this._showCrossHair(state)
|
|
30
|
+
} else {
|
|
31
|
+
this._hideCrossHair(state)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Bind all handlers once
|
|
35
|
+
const bind = (name, fn) => (this[name] = fn.bind(this, state))
|
|
36
|
+
const handlers = {
|
|
37
|
+
keydownHandler: this.onKeydown,
|
|
38
|
+
keyupHandler: this.onKeyup,
|
|
39
|
+
blurHandler: this.onBlur,
|
|
40
|
+
createHandler: this.onCreate,
|
|
41
|
+
moveHandler: this.onMove,
|
|
42
|
+
pointerdownHandler: this.onPointerdown,
|
|
43
|
+
pointermoveHandler: this.onPointermove,
|
|
44
|
+
pointerupHandler: this.onPointerup,
|
|
45
|
+
vertexButtonClickHandler: this.onVertexButtonClick,
|
|
46
|
+
undoHandler: this.onUndo,
|
|
47
|
+
interfaceTypeChangeHandler: this.onInterfaceTypeChange
|
|
48
|
+
}
|
|
49
|
+
Object.entries(handlers).forEach(([k, fn]) => bind(k, fn))
|
|
50
|
+
|
|
51
|
+
// Register events
|
|
52
|
+
this._listeners = [
|
|
53
|
+
[window, 'keydown', this.keydownHandler],
|
|
54
|
+
[window, 'keyup', this.keyupHandler],
|
|
55
|
+
[window, 'click', this.vertexButtonClickHandler],
|
|
56
|
+
[container, 'blur', this.blurHandler],
|
|
57
|
+
[container, 'pointermove', this.pointermoveHandler],
|
|
58
|
+
[container, 'pointerup', this.pointerupHandler],
|
|
59
|
+
[map, 'pointerdown', this.pointerdownHandler],
|
|
60
|
+
[map, 'draw.create', this.createHandler],
|
|
61
|
+
[map, 'move', this.moveHandler],
|
|
62
|
+
[map, 'draw.undo', this.undoHandler],
|
|
63
|
+
[map, 'draw.interfacetypechange', this.interfaceTypeChangeHandler]
|
|
64
|
+
]
|
|
65
|
+
this._listeners.forEach(([t, e, h]) => t.addEventListener ? t.addEventListener(e, h) : t.on(e, h))
|
|
66
|
+
|
|
67
|
+
return state
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
onStop (state) {
|
|
71
|
+
ParentMode.onStop.call(this, state)
|
|
72
|
+
this._listeners.forEach(([t, e, h]) => t.removeEventListener ? t.removeEventListener(e, h) : t.off(e, h))
|
|
73
|
+
this._hideCrossHair(state)
|
|
74
|
+
// Sync the final interfaceType from draw mode back to app state so crosshair
|
|
75
|
+
// visibility is correct when exiting draw mode (e.g., if user switched from mouse to keyboard)
|
|
76
|
+
this.map.fire('draw.interfacetypechange', { interfaceType: state.interfaceType })
|
|
77
|
+
}
|
|
78
|
+
})
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { setup, firedWith, DrawPolygonMode, DrawLineMode } from './__helpers__/harness.js'
|
|
2
|
+
|
|
3
|
+
describe('setup and crosshair', () => {
|
|
4
|
+
test('mouse interface hides the vertex marker, keyboard shows it', () => {
|
|
5
|
+
const mouse = setup(DrawPolygonMode)
|
|
6
|
+
expect(mouse.marker.style.display).toBe('none')
|
|
7
|
+
|
|
8
|
+
const keyboard = setup(DrawPolygonMode, { interfaceType: 'keyboard' })
|
|
9
|
+
expect(keyboard.marker.style.display).toBe('block')
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
test('getInterfaceType() takes precedence over the static interfaceType option', () => {
|
|
13
|
+
const { state, marker } = setup(DrawPolygonMode, { interfaceType: 'mouse', getInterfaceType: () => 'touch' })
|
|
14
|
+
expect(state.interfaceType).toBe('touch')
|
|
15
|
+
expect(marker.style.display).toBe('block')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('a crossHair object is used instead of the vertex marker when provided', () => {
|
|
19
|
+
const crossHair = { show: jest.fn(), hide: jest.fn() }
|
|
20
|
+
const { ctx, state } = setup(DrawPolygonMode, { interfaceType: 'keyboard', crossHair })
|
|
21
|
+
expect(crossHair.show).toHaveBeenCalled()
|
|
22
|
+
ctx.pointermoveHandler({ pointerType: 'mouse' })
|
|
23
|
+
expect(crossHair.hide).toHaveBeenCalled()
|
|
24
|
+
ctx.onBlur(state, { target: document.body })
|
|
25
|
+
expect(crossHair.hide).toHaveBeenCalledTimes(2)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('line mode creates a fresh feature even when featureId is passed', () => {
|
|
29
|
+
const { state } = setup(DrawLineMode)
|
|
30
|
+
expect(state.line).toBeDefined()
|
|
31
|
+
expect(state.featureId).toBe('shape-1')
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe('onStop', () => {
|
|
36
|
+
test('removes listeners, hides the crosshair and reports the final interface type', () => {
|
|
37
|
+
const { ctx, state, marker, container } = setup(DrawPolygonMode, { interfaceType: 'keyboard' })
|
|
38
|
+
ctx.onStop(state)
|
|
39
|
+
expect(marker.style.display).toBe('none')
|
|
40
|
+
expect(firedWith(ctx.map, 'draw.interfacetypechange')).toEqual([{ interfaceType: 'keyboard' }])
|
|
41
|
+
|
|
42
|
+
// Window/container/map listeners are gone: keydown no longer shows the crosshair
|
|
43
|
+
container.focus()
|
|
44
|
+
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
|
|
45
|
+
expect(marker.style.display).toBe('none')
|
|
46
|
+
expect(ctx.map.off).toHaveBeenCalledWith('draw.create', ctx.createHandler)
|
|
47
|
+
})
|
|
48
|
+
})
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getSnapInstance, isSnapEnabled, getSnapLngLat, triggerSnapAtPoint, triggerSnapAtCenter
|
|
3
|
+
} from '../../utils/snapHelpers.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Pointer / touch handling for the shared draw mode: touch and mouse interface
|
|
7
|
+
* switching, rubber-band movement (with snapping) and blur. Part of createDrawMode.
|
|
8
|
+
*/
|
|
9
|
+
export const createPointerHandlers = ({ ParentMode, getFeature, getCoords }) => ({
|
|
10
|
+
onTouchStart (state, e) {
|
|
11
|
+
this._setInterface(state, 'touch')
|
|
12
|
+
this.onMove(state, e)
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
onTouchEnd (state, e) {
|
|
16
|
+
this._setInterface(state, 'touch')
|
|
17
|
+
this.onMove(state, e)
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
// The global interface type (e.g. switching to touch and panning via MoveControl)
|
|
21
|
+
// can change mid-session without any touch/pointer/key event ever landing on the
|
|
22
|
+
// map container, so this can't rely on onTouchStart/onPointerdown alone — refresh
|
|
23
|
+
// the rubber band immediately rather than waiting for the next incidental 'move'.
|
|
24
|
+
// Also fires once on every mode entry (DrawInit.jsx syncs setInterfaceType whenever
|
|
25
|
+
// draw_polygon/draw_line starts), so this must only show the crosshair for
|
|
26
|
+
// touch/keyboard — same as onSetup/onMove — not unconditionally like touch's own
|
|
27
|
+
// onTouchStart/onTouchEnd, or a mouse-driven session would flash it on every entry.
|
|
28
|
+
onInterfaceTypeChange (state, e) {
|
|
29
|
+
this._setInterface(state, e.interfaceType, ['touch', 'keyboard'].includes(e.interfaceType))
|
|
30
|
+
this.onMove(state)
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
onBlur (state, e) {
|
|
34
|
+
if (e.target !== state.container) {
|
|
35
|
+
this._hideCrossHair(state)
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
onMouseMove (state, e) {
|
|
40
|
+
if (isSnapEnabled(state)) {
|
|
41
|
+
const snap = getSnapInstance(this.map)
|
|
42
|
+
triggerSnapAtPoint(snap, this.map, e.point)
|
|
43
|
+
|
|
44
|
+
const snappedLngLat = getSnapLngLat(snap)
|
|
45
|
+
if (snappedLngLat) {
|
|
46
|
+
e = { ...e, lngLat: snappedLngLat }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
ParentMode.onMouseMove.call(this, state, e)
|
|
51
|
+
|
|
52
|
+
// Fired after the parent updates the rubber band so the payload (and the live
|
|
53
|
+
// invalid-stroke check driven by it) reflects the current cursor position.
|
|
54
|
+
this.map.fire('draw.geometrychange', state.polygon || state.line)
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
onMove (state) {
|
|
58
|
+
if (['touch', 'keyboard'].includes(state.interfaceType)) {
|
|
59
|
+
if (isSnapEnabled(state)) {
|
|
60
|
+
triggerSnapAtCenter(getSnapInstance(this.map), this.map)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const snap = getSnapInstance(this.map)
|
|
64
|
+
const snappedLngLat = isSnapEnabled(state) && getSnapLngLat(snap)
|
|
65
|
+
|
|
66
|
+
if (snappedLngLat) {
|
|
67
|
+
const point = this.map.project([snappedLngLat.lng, snappedLngLat.lat])
|
|
68
|
+
ParentMode.onMouseMove.call(this, state, {
|
|
69
|
+
lngLat: snappedLngLat,
|
|
70
|
+
point,
|
|
71
|
+
originalEvent: new MouseEvent('mousemove', {
|
|
72
|
+
clientX: point.x,
|
|
73
|
+
clientY: point.y,
|
|
74
|
+
bubbles: true,
|
|
75
|
+
cancelable: true
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
this._ctx.store.render()
|
|
79
|
+
// Parity with _simulateMouse: report the rubber-band move so the live
|
|
80
|
+
// invalid-stroke check sees snapped touch/keyboard moves too.
|
|
81
|
+
this.map.fire('draw.geometrychange', state.polygon || state.line)
|
|
82
|
+
} else {
|
|
83
|
+
this._simulateMouse('mousemove', ParentMode.onMouseMove, state)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
onPointerdown (state, e) {
|
|
89
|
+
if (e.pointerType !== 'touch') {
|
|
90
|
+
this._setInterface(state, 'mouse', false)
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
onPointermove (state, e) {
|
|
95
|
+
if (e.pointerType !== 'touch') {
|
|
96
|
+
this._hideCrossHair(state)
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
onPointerup (state) {
|
|
101
|
+
this.dispatchVertexChange(getCoords(getFeature(state)))
|
|
102
|
+
}
|
|
103
|
+
})
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { setup, clickAt, firedWith, activeSnap, DrawPolygonMode, DrawLineMode, CENTER } from './__helpers__/harness.js'
|
|
2
|
+
|
|
3
|
+
describe('touch and pointer interface', () => {
|
|
4
|
+
test('touch start/end switch to touch interface and show the crosshair', () => {
|
|
5
|
+
const { ctx, state, marker } = setup(DrawPolygonMode)
|
|
6
|
+
ctx.onTouchStart(state, {})
|
|
7
|
+
expect(state.interfaceType).toBe('touch')
|
|
8
|
+
expect(marker.style.display).toBe('block')
|
|
9
|
+
ctx.onTouchEnd(state, {})
|
|
10
|
+
expect(state.interfaceType).toBe('touch')
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('non-touch pointerdown switches back to mouse without showing the crosshair; touch does not', () => {
|
|
14
|
+
const { ctx, state, marker } = setup(DrawPolygonMode, { interfaceType: 'keyboard' })
|
|
15
|
+
ctx.map.fire('pointerdown', { pointerType: 'mouse' })
|
|
16
|
+
expect(state.interfaceType).toBe('mouse')
|
|
17
|
+
expect(marker.style.display).toBe('block') // unchanged, not re-shown or hidden
|
|
18
|
+
|
|
19
|
+
ctx.map.fire('pointerdown', { pointerType: 'touch' })
|
|
20
|
+
expect(state.interfaceType).toBe('mouse')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('mouse pointermove hides the crosshair; touch pointermove does not', () => {
|
|
24
|
+
const { ctx, marker } = setup(DrawPolygonMode, { interfaceType: 'keyboard' })
|
|
25
|
+
ctx.pointermoveHandler({ pointerType: 'touch' })
|
|
26
|
+
expect(marker.style.display).toBe('block')
|
|
27
|
+
ctx.pointermoveHandler({ pointerType: 'mouse' })
|
|
28
|
+
expect(marker.style.display).toBe('none')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('pointerup reports the current vertex count', () => {
|
|
32
|
+
const { ctx, state } = setup(DrawPolygonMode)
|
|
33
|
+
clickAt(ctx, state, 0, 0)
|
|
34
|
+
ctx.map.fire.mockClear()
|
|
35
|
+
ctx.pointerupHandler({})
|
|
36
|
+
expect(firedWith(ctx.map, 'draw.vertexchange')).toEqual([{ numVertecies: 1 }])
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('blur away from the container hides the crosshair; blur on the container does not', () => {
|
|
40
|
+
const { ctx, marker, container } = setup(DrawPolygonMode, { interfaceType: 'keyboard' })
|
|
41
|
+
ctx.blurHandler({ target: container })
|
|
42
|
+
expect(marker.style.display).toBe('block')
|
|
43
|
+
ctx.blurHandler({ target: document.body })
|
|
44
|
+
expect(marker.style.display).toBe('none')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('draw.interfacetypechange (e.g. switching to touch and panning via MoveControl mid-session) updates the interface and refreshes the rubber band immediately', () => {
|
|
48
|
+
const { ctx, state } = setup(DrawPolygonMode)
|
|
49
|
+
clickAt(ctx, state, 0, 0)
|
|
50
|
+
ctx.map.fire('draw.interfacetypechange', { interfaceType: 'touch' })
|
|
51
|
+
expect(state.interfaceType).toBe('touch')
|
|
52
|
+
expect(state.polygon.coordinates[0].at(-1)).toEqual([CENTER.lng, CENTER.lat])
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('draw.interfacetypechange to touch/keyboard shows the crosshair; to mouse it does not', () => {
|
|
56
|
+
const { ctx, marker } = setup(DrawPolygonMode)
|
|
57
|
+
ctx.map.fire('draw.interfacetypechange', { interfaceType: 'touch' })
|
|
58
|
+
expect(marker.style.display).toBe('block')
|
|
59
|
+
|
|
60
|
+
marker.style.display = 'none' // reset, isolate the next assertion
|
|
61
|
+
ctx.map.fire('draw.interfacetypechange', { interfaceType: 'keyboard' })
|
|
62
|
+
expect(marker.style.display).toBe('block')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('draw.interfacetypechange to mouse never shows the crosshair — fires on every mode entry regardless of device', () => {
|
|
66
|
+
const { ctx, marker } = setup(DrawPolygonMode)
|
|
67
|
+
ctx.map.fire('draw.interfacetypechange', { interfaceType: 'mouse' })
|
|
68
|
+
expect(marker.style.display).toBe('none')
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
describe('rubber band and snapping while moving', () => {
|
|
73
|
+
test('onMouseMove moves the rubber band, preferring the snapped position', () => {
|
|
74
|
+
const { ctx, state } = setup(DrawPolygonMode, { getSnapEnabled: () => true })
|
|
75
|
+
clickAt(ctx, state, 0, 0)
|
|
76
|
+
ctx.map._snapInstance = activeSnap()
|
|
77
|
+
ctx.onMouseMove(state, { lngLat: { lng: 3, lat: 3 }, point: { x: 3, y: 3 } })
|
|
78
|
+
expect(ctx.map._snapInstance.snapToClosestPoint).toHaveBeenCalled()
|
|
79
|
+
expect(state.polygon.coordinates[0].at(-1)).toEqual([9, 9])
|
|
80
|
+
expect(firedWith(ctx.map, 'draw.geometrychange').length).toBeGreaterThan(0)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test('onMouseMove without snap uses the pointer position', () => {
|
|
84
|
+
const { ctx, state } = setup(DrawPolygonMode)
|
|
85
|
+
clickAt(ctx, state, 0, 0)
|
|
86
|
+
ctx.onMouseMove(state, { lngLat: { lng: 3, lat: 3 }, point: { x: 3, y: 3 } })
|
|
87
|
+
expect(state.polygon.coordinates[0].at(-1)).toEqual([3, 3])
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('onMouseMove with snapping enabled but no snap point falls back to the pointer position', () => {
|
|
91
|
+
const { ctx, state } = setup(DrawPolygonMode, { getSnapEnabled: () => true })
|
|
92
|
+
clickAt(ctx, state, 0, 0)
|
|
93
|
+
ctx.map._snapInstance = { status: true, snapStatus: false, snapCoords: null, snapToClosestPoint: jest.fn() }
|
|
94
|
+
ctx.onMouseMove(state, { lngLat: { lng: 3, lat: 3 }, point: { x: 3, y: 3 } })
|
|
95
|
+
expect(state.polygon.coordinates[0].at(-1)).toEqual([3, 3])
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('map move in keyboard interface keeps the rubber band at center, or at the snap point when snapping', () => {
|
|
99
|
+
const { ctx, state } = setup(DrawPolygonMode, { interfaceType: 'keyboard' })
|
|
100
|
+
clickAt(ctx, state, 0, 0)
|
|
101
|
+
ctx.map.fire('move')
|
|
102
|
+
expect(state.polygon.coordinates[0].at(-1)).toEqual([CENTER.lng, CENTER.lat])
|
|
103
|
+
|
|
104
|
+
state.getSnapEnabled = () => true
|
|
105
|
+
ctx.map._snapInstance = activeSnap()
|
|
106
|
+
ctx.map.fire('move')
|
|
107
|
+
expect(ctx.map._snapInstance.snapToClosestPoint).toHaveBeenCalled()
|
|
108
|
+
expect(state.polygon.coordinates[0].at(-1)).toEqual([9, 9])
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test('map move in mouse interface leaves the rubber band alone', () => {
|
|
112
|
+
const { ctx, state } = setup(DrawPolygonMode)
|
|
113
|
+
clickAt(ctx, state, 0, 0)
|
|
114
|
+
const before = [...state.polygon.coordinates[0].map((c) => [...c])]
|
|
115
|
+
ctx.map.fire('move')
|
|
116
|
+
expect(state.polygon.coordinates[0]).toEqual(before)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test('map move in keyboard line mode with snap fires geometrychange with state.line', () => {
|
|
120
|
+
const { ctx, state } = setup(DrawLineMode, { interfaceType: 'keyboard', getSnapEnabled: () => true })
|
|
121
|
+
clickAt(ctx, state, 0, 0)
|
|
122
|
+
ctx.map._snapInstance = activeSnap()
|
|
123
|
+
ctx.map.fire.mockClear()
|
|
124
|
+
ctx.map.fire('move')
|
|
125
|
+
expect(firedWith(ctx.map, 'draw.geometrychange').pop()).toBe(state.line)
|
|
126
|
+
})
|
|
127
|
+
})
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendering helpers for the shared draw mode: crosshair/vertex-marker visibility,
|
|
3
|
+
* interface switching, simulated mouse events at map centre, and the display-feature
|
|
4
|
+
* pass that adds a marker on every placed vertex. Part of createDrawMode.
|
|
5
|
+
*/
|
|
6
|
+
export const createRenderHelpers = ({ ParentMode, geometryType, getFeature, getPlacedCoords }) => ({
|
|
7
|
+
_simulateMouse (type, fn, state) {
|
|
8
|
+
const { map } = this
|
|
9
|
+
const center = map.getCenter()
|
|
10
|
+
const point = map.project(center)
|
|
11
|
+
fn.call(this, state, {
|
|
12
|
+
lngLat: center,
|
|
13
|
+
point,
|
|
14
|
+
originalEvent: new MouseEvent(type, {
|
|
15
|
+
clientX: point.x,
|
|
16
|
+
clientY: point.y,
|
|
17
|
+
bubbles: true,
|
|
18
|
+
cancelable: true
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
this._ctx.store.render()
|
|
22
|
+
|
|
23
|
+
this.map.fire('draw.geometrychange', state.polygon || state.line)
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
_showCrossHair (state) {
|
|
27
|
+
if (state.crossHair) { state.crossHair.show() } else { state.vertexMarker.style.display = 'block' }
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
_hideCrossHair (state) {
|
|
31
|
+
if (state.crossHair) { state.crossHair.hide() } else { state.vertexMarker.style.display = 'none' }
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
_setInterface (state, type, show = true) {
|
|
35
|
+
state.interfaceType = type
|
|
36
|
+
if (show) {
|
|
37
|
+
this._showCrossHair(state)
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
toDisplayFeatures (state, geojson, display) {
|
|
42
|
+
ParentMode.toDisplayFeatures.call(this, state, geojson, display)
|
|
43
|
+
|
|
44
|
+
// Display features carry the id in properties.id (no top-level id)
|
|
45
|
+
const feature = getFeature(state)
|
|
46
|
+
if (geojson.geometry.type === geometryType && geojson.properties.id === feature.id) {
|
|
47
|
+
// Parent modes render only some placed vertices (which ones varies by
|
|
48
|
+
// mapbox-gl-draw version) — add a marker on every placed vertex. The
|
|
49
|
+
// 'draw-vertex' meta is display-only: it's not in mapbox-gl-draw's
|
|
50
|
+
// META_TYPES, so featuresAt ignores these markers and the parent's own
|
|
51
|
+
// vertex click targets (click first/last to finish) keep working.
|
|
52
|
+
getPlacedCoords(geojson).forEach((coordinates) => display({
|
|
53
|
+
type: 'Feature',
|
|
54
|
+
properties: { meta: 'draw-vertex', parent: feature.id, active: 'false' },
|
|
55
|
+
geometry: { type: 'Point', coordinates }
|
|
56
|
+
}))
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
})
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { setup, clickAt, DrawPolygonMode, DrawLineMode } from './__helpers__/harness.js'
|
|
2
|
+
|
|
3
|
+
describe('vertex marker display', () => {
|
|
4
|
+
const displayed = (ctx, state, geometry, id) => {
|
|
5
|
+
const display = jest.fn()
|
|
6
|
+
ctx.toDisplayFeatures(state, { type: 'Feature', properties: { id }, geometry }, display)
|
|
7
|
+
return display.mock.calls.map(([f]) => f).filter((f) => f.properties.meta === 'draw-vertex')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
test('every placed polygon vertex gets a display-only draw-vertex marker', () => {
|
|
11
|
+
const { ctx, state } = setup(DrawPolygonMode)
|
|
12
|
+
clickAt(ctx, state, 0, 0)
|
|
13
|
+
clickAt(ctx, state, 10, 0)
|
|
14
|
+
clickAt(ctx, state, 10, 10)
|
|
15
|
+
const ring = [[0, 0], [10, 0], [10, 10], [5, 5], [0, 0]] // placed + rubber + closing
|
|
16
|
+
const markers = displayed(ctx, state, { type: 'Polygon', coordinates: [ring] }, state.polygon.id)
|
|
17
|
+
expect(markers.map((m) => m.geometry.coordinates)).toEqual([[0, 0], [10, 0], [10, 10]])
|
|
18
|
+
expect(markers[0].properties).toEqual({ meta: 'draw-vertex', parent: state.polygon.id, active: 'false' })
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('every placed line vertex gets a marker', () => {
|
|
22
|
+
const { ctx, state } = setup(DrawLineMode)
|
|
23
|
+
ctx.doClick(state)
|
|
24
|
+
const coords = [[5, 5], [8, 8]] // placed + rubber
|
|
25
|
+
const markers = displayed(ctx, state, { type: 'LineString', coordinates: coords }, state.line.id)
|
|
26
|
+
expect(markers.map((m) => m.geometry.coordinates)).toEqual([[5, 5]])
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('other features get no markers', () => {
|
|
30
|
+
const { ctx, state } = setup(DrawPolygonMode)
|
|
31
|
+
const ring = [[0, 0], [10, 0], [10, 10], [0, 0]]
|
|
32
|
+
expect(displayed(ctx, state, { type: 'Polygon', coordinates: [ring] }, 'other-feature')).toEqual([])
|
|
33
|
+
})
|
|
34
|
+
})
|