@defra/interactive-map 0.0.39-alpha → 0.0.41-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/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/control-definition.md +9 -1
- package/docs/api/slots.md +26 -5
- package/docs/api.md +4 -4
- package/docs/assets/images/slot-map.svg +61 -262
- package/docs/examples/draw-tools.mdx +3 -3
- package/docs/plugins/draw.md +80 -17
- package/docs/plugins/search.md +1 -1
- package/govuk-prototype-kit.config.json +2 -0
- package/jest.setup.js +5 -0
- package/package.json +9 -1
- 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/esm/im-map-styles-plugin.js +1 -1
- package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -1
- package/plugins/beta/map-styles/dist/umd/index.js +1 -1
- package/plugins/datasets/dist/css/index.css +1 -1
- package/plugins/datasets/dist/esm/esriLayerAdapter.js +1 -1
- package/plugins/datasets/dist/esm/im-datasets-ml-adapter.js +1 -1
- package/plugins/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/datasets/dist/umd/im-datasets-esri-adapter.js +1 -1
- package/plugins/datasets/dist/umd/im-datasets-ml-adapter.js +1 -1
- package/plugins/datasets/dist/umd/im-datasets-plugin.js +1 -1
- package/plugins/datasets/dist/umd/index.js +1 -1
- package/plugins/datasets/src/adapters/esri/esriLayerAdapter.js +2 -3
- package/plugins/datasets/src/adapters/esri/esriLayerAdapter.test.js +175 -0
- package/plugins/datasets/src/adapters/esri/registry/esriDataset.js +1 -1
- package/plugins/datasets/src/adapters/esri/registry/esriDataset.test.js +55 -0
- package/plugins/datasets/src/adapters/loadLayerAdapter.js +1 -1
- package/plugins/datasets/src/adapters/loadLayerAdapter.test.js +61 -0
- package/plugins/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +1 -3
- package/plugins/datasets/src/adapters/maplibre/maplibreLayerAdapter.test.js +17 -3
- package/plugins/datasets/src/api/setOpacity.test.js +9 -0
- package/plugins/datasets/src/components/LayersMenu/LayersMenu.jsx +10 -25
- package/plugins/datasets/src/components/LayersMenu/LayersMenu.test.jsx +0 -42
- package/plugins/datasets/src/datasets.scss +0 -1
- package/plugins/datasets/src/fetch/createDynamicSource.js +135 -129
- package/plugins/datasets/src/fetch/createDynamicSource.test.js +318 -0
- package/plugins/datasets/src/fetch/fetchGeoJSON.test.js +83 -0
- package/plugins/datasets/src/index.test.js +36 -0
- package/plugins/datasets/src/initialise/DatasetsInit.jsx +17 -7
- package/plugins/datasets/src/initialise/DatasetsInit.test.jsx +262 -0
- package/plugins/datasets/src/initialise/initialiseDatasets.js +16 -5
- package/plugins/datasets/src/initialise/initialiseDatasets.test.js +42 -0
- package/plugins/datasets/src/manifest.js +0 -38
- package/plugins/datasets/src/manifest.test.js +49 -0
- package/plugins/datasets/src/reducers/datasetsToMenu.test.js +54 -1
- package/plugins/datasets/src/reducers/pluginState.js +3 -16
- package/plugins/datasets/src/reducers/pluginState.test.js +23 -0
- package/plugins/datasets/src/registry/dataset.js +15 -4
- package/plugins/datasets/src/registry/dataset.test.js +61 -0
- package/plugins/datasets/src/registry/datasetRegistry.js +12 -9
- package/plugins/datasets/src/registry/datasetRegistry.test.js +17 -5
- package/plugins/datasets/src/registry/globalDataset.js +0 -3
- package/plugins/datasets/src/registry/isVisibleWhen.js +6 -3
- package/plugins/datasets/src/registry/isVisibleWhen.test.js +11 -1
- package/plugins/datasets/src/utils/bbox.test.js +125 -0
- package/plugins/draw/dist/esm/im-draw-ml-adapter.js +1 -1
- package/plugins/draw/dist/esm/im-draw-ol-adapter.js +1 -1
- package/plugins/draw/dist/esm/im-draw-plugin.js +1 -1
- package/plugins/draw/dist/esm/index.js +1 -1
- package/plugins/draw/dist/umd/im-draw-ml-adapter.js +2 -1
- package/plugins/draw/dist/umd/im-draw-ml-adapter.js.LICENSE.txt +1 -0
- package/plugins/draw/dist/umd/im-draw-ol-adapter.js +1 -1
- package/plugins/draw/dist/umd/im-draw-plugin.js +1 -1
- package/plugins/draw/dist/umd/index.js +1 -1
- package/plugins/draw/src/DrawInit.jsx +12 -4
- package/plugins/draw/src/DrawInit.test.jsx +51 -10
- package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.js +92 -12
- package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.test.js +246 -4
- package/plugins/draw/src/adapters/maplibre/mapboxDraw.js +109 -14
- package/plugins/draw/src/adapters/maplibre/mapboxDraw.test.js +174 -11
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/clickHandlers.test.js +12 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.js +9 -1
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.test.js +24 -3
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/pointerHandlers.js +1 -1
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/pointerHandlers.test.js +1 -1
- package/plugins/draw/src/adapters/maplibre/modes/drawPointMode.js +194 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawPointMode.test.js +457 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/__helpers__/harness.js +102 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/keyboardHandlers.js +82 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/keyboardHandlers.test.js +83 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointOperations.js +55 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointOperations.test.js +62 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointerHandlers.js +138 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointerHandlers.test.js +197 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/touchHandlers.js +92 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/touchHandlers.test.js +101 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/undoHandlers.js +46 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/undoHandlers.test.js +42 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode.js +151 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode.test.js +156 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.js +5 -28
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.test.js +24 -39
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/pointerHandlers.js +4 -5
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.js +20 -23
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.test.js +25 -5
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.js +6 -36
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.test.js +7 -49
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.js +4 -54
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.test.js +8 -33
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.js +29 -72
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.test.js +11 -4
- package/plugins/draw/src/adapters/maplibre/pointSymbolImages.js +158 -0
- package/plugins/draw/src/adapters/maplibre/pointSymbolImages.test.js +314 -0
- package/plugins/draw/src/adapters/maplibre/snap/prototypePatches.js +6 -1
- package/plugins/draw/src/adapters/maplibre/snap/prototypePatches.test.js +21 -0
- package/plugins/draw/src/adapters/maplibre/snap/snapInstance.js +6 -2
- package/plugins/draw/src/adapters/maplibre/snap/snapInstance.test.js +12 -0
- package/plugins/draw/src/adapters/maplibre/styles.js +38 -11
- package/plugins/draw/src/adapters/maplibre/styles.test.js +39 -0
- package/plugins/draw/src/adapters/maplibre/utils/editModeEvents.js +79 -0
- package/plugins/draw/src/adapters/maplibre/utils/editModeEvents.test.js +135 -0
- package/plugins/draw/src/adapters/maplibre/utils/geometryValidation.js +42 -0
- package/plugins/draw/src/adapters/maplibre/utils/geometryValidation.test.js +85 -0
- package/plugins/draw/src/adapters/maplibre/utils/keyboardShortcuts.js +34 -0
- package/plugins/draw/src/adapters/maplibre/utils/keyboardShortcuts.test.js +78 -0
- package/plugins/draw/src/adapters/maplibre/utils/snapHelpers.js +16 -3
- package/plugins/draw/src/adapters/maplibre/utils/snapHelpers.test.js +21 -3
- package/plugins/draw/src/adapters/maplibre/utils/snapMovement.js +59 -0
- package/plugins/draw/src/adapters/maplibre/utils/snapMovement.test.js +73 -0
- package/plugins/draw/src/adapters/maplibre/utils/touchDragMath.js +33 -0
- package/plugins/draw/src/adapters/maplibre/utils/touchDragMath.test.js +70 -0
- package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.js +27 -2
- package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.test.js +93 -0
- package/plugins/draw/src/adapters/openlayers/__helpers__/harness.js +9 -0
- package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.js +19 -1
- package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.test.js +34 -1
- package/plugins/draw/src/adapters/openlayers/core/styles.js +73 -1
- package/plugins/draw/src/adapters/openlayers/core/styles.test.js +122 -3
- package/plugins/draw/src/adapters/openlayers/draw/DrawMode.js +1 -0
- package/plugins/draw/src/adapters/openlayers/draw/DrawMode.test.js +27 -1
- package/plugins/draw/src/adapters/openlayers/draw/drawInput.js +24 -4
- package/plugins/draw/src/adapters/openlayers/draw/drawInput.test.js +47 -3
- package/plugins/draw/src/adapters/openlayers/edit/EditMode.js +1 -1
- package/plugins/draw/src/adapters/openlayers/edit/EditMode.test.js +32 -1
- package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.js +9 -5
- package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.test.js +34 -1
- package/plugins/draw/src/adapters/openlayers/edit/modifyInteraction.js +5 -3
- package/plugins/draw/src/adapters/openlayers/edit/modifyInteraction.test.js +11 -0
- package/plugins/draw/src/adapters/openlayers/edit/nudge.js +12 -5
- package/plugins/draw/src/adapters/openlayers/edit/nudge.test.js +26 -3
- package/plugins/draw/src/adapters/openlayers/edit/touchHandler.js +18 -13
- package/plugins/draw/src/adapters/openlayers/edit/touchHandler.test.js +46 -1
- package/plugins/draw/src/adapters/openlayers/olDraw.js +11 -4
- package/plugins/draw/src/adapters/openlayers/olDraw.test.js +48 -6
- package/plugins/draw/src/adapters/openlayers/point/drawPointMode.js +156 -0
- package/plugins/draw/src/adapters/openlayers/point/drawPointMode.test.js +246 -0
- package/plugins/draw/src/adapters/openlayers/point/editPointMode.js +267 -0
- package/plugins/draw/src/adapters/openlayers/point/editPointMode.test.js +327 -0
- package/plugins/draw/src/adapters/openlayers/point/pointDragInteraction.js +71 -0
- package/plugins/draw/src/adapters/openlayers/point/pointOps.js +34 -0
- package/plugins/draw/src/adapters/openlayers/point/pointOps.test.js +32 -0
- package/plugins/draw/src/adapters/openlayers/point/pointSelectionState.js +82 -0
- package/plugins/draw/src/adapters/openlayers/point/pointSelectionState.test.js +107 -0
- package/plugins/draw/src/adapters/openlayers/point/pointSymbolImages.js +83 -0
- package/plugins/draw/src/adapters/openlayers/point/pointSymbolImages.test.js +214 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapIndicator.js +3 -1
- package/plugins/draw/src/adapters/openlayers/snap/snapInteraction.js +6 -9
- package/plugins/draw/src/adapters/openlayers/snap/snapInteraction.test.js +15 -4
- package/plugins/draw/src/adapters/openlayers/utils/olCoords.js +1 -1
- package/plugins/draw/src/api/addFeature.js +16 -1
- package/plugins/draw/src/api/addFeature.test.js +62 -5
- package/plugins/draw/src/api/editFeature.js +7 -4
- package/plugins/draw/src/api/editFeature.test.js +16 -0
- package/plugins/draw/src/api/newPoint.js +42 -0
- package/plugins/draw/src/api/newPoint.test.js +117 -0
- package/plugins/draw/src/api/setStyle.js +43 -0
- package/plugins/draw/src/api/setStyle.test.js +75 -0
- package/plugins/draw/src/events.js +21 -14
- package/plugins/draw/src/events.test.js +15 -1
- package/plugins/draw/src/manifest.js +18 -6
- package/plugins/draw/src/manifest.test.js +32 -1
- package/plugins/draw/src/reducer.test.js +7 -0
- package/plugins/draw/src/utils/stripInternalProperties.js +19 -0
- package/plugins/draw/src/utils/stripInternalProperties.test.js +50 -0
- package/plugins/draw/src/utils/symbolKeys.js +5 -0
- package/plugins/draw/src/validation/liveDrawChecks.test.js +54 -0
- package/plugins/draw/src/validation/liveStroke.test.js +64 -1
- 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/dist/umd/index.js +1 -1
- package/plugins/interact/src/hooks/useCrossHairVisibility.js +12 -4
- package/plugins/interact/src/hooks/useCrossHairVisibility.test.js +88 -1
- package/plugins/interact/src/hooks/useHighlightSync.js +33 -12
- package/plugins/interact/src/hooks/useHighlightSync.test.js +96 -25
- package/plugins/interact/src/utils/featureQueries.js +18 -0
- package/plugins/interact/src/utils/featureQueries.test.js +15 -0
- package/plugins/interact/src/utils/spatial.js +9 -3
- package/plugins/interact/src/utils/spatial.test.js +12 -1
- package/plugins/map-key/dist/css/index.css +62 -0
- package/plugins/map-key/dist/esm/im-map-key-plugin.js +1 -0
- package/plugins/map-key/dist/esm/index.js +1 -0
- package/plugins/map-key/dist/umd/im-map-key-plugin.js +2 -0
- package/plugins/map-key/dist/umd/im-map-key-plugin.js.LICENSE.txt +1 -0
- package/plugins/map-key/dist/umd/index.js +2 -0
- package/plugins/map-key/dist/umd/index.js.LICENSE.txt +1 -0
- package/plugins/map-key/package.json +28 -0
- package/plugins/map-key/src/components/Key/EmptyKey.jsx +7 -0
- package/plugins/{datasets → map-key}/src/components/Key/EmptyKey.test.jsx +2 -2
- package/plugins/map-key/src/components/Key/Key.jsx +41 -0
- package/plugins/{datasets → map-key}/src/components/Key/Key.module.scss +15 -14
- package/plugins/map-key/src/components/Key/Key.test.jsx +73 -0
- package/plugins/map-key/src/components/Key/KeyGroupItem.jsx +16 -0
- package/plugins/map-key/src/components/Key/KeyGroupItem.test.jsx +53 -0
- package/plugins/map-key/src/components/Key/KeyItem.jsx +29 -0
- package/plugins/map-key/src/components/Key/KeyItem.test.jsx +67 -0
- package/plugins/map-key/src/components/Key/KeySvg.jsx +42 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvg.test.jsx +14 -12
- package/plugins/map-key/src/components/Key/KeySvgLine.jsx +28 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgLine.test.jsx +1 -1
- package/plugins/map-key/src/components/Key/KeySvgPattern.jsx +23 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgPattern.test.jsx +2 -2
- package/plugins/map-key/src/components/Key/KeySvgRect.jsx +33 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgRect.test.jsx +1 -1
- package/plugins/map-key/src/components/Key/KeySvgSymbol.jsx +27 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgSymbol.test.jsx +5 -6
- package/plugins/map-key/src/components/Key/MapKey.jsx +50 -0
- package/plugins/map-key/src/components/Key/MapKey.test.jsx +130 -0
- package/plugins/{datasets → map-key}/src/components/Key/svgProperties.js +2 -2
- package/plugins/{datasets → map-key}/src/components/Key/svgProperties.test.js +2 -2
- package/plugins/map-key/src/index.js +13 -0
- package/plugins/map-key/src/index.test.js +31 -0
- package/plugins/map-key/src/initialise/MapKeyInit.jsx +15 -0
- package/plugins/map-key/src/initialise/MapKeyInit.test.jsx +39 -0
- package/plugins/map-key/src/manifest.js +30 -0
- package/plugins/map-key/src/manifest.test.js +63 -0
- package/plugins/map-key/src/mapKey.scss +4 -0
- package/plugins/map-key/src/registry/getDatasetRegistry.js +6 -0
- package/plugins/map-key/src/registry/getDatasetRegistry.test.js +23 -0
- package/plugins/map-key/src/registry/index.js +3 -0
- package/plugins/map-key/src/registry/index.test.js +15 -0
- package/plugins/menu/dist/css/index.css +80 -0
- package/plugins/menu/dist/esm/im-menu-plugin.js +1 -0
- package/plugins/menu/dist/esm/index.js +1 -0
- package/plugins/menu/dist/umd/im-menu-plugin.js +1 -0
- package/plugins/menu/dist/umd/index.js +2 -0
- package/plugins/menu/dist/umd/index.js.LICENSE.txt +1 -0
- package/plugins/menu/package.json +28 -0
- package/plugins/menu/src/components/Menu/CheckboxGroupWrapper.jsx +21 -0
- package/plugins/menu/src/components/Menu/GroupLegend.jsx +17 -0
- package/plugins/menu/src/components/Menu/GroupLegend.test.jsx +68 -0
- package/plugins/menu/src/components/Menu/Menu.jsx +19 -0
- package/plugins/menu/src/components/Menu/Menu.module.scss +90 -0
- package/plugins/menu/src/components/Menu/Menu.test.jsx +161 -0
- package/plugins/menu/src/components/Menu/MenuCheckbox.jsx +35 -0
- package/plugins/menu/src/components/Menu/MenuCheckbox.test.jsx +93 -0
- package/plugins/menu/src/components/Menu/MenuGroup.jsx +11 -0
- package/plugins/{datasets/src/components/LayersMenu/LayersMenuRadio.jsx → menu/src/components/Menu/MenuRadio.jsx} +6 -4
- package/plugins/{datasets/src/components/LayersMenu/LayersMenuRadio.test.jsx → menu/src/components/Menu/MenuRadio.test.jsx} +18 -18
- package/plugins/menu/src/components/Menu/RadioGroupWrapper.jsx +32 -0
- package/plugins/{datasets/src/components/LayersMenu/LayersRadioGroupWrapper.test.jsx → menu/src/components/Menu/RadioGroupWrapper.test.jsx} +21 -21
- package/plugins/menu/src/components/Menu/useIdPrefix.jsx +4 -0
- package/plugins/menu/src/index.js +13 -0
- package/plugins/menu/src/index.test.js +31 -0
- package/plugins/menu/src/initialise/MenuInit.jsx +24 -0
- package/plugins/menu/src/initialise/MenuInit.test.jsx +50 -0
- package/plugins/menu/src/initialise/createMenu.js +10 -0
- package/plugins/menu/src/initialise/createMenu.test.js +37 -0
- package/plugins/menu/src/manifest.js +35 -0
- package/plugins/menu/src/manifest.test.js +63 -0
- package/plugins/menu/src/menu.scss +4 -0
- package/plugins/menu/src/reducers/menuStateReducer.test.js +38 -0
- package/plugins/menu/src/reducers/pluginState.js +35 -0
- package/plugins/menu/src/reducers/pluginState.test.js +57 -0
- package/plugins/menu/src/registry/getDatasetRegistry.js +9 -0
- package/plugins/menu/src/registry/getDatasetRegistry.test.js +23 -0
- package/plugins/menu/src/registry/index.js +3 -0
- package/plugins/menu/src/registry/index.test.js +15 -0
- package/plugins/menu/src/registry/isVisibleWhen.js +43 -0
- package/plugins/menu/src/registry/isVisibleWhen.test.js +48 -0
- package/plugins/search/dist/css/index.css +1 -1
- package/plugins/search/dist/esm/im-search-plugin.js +1 -1
- package/plugins/search/dist/esm/index.js +1 -1
- package/plugins/search/dist/umd/im-search-plugin.js +1 -1
- package/plugins/search/dist/umd/index.js +1 -1
- package/plugins/search/src/Search.jsx +7 -5
- package/plugins/search/src/components/Form/Form.module.scss +9 -20
- package/plugins/search/src/index.js +0 -1
- package/plugins/search/src/index.test.js +2 -4
- package/plugins/search/src/manifest.js +1 -1
- package/plugins/search/src/search.scss +17 -16
- package/providers/beta/openlayers/dist/esm/im-openlayers-provider.js +1 -1
- package/providers/beta/openlayers/dist/umd/im-openlayers-provider.js +1 -1
- package/providers/beta/openlayers/dist/umd/index.js +1 -1
- package/providers/beta/openlayers/src/openlayersProvider.js +4 -1
- package/providers/beta/openlayers/src/openlayersProvider.test.js +18 -0
- package/providers/beta/openlayers/src/utils/highlightFeatures.js +52 -5
- package/providers/beta/openlayers/src/utils/highlightFeatures.test.js +176 -0
- package/providers/beta/openlayers/src/utils/symbolImages.js +40 -0
- package/providers/beta/openlayers/src/utils/symbolImages.test.js +58 -0
- package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/umd/index.js +1 -1
- package/providers/maplibre/src/utils/drawLayerBuckets.js +14 -0
- package/providers/maplibre/src/utils/drawLayerBuckets.test.js +18 -0
- package/providers/maplibre/src/utils/highlightFeatures.js +80 -55
- package/providers/maplibre/src/utils/highlightFeatures.test.js +156 -17
- package/providers/maplibre/src/utils/hoverCursor.js +30 -14
- package/providers/maplibre/src/utils/hoverCursor.test.js +35 -0
- package/providers/maplibre/src/utils/patternImages.js +1 -1
- package/providers/maplibre/src/utils/symbolImages.js +41 -3
- package/providers/maplibre/src/utils/symbolImages.test.js +40 -6
- package/rollup.esm.mjs +11 -1
- package/sonar-project.properties +7 -1
- package/src/App/components/Actions/Actions.test.jsx +47 -0
- package/src/App/components/Attributions/Attributions.module.scss +10 -0
- package/src/App/components/Hints/Hints.jsx +7 -2
- package/src/App/components/Hints/Hints.test.jsx +13 -0
- package/src/App/components/KeyboardHelp/KeyboardHelp.jsx +2 -3
- package/src/App/components/KeyboardHelp/KeyboardHelp.test.jsx +3 -6
- package/src/App/components/{MoveControl/MoveControl.jsx → MoveControls/MoveControls.jsx} +10 -10
- package/src/App/components/{MoveControl/MoveControl.module.scss → MoveControls/MoveControls.module.scss} +60 -29
- package/src/App/components/{MoveControl/MoveControl.test.jsx → MoveControls/MoveControls.test.jsx} +48 -48
- package/src/App/components/Panel/Panel.jsx +18 -3
- package/src/App/components/Panel/Panel.module.scss +18 -8
- package/src/App/components/Panel/Panel.test.jsx +34 -0
- package/src/App/components/Tooltip/Tooltip.test.jsx +12 -14
- package/src/App/components/Viewport/Viewport.jsx +9 -1
- package/src/App/components/Viewport/Viewport.test.jsx +11 -0
- package/src/App/hooks/useFeatureItems.js +16 -7
- package/src/App/hooks/useFeatureItems.test.js +57 -0
- package/src/App/hooks/useLayoutMeasurements.js +95 -84
- package/src/App/hooks/useLayoutMeasurements.test.js +61 -1
- package/src/App/initialiseApp.js +11 -8
- package/src/App/initialiseApp.test.js +15 -3
- package/src/App/layout/Layout.jsx +12 -11
- package/src/App/layout/Layout.test.jsx +2 -0
- package/src/App/layout/layout.module.scss +31 -17
- package/src/App/registry/keyboardShortcutRegistry.js +25 -13
- package/src/App/registry/keyboardShortcutRegistry.test.js +49 -32
- package/src/App/registry/pluginRegistry.js +2 -3
- package/src/App/registry/pluginRegistry.test.js +16 -3
- package/src/App/renderer/HtmlElementHost.jsx +18 -5
- package/src/App/renderer/HtmlElementHost.test.jsx +54 -0
- package/src/App/renderer/mapButtons.js +28 -12
- package/src/App/renderer/mapButtons.test.js +17 -4
- package/src/App/renderer/mapControls.js +3 -1
- package/src/App/renderer/mapControls.test.js +16 -0
- package/src/App/renderer/mapPanels.js +44 -9
- package/src/App/renderer/mapPanels.test.js +51 -2
- package/src/App/renderer/orderItems.js +28 -0
- package/src/App/renderer/orderItems.test.js +50 -0
- package/src/App/renderer/slotAggregator.js +2 -18
- package/src/App/renderer/slotHelpers.js +3 -1
- package/src/App/renderer/slotHelpers.test.js +5 -0
- package/src/App/renderer/slots.js +3 -0
- package/src/App/store/AppProvider.jsx +4 -2
- package/src/App/store/appDispatchMiddleware.js +1 -1
- package/src/App/store/appDispatchMiddleware.test.js +8 -0
- package/src/InteractiveMap/InteractiveMap.js +8 -2
- package/src/InteractiveMap/InteractiveMap.test.js +20 -3
- package/src/config/appConfig.js +17 -15
- package/src/config/appConfig.test.js +22 -22
- package/src/config/defaults.js +3 -1
- package/src/config/events.js +2 -0
- package/src/config/mergeConfig.js +7 -0
- package/src/config/mergeConfig.test.js +20 -0
- package/src/scss/main.scss +1 -1
- package/src/scss/settings/_colors.scss +1 -0
- package/src/scss/settings/_dimensions.scss +2 -3
- package/src/scss/settings/_transition.scss +1 -1
- package/src/services/eventBus.js +44 -0
- package/src/services/eventBus.test.js +83 -0
- package/src/services/symbolRegistry.js +1 -1
- package/src/test-utils.js +5 -0
- package/src/types.js +25 -7
- package/webpack.umd.mjs +2 -0
- package/plugins/datasets/src/components/Key/EmptyKey.jsx +0 -7
- package/plugins/datasets/src/components/Key/Key.jsx +0 -50
- package/plugins/datasets/src/components/Key/KeyGroupItem.jsx +0 -18
- package/plugins/datasets/src/components/Key/KeyItem.jsx +0 -23
- package/plugins/datasets/src/components/Key/KeySvg.jsx +0 -22
- package/plugins/datasets/src/components/Key/KeySvgLine.jsx +0 -19
- package/plugins/datasets/src/components/Key/KeySvgPattern.jsx +0 -13
- package/plugins/datasets/src/components/Key/KeySvgRect.jsx +0 -22
- package/plugins/datasets/src/components/Key/KeySvgSymbol.jsx +0 -16
- package/plugins/datasets/src/components/LayersMenu/LayersRadioGroupWrapper.jsx +0 -41
- /package/plugins/{datasets → menu}/src/reducers/menuStateReducer.js +0 -0
- /package/{providers/maplibre/src → src}/utils/rasteriseToImageData.js +0 -0
- /package/{providers/maplibre/src → src}/utils/rasteriseToImageData.test.js +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { createHarness } from './__helpers__/harness.js'
|
|
2
|
+
|
|
3
|
+
describe('keyboardHandlers', () => {
|
|
4
|
+
const keydown = (ctx, state, key, extra = {}) => ctx.onKeydown(state, { key, preventDefault: jest.fn(), stopPropagation: jest.fn(), ...extra })
|
|
5
|
+
const keyup = (ctx, state, key) => ctx.onKeyup(state, { key, stopPropagation: jest.fn() })
|
|
6
|
+
|
|
7
|
+
// isInteractiveElementFocused's own branches are covered by utils/keyboardShortcuts.test.js
|
|
8
|
+
// — this just checks onKeydown/onKeyup actually consult it.
|
|
9
|
+
test('shortcuts are ignored while a form control outside the viewport has focus', () => {
|
|
10
|
+
const { ctx, state } = createHarness()
|
|
11
|
+
const input = document.createElement('input')
|
|
12
|
+
document.body.appendChild(input)
|
|
13
|
+
input.focus()
|
|
14
|
+
keydown(ctx, state, 'ArrowRight')
|
|
15
|
+
keyup(ctx, state, 'ArrowRight')
|
|
16
|
+
expect(state.interfaceType).not.toBe('keyboard')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('space prevents the page scrolling — there is nothing to select, the point already is', () => {
|
|
20
|
+
const { ctx, state } = createHarness()
|
|
21
|
+
const e = { key: ' ', preventDefault: jest.fn(), stopPropagation: jest.fn() }
|
|
22
|
+
ctx.onKeydown(state, e)
|
|
23
|
+
expect(e.preventDefault).toHaveBeenCalled()
|
|
24
|
+
expect(state.interfaceType).toBe('keyboard')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// resolveSnapTarget's own snap/break-out-of-snap branches are covered by
|
|
28
|
+
// utils/snapMovement.test.js — this just checks onKeydown wires an arrow key through to it,
|
|
29
|
+
// records the starting position for undo on the first move, and leaves it fixed for the
|
|
30
|
+
// rest of a held-key sequence.
|
|
31
|
+
test('an arrow key moves the point and starts a keyboard-move undo sequence', () => {
|
|
32
|
+
const { ctx, state } = createHarness()
|
|
33
|
+
keydown(ctx, state, 'ArrowRight')
|
|
34
|
+
expect(state._keyboardMoveStartPosition).toEqual([5, 5])
|
|
35
|
+
expect(state.feature.coordinates).not.toEqual([5, 5])
|
|
36
|
+
|
|
37
|
+
keydown(ctx, state, 'ArrowRight') // still held → start position isn't reset
|
|
38
|
+
expect(state._keyboardMoveStartPosition).toEqual([5, 5])
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('arrow key guards a missing feature', () => {
|
|
42
|
+
const { ctx, state, map } = createHarness()
|
|
43
|
+
keydown(ctx, { ...state, featureId: 'missing' }, 'ArrowRight')
|
|
44
|
+
expect(map._undoStack).toHaveLength(0)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// isUndoShortcut/handleUndoShortcut's own edge cases (shift, wrong key, text-field focus)
|
|
48
|
+
// are covered by utils/keyboardShortcuts.test.js — this just checks onKeydown wires
|
|
49
|
+
// Cmd/Ctrl+Z through to handleUndo, and that Alt+arrow and Escape are left unbound here.
|
|
50
|
+
test('Cmd/Ctrl+Z undoes; Alt+arrow and Escape are left unbound', () => {
|
|
51
|
+
const { ctx, state } = createHarness()
|
|
52
|
+
const moveSpy = jest.spyOn(ctx, 'movePointByKey').mockImplementation(() => {})
|
|
53
|
+
keydown(ctx, state, 'ArrowRight', { altKey: true })
|
|
54
|
+
expect(moveSpy).toHaveBeenCalled() // Alt is not special-cased — nothing to navigate to
|
|
55
|
+
|
|
56
|
+
keydown(ctx, state, 'Escape')
|
|
57
|
+
expect(state.interfaceType).toBe('keyboard') // Escape does nothing but set the interface type
|
|
58
|
+
|
|
59
|
+
const undoSpy = jest.spyOn(ctx, 'handleUndo').mockImplementation(() => {})
|
|
60
|
+
keydown(ctx, state, 'z', { metaKey: true })
|
|
61
|
+
expect(undoSpy).toHaveBeenCalledTimes(1)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('onKeyup pushes a move undo after a held-arrow sequence, no-ops otherwise, and allows viewport focus', () => {
|
|
65
|
+
const { ctx, state, map } = createHarness()
|
|
66
|
+
state._keyboardMoveStartPosition = [10, 0]
|
|
67
|
+
keyup(ctx, state, 'ArrowRight')
|
|
68
|
+
expect(map._undoStack.pop()).toMatchObject({ type: 'move_point', vertexIndex: 0, previousPosition: [10, 0] })
|
|
69
|
+
|
|
70
|
+
keyup(ctx, state, 'ArrowRight') // no active sequence → no undo
|
|
71
|
+
expect(map._undoStack).toHaveLength(0)
|
|
72
|
+
|
|
73
|
+
const child = document.createElement('button') // focus inside the viewport is non-blocking
|
|
74
|
+
state.container.appendChild(child)
|
|
75
|
+
child.focus()
|
|
76
|
+
keyup(ctx, state, 'ArrowRight')
|
|
77
|
+
expect(state.interfaceType).toBe('keyboard')
|
|
78
|
+
|
|
79
|
+
// A non-arrow key just tracks the interface type, nothing else
|
|
80
|
+
keyup(ctx, state, 'z')
|
|
81
|
+
expect(map._undoStack).toHaveLength(0)
|
|
82
|
+
})
|
|
83
|
+
})
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { sharedSnapMovement } from '../../utils/snapMovement.js'
|
|
2
|
+
|
|
3
|
+
// A Point has a single coordinate, always selected — no ring/segment/index bookkeeping the
|
|
4
|
+
// way editVertexMode's vertexOperations.js needs. getOffset/getOffsetByDelta/resolveSnapTarget
|
|
5
|
+
// come from the shared mixin (utils/snapMovement.js) — they're already pure single-coordinate
|
|
6
|
+
// math with no ring dependency; movePoint/nudgePointByDelta replace moveVertex/
|
|
7
|
+
// nudgeVertexByDelta with the direct Point.prototype.updateCoordinate('', lng, lat) mutation
|
|
8
|
+
// mapbox-gl-draw's own Point feature model already provides, instead of the toGeoJSON-mutate-
|
|
9
|
+
// api.add() ring round-trip.
|
|
10
|
+
export const pointOperations = {
|
|
11
|
+
...sharedSnapMovement,
|
|
12
|
+
|
|
13
|
+
// The feature's own current coordinate — mapbox-gl-draw's Point model stores it flat as
|
|
14
|
+
// `.coordinates`, no getCoords()/index lookup needed the way a ring feature requires.
|
|
15
|
+
getPointCoord (state) {
|
|
16
|
+
return this.getFeature(state.featureId)?.coordinates
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
getNewCoord (state, e) {
|
|
20
|
+
return this.getOffset(this.getPointCoord(state), e)
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
// Point.prototype.updateCoordinate ignores its first (path) argument when called with
|
|
24
|
+
// three arguments — mapbox-gl-draw's own Point model has nothing path-shaped to address —
|
|
25
|
+
// and mutates + calls changed() in place, so unlike moveVertex there's no
|
|
26
|
+
// toGeoJSON-mutate-api.add() round-trip needed for the data itself. But that round-trip's
|
|
27
|
+
// api.add() call is also mapbox-gl-draw's own render trigger — updateCoordinate's changed()
|
|
28
|
+
// only marks the feature dirty and fires an internal event, it never repaints (see
|
|
29
|
+
// store.js's featureChanged/render). Mouse drag/click get a repaint for free regardless,
|
|
30
|
+
// since mapbox-gl-draw's own dispatch (lib/mode_handler.js) auto-renders after every
|
|
31
|
+
// onDrag/onClick/onMouseUp it calls — but keyboard, touch and the D-pad all move a point
|
|
32
|
+
// through our own custom addEventListener-bound handlers, entirely outside that dispatch,
|
|
33
|
+
// so without this explicit call the coordinate was updating with nothing on screen ever
|
|
34
|
+
// reflecting it.
|
|
35
|
+
movePoint (state, coord) {
|
|
36
|
+
state.feature.updateCoordinate('', coord.lng, coord.lat)
|
|
37
|
+
this._ctx.store.render()
|
|
38
|
+
this.map.fire('draw.geometrychange', state.feature)
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
// Moves the point by an explicit (dx, dy) unit direction — the entry point for
|
|
42
|
+
// MoveControls' D-pad (see mapProvider.activeMoveTarget in events.js). Each call is one
|
|
43
|
+
// complete, undoable action (no held-key sequencing — a button click has no "held" state
|
|
44
|
+
// the way arrow keys do), honouring snap via the shared resolveSnapTarget above.
|
|
45
|
+
nudgePointByDelta (state, dx, dy, isLargeStep) {
|
|
46
|
+
const currentCoord = this.getPointCoord(state)
|
|
47
|
+
if (!currentCoord) {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
const previousPosition = [...currentCoord]
|
|
51
|
+
const target = this.resolveSnapTarget(state, dx, dy, currentCoord, () => this.getOffsetByDelta(currentCoord, dx, dy, isLargeStep))
|
|
52
|
+
this.movePoint(state, target)
|
|
53
|
+
this.pushUndo({ type: 'move_point', featureId: state.featureId, vertexIndex: 0, previousPosition })
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createHarness } from './__helpers__/harness.js'
|
|
2
|
+
|
|
3
|
+
// getOffset/getOffsetByDelta/resolveSnapTarget are the shared mixin from
|
|
4
|
+
// utils/snapMovement.js — their own behaviour is covered by utils/snapMovement.test.js.
|
|
5
|
+
// This file only tests what's specific to a single-coordinate Point.
|
|
6
|
+
describe('pointOperations', () => {
|
|
7
|
+
test('getPointCoord reads the feature\'s own coordinate, no path/index lookup needed', () => {
|
|
8
|
+
const { ctx, state } = createHarness()
|
|
9
|
+
expect(ctx.getPointCoord(state)).toEqual([5, 5])
|
|
10
|
+
expect(ctx.getPointCoord({ ...state, featureId: 'missing' })).toBeUndefined()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('getNewCoord offsets the point\'s current coordinate', () => {
|
|
14
|
+
const { ctx, state } = createHarness()
|
|
15
|
+
expect(ctx.getNewCoord(state, { key: 'ArrowRight', shiftKey: false })).toHaveProperty('lng')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('movePoint mutates the coordinate directly via updateCoordinate and fires geometrychange', () => {
|
|
19
|
+
const { ctx, state, map } = createHarness()
|
|
20
|
+
ctx.movePoint(state, { lng: 1, lat: 2 })
|
|
21
|
+
expect(state.feature.coordinates).toEqual([1, 2])
|
|
22
|
+
expect(map.fire).toHaveBeenCalledWith('draw.geometrychange', state.feature)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
// Regression: updateCoordinate's own changed() call only marks the feature dirty and fires
|
|
26
|
+
// an internal event — it never repaints (store.js's featureChanged/render). Mouse drag/click
|
|
27
|
+
// get a repaint for free via mapbox-gl-draw's own dispatch (lib/mode_handler.js auto-renders
|
|
28
|
+
// after every onDrag/onClick/onMouseUp it calls), but keyboard, touch and the D-pad all move
|
|
29
|
+
// a point through our own addEventListener-bound handlers, entirely outside that dispatch —
|
|
30
|
+
// without this, the coordinate updated with nothing on screen ever reflecting it.
|
|
31
|
+
test('movePoint explicitly triggers a render — keyboard/touch/D-pad have no other path to one', () => {
|
|
32
|
+
const { ctx, state } = createHarness()
|
|
33
|
+
ctx.movePoint(state, { lng: 1, lat: 2 })
|
|
34
|
+
expect(ctx._ctx.store.render).toHaveBeenCalled()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('nudgePointByDelta moves the point and pushes a single undo entry', () => {
|
|
38
|
+
const { ctx, state, map } = createHarness()
|
|
39
|
+
const before = [...state.feature.coordinates]
|
|
40
|
+
ctx.nudgePointByDelta(state, 1, 0, true)
|
|
41
|
+
expect(state.feature.coordinates).not.toEqual(before)
|
|
42
|
+
expect(map._undoStack.pop()).toMatchObject({ type: 'move_point', vertexIndex: 0, previousPosition: before })
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('nudgePointByDelta no-ops when the feature is missing', () => {
|
|
46
|
+
const { ctx, state, map } = createHarness()
|
|
47
|
+
ctx.nudgePointByDelta({ ...state, featureId: 'missing' }, 1, 0, true)
|
|
48
|
+
expect(map._undoStack).toHaveLength(0)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// resolveSnapTarget's own snap/break-out-of-snap branches are covered by
|
|
52
|
+
// utils/snapMovement.test.js — this just checks nudgePointByDelta wires a snap hit through
|
|
53
|
+
// to the moved coordinate.
|
|
54
|
+
test('nudgePointByDelta honours an active snap', () => {
|
|
55
|
+
const { ctx, state, map } = createHarness()
|
|
56
|
+
state.getSnapEnabled = () => true
|
|
57
|
+
map._snapInstance = { status: true, snapStatus: true, snapCoords: [7, 8], snapToClosestPoint: jest.fn() }
|
|
58
|
+
ctx.nudgePointByDelta(state, 0, -1, true)
|
|
59
|
+
expect(state._isSnapped).toBe(true)
|
|
60
|
+
expect(state.feature.coordinates).toEqual([7, 8])
|
|
61
|
+
})
|
|
62
|
+
})
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import DirectSelect from '../../../../../../../node_modules/@mapbox/mapbox-gl-draw/src/modes/direct_select.js' // NOSONAR
|
|
2
|
+
import { isActiveFeature } from '../../../../../../../node_modules/@mapbox/mapbox-gl-draw/src/lib/common_selectors.js' // NOSONAR
|
|
3
|
+
import {
|
|
4
|
+
getSnapInstance, isSnapEnabled, getSnapLngLat, triggerSnapAtPoint, clearSnapState, setAutoSnapSuspended
|
|
5
|
+
} from '../../utils/snapHelpers.js'
|
|
6
|
+
import { validatePlacement } from '../../../../validation/validateGeometry.js'
|
|
7
|
+
|
|
8
|
+
const MODE = 'edit_point'
|
|
9
|
+
|
|
10
|
+
// A point has no ring/rubber-band to build a candidate from — just the coordinate about to
|
|
11
|
+
// be moved to. Mirrors drawPointMode.js's own canPlace, which mirrors validateGeometry.js's
|
|
12
|
+
// attemptPlacement — that helper hardcodes Polygon/LineString via MODE_BY_GEOMETRY, so a
|
|
13
|
+
// Point candidate is built and validated directly instead.
|
|
14
|
+
const canPlaceAt = (map, coord) => {
|
|
15
|
+
const candidate = { type: 'Feature', geometry: { type: 'Point', coordinates: coord }, properties: {} }
|
|
16
|
+
const { valid, reason } = validatePlacement(
|
|
17
|
+
candidate,
|
|
18
|
+
{ mode: MODE, vertexIndex: 0 },
|
|
19
|
+
{ onGeometryChange: map._drawGeometryValidator }
|
|
20
|
+
)
|
|
21
|
+
if (!valid) {
|
|
22
|
+
map.fire('draw.placementblocked', { feature: candidate, reason: reason ?? null, phase: 'place', mode: MODE, vertexIndex: 0 })
|
|
23
|
+
}
|
|
24
|
+
return valid
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Mouse/pointer interaction for the point-edit mode: drag (with snapping), click-elsewhere
|
|
29
|
+
* to relocate, and mouse-up undo bookkeeping. Wraps the parent DirectSelect handlers — with
|
|
30
|
+
* state.selectedCoordPaths built as [] (see editPointMode.js), DirectSelect's own onMouseDown
|
|
31
|
+
* (== onTouchStart, left unoverridden here — only touchHandlers.js's touch stubs neutralise
|
|
32
|
+
* the touch half) already routes a click on the point to onFeature/startDragging and its own
|
|
33
|
+
* onDrag/dragFeature already moves it via a whole-feature delta — this only adds the
|
|
34
|
+
* undo/snap/relocate behaviour stock DirectSelect has no concept of. Mixed into EditPointMode.
|
|
35
|
+
*/
|
|
36
|
+
export const pointerHandlers = {
|
|
37
|
+
onFeature (state, e) {
|
|
38
|
+
if (!state.dragMoving) {
|
|
39
|
+
state._moveStartPosition = [...state.feature.coordinates]
|
|
40
|
+
// Fixed lng/lat offset between the click and the anchor, captured once — onDrag adds it
|
|
41
|
+
// to the pointer's current position on every move so a click away from the anchor keeps
|
|
42
|
+
// its grab point for the whole drag.
|
|
43
|
+
state._grabOffset = [state.feature.coordinates[0] - e.lngLat.lng, state.feature.coordinates[1] - e.lngLat.lat]
|
|
44
|
+
// Silence the snap library's own auto-query for the drag — see setAutoSnapSuspended.
|
|
45
|
+
setAutoSnapSuspended(getSnapInstance(this.map), true)
|
|
46
|
+
}
|
|
47
|
+
DirectSelect.onFeature.call(this, state, e)
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
// Click-elsewhere relocates the point — the WCAG 2.5.7 single-pointer alternative to
|
|
51
|
+
// dragging. Clicking the point itself is a no-op (unlike DirectSelect.clickActiveFeature,
|
|
52
|
+
// which would clear the selection — the point stays selected for the whole edit session).
|
|
53
|
+
onClick (state, e) {
|
|
54
|
+
if (isActiveFeature(e)) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
if (!canPlaceAt(this.map, [e.lngLat.lng, e.lngLat.lat])) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const previousPosition = [...state.feature.coordinates]
|
|
62
|
+
const snap = getSnapInstance(this.map)
|
|
63
|
+
let target = e.lngLat
|
|
64
|
+
if (isSnapEnabled(state) && snap) {
|
|
65
|
+
triggerSnapAtPoint(snap, this.map, e.point)
|
|
66
|
+
target = getSnapLngLat(snap) || e.lngLat
|
|
67
|
+
}
|
|
68
|
+
this.movePoint(state, target)
|
|
69
|
+
this.pushUndo({ type: 'move_point', featureId: state.featureId, vertexIndex: 0, previousPosition })
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
onMouseUp (state, e) {
|
|
73
|
+
const snap = getSnapInstance(this.map)
|
|
74
|
+
clearSnapState(snap)
|
|
75
|
+
setAutoSnapSuspended(snap, false)
|
|
76
|
+
if (this._didPointMove(state)) {
|
|
77
|
+
this.pushUndo({ type: 'move_point', featureId: state.featureId, vertexIndex: 0, previousPosition: state._moveStartPosition })
|
|
78
|
+
}
|
|
79
|
+
state._moveStartPosition = null
|
|
80
|
+
DirectSelect.onMouseUp.call(this, state, e)
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// Did the point actually change position during this interaction? Reads the live
|
|
84
|
+
// feature coordinate directly — a Point has no ring index to look up.
|
|
85
|
+
_didPointMove (state) {
|
|
86
|
+
if (!state._moveStartPosition) {
|
|
87
|
+
return false
|
|
88
|
+
}
|
|
89
|
+
const current = state.feature.coordinates
|
|
90
|
+
return current[0] !== state._moveStartPosition[0] || current[1] !== state._moveStartPosition[1]
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
onDrag (state, e) {
|
|
94
|
+
if (state.interfaceType === 'touch') {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this.map.fire('draw.geometrychange', state.feature)
|
|
99
|
+
|
|
100
|
+
const snap = getSnapInstance(this.map)
|
|
101
|
+
if (snap) {
|
|
102
|
+
snap.snapStatus = false
|
|
103
|
+
snap.snapCoords = null
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!isSnapEnabled(state) || !snap?.status) {
|
|
107
|
+
// selectedCoordPaths is [] for a point (see editPointMode.js) — DirectSelect.onDrag's
|
|
108
|
+
// own selectedCoordPaths.length > 0 check routes this to dragFeature, its built-in
|
|
109
|
+
// whole-feature delta move, which already handles a Point feature correctly.
|
|
110
|
+
DirectSelect.onDrag.call(this, state, e)
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!state.canDragMove) {
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
state.dragMoving = true
|
|
119
|
+
e.originalEvent.stopPropagation()
|
|
120
|
+
|
|
121
|
+
// Candidate = pointer's current absolute position + the fixed grab offset — not an
|
|
122
|
+
// incremental delta, which would compound onto an already-snapped position and leave the
|
|
123
|
+
// pin stuck on the same target no matter how far the mouse then moves.
|
|
124
|
+
const candidateLngLat = {
|
|
125
|
+
lng: e.lngLat.lng + state._grabOffset[0],
|
|
126
|
+
lat: e.lngLat.lat + state._grabOffset[1]
|
|
127
|
+
}
|
|
128
|
+
triggerSnapAtPoint(snap, this.map, this.map.project([candidateLngLat.lng, candidateLngLat.lat]))
|
|
129
|
+
|
|
130
|
+
const finalLngLat = getSnapLngLat(snap) || candidateLngLat
|
|
131
|
+
state.feature.updateCoordinate('', finalLngLat.lng, finalLngLat.lat)
|
|
132
|
+
state.dragMoveLocation = e.lngLat
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
// Whole-feature deletion is deleteFeature()'s job, not this mode's — deleting a Point's
|
|
136
|
+
// one coordinate isn't a "delete vertex" operation the way it is for a ring.
|
|
137
|
+
onTrash () {}
|
|
138
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { createHarness, dragEvt } from './__helpers__/harness.js'
|
|
2
|
+
|
|
3
|
+
const activeFeatureTarget = { properties: { active: 'true', meta: 'feature' } }
|
|
4
|
+
|
|
5
|
+
describe('pointerHandlers', () => {
|
|
6
|
+
test('onFeature snapshots the move start and starts dragging via stock DirectSelect (selectedCoordPaths: [])', () => {
|
|
7
|
+
const { ctx, state } = createHarness()
|
|
8
|
+
ctx.onFeature(state, { featureTarget: activeFeatureTarget, lngLat: { lng: 5, lat: 5 } })
|
|
9
|
+
expect(state._moveStartPosition).toEqual([5, 5])
|
|
10
|
+
expect(state.canDragMove).toBe(true)
|
|
11
|
+
expect(state.dragMoveLocation).toEqual({ lng: 5, lat: 5 })
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
// Feature anchor is [5, 5] (createHarness's default) — clicking at [2, 2] is 3 units away,
|
|
15
|
+
// e.g. near the top of a pin icon rather than its tip.
|
|
16
|
+
test('onFeature captures the click\'s fixed offset from the anchor', () => {
|
|
17
|
+
const { ctx, state } = createHarness()
|
|
18
|
+
ctx.onFeature(state, { featureTarget: activeFeatureTarget, lngLat: { lng: 2, lat: 2 } })
|
|
19
|
+
expect(state._grabOffset).toEqual([3, 3])
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('onFeature does not re-snapshot mid-drag', () => {
|
|
23
|
+
const { ctx, state } = createHarness()
|
|
24
|
+
state.dragMoving = true
|
|
25
|
+
state._moveStartPosition = [1, 1]
|
|
26
|
+
state._grabOffset = [0, 0]
|
|
27
|
+
ctx.onFeature(state, { featureTarget: activeFeatureTarget, lngLat: { lng: 9, lat: 9 } })
|
|
28
|
+
expect(state._moveStartPosition).toEqual([1, 1])
|
|
29
|
+
expect(state._grabOffset).toEqual([0, 0])
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
// Silencing the snap library's own raw-cursor auto-query for the whole drag is what keeps
|
|
33
|
+
// the indicator and the dragged feature from falling out of sync — see
|
|
34
|
+
// snapHelpers.js's setAutoSnapSuspended for the full mechanism.
|
|
35
|
+
test('onFeature suspends the snap library\'s own auto-query at drag start; onMouseUp resumes it', () => {
|
|
36
|
+
const { ctx, state, map } = createHarness()
|
|
37
|
+
map._snapInstance = { status: true, snapStatus: false, snapCoords: null, snapToClosestPoint: jest.fn() }
|
|
38
|
+
|
|
39
|
+
ctx.onFeature(state, { featureTarget: activeFeatureTarget, lngLat: { lng: 5, lat: 5 } })
|
|
40
|
+
expect(map._snapInstance._autoSnapSuspended).toBe(true)
|
|
41
|
+
|
|
42
|
+
ctx.onMouseUp(state, {})
|
|
43
|
+
expect(map._snapInstance._autoSnapSuspended).toBe(false)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('onFeature suspending auto-snap is a harmless no-op when there is no snap instance', () => {
|
|
47
|
+
const { ctx, state } = createHarness()
|
|
48
|
+
expect(() => ctx.onFeature(state, { featureTarget: activeFeatureTarget, lngLat: { lng: 5, lat: 5 } })).not.toThrow()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('onClick on the active feature is a no-op — the point stays selected for the whole session', () => {
|
|
52
|
+
const { ctx, state, map } = createHarness()
|
|
53
|
+
const before = [...state.feature.coordinates]
|
|
54
|
+
ctx.onClick(state, { featureTarget: activeFeatureTarget })
|
|
55
|
+
expect(state.feature.coordinates).toEqual(before)
|
|
56
|
+
expect(map._undoStack).toHaveLength(0)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('onClick elsewhere relocates the point (WCAG 2.5.7 single-pointer alternative to drag), pushing an undo entry', () => {
|
|
60
|
+
const { ctx, state, map } = createHarness()
|
|
61
|
+
ctx.onClick(state, { featureTarget: undefined, lngLat: { lng: 9, lat: 9 }, point: { x: 90, y: 90 } })
|
|
62
|
+
expect(state.feature.coordinates).toEqual([9, 9])
|
|
63
|
+
expect(map._undoStack.pop()).toMatchObject({ type: 'move_point', vertexIndex: 0, previousPosition: [5, 5] })
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('onClick honours snap when enabled and active, falling back to the raw target when inactive', () => {
|
|
67
|
+
const { ctx, state, map } = createHarness()
|
|
68
|
+
state.getSnapEnabled = () => true
|
|
69
|
+
map._snapInstance = { status: true, snapStatus: true, snapCoords: [7, 8], snapToClosestPoint: jest.fn() }
|
|
70
|
+
ctx.onClick(state, { featureTarget: undefined, lngLat: { lng: 9, lat: 9 }, point: { x: 90, y: 90 } })
|
|
71
|
+
expect(state.feature.coordinates).toEqual([7, 8])
|
|
72
|
+
|
|
73
|
+
map._snapInstance = { status: true, snapStatus: false, snapCoords: null, snapToClosestPoint: jest.fn() }
|
|
74
|
+
ctx.onClick(state, { featureTarget: undefined, lngLat: { lng: 3, lat: 3 }, point: { x: 30, y: 30 } })
|
|
75
|
+
expect(state.feature.coordinates).toEqual([3, 3])
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('onClick is vetoed by a rejecting geometry validator, firing draw.placementblocked with a fallback null reason, and leaving the point untouched', () => {
|
|
79
|
+
const { ctx, state, map } = createHarness()
|
|
80
|
+
map._drawGeometryValidator = () => false // no {reason} shape
|
|
81
|
+
ctx.onClick(state, { featureTarget: undefined, lngLat: { lng: 9, lat: 9 }, point: { x: 90, y: 90 } })
|
|
82
|
+
expect(state.feature.coordinates).toEqual([5, 5])
|
|
83
|
+
expect(map.fire).toHaveBeenCalledWith('draw.placementblocked', expect.objectContaining({ reason: null }))
|
|
84
|
+
expect(map._undoStack).toHaveLength(0)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('onMouseUp clears snap state, records a move undo only if the point actually moved, and delegates to stock DirectSelect', () => {
|
|
88
|
+
const { ctx, state, map } = createHarness()
|
|
89
|
+
ctx.onMouseUp(state, {}) // no prior snapshot → nothing recorded
|
|
90
|
+
expect(map._undoStack).toHaveLength(0)
|
|
91
|
+
|
|
92
|
+
state._moveStartPosition = [5, 5]
|
|
93
|
+
ctx.movePoint(state, { lng: 9, lat: 9 })
|
|
94
|
+
ctx.onMouseUp(state, {})
|
|
95
|
+
expect(map._undoStack.pop()).toMatchObject({ type: 'move_point', vertexIndex: 0, previousPosition: [5, 5] })
|
|
96
|
+
expect(state._moveStartPosition).toBeNull()
|
|
97
|
+
|
|
98
|
+
// Snapshot present but the point never actually moved → no undo entry
|
|
99
|
+
state._moveStartPosition = [9, 9]
|
|
100
|
+
ctx.onMouseUp(state, {})
|
|
101
|
+
expect(map._undoStack).toHaveLength(0)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('onDrag skips touch, delegates without snap, and snaps when enabled', () => {
|
|
105
|
+
const { ctx, state, map } = createHarness()
|
|
106
|
+
ctx.onDrag({ ...state, interfaceType: 'touch' }, dragEvt(1, 1))
|
|
107
|
+
expect(state.feature.coordinates).toEqual([5, 5]) // untouched — bailed early for touch
|
|
108
|
+
|
|
109
|
+
// no snap → stock DirectSelect.onDrag/dragFeature moves the point via moveFeatures
|
|
110
|
+
Object.assign(state, { canDragMove: true, dragMoveLocation: { lng: 0, lat: 0 } })
|
|
111
|
+
ctx.onDrag(state, dragEvt(2, 2))
|
|
112
|
+
expect(map.fire).toHaveBeenCalledWith('draw.geometrychange', expect.anything())
|
|
113
|
+
expect(state.feature.coordinates).toEqual([7, 7])
|
|
114
|
+
|
|
115
|
+
// snap enabled but inactive → resolves via dragging still (canDragMove true)
|
|
116
|
+
state.getSnapEnabled = () => true
|
|
117
|
+
map._snapInstance = { status: true, snapStatus: false, snapCoords: null, snapToClosestPoint: jest.fn() }
|
|
118
|
+
Object.assign(state, { dragMoveLocation: { lng: 2, lat: 2 }, _grabOffset: [0, 0] })
|
|
119
|
+
ctx.onDrag(state, dragEvt(3, 3))
|
|
120
|
+
expect(state.dragMoving).toBe(true)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
test('onDrag with snap active writes the snapped coordinate directly', () => {
|
|
124
|
+
const { ctx, state, map } = createHarness() // feature anchor at [5, 5]
|
|
125
|
+
state.getSnapEnabled = () => true
|
|
126
|
+
ctx.onFeature(state, { featureTarget: activeFeatureTarget, lngLat: { lng: 0, lat: 0 } }) // grab offset [5,5]
|
|
127
|
+
map._snapInstance = { status: true, snapStatus: false, snapCoords: null, snapToClosestPoint: jest.fn(() => { map._snapInstance.snapStatus = true; map._snapInstance.snapCoords = [7, 8] }) }
|
|
128
|
+
ctx.onDrag(state, dragEvt(2, 2)) // candidate = [2,2] + grab offset [5,5] = [7,7], queried and snapped
|
|
129
|
+
expect(state.feature.coordinates).toEqual([7, 8])
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
// Regression: the candidate used to be built by accumulating the pointer's frame-to-frame
|
|
133
|
+
// delta onto the feature's own (possibly already-snapped) coordinate — so once a snap moved
|
|
134
|
+
// the feature onto a target, the next frame's small pointer movement produced a candidate
|
|
135
|
+
// still within that same target's radius and got pulled straight back onto it, reading as
|
|
136
|
+
// permanently stuck regardless of how far the mouse then moved. The candidate must instead
|
|
137
|
+
// always be the pointer's current absolute position plus the fixed grab offset from drag
|
|
138
|
+
// start, independent of wherever a previous frame's snap left the feature.
|
|
139
|
+
test('onDrag recovers on the next frame instead of staying stuck at a target it just snapped to', () => {
|
|
140
|
+
const { ctx, state, map } = createHarness() // feature anchor at [5, 5]
|
|
141
|
+
ctx.onFeature(state, { featureTarget: activeFeatureTarget, lngLat: { lng: 0, lat: 0 } }) // grab offset [5,5]
|
|
142
|
+
state.getSnapEnabled = () => true
|
|
143
|
+
map._snapInstance = {
|
|
144
|
+
status: true,
|
|
145
|
+
snapStatus: false,
|
|
146
|
+
snapCoords: null,
|
|
147
|
+
snapToClosestPoint: jest.fn(() => { map._snapInstance.snapStatus = true; map._snapInstance.snapCoords = [7, 8] })
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Frame 1: pointer at [2,2] → candidate [7,7], found and snapped onto the target [7,8].
|
|
151
|
+
ctx.onDrag(state, dragEvt(2, 2))
|
|
152
|
+
expect(state.feature.coordinates).toEqual([7, 8])
|
|
153
|
+
|
|
154
|
+
// Frame 2: pointer moves on by just one unit, to [3,2]. The fresh candidate must be built
|
|
155
|
+
// from the pointer's own absolute position ([3,2] + [5,5] = [8,7]), not from last frame's
|
|
156
|
+
// already-snapped [7,8] plus this frame's tiny delta (which would still read as sitting on
|
|
157
|
+
// the target).
|
|
158
|
+
map._snapInstance.snapToClosestPoint = jest.fn(() => { map._snapInstance.snapStatus = false; map._snapInstance.snapCoords = null })
|
|
159
|
+
ctx.onDrag(state, dragEvt(3, 2))
|
|
160
|
+
expect(map.project).toHaveBeenCalledWith([8, 7])
|
|
161
|
+
expect(state.feature.coordinates).toEqual([8, 7])
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
// The click that starts a drag can land anywhere on the icon, not just on top of its anchor
|
|
165
|
+
// coordinate — the anchor must move by the same delta as the pointer and snap must be queried
|
|
166
|
+
// at that offset-corrected candidate, not at the raw pointer (which would snap to whatever's
|
|
167
|
+
// near the cursor and jump the anchor under it the moment a candidate is found). Mirrors the
|
|
168
|
+
// OL adapter's point/pointDragInteraction.js regression test for the identical bug.
|
|
169
|
+
test('onDrag preserves the click\'s offset from the anchor and queries snap at the anchor\'s own candidate, not the raw pointer', () => {
|
|
170
|
+
const { ctx, state, map } = createHarness() // feature anchor at [5, 5]
|
|
171
|
+
// Mousedown at [2, 2] — 3 units away from the anchor, e.g. near the top of a pin icon.
|
|
172
|
+
ctx.onFeature(state, { featureTarget: activeFeatureTarget, lngLat: { lng: 2, lat: 2 } })
|
|
173
|
+
state.getSnapEnabled = () => true
|
|
174
|
+
map._snapInstance = { status: true, snapStatus: false, snapCoords: null, snapToClosestPoint: jest.fn() }
|
|
175
|
+
|
|
176
|
+
// Pointer moves from [2,2] to [4,2] — a (+2, 0) delta — no snap candidate found.
|
|
177
|
+
ctx.onDrag(state, dragEvt(4, 2))
|
|
178
|
+
expect(state.feature.coordinates).toEqual([7, 5]) // anchor [5,5] + the pointer's own delta
|
|
179
|
+
expect(map.project).toHaveBeenCalledWith([7, 5]) // queried at the anchor's candidate…
|
|
180
|
+
expect(map.project).not.toHaveBeenCalledWith([4, 2]) // …never at the raw pointer position
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
test('onDrag with snap enabled but not canDragMove bails out', () => {
|
|
184
|
+
const { ctx, state } = createHarness()
|
|
185
|
+
state.getSnapEnabled = () => true
|
|
186
|
+
state.canDragMove = false
|
|
187
|
+
ctx.map._snapInstance = { status: true, snapStatus: true, snapCoords: [1, 1], snapToClosestPoint: jest.fn() }
|
|
188
|
+
ctx.onDrag(state, dragEvt(2, 2))
|
|
189
|
+
expect(state.feature.coordinates).toEqual([5, 5])
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
test('onTrash is a no-op — whole-feature deletion is deleteFeature\'s job, not this mode\'s', () => {
|
|
193
|
+
const { ctx, state } = createHarness()
|
|
194
|
+
expect(() => ctx.onTrash(state)).not.toThrow()
|
|
195
|
+
expect(state.feature.coordinates).toEqual([5, 5])
|
|
196
|
+
})
|
|
197
|
+
})
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { getSnapInstance, clearSnapState } from '../../utils/snapHelpers.js'
|
|
2
|
+
import { isOnSVG, scalePoint } from '../editVertexMode/helpers.js'
|
|
3
|
+
import { createTouchTarget } from '../../../../utils/touchTarget.js'
|
|
4
|
+
// Reused, not redefined — mapboxDraw.js's style-reload path already imports this same symbol
|
|
5
|
+
// and keys off map._drawEditContainer, which edit_point also sets, so the recolour-on-
|
|
6
|
+
// style-change path works for the point's touch target with zero changes elsewhere.
|
|
7
|
+
import { applyTouchVertexColors } from '../editVertexMode/touchHandlers.js'
|
|
8
|
+
import { getTouchPoint, computeTouchDragAnchors, resolveTouchDragCoord } from '../../utils/touchDragMath.js'
|
|
9
|
+
|
|
10
|
+
// The offset-drag delta math here (utils/touchDragMath.js) is shared with editVertexMode/
|
|
11
|
+
// touchHandlers.js's onTouchstart/onTouchmove — it's already single-coordinate, not
|
|
12
|
+
// ring-specific — just sourced from the point's own coordinate instead of a vertex list
|
|
13
|
+
// index. onTap does NOT relocate the point (a stray tap during a pan-ish gesture shouldn't
|
|
14
|
+
// silently move it — the offset target drag and the D-pad are touch's two ways to move it).
|
|
15
|
+
export const touchHandlers = {
|
|
16
|
+
addTouchPointTarget (state) {
|
|
17
|
+
state.touchPointTarget = createTouchTarget(state.container)
|
|
18
|
+
applyTouchVertexColors(state.touchPointTarget, this.map._drawCurrentMapStyle, this.map._drawPluginConfig)
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
updateTouchPointTarget (state, point) {
|
|
22
|
+
if (point && state.interfaceType === 'touch') {
|
|
23
|
+
Object.assign(state.touchPointTarget.style, { display: 'block', top: `${point.y}px`, left: `${point.x}px` })
|
|
24
|
+
} else {
|
|
25
|
+
state.touchPointTarget.style.display = 'none'
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
hideTouchPointIndicator (state) {
|
|
30
|
+
state.touchPointTarget.style.display = 'none'
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// Point is always selected, so — unlike editVertexMode's onPointerevent — there's nothing
|
|
34
|
+
// to deselect on a non-touch pointermove; just track the interface type.
|
|
35
|
+
onPointerevent (state, e) {
|
|
36
|
+
state.interfaceType = e.pointerType === 'touch' ? 'touch' : 'mouse'
|
|
37
|
+
state.isPanEnabled = true
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
// Empty stubs required by DirectSelect
|
|
41
|
+
onTouchStart () {},
|
|
42
|
+
onTouchMove () {},
|
|
43
|
+
onTouchEnd () {},
|
|
44
|
+
|
|
45
|
+
onTouchend (state) {
|
|
46
|
+
clearSnapState(getSnapInstance(this.map))
|
|
47
|
+
if (state._touchMoved && state._moveStartPosition) {
|
|
48
|
+
this.pushUndo({ type: 'move_point', featureId: state.featureId, vertexIndex: 0, previousPosition: state._moveStartPosition })
|
|
49
|
+
}
|
|
50
|
+
state._moveStartPosition = null
|
|
51
|
+
state._touchMoved = false
|
|
52
|
+
|
|
53
|
+
// Re-sync the target to the point's actual final coordinate. Mid-drag it tracks the raw
|
|
54
|
+
// finger 1:1 from its touchstart-time offset (see onTouchmove) — a mid-drag snap moves the
|
|
55
|
+
// point without moving the target by the same amount, leaving them out of alignment. Left
|
|
56
|
+
// uncorrected, the next onTouchstart's delta math re-anchors off that drifted position,
|
|
57
|
+
// compounding a little further with every snapped drag.
|
|
58
|
+
if (state.feature) {
|
|
59
|
+
this.updateTouchPointTarget(state, scalePoint(this.map.project(state.feature.coordinates), state.scale))
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
// Touch tap on the point does nothing — relocating it is the offset target drag or the
|
|
64
|
+
// D-pad's job, not a raw tap.
|
|
65
|
+
onTap () {},
|
|
66
|
+
|
|
67
|
+
onTouchstart (state, e) {
|
|
68
|
+
clearSnapState(getSnapInstance(this.map))
|
|
69
|
+
const vertex = this.getPointCoord(state)
|
|
70
|
+
if (!vertex || !isOnSVG(e.target.parentNode)) {
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
state._moveStartPosition = [...vertex]
|
|
75
|
+
state._touchMoved = false
|
|
76
|
+
|
|
77
|
+
const touch = getTouchPoint(e)
|
|
78
|
+
Object.assign(state, computeTouchDragAnchors(this.map, state.touchPointTarget, touch, vertex, state.scale))
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
onTouchmove (state, e) {
|
|
82
|
+
if (!isOnSVG(e.target.parentNode)) {
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
state._touchMoved = true
|
|
87
|
+
|
|
88
|
+
const touch = getTouchPoint(e)
|
|
89
|
+
this.movePoint(state, resolveTouchDragCoord(this.map, state, touch))
|
|
90
|
+
this.updateTouchPointTarget(state, { x: touch.x - state.deltaTarget.x, y: touch.y - state.deltaTarget.y })
|
|
91
|
+
}
|
|
92
|
+
}
|