@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,327 @@
|
|
|
1
|
+
import { createEditPointMode } from './editPointMode.js'
|
|
2
|
+
import { createFeatureStore } from '../core/featureStore.js'
|
|
3
|
+
import { STYLES_CHANGED_EVENT } from '../core/internalEvents.js'
|
|
4
|
+
import { ADAPTER_EVENTS } from '../../../adapterEvents.js'
|
|
5
|
+
import { createFakeMap, createFakeManager, createContainer, domEvent } from '../__helpers__/harness.js'
|
|
6
|
+
|
|
7
|
+
const setup = (coordinates = [5, 5], snap = null) => {
|
|
8
|
+
const map = createFakeMap()
|
|
9
|
+
const manager = createFakeManager()
|
|
10
|
+
manager.store = createFeatureStore()
|
|
11
|
+
manager.store.add({ type: 'Feature', id: 'f1', properties: {}, geometry: { type: 'Point', coordinates } })
|
|
12
|
+
const container = createContainer()
|
|
13
|
+
map.getViewport().appendChild(container)
|
|
14
|
+
const mode = createEditPointMode({
|
|
15
|
+
map,
|
|
16
|
+
manager,
|
|
17
|
+
options: { featureId: 'f1', container, interfaceType: 'mouse', snap }
|
|
18
|
+
})
|
|
19
|
+
liveModes.push(mode)
|
|
20
|
+
const olFeature = manager.store.getOL('f1')
|
|
21
|
+
const tapAt = (x, y) => {
|
|
22
|
+
container.dispatchEvent(domEvent('touchstart', { touches: [{ clientX: x, clientY: y }] }))
|
|
23
|
+
container.dispatchEvent(domEvent('touchend', { changedTouches: [{ clientX: x, clientY: y }] }))
|
|
24
|
+
}
|
|
25
|
+
return { map, manager, container, mode, olFeature, coord: () => olFeature.getGeometry().getCoordinates(), tapAt }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const liveModes = []
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
liveModes.splice(0).forEach((m) => m?.destroy())
|
|
31
|
+
document.body.innerHTML = ''
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const key = (type, props) => window.dispatchEvent(new KeyboardEvent(type, { cancelable: true, ...props }))
|
|
35
|
+
|
|
36
|
+
test('returns null for an unknown feature id', () => {
|
|
37
|
+
const { map, manager } = setup()
|
|
38
|
+
expect(createEditPointMode({ map, manager, options: { featureId: 'nope', container: createContainer() } })).toBeNull()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('entering edit mode claims the D-pad exactly once, never emitting VERTEX_CHANGE', () => {
|
|
42
|
+
const { manager } = setup()
|
|
43
|
+
expect(manager.emit).toHaveBeenCalledWith(ADAPTER_EVENTS.VERTEX_SELECTION, { index: 0, numVertices: 1 })
|
|
44
|
+
expect(manager.emit).not.toHaveBeenCalledWith(ADAPTER_EVENTS.VERTEX_CHANGE, expect.anything())
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('sets the feature style once at construction, to the selected-icon override — nothing else re-touches it', () => {
|
|
48
|
+
const { olFeature, container, manager } = setup()
|
|
49
|
+
expect(olFeature.getStyle()).toEqual(expect.any(Function))
|
|
50
|
+
olFeature.getStyle()()
|
|
51
|
+
expect(manager.styles.selectedPointStyleFor).toHaveBeenCalledWith(olFeature)
|
|
52
|
+
|
|
53
|
+
const setStyleSpy = jest.spyOn(olFeature, 'setStyle')
|
|
54
|
+
olFeature.getGeometry().setCoordinates([9, 9])
|
|
55
|
+
key('keydown', { key: 'ArrowRight' })
|
|
56
|
+
key('keyup', { key: 'ArrowRight' })
|
|
57
|
+
container.dispatchEvent(domEvent('touchstart', { touches: [{ clientX: 9, clientY: 9 }] }))
|
|
58
|
+
expect(setStyleSpy).not.toHaveBeenCalled()
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('destroy restores the feature\'s pre-edit style', () => {
|
|
62
|
+
const { mode, olFeature } = setup()
|
|
63
|
+
const selectedStyleFn = olFeature.getStyle()
|
|
64
|
+
mode.destroy()
|
|
65
|
+
expect(olFeature.getStyle()).toBeNull() // unset before entering edit_point → falls back to the layer style
|
|
66
|
+
expect(olFeature.getStyle()).not.toBe(selectedStyleFn)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('validity flips (user callback only — a Point has no built-in HARD_RULES violation) gate the Done button', () => {
|
|
70
|
+
jest.useFakeTimers()
|
|
71
|
+
const { manager, olFeature } = setup()
|
|
72
|
+
manager._geometryValidator = jest.fn(() => ({ valid: false, reason: 'outside region' }))
|
|
73
|
+
olFeature.getGeometry().setCoordinates([9, 9])
|
|
74
|
+
jest.runAllTimers()
|
|
75
|
+
expect(manager.emit).toHaveBeenCalledWith(ADAPTER_EVENTS.VALIDITY_CHANGE,
|
|
76
|
+
expect.objectContaining({ valid: false, reason: 'outside region' }))
|
|
77
|
+
jest.useRealTimers()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('done() emits the edited feature; cancel() is a no-op', () => {
|
|
81
|
+
const { manager, mode } = setup()
|
|
82
|
+
mode.done()
|
|
83
|
+
expect(manager.emit).toHaveBeenCalledWith(ADAPTER_EVENTS.EDIT_FINISH, expect.objectContaining({ id: 'f1' }))
|
|
84
|
+
expect(mode.cancel()).toBeUndefined()
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('setInvalid routes through the live-stroke controller without touching the feature style', () => {
|
|
88
|
+
const { mode, olFeature } = setup()
|
|
89
|
+
const setStyleSpy = jest.spyOn(olFeature, 'setStyle')
|
|
90
|
+
mode.setInvalid(true)
|
|
91
|
+
mode.setInvalid(false)
|
|
92
|
+
expect(setStyleSpy).not.toHaveBeenCalled()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test('deleteVertex is a documented no-op — whole-feature deletion is deleteFeature\'s job', () => {
|
|
96
|
+
const { mode, coord } = setup()
|
|
97
|
+
const before = coord()
|
|
98
|
+
expect(() => mode.deleteVertex()).not.toThrow()
|
|
99
|
+
expect(coord()).toEqual(before)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('a mouse drag pushes a move_vertex op derived from the before/after coordinate', () => {
|
|
103
|
+
const { map, manager, olFeature } = setup()
|
|
104
|
+
const interaction = map.interactions[0]
|
|
105
|
+
map.forEachFeatureAtPixel = jest.fn((pixel, cb) => cb(olFeature))
|
|
106
|
+
const down = { originalEvent: {}, pixel: [5, 5] }
|
|
107
|
+
expect(interaction.handleDownEvent(down)).toBe(true)
|
|
108
|
+
interaction.handleDragEvent({ pixel: [9, 9] })
|
|
109
|
+
interaction.handleUpEvent()
|
|
110
|
+
expect(manager.undoStack.pop()).toEqual({ type: 'move_vertex', vertexIndex: 0, previousCoord: [5, 5] })
|
|
111
|
+
|
|
112
|
+
expect(interaction.handleDownEvent(down)).toBe(true)
|
|
113
|
+
interaction.handleUpEvent() // no drag in between — no coordinate change, nothing pushed
|
|
114
|
+
expect(manager.undoStack.length).toBe(0)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
// OL only calls handleDragEvent/handleUpEvent between a handleDownEvent that returned true and
|
|
118
|
+
// the matching release, but both guard against being invoked outside that sequence regardless.
|
|
119
|
+
test('handleDragEvent and handleUpEvent are no-ops without a preceding successful handleDownEvent', () => {
|
|
120
|
+
const { map, manager, olFeature, coord } = setup()
|
|
121
|
+
const interaction = map.interactions[0]
|
|
122
|
+
|
|
123
|
+
interaction.handleDragEvent({ pixel: [9, 9] }) // no down event — no grab offset captured yet
|
|
124
|
+
expect(coord()).toEqual([5, 5])
|
|
125
|
+
|
|
126
|
+
expect(() => interaction.handleUpEvent()).not.toThrow()
|
|
127
|
+
expect(manager.undoStack.length).toBe(0) // no start coordinate — nothing to derive an op from
|
|
128
|
+
expect(olFeature.getGeometry().getCoordinates()).toEqual([5, 5])
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// The mouse click can land anywhere on the icon, not just on top of its anchor coordinate —
|
|
132
|
+
// dragging must move the point by the same delta as the pointer, not snap the anchor under it
|
|
133
|
+
// (unlike OL's own Modify, which always sets the dragged coordinate to the pointer position).
|
|
134
|
+
test('a mouse drag preserves the click\'s offset from the anchor instead of snapping to the pointer', () => {
|
|
135
|
+
const { map, olFeature } = setup([5, 5])
|
|
136
|
+
const interaction = map.interactions[0]
|
|
137
|
+
map.forEachFeatureAtPixel = jest.fn((pixel, cb) => cb(olFeature))
|
|
138
|
+
// Click 3 map-units up-left of the anchor (e.g. near the top of a pin icon).
|
|
139
|
+
interaction.handleDownEvent({ originalEvent: {}, pixel: [2, 2] })
|
|
140
|
+
interaction.handleDragEvent({ pixel: [2, 2] }) // no pointer movement yet
|
|
141
|
+
expect(olFeature.getGeometry().getCoordinates()).toEqual([5, 5]) // anchor did NOT jump to [2, 2]
|
|
142
|
+
|
|
143
|
+
interaction.handleDragEvent({ pixel: [12, 2] }) // pointer moves 10 units right
|
|
144
|
+
expect(olFeature.getGeometry().getCoordinates()).toEqual([15, 5]) // anchor moves the same 10 units
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
// snap/snapInteraction.js's OL Interaction rewrites mapBrowserEvent.coordinate to the nearest
|
|
148
|
+
// candidate near the raw POINTER position — right for Modify, wrong here, since it would snap
|
|
149
|
+
// to whatever's near the cursor rather than near the icon's own anchor. This asserts snap.apply
|
|
150
|
+
// gets called with the offset-corrected anchor candidate, not the raw event coordinate.
|
|
151
|
+
test('a mouse drag snaps the icon\'s own offset-corrected position, not the raw pointer', () => {
|
|
152
|
+
const snap = { apply: jest.fn((coord) => [coord[0] + 100, coord[1] + 100]), hideIndicator: jest.fn() }
|
|
153
|
+
const { map, olFeature } = setup([5, 5], snap)
|
|
154
|
+
const interaction = map.interactions[0]
|
|
155
|
+
map.forEachFeatureAtPixel = jest.fn((pixel, cb) => cb(olFeature))
|
|
156
|
+
|
|
157
|
+
// Click 3 map-units up-left of the anchor, then drag 10 units right.
|
|
158
|
+
interaction.handleDownEvent({ originalEvent: {}, pixel: [2, 2] })
|
|
159
|
+
// mapBrowserEvent.coordinate deliberately left stale/wrong here — a real snapInteraction may
|
|
160
|
+
// have already rewritten it to something near the cursor before this interaction runs, and
|
|
161
|
+
// the fix must ignore it in favour of .pixel.
|
|
162
|
+
interaction.handleDragEvent({ pixel: [12, 2], coordinate: [999, 999] })
|
|
163
|
+
|
|
164
|
+
expect(snap.apply).toHaveBeenCalledWith([15, 5]) // the icon's own candidate position, not [999, 999]
|
|
165
|
+
expect(olFeature.getGeometry().getCoordinates()).toEqual([115, 105]) // snap.apply's result is applied
|
|
166
|
+
|
|
167
|
+
// The indicator must stay showing after the drag ends — snap.apply()'s own last call already
|
|
168
|
+
// left it correctly reflecting the snapped position (matches the ML adapter, which never
|
|
169
|
+
// hides its own indicator on release either).
|
|
170
|
+
interaction.handleUpEvent()
|
|
171
|
+
expect(snap.hideIndicator).not.toHaveBeenCalled()
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
// A symbol icon renders far bigger than PIXEL_TOLERANCE (tuned for a plain ~6px vertex dot)
|
|
175
|
+
// covers, so the mouse hit-test uses OL's own forEachFeatureAtPixel against the icon's real
|
|
176
|
+
// rendered pixels instead of a fixed-radius distance check.
|
|
177
|
+
describe('mouse hit-testing respects the icon\'s own rendered pixels, not a fixed radius', () => {
|
|
178
|
+
test('engages when forEachFeatureAtPixel hits this exact feature', () => {
|
|
179
|
+
const { map, olFeature } = setup()
|
|
180
|
+
map.forEachFeatureAtPixel = jest.fn((pixel, cb) => cb(olFeature))
|
|
181
|
+
const interaction = map.interactions[0]
|
|
182
|
+
expect(interaction.handleDownEvent({ originalEvent: { clientX: 5, clientY: 5 }, pixel: [5, 5] })).toBe(true)
|
|
183
|
+
expect(map.forEachFeatureAtPixel).toHaveBeenCalledWith([5, 5], expect.any(Function), { hitTolerance: expect.any(Number) })
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test('does not engage when forEachFeatureAtPixel finds nothing', () => {
|
|
187
|
+
const { map } = setup()
|
|
188
|
+
map.forEachFeatureAtPixel = jest.fn(() => undefined)
|
|
189
|
+
const interaction = map.interactions[0]
|
|
190
|
+
expect(interaction.handleDownEvent({ originalEvent: { clientX: 5, clientY: 5 }, pixel: [5, 5] })).toBe(false)
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
test('does not engage for a different feature under the pixel', () => {
|
|
194
|
+
const { map } = setup()
|
|
195
|
+
const otherFeature = {}
|
|
196
|
+
map.forEachFeatureAtPixel = jest.fn((pixel, cb) => cb(otherFeature))
|
|
197
|
+
const interaction = map.interactions[0]
|
|
198
|
+
expect(interaction.handleDownEvent({ originalEvent: { clientX: 5, clientY: 5 }, pixel: [5, 5] })).toBe(false)
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test('bails for touch without even querying', () => {
|
|
202
|
+
const { map, mode } = setup()
|
|
203
|
+
mode.setInterfaceType('touch')
|
|
204
|
+
map.forEachFeatureAtPixel = jest.fn()
|
|
205
|
+
const interaction = map.interactions[0]
|
|
206
|
+
expect(interaction.handleDownEvent({ originalEvent: { clientX: 5, clientY: 5 }, pixel: [5, 5] })).toBe(false)
|
|
207
|
+
expect(map.forEachFeatureAtPixel).not.toHaveBeenCalled()
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test('nudgeSelectedVertex (MoveControls D-pad) moves the point and is undoable', () => {
|
|
212
|
+
const { manager, mode, coord } = setup()
|
|
213
|
+
mode.nudgeSelectedVertex(1, 0, true)
|
|
214
|
+
expect(coord()).not.toEqual([5, 5])
|
|
215
|
+
expect(manager.undoStack.length).toBe(1)
|
|
216
|
+
|
|
217
|
+
mode.undo()
|
|
218
|
+
expect(coord()).toEqual([5, 5])
|
|
219
|
+
mode.undo() // empty stack — no-op
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
test('undo re-validates with the commit-move phase', () => {
|
|
223
|
+
jest.useFakeTimers()
|
|
224
|
+
const { manager, mode } = setup()
|
|
225
|
+
mode.nudgeSelectedVertex(1, 0, true)
|
|
226
|
+
jest.runAllTimers()
|
|
227
|
+
manager.emit.mockClear()
|
|
228
|
+
mode.undo()
|
|
229
|
+
jest.runAllTimers()
|
|
230
|
+
expect(manager.emit).toHaveBeenCalledWith(ADAPTER_EVENTS.GEOMETRY_CHANGE, expect.objectContaining({
|
|
231
|
+
phase: 'commit-move',
|
|
232
|
+
vertexIndex: 0,
|
|
233
|
+
feature: expect.any(Object)
|
|
234
|
+
}))
|
|
235
|
+
jest.useRealTimers()
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
test('keyboard: arrows nudge the point and commit one undo op on keyup', () => {
|
|
239
|
+
const { manager, coord } = setup()
|
|
240
|
+
key('keydown', { key: 'ArrowRight' })
|
|
241
|
+
key('keyup', { key: 'ArrowRight' })
|
|
242
|
+
expect(coord()).not.toEqual([5, 5])
|
|
243
|
+
expect(manager.undoStack.pop()).toEqual({ type: 'move_vertex', vertexIndex: 0, previousCoord: [5, 5] })
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
test('keyboard: Delete is a no-op — there is no delete-vertex action for a point', () => {
|
|
247
|
+
const { coord } = setup()
|
|
248
|
+
key('keyup', { key: 'Delete' })
|
|
249
|
+
expect(coord()).toEqual([5, 5])
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
test('keyboard: Cmd/Ctrl+Z undoes via the same op-popping logic as mode.undo()', () => {
|
|
253
|
+
const { manager, coord } = setup()
|
|
254
|
+
key('keydown', { key: 'ArrowRight' })
|
|
255
|
+
key('keyup', { key: 'ArrowRight' })
|
|
256
|
+
expect(manager.undoStack.length).toBe(1)
|
|
257
|
+
key('keydown', { key: 'z', metaKey: true })
|
|
258
|
+
expect(coord()).toEqual([5, 5])
|
|
259
|
+
expect(manager.undoStack.length).toBe(0)
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
test('keyboard: Escape and Space are inert — the point cannot be deselected, and there is nothing else to select', () => {
|
|
263
|
+
const { manager, coord } = setup()
|
|
264
|
+
manager.emit.mockClear()
|
|
265
|
+
key('keydown', { key: 'Escape' })
|
|
266
|
+
key('keydown', { key: ' ' })
|
|
267
|
+
expect(manager.emit).not.toHaveBeenCalledWith(ADAPTER_EVENTS.VERTEX_SELECTION, expect.anything())
|
|
268
|
+
key('keydown', { key: 'ArrowRight' })
|
|
269
|
+
key('keyup', { key: 'ArrowRight' })
|
|
270
|
+
expect(coord()).not.toEqual([5, 5]) // arrows still work after Escape/Space
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
test('touch: dragging the offset target moves the point and records one move op', () => {
|
|
274
|
+
const { container, manager, mode, tapAt, coord } = setup()
|
|
275
|
+
tapAt(5, 5)
|
|
276
|
+
mode.setInterfaceType('touch')
|
|
277
|
+
const grip = container.querySelector('[data-im-draw-touch-target] circle')
|
|
278
|
+
grip.dispatchEvent(domEvent('touchstart', { touches: [{ clientX: 5, clientY: 5 }] }))
|
|
279
|
+
grip.dispatchEvent(domEvent('touchmove', { touches: [{ clientX: 20, clientY: 15 }] }))
|
|
280
|
+
grip.dispatchEvent(domEvent('touchend', { changedTouches: [{ clientX: 20, clientY: 15 }] }))
|
|
281
|
+
expect(coord()).toEqual([20, 15])
|
|
282
|
+
expect(manager.undoStack.pop()).toEqual({ type: 'move_vertex', vertexIndex: 0, previousCoord: [5, 5] })
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
test('touch: a tap never relocates the point — only the offset-target drag and the D-pad do', () => {
|
|
286
|
+
const { manager, tapAt, coord } = setup()
|
|
287
|
+
manager.undoStack.clear()
|
|
288
|
+
tapAt(5, 5)
|
|
289
|
+
expect(coord()).toEqual([5, 5])
|
|
290
|
+
expect(manager.undoStack.length).toBe(0)
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
test('setInterfaceType shows/hides the touch target, and is a no-op for the same type', () => {
|
|
294
|
+
const { container, mode } = setup()
|
|
295
|
+
const target = container.querySelector('[data-im-draw-touch-target]')
|
|
296
|
+
mode.setInterfaceType('touch')
|
|
297
|
+
expect(target.style.display).toBe('block')
|
|
298
|
+
mode.setInterfaceType('touch') // same type — no change
|
|
299
|
+
mode.setInterfaceType('mouse')
|
|
300
|
+
expect(target.style.display).toBe('none')
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
test('style changes refresh the touch target colours, without touching the feature style', () => {
|
|
304
|
+
const { manager, olFeature } = setup()
|
|
305
|
+
const setStyleSpy = jest.spyOn(olFeature, 'setStyle')
|
|
306
|
+
const newStyles = { ...manager.styles }
|
|
307
|
+
manager.styles = newStyles
|
|
308
|
+
manager.emit(STYLES_CHANGED_EVENT, newStyles)
|
|
309
|
+
expect(setStyleSpy).not.toHaveBeenCalled()
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
test('map resize repositions the touch target after the next render — touch only', () => {
|
|
313
|
+
const { map, mode } = setup()
|
|
314
|
+
map.emit('change:size') // mouse interface — ignored
|
|
315
|
+
mode.setInterfaceType('touch')
|
|
316
|
+
map.emit('change:size')
|
|
317
|
+
map.emit('postrender') // once-listener fires; must not throw
|
|
318
|
+
expect(map.once).toHaveBeenCalledWith('postrender', expect.any(Function))
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
test('destroy removes the point drag interaction and listeners', () => {
|
|
322
|
+
const { map, mode } = setup()
|
|
323
|
+
const interactionsBefore = map.interactions.length
|
|
324
|
+
mode.destroy()
|
|
325
|
+
expect(map.removeInteraction).toHaveBeenCalled()
|
|
326
|
+
expect(map.interactions.length).toBeLessThan(interactionsBefore)
|
|
327
|
+
})
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import PointerInteraction from 'ol/interaction/Pointer.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mouse/pointer drag for edit_point — a hand-rolled `ol/interaction/Pointer` rather than
|
|
5
|
+
* edit/modifyInteraction.js's OL `Modify` wrapper. `Modify` snaps the dragged coordinate
|
|
6
|
+
* straight to the pointer, which is fine for a small vertex dot but jumps a symbol icon under
|
|
7
|
+
* the cursor the moment you click anywhere off its anchor — this instead holds a fixed
|
|
8
|
+
* click-to-anchor offset for the whole drag, so the icon moves exactly as far as the pointer
|
|
9
|
+
* does. Snap is applied to that offset-corrected candidate (read from .pixel, since
|
|
10
|
+
* snap/snapInteraction.js may already have rewritten .coordinate to something near the raw
|
|
11
|
+
* pointer) rather than the raw pointer itself, for the same reason.
|
|
12
|
+
*
|
|
13
|
+
* @param {{ map, olFeature, getState, condition, snap, onModifyEnd }} options
|
|
14
|
+
* @param {(mapBrowserEvent) => boolean} options.condition - gates drag start, e.g.
|
|
15
|
+
* buildPointModifyCondition's forEachFeatureAtPixel icon hit-test.
|
|
16
|
+
* @param {{ apply } | null} [options.snap] - snap/snapManager.js's controller, or
|
|
17
|
+
* null/undefined when no snap layers are configured.
|
|
18
|
+
* @param {(prevCoords: number[][]) => void} options.onModifyEnd - called once at drag end with
|
|
19
|
+
* the single-coordinate vertices array as it stood at drag start — same shape
|
|
20
|
+
* edit/modifyInteraction.js's onModifyEnd gets, so callers can share its deriveModifyOp.
|
|
21
|
+
* @returns {{ destroy: () => void }}
|
|
22
|
+
*/
|
|
23
|
+
export const createPointDragInteraction = ({ map, olFeature, getState, condition, snap, onModifyEnd }) => {
|
|
24
|
+
let grabOffset = null // [dx, dy] map-coordinate offset (anchor − pointer), held for the drag
|
|
25
|
+
let startCoord = null
|
|
26
|
+
|
|
27
|
+
// .pixel is never rewritten by snap/snapInteraction.js (unlike .coordinate), so it's the
|
|
28
|
+
// reliable way to get the true pointer position.
|
|
29
|
+
const pointerCoord = (mapBrowserEvent) => map.getCoordinateFromPixel(mapBrowserEvent.pixel)
|
|
30
|
+
|
|
31
|
+
const handleDownEvent = (mapBrowserEvent) => {
|
|
32
|
+
if (getState().interfaceType === 'touch' || !condition(mapBrowserEvent)) {
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
const coord = olFeature.getGeometry().getCoordinates()
|
|
36
|
+
const pointer = pointerCoord(mapBrowserEvent)
|
|
37
|
+
grabOffset = [coord[0] - pointer[0], coord[1] - pointer[1]]
|
|
38
|
+
startCoord = [...coord]
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const handleDragEvent = (mapBrowserEvent) => {
|
|
43
|
+
if (!grabOffset) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
const pointer = pointerCoord(mapBrowserEvent)
|
|
47
|
+
const rawCoord = [pointer[0] + grabOffset[0], pointer[1] + grabOffset[1]]
|
|
48
|
+
const newCoord = snap ? snap.apply(rawCoord) : rawCoord
|
|
49
|
+
olFeature.getGeometry().setCoordinates(newCoord)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const handleUpEvent = () => {
|
|
53
|
+
grabOffset = null
|
|
54
|
+
// Not hiding the snap indicator here — handleDragEvent's own snap.apply() already left it
|
|
55
|
+
// showing correctly, and it should stay that way after the drag ends.
|
|
56
|
+
if (startCoord) {
|
|
57
|
+
onModifyEnd([startCoord])
|
|
58
|
+
}
|
|
59
|
+
startCoord = null
|
|
60
|
+
return false // ends the drag sequence — never chains into another
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const interaction = new PointerInteraction({ handleDownEvent, handleDragEvent, handleUpEvent })
|
|
64
|
+
map.addInteraction(interaction)
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
destroy () {
|
|
68
|
+
map.removeInteraction(interaction)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coordinate mutation for edit_point. A Point has one coordinate, always selected — no
|
|
3
|
+
* ring/segment bookkeeping the way edit/undoOps.js and edit/vertexOps.js need for a
|
|
4
|
+
* Polygon/LineString. movePoint's (olFeature, _index, coord) signature matches
|
|
5
|
+
* edit/vertexOps.js's moveVertex exactly (the unused index is only there so nudge.js/
|
|
6
|
+
* touchHandler.js can call either interchangeably via the injected moveCoord parameter).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Signature-compatible with edit/vertexOps.js's moveVertex — _index is unused, a Point has none. */
|
|
10
|
+
export const movePoint = (olFeature, _index, coord) => {
|
|
11
|
+
olFeature.getGeometry().setCoordinates([...coord])
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Op type stays 'move_vertex' (not 'move_point') — deriveModifyOp (edit/modifyInteraction.js,
|
|
15
|
+
// reused unchanged) always produces that type, and keeping it lets applyPointUndo below mirror
|
|
16
|
+
// edit/undoOps.js's applyUndo dispatch pattern with the shared leaf handlers' onVertexMoved
|
|
17
|
+
// callbacks (which push {type: 'move_vertex', ...}) working completely unmodified.
|
|
18
|
+
export const undoMovePoint = (olFeature, op) => {
|
|
19
|
+
const { previousCoord } = op
|
|
20
|
+
olFeature.getGeometry().setCoordinates([...previousCoord])
|
|
21
|
+
return 0
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Dispatch the correct undo handler based on operation type — mirrors edit/undoOps.js's
|
|
26
|
+
* applyUndo, but with the single op type a Point ever produces.
|
|
27
|
+
* @returns {number} vertex index to re-select after undo (always 0), or -1 for none
|
|
28
|
+
*/
|
|
29
|
+
export const applyPointUndo = (olFeature, op) => {
|
|
30
|
+
if (op.type === 'move_vertex') {
|
|
31
|
+
return undoMovePoint(olFeature, op)
|
|
32
|
+
}
|
|
33
|
+
return -1
|
|
34
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { movePoint, undoMovePoint, applyPointUndo } from './pointOps.js'
|
|
2
|
+
import { pointFeature } from '../__helpers__/harness.js'
|
|
3
|
+
|
|
4
|
+
describe('movePoint', () => {
|
|
5
|
+
test('writes the coordinate directly, ignoring the unused index', () => {
|
|
6
|
+
const feature = pointFeature([0, 0])
|
|
7
|
+
movePoint(feature, 0, [5, 5])
|
|
8
|
+
expect(feature.getGeometry().getCoordinates()).toEqual([5, 5])
|
|
9
|
+
})
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
describe('undoMovePoint', () => {
|
|
13
|
+
test('restores the previous coordinate and returns vertex index 0', () => {
|
|
14
|
+
const feature = pointFeature([5, 5])
|
|
15
|
+
expect(undoMovePoint(feature, { previousCoord: [1, 2] })).toBe(0)
|
|
16
|
+
expect(feature.getGeometry().getCoordinates()).toEqual([1, 2])
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
describe('applyPointUndo', () => {
|
|
21
|
+
test('dispatches move_vertex to undoMovePoint', () => {
|
|
22
|
+
const feature = pointFeature([5, 5])
|
|
23
|
+
expect(applyPointUndo(feature, { type: 'move_vertex', previousCoord: [3, 4] })).toBe(0)
|
|
24
|
+
expect(feature.getGeometry().getCoordinates()).toEqual([3, 4])
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('returns -1 for an unknown operation type', () => {
|
|
28
|
+
const feature = pointFeature([5, 5])
|
|
29
|
+
expect(applyPointUndo(feature, { type: 'unknown' })).toBe(-1)
|
|
30
|
+
expect(feature.getGeometry().getCoordinates()).toEqual([5, 5])
|
|
31
|
+
})
|
|
32
|
+
})
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ADAPTER_EVENTS } from '../../../adapterEvents.js'
|
|
2
|
+
|
|
3
|
+
// Deferred commit-level geometrychange emitter (feature + change phase + vertex index)
|
|
4
|
+
// consumed by the validation layer — copied from edit/selectionState.js's own (identical
|
|
5
|
+
// shape, no ring dependency to strip).
|
|
6
|
+
const createGeometryValidationEmitter = (manager, store, olFeature) => (phase, vertexIndex) => {
|
|
7
|
+
if (!phase) { return }
|
|
8
|
+
setTimeout(() => {
|
|
9
|
+
manager.emit(ADAPTER_EVENTS.GEOMETRY_CHANGE, { feature: store.toGeoJSON(olFeature), phase, vertexIndex })
|
|
10
|
+
}, 0)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Mutable edit_point state, matching edit/selectionState.js's public surface
|
|
15
|
+
* ({state, getState, setState, syncGeom, emitGeometryValidation, setHooks, destroy}) closely
|
|
16
|
+
* enough that edit/modifyInteraction.js, edit/touchHandler.js and edit/keyboardHandler.js all
|
|
17
|
+
* run over it unmodified (beyond the moveCoord injection) — but a point has one coordinate,
|
|
18
|
+
* always selected, and no ring/midpoint list to navigate, so this is a standalone
|
|
19
|
+
* implementation rather than a wrapper around the ring-shaped one. No handle layer to keep in
|
|
20
|
+
* step either — the point's own symbol icon is the only visual, styled by the draw layer.
|
|
21
|
+
*
|
|
22
|
+
* @param {{ manager, store, olFeature, interfaceType }} options
|
|
23
|
+
* @returns {{ state, getState, setState, syncGeom, emitGeometryValidation, setHooks, destroy }}
|
|
24
|
+
*/
|
|
25
|
+
export const createPointSelectionState = ({ manager, store, olFeature, interfaceType }) => {
|
|
26
|
+
const state = {
|
|
27
|
+
olFeature,
|
|
28
|
+
selectedVertexIndex: 0,
|
|
29
|
+
selectedVertexType: 'vertex',
|
|
30
|
+
vertices: [],
|
|
31
|
+
midpoints: [],
|
|
32
|
+
interfaceType: interfaceType ?? 'mouse'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const hooks = { onUpdate: null }
|
|
36
|
+
const setHooks = ({ onUpdate }) => { hooks.onUpdate = onUpdate }
|
|
37
|
+
|
|
38
|
+
const refreshCoord = () => {
|
|
39
|
+
state.vertices = [olFeature.getGeometry().getCoordinates()]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// The point is always selected — a selectedVertexIndex/Type write (e.g. Escape or
|
|
43
|
+
// Alt+Arrow in the reused edit/keyboardHandler.js, which try to deselect/navigate) is
|
|
44
|
+
// silently dropped rather than stranding the point with no selection. Every other update
|
|
45
|
+
// (interfaceType, vertices) still applies normally.
|
|
46
|
+
const setState = (updates) => {
|
|
47
|
+
const { selectedVertexIndex, selectedVertexType, ...rest } = updates
|
|
48
|
+
Object.assign(state, rest)
|
|
49
|
+
if (updates.vertices !== undefined) {
|
|
50
|
+
hooks.onUpdate?.()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Never emits VERTEX_CHANGE — that event unconditionally releases
|
|
55
|
+
// mapProvider.activeMoveTarget (events.js's onVertexChange). The D-pad claim is made once
|
|
56
|
+
// below (VERTEX_SELECTION) and held for the whole edit session.
|
|
57
|
+
const syncGeom = () => {
|
|
58
|
+
refreshCoord()
|
|
59
|
+
manager.emit(ADAPTER_EVENTS.UPDATE, store.toGeoJSON(olFeature))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const emitGeometryValidation = createGeometryValidationEmitter(manager, store, olFeature)
|
|
63
|
+
|
|
64
|
+
// Keeps the cached coordinate in sync on every geometry change (e.g. during a Modify drag)
|
|
65
|
+
const onGeometryChange = () => refreshCoord()
|
|
66
|
+
olFeature.getGeometry().on('change', onGeometryChange)
|
|
67
|
+
|
|
68
|
+
refreshCoord()
|
|
69
|
+
manager.emit(ADAPTER_EVENTS.VERTEX_SELECTION, { index: 0, numVertices: 1 })
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
state,
|
|
73
|
+
getState: () => state,
|
|
74
|
+
setState,
|
|
75
|
+
syncGeom,
|
|
76
|
+
emitGeometryValidation,
|
|
77
|
+
setHooks,
|
|
78
|
+
destroy () {
|
|
79
|
+
olFeature.getGeometry().un('change', onGeometryChange)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { createPointSelectionState } from './pointSelectionState.js'
|
|
2
|
+
import { createFakeManager, pointFeature } from '../__helpers__/harness.js'
|
|
3
|
+
import { ADAPTER_EVENTS } from '../../../adapterEvents.js'
|
|
4
|
+
|
|
5
|
+
const setup = (interfaceType) => {
|
|
6
|
+
const manager = createFakeManager()
|
|
7
|
+
const olFeature = pointFeature([5, 5])
|
|
8
|
+
const store = { toGeoJSON: jest.fn(() => ({ id: 'f1' })) }
|
|
9
|
+
const selection = createPointSelectionState({ manager, store, olFeature, interfaceType })
|
|
10
|
+
return { manager, olFeature, store, selection }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
test('getState returns the live state object', () => {
|
|
14
|
+
const { selection } = setup()
|
|
15
|
+
expect(selection.getState()).toBe(selection.state)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('defaults to mouse interface when none is given, and is always selected at index 0', () => {
|
|
19
|
+
const { selection } = setup()
|
|
20
|
+
expect(selection.state.interfaceType).toBe('mouse')
|
|
21
|
+
expect(selection.state.selectedVertexIndex).toBe(0)
|
|
22
|
+
expect(selection.state.selectedVertexType).toBe('vertex')
|
|
23
|
+
expect(setup('touch').selection.state.interfaceType).toBe('touch')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('claims the D-pad exactly once at construction, never VERTEX_CHANGE', () => {
|
|
27
|
+
const { manager } = setup()
|
|
28
|
+
expect(manager.emit).toHaveBeenCalledWith(ADAPTER_EVENTS.VERTEX_SELECTION, { index: 0, numVertices: 1 })
|
|
29
|
+
expect(manager.emit).not.toHaveBeenCalledWith(ADAPTER_EVENTS.VERTEX_CHANGE, expect.anything())
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('populates the initial coordinate at construction', () => {
|
|
33
|
+
const { selection } = setup()
|
|
34
|
+
expect(selection.state.vertices).toEqual([[5, 5]])
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('a selectedVertexIndex/Type write is silently dropped — the point stays always-selected', () => {
|
|
38
|
+
const { selection, manager } = setup()
|
|
39
|
+
manager.emit.mockClear()
|
|
40
|
+
selection.setState({ selectedVertexIndex: -1, selectedVertexType: null })
|
|
41
|
+
expect(selection.state.selectedVertexIndex).toBe(0)
|
|
42
|
+
expect(selection.state.selectedVertexType).toBe('vertex')
|
|
43
|
+
expect(manager.emit).not.toHaveBeenCalledWith(ADAPTER_EVENTS.VERTEX_SELECTION, expect.anything())
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('other setState updates apply normally, without firing onUpdate unless vertices changes', () => {
|
|
47
|
+
const { selection } = setup()
|
|
48
|
+
const onUpdate = jest.fn()
|
|
49
|
+
selection.setHooks({ onUpdate })
|
|
50
|
+
selection.setState({ interfaceType: 'keyboard' })
|
|
51
|
+
expect(selection.state.interfaceType).toBe('keyboard')
|
|
52
|
+
expect(onUpdate).not.toHaveBeenCalled()
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('a vertices update fires the onUpdate hook', () => {
|
|
56
|
+
const { selection } = setup()
|
|
57
|
+
const onUpdate = jest.fn()
|
|
58
|
+
selection.setHooks({ onUpdate })
|
|
59
|
+
selection.setState({ vertices: [[9, 9]] })
|
|
60
|
+
expect(onUpdate).toHaveBeenCalled()
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test('syncGeom derives the coordinate from the geometry and emits UPDATE only (never VERTEX_CHANGE)', () => {
|
|
64
|
+
const { selection, manager, store, olFeature } = setup()
|
|
65
|
+
manager.emit.mockClear()
|
|
66
|
+
olFeature.getGeometry().setCoordinates([7, 8])
|
|
67
|
+
selection.syncGeom()
|
|
68
|
+
expect(selection.state.vertices).toEqual([[7, 8]])
|
|
69
|
+
expect(manager.emit).toHaveBeenCalledWith(ADAPTER_EVENTS.UPDATE, store.toGeoJSON())
|
|
70
|
+
expect(manager.emit).not.toHaveBeenCalledWith(ADAPTER_EVENTS.VERTEX_CHANGE, expect.anything())
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('emitGeometryValidation emits a deferred commit-level geometrychange with the change phase', () => {
|
|
74
|
+
jest.useFakeTimers()
|
|
75
|
+
const { selection, manager, store } = setup()
|
|
76
|
+
manager.emit.mockClear()
|
|
77
|
+
|
|
78
|
+
selection.emitGeometryValidation('commit-move', 0)
|
|
79
|
+
expect(manager.emit).not.toHaveBeenCalledWith(ADAPTER_EVENTS.GEOMETRY_CHANGE, expect.anything())
|
|
80
|
+
|
|
81
|
+
jest.runAllTimers()
|
|
82
|
+
expect(manager.emit).toHaveBeenCalledWith(ADAPTER_EVENTS.GEOMETRY_CHANGE, {
|
|
83
|
+
feature: store.toGeoJSON(),
|
|
84
|
+
phase: 'commit-move',
|
|
85
|
+
vertexIndex: 0
|
|
86
|
+
})
|
|
87
|
+
jest.useRealTimers()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('emitGeometryValidation is a no-op without a change phase', () => {
|
|
91
|
+
jest.useFakeTimers()
|
|
92
|
+
const { selection, manager } = setup()
|
|
93
|
+
manager.emit.mockClear()
|
|
94
|
+
selection.emitGeometryValidation(undefined, 0)
|
|
95
|
+
jest.runAllTimers()
|
|
96
|
+
expect(manager.emit).not.toHaveBeenCalledWith(ADAPTER_EVENTS.GEOMETRY_CHANGE, expect.anything())
|
|
97
|
+
jest.useRealTimers()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test('geometry changes refresh the cached coordinate until destroy unbinds the listener', () => {
|
|
101
|
+
const { selection, olFeature } = setup()
|
|
102
|
+
olFeature.getGeometry().setCoordinates([20, 20])
|
|
103
|
+
expect(selection.state.vertices).toEqual([[20, 20]])
|
|
104
|
+
selection.destroy()
|
|
105
|
+
olFeature.getGeometry().setCoordinates([30, 30])
|
|
106
|
+
expect(selection.state.vertices).toEqual([[20, 20]])
|
|
107
|
+
})
|