@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,246 @@
|
|
|
1
|
+
import { createDrawPointMode } from './drawPointMode.js'
|
|
2
|
+
import { wireInputEvents, applyCrossHairVisibility } from '../draw/drawInput.js'
|
|
3
|
+
import { createFeatureStore } from '../core/featureStore.js'
|
|
4
|
+
import { ADAPTER_EVENTS } from '../../../adapterEvents.js'
|
|
5
|
+
import { createFakeMap, createFakeManager, pointFeature } from '../__helpers__/harness.js'
|
|
6
|
+
|
|
7
|
+
jest.mock('../draw/drawInput.js', () => ({
|
|
8
|
+
wireInputEvents: jest.fn(() => ({ destroy: jest.fn() })),
|
|
9
|
+
applyCrossHairVisibility: jest.fn()
|
|
10
|
+
}))
|
|
11
|
+
|
|
12
|
+
// Matches DrawMode.test.js's own convention: real OL interactions are used, but drawstart/
|
|
13
|
+
// drawend/drawabort are dispatched as plain event objects directly (rather than exercised
|
|
14
|
+
// through the real appendCoordinates/startDrawing_/finishDrawing internals, which need a
|
|
15
|
+
// fully-featured OL map — getMap().getView().getProjection() — well beyond what the
|
|
16
|
+
// lightweight fake map here provides or needs to).
|
|
17
|
+
const setup = (options = {}) => {
|
|
18
|
+
const map = createFakeMap()
|
|
19
|
+
const manager = createFakeManager()
|
|
20
|
+
manager.store = createFeatureStore()
|
|
21
|
+
const emitted = () => manager.emit.mock.calls.map(([type, payload]) => ({ type, payload }))
|
|
22
|
+
const mode = createDrawPointMode({
|
|
23
|
+
map,
|
|
24
|
+
manager,
|
|
25
|
+
options: {
|
|
26
|
+
featureId: 'point-1',
|
|
27
|
+
properties: { symbol: 'pin' },
|
|
28
|
+
container: null,
|
|
29
|
+
crossHair: null,
|
|
30
|
+
snap: null,
|
|
31
|
+
mapProvider: { getCenter: () => [5, 5] },
|
|
32
|
+
...options
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
const interaction = map.interactions[0]
|
|
36
|
+
const wireArgs = wireInputEvents.mock.calls.at(-1)[0]
|
|
37
|
+
return { map, manager, emitted, mode, interaction, wireArgs }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
afterEach(() => jest.clearAllMocks())
|
|
41
|
+
|
|
42
|
+
describe('createDrawPointMode', () => {
|
|
43
|
+
test('adds a Point-type Draw interaction to the map', () => {
|
|
44
|
+
const { interaction } = setup()
|
|
45
|
+
expect(interaction).toBeDefined()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('suppresses OL\'s default sketch style — no blue cursor-following circle', () => {
|
|
49
|
+
const { interaction } = setup()
|
|
50
|
+
// OL stores the resolved style on the sketch overlay layer, not on the interaction
|
|
51
|
+
// itself — this is what OL actually calls per frame to render the sketch/hover point.
|
|
52
|
+
expect(interaction.overlay_.getStyle()()).toEqual([])
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('drawend commits the sketch under the requested id and fires CREATE', () => {
|
|
56
|
+
const { interaction, manager, emitted } = setup()
|
|
57
|
+
interaction.dispatchEvent({ type: 'drawend', feature: pointFeature([1, 2], 'ol-generated-id') })
|
|
58
|
+
|
|
59
|
+
const created = emitted().filter((e) => e.type === ADAPTER_EVENTS.CREATE)
|
|
60
|
+
expect(created).toHaveLength(1)
|
|
61
|
+
expect(created[0].payload.id).toBe('point-1') // re-id'd, not the OL-generated one
|
|
62
|
+
expect(created[0].payload.geometry.coordinates).toEqual([1, 2])
|
|
63
|
+
expect(created[0].payload.properties).toEqual({ symbol: 'pin' })
|
|
64
|
+
expect(manager.store.get('point-1')).toBeTruthy()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('calls the adapter-injected resolvePointSymbol hook with the committed OL feature, after re-id', () => {
|
|
68
|
+
const resolvePointSymbol = jest.fn()
|
|
69
|
+
const { interaction } = setup({ resolvePointSymbol })
|
|
70
|
+
interaction.dispatchEvent({ type: 'drawend', feature: pointFeature([1, 2], 'ol-generated-id') })
|
|
71
|
+
|
|
72
|
+
expect(resolvePointSymbol).toHaveBeenCalledTimes(1)
|
|
73
|
+
const [olFeature] = resolvePointSymbol.mock.calls[0]
|
|
74
|
+
expect(olFeature.getId()).toBe('point-1')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('does not throw when no resolvePointSymbol hook was supplied', () => {
|
|
78
|
+
const { interaction } = setup()
|
|
79
|
+
expect(() => interaction.dispatchEvent({ type: 'drawend', feature: pointFeature([1, 2], 'ol-generated-id') })).not.toThrow()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('defaults properties to {} when none are supplied', () => {
|
|
83
|
+
const { interaction, manager, emitted } = setup({ properties: undefined })
|
|
84
|
+
interaction.dispatchEvent({ type: 'drawend', feature: pointFeature([1, 2], 'ol-generated-id') })
|
|
85
|
+
const created = emitted().filter((e) => e.type === ADAPTER_EVENTS.CREATE)
|
|
86
|
+
// OL's GeoJSON writer reports no properties as null (not {}) — the meaningful
|
|
87
|
+
// assertion is what setProperties() was actually called with, on the stored feature.
|
|
88
|
+
expect(created[0].payload.properties).toBeNull()
|
|
89
|
+
const { geometry, ...nonGeometryProps } = manager.store.getOL('point-1').getProperties()
|
|
90
|
+
expect(nonGeometryProps).toEqual({})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('condition vetoes placement via onGeometryChange', () => {
|
|
94
|
+
const { interaction, manager, emitted } = setup()
|
|
95
|
+
manager._geometryValidator = () => ({ valid: false, reason: 'not allowed here' })
|
|
96
|
+
const condition = interaction.condition_
|
|
97
|
+
const allowed = condition({ coordinate: [1, 2], originalEvent: {} })
|
|
98
|
+
expect(allowed).toBe(false)
|
|
99
|
+
const blocked = emitted().filter((e) => e.type === ADAPTER_EVENTS.PLACEMENT_BLOCKED)
|
|
100
|
+
expect(blocked).toHaveLength(1)
|
|
101
|
+
expect(blocked[0].payload).toMatchObject({ reason: 'not allowed here', phase: 'place', mode: 'draw_point' })
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('condition allows an unvetoed placement', () => {
|
|
105
|
+
const { interaction } = setup()
|
|
106
|
+
const condition = interaction.condition_
|
|
107
|
+
expect(condition({ coordinate: [1, 2], originalEvent: {} })).toBe(true)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('condition blocks modifier-key clicks', () => {
|
|
111
|
+
const { interaction } = setup()
|
|
112
|
+
const condition = interaction.condition_
|
|
113
|
+
expect(condition({ coordinate: [1, 2], originalEvent: { shiftKey: true } })).toBe(false)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('drawabort emits CANCEL', () => {
|
|
117
|
+
const { interaction, emitted } = setup()
|
|
118
|
+
interaction.dispatchEvent({ type: 'drawabort' })
|
|
119
|
+
expect(emitted().some((e) => e.type === ADAPTER_EVENTS.CANCEL)).toBe(true)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
describe('touch/keyboard placement (via the wired placeVertex callback)', () => {
|
|
123
|
+
// appendCoordinates()/finishDrawing() are OL-internal machinery already exercised by
|
|
124
|
+
// OL's own test suite — what this mode owns is calling them with the right (possibly
|
|
125
|
+
// snapped) coordinate, gated by the same placement veto the mouse path uses.
|
|
126
|
+
const spyOnCommit = (interaction) => ({
|
|
127
|
+
append: jest.spyOn(interaction, 'appendCoordinates').mockImplementation(() => {}),
|
|
128
|
+
finish: jest.spyOn(interaction, 'finishDrawing').mockImplementation(() => {})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test('places at the (possibly snapped) map centre and hides the indicator', () => {
|
|
132
|
+
const snap = { apply: jest.fn((c) => [c[0] + 1, c[1] + 1]), hideIndicator: jest.fn() }
|
|
133
|
+
const mapProvider = { getCenter: () => [5, 5] }
|
|
134
|
+
const { wireArgs, interaction } = setup({ snap, mapProvider, interfaceType: 'touch' })
|
|
135
|
+
const { append, finish } = spyOnCommit(interaction)
|
|
136
|
+
|
|
137
|
+
wireArgs.placeVertex()
|
|
138
|
+
|
|
139
|
+
expect(snap.apply).toHaveBeenCalledWith([5, 5])
|
|
140
|
+
expect(snap.hideIndicator).toHaveBeenCalled()
|
|
141
|
+
expect(append).toHaveBeenCalledWith([[6, 6]])
|
|
142
|
+
expect(finish).toHaveBeenCalled()
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
test('does not snap on mouse interface', () => {
|
|
146
|
+
const snap = { apply: jest.fn(), hideIndicator: jest.fn() }
|
|
147
|
+
const { wireArgs, interaction } = setup({ snap, interfaceType: 'mouse' })
|
|
148
|
+
const { append } = spyOnCommit(interaction)
|
|
149
|
+
wireArgs.placeVertex()
|
|
150
|
+
expect(snap.apply).not.toHaveBeenCalled()
|
|
151
|
+
expect(append).toHaveBeenCalledWith([[5, 5]])
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test('a vetoed crosshair placement never commits', () => {
|
|
155
|
+
const { wireArgs, manager, emitted, interaction } = setup()
|
|
156
|
+
const { append, finish } = spyOnCommit(interaction)
|
|
157
|
+
manager._geometryValidator = () => false
|
|
158
|
+
wireArgs.placeVertex()
|
|
159
|
+
expect(append).not.toHaveBeenCalled()
|
|
160
|
+
expect(finish).not.toHaveBeenCalled()
|
|
161
|
+
expect(emitted().filter((e) => e.type === ADAPTER_EVENTS.PLACEMENT_BLOCKED)).toHaveLength(1)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
test('updateRubberbanding shows the snap indicator at the crosshair for touch/keyboard, not mouse', () => {
|
|
165
|
+
const snap = { apply: jest.fn(), hideIndicator: jest.fn() }
|
|
166
|
+
const mapProvider = { getCenter: () => [7, 7] }
|
|
167
|
+
const { wireArgs } = setup({ snap, mapProvider, interfaceType: 'keyboard' })
|
|
168
|
+
wireArgs.updateRubberbanding()
|
|
169
|
+
expect(snap.apply).toHaveBeenCalledWith([7, 7])
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
test('updateRubberbanding does nothing on mouse interface', () => {
|
|
173
|
+
const snap = { apply: jest.fn(), hideIndicator: jest.fn() }
|
|
174
|
+
const { wireArgs } = setup({ snap, interfaceType: 'mouse' })
|
|
175
|
+
wireArgs.updateRubberbanding()
|
|
176
|
+
expect(snap.apply).not.toHaveBeenCalled()
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
// Regression: without this, switching to touch/keyboard left the indicator hidden until
|
|
180
|
+
// the next pointermove/change:center — mirrors draw/drawInput.js's createDrawInput and
|
|
181
|
+
// the MapLibre adapter's onInterfaceTypeChange (which calls onMove(state) for the same
|
|
182
|
+
// reason).
|
|
183
|
+
test('switching interface type via the wired setInterfaceType immediately refreshes the snap indicator at the crosshair', () => {
|
|
184
|
+
const snap = { apply: jest.fn(), hideIndicator: jest.fn() }
|
|
185
|
+
const mapProvider = { getCenter: () => [9, 9] }
|
|
186
|
+
const { wireArgs } = setup({ snap, mapProvider, interfaceType: 'mouse' })
|
|
187
|
+
wireArgs.setInterfaceType('touch')
|
|
188
|
+
expect(snap.apply).toHaveBeenCalledWith([9, 9])
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
test('switching interface type via the wired setInterfaceType does not query snap for mouse', () => {
|
|
192
|
+
const snap = { apply: jest.fn(), hideIndicator: jest.fn() }
|
|
193
|
+
const { wireArgs } = setup({ snap, interfaceType: 'touch' })
|
|
194
|
+
wireArgs.setInterfaceType('mouse')
|
|
195
|
+
expect(snap.apply).not.toHaveBeenCalled()
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
test('clearLastCoord is a harmless no-op (points have no lastPlacedCoord bookkeeping)', () => {
|
|
199
|
+
const { wireArgs } = setup()
|
|
200
|
+
expect(() => wireArgs.clearLastCoord()).not.toThrow()
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
test('applies crosshair visibility for the initial interface type on setup', () => {
|
|
205
|
+
setup({ interfaceType: 'touch' })
|
|
206
|
+
expect(applyCrossHairVisibility).toHaveBeenCalledWith(null, 'touch')
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
describe('mode API', () => {
|
|
210
|
+
test('done/undo are no-ops — a single click has nothing pending or to undo', () => {
|
|
211
|
+
const { mode } = setup()
|
|
212
|
+
expect(() => mode.done()).not.toThrow()
|
|
213
|
+
expect(() => mode.undo()).not.toThrow()
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
test('cancel aborts the in-progress sketch', () => {
|
|
217
|
+
const { mode, interaction } = setup()
|
|
218
|
+
const abortSpy = jest.spyOn(interaction, 'abortDrawing').mockImplementation(() => {})
|
|
219
|
+
mode.cancel()
|
|
220
|
+
expect(abortSpy).toHaveBeenCalled()
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
test('setInterfaceType delegates to applyCrossHairVisibility', () => {
|
|
224
|
+
const { mode } = setup()
|
|
225
|
+
applyCrossHairVisibility.mockClear()
|
|
226
|
+
mode.setInterfaceType('touch')
|
|
227
|
+
expect(applyCrossHairVisibility).toHaveBeenCalledWith(null, 'touch')
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
test('setInterfaceType also refreshes the snap indicator at the crosshair for touch/keyboard', () => {
|
|
231
|
+
const snap = { apply: jest.fn(), hideIndicator: jest.fn() }
|
|
232
|
+
const mapProvider = { getCenter: () => [3, 4] }
|
|
233
|
+
const { mode } = setup({ snap, mapProvider })
|
|
234
|
+
mode.setInterfaceType('keyboard')
|
|
235
|
+
expect(snap.apply).toHaveBeenCalledWith([3, 4])
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
test('destroy emits the final interface type, tears down input and removes the interaction', () => {
|
|
239
|
+
const { mode, map, interaction, emitted } = setup()
|
|
240
|
+
const removeSpy = jest.spyOn(map, 'removeInteraction')
|
|
241
|
+
mode.destroy()
|
|
242
|
+
expect(emitted().some((e) => e.type === ADAPTER_EVENTS.INTERFACE_TYPE_CHANGE)).toBe(true)
|
|
243
|
+
expect(removeSpy).toHaveBeenCalledWith(interaction)
|
|
244
|
+
})
|
|
245
|
+
})
|
|
246
|
+
})
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { createPointSelectionState } from './pointSelectionState.js'
|
|
2
|
+
import { deriveModifyOp } from '../edit/modifyInteraction.js'
|
|
3
|
+
import { createPointDragInteraction } from './pointDragInteraction.js'
|
|
4
|
+
import { PIXEL_TOLERANCE } from '../edit/vertexHitTest.js'
|
|
5
|
+
import { createTouchHandler } from '../edit/touchHandler.js'
|
|
6
|
+
import { createKeyboardHandler } from '../edit/keyboardHandler.js'
|
|
7
|
+
import { movePoint, applyPointUndo } from './pointOps.js'
|
|
8
|
+
import { ADAPTER_EVENTS } from '../../../adapterEvents.js'
|
|
9
|
+
import { STYLES_CHANGED_EVENT } from '../core/internalEvents.js'
|
|
10
|
+
import { createLiveStroke } from '../../../validation/liveStroke.js'
|
|
11
|
+
|
|
12
|
+
const TOUCH_INTERFACE = 'touch'
|
|
13
|
+
// Shared phase name with edit_vertex's own move_vertex op — a moved point re-validates the
|
|
14
|
+
// same way (validation/rules.js has no Point-specific rule, so this only ever gates a
|
|
15
|
+
// consumer's own onGeometryChange callback).
|
|
16
|
+
const MOVE_VERTEX = 'commit-move'
|
|
17
|
+
|
|
18
|
+
// A symbol icon renders far bigger than buildModifyCondition's fixed-radius hit-test (tuned
|
|
19
|
+
// for a plain vertex dot), so this hit-tests the icon's own rendered pixels instead. No touch
|
|
20
|
+
// check — pointDragInteraction.js's handleDownEvent already gates on that before calling this.
|
|
21
|
+
const buildPointModifyCondition = ({ map, olFeature }) => (mapBrowserEvent) => {
|
|
22
|
+
const pixel = map.getEventPixel(mapBrowserEvent.originalEvent)
|
|
23
|
+
return !!map.forEachFeatureAtPixel(pixel, (feature) => feature === olFeature, { hitTolerance: PIXEL_TOLERANCE })
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Live invalid-stroke wiring for edit_point — unlike edit/EditMode.js's wireLiveStroke, this
|
|
28
|
+
* must NOT call olFeature.setStyle(...): a Stroke/Fill style renders nothing for a Point (see
|
|
29
|
+
* core/styles.js's own placeholder-style comment) and would make the icon vanish. There is
|
|
30
|
+
* also no dashed-stroke visual for a point to begin with (validation/rules.js has no
|
|
31
|
+
* Point-specific rule — see the module doc above), so onChange only relays the verdict to
|
|
32
|
+
* gate the Done button, never touching the feature's style.
|
|
33
|
+
*/
|
|
34
|
+
const wirePointLiveValidity = ({ manager, olFeature }) => {
|
|
35
|
+
const liveStroke = createLiveStroke({
|
|
36
|
+
onChange: (invalid, reason) => {
|
|
37
|
+
manager.emit(ADAPTER_EVENTS.VALIDITY_CHANGE, { valid: !invalid, reason })
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
const updateLiveValidity = () => {
|
|
41
|
+
const coordinates = olFeature.getGeometry().getCoordinates()
|
|
42
|
+
liveStroke.update({
|
|
43
|
+
feature: { type: 'Feature', geometry: { type: 'Point', coordinates } },
|
|
44
|
+
context: { mode: 'edit_point' },
|
|
45
|
+
numVertices: 1,
|
|
46
|
+
onGeometryChange: manager._geometryValidator
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
olFeature.getGeometry().on('change', updateLiveValidity)
|
|
50
|
+
return {
|
|
51
|
+
liveStroke,
|
|
52
|
+
destroy () {
|
|
53
|
+
olFeature.getGeometry().un('change', updateLiveValidity)
|
|
54
|
+
liveStroke.destroy()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const wireTouchHandler = ({ map, container, manager, snap, undoStack, selection }) => {
|
|
60
|
+
const { getState, setState, syncGeom, emitGeometryValidation } = selection
|
|
61
|
+
|
|
62
|
+
const touchHandler = createTouchHandler({
|
|
63
|
+
map,
|
|
64
|
+
container,
|
|
65
|
+
getState,
|
|
66
|
+
setState,
|
|
67
|
+
colors: manager.colors,
|
|
68
|
+
snap,
|
|
69
|
+
moveCoord: movePoint,
|
|
70
|
+
onVertexMoved ({ vertexIndex, previousCoord }) {
|
|
71
|
+
undoStack.push({ type: 'move_vertex', vertexIndex, previousCoord })
|
|
72
|
+
syncGeom()
|
|
73
|
+
emitGeometryValidation(MOVE_VERTEX, vertexIndex)
|
|
74
|
+
},
|
|
75
|
+
// A stray tap never relocates the point — only the offset-target drag and the D-pad do
|
|
76
|
+
// (mirrors the ML adapter's editPointMode/touchHandlers.js onTap no-op).
|
|
77
|
+
onTap () {}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
selection.setHooks({
|
|
81
|
+
onUpdate () {
|
|
82
|
+
if (getState().interfaceType === TOUCH_INTERFACE) {
|
|
83
|
+
touchHandler.updateTargetPosition()
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
return touchHandler
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Undo the last move — shared by the keyboard's Cmd/Ctrl+Z shortcut and the mode API's own
|
|
92
|
+
// undo() (Undo button/menu action), so both apply the exact same op-popping logic.
|
|
93
|
+
const createUndoAction = ({ olFeature, undoStack, selection }) => () => {
|
|
94
|
+
const op = undoStack.pop()
|
|
95
|
+
if (!op) {
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
applyPointUndo(olFeature, op)
|
|
99
|
+
selection.syncGeom()
|
|
100
|
+
// An undo commits the inverse change, so it must re-validate like any other commit —
|
|
101
|
+
// otherwise the Done gate could go stale. Always vertexIndex 0 — a point has only one.
|
|
102
|
+
selection.emitGeometryValidation(MOVE_VERTEX, 0)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const wireKeyboardHandler = ({ map, container, snap, undoStack, selection, touchHandler, doUndo }) => {
|
|
106
|
+
const { getState, setState, syncGeom, emitGeometryValidation } = selection
|
|
107
|
+
|
|
108
|
+
return createKeyboardHandler({
|
|
109
|
+
map,
|
|
110
|
+
getState,
|
|
111
|
+
setState,
|
|
112
|
+
snap,
|
|
113
|
+
moveCoord: movePoint,
|
|
114
|
+
onVertexMoved ({ vertexIndex, previousCoord }) {
|
|
115
|
+
undoStack.push({ type: 'move_vertex', vertexIndex, previousCoord })
|
|
116
|
+
syncGeom()
|
|
117
|
+
emitGeometryValidation(MOVE_VERTEX, vertexIndex)
|
|
118
|
+
},
|
|
119
|
+
// No midpoint concept for a point, so this is never actually reached — required by
|
|
120
|
+
// createKeyboardHandler's interface all the same. Not reachable through this module's
|
|
121
|
+
// public surface either, so there's no meaningful way to exercise it from a test.
|
|
122
|
+
/* istanbul ignore next */
|
|
123
|
+
onInserted () {},
|
|
124
|
+
// No delete-vertex action for a point — deleteVertex() below is a documented no-op.
|
|
125
|
+
onDeleted () {},
|
|
126
|
+
onUndo: doUndo,
|
|
127
|
+
onKeyboardActive () {
|
|
128
|
+
if (getState().interfaceType === 'keyboard') {
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
getState().interfaceType = 'keyboard'
|
|
132
|
+
touchHandler.hide()
|
|
133
|
+
container.focus({ preventScroll: true })
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Style hot-swap on map style change + touch-target reposition on map resize — mirrors
|
|
139
|
+
// edit/EditMode.js's wireMapSync, minus the vertex/midpoint handle layers a point has none of.
|
|
140
|
+
const wireMapSync = ({ map, manager, selection, touchHandler, live }) => {
|
|
141
|
+
const { state } = selection
|
|
142
|
+
|
|
143
|
+
const onStylesChanged = () => {
|
|
144
|
+
live.liveStroke.refresh()
|
|
145
|
+
touchHandler.updateColors(manager.colors)
|
|
146
|
+
}
|
|
147
|
+
manager.on(STYLES_CHANGED_EVENT, onStylesChanged)
|
|
148
|
+
|
|
149
|
+
const onMapSizeChange = () => {
|
|
150
|
+
if (state.interfaceType !== TOUCH_INTERFACE) {
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
map.once('postrender', () => touchHandler.updateTargetPosition())
|
|
154
|
+
}
|
|
155
|
+
map.on('change:size', onMapSizeChange)
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
destroy () {
|
|
159
|
+
manager.off(STYLES_CHANGED_EVENT, onStylesChanged)
|
|
160
|
+
map.un('change:size', onMapSizeChange)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @returns {{ setInterfaceType, done, setInvalid, cancel, undo, deleteVertex, nudgeSelectedVertex, destroy } | null}
|
|
167
|
+
*/
|
|
168
|
+
export const createEditPointMode = ({ map, manager, options }) => {
|
|
169
|
+
const { featureId, container, interfaceType, snap } = options
|
|
170
|
+
const { store, undoStack } = manager
|
|
171
|
+
|
|
172
|
+
const olFeature = store.getOL(featureId)
|
|
173
|
+
if (!olFeature) {
|
|
174
|
+
return null
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// edit_vertex's first interaction is always a click that selects a vertex, which as a side
|
|
178
|
+
// effect moves focus onto the map — satisfying keyboardHandler.js's isInteractiveElementFocused
|
|
179
|
+
// guard. A point is already selected from the moment this mode starts, so a keyboard-only
|
|
180
|
+
// session can go straight to arrow keys with no click in between; claim focus explicitly so
|
|
181
|
+
// that guard reads correctly from the first keypress (mirrors the ML adapter's own fix).
|
|
182
|
+
container?.focus({ preventScroll: true })
|
|
183
|
+
|
|
184
|
+
const originalFeatureStyle = olFeature.getStyle()
|
|
185
|
+
// Keeps showing the same "selected" (black ring) icon variant the interact plugin's own
|
|
186
|
+
// highlight uses, for the whole edit session — a style FUNCTION, not a one-off Style
|
|
187
|
+
// instance, so it re-reads manager.styles fresh on every render and survives a style
|
|
188
|
+
// hot-swap without needing to be re-applied. See core/styles.js's selectedPointStyleFor.
|
|
189
|
+
olFeature.setStyle(() => manager.styles.selectedPointStyleFor(olFeature))
|
|
190
|
+
|
|
191
|
+
const selection = createPointSelectionState({ manager, store, olFeature, interfaceType })
|
|
192
|
+
const { getState, setState } = selection
|
|
193
|
+
|
|
194
|
+
const pointDrag = createPointDragInteraction({
|
|
195
|
+
map,
|
|
196
|
+
olFeature,
|
|
197
|
+
getState,
|
|
198
|
+
condition: buildPointModifyCondition({ map, olFeature }),
|
|
199
|
+
snap,
|
|
200
|
+
onModifyEnd (prevCoords) {
|
|
201
|
+
selection.syncGeom()
|
|
202
|
+
const op = prevCoords && deriveModifyOp(prevCoords, getState().vertices)
|
|
203
|
+
if (!op) {
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
undoStack.push(op)
|
|
207
|
+
selection.emitGeometryValidation(MOVE_VERTEX, op.vertexIndex)
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
const doUndo = createUndoAction({ olFeature, undoStack, selection })
|
|
212
|
+
const live = wirePointLiveValidity({ manager, olFeature })
|
|
213
|
+
const touchHandler = wireTouchHandler({ map, container, manager, snap, undoStack, selection })
|
|
214
|
+
const keyboardHandler = wireKeyboardHandler({ map, container, snap, undoStack, selection, touchHandler, doUndo })
|
|
215
|
+
const mapSync = wireMapSync({ map, manager, selection, touchHandler, live })
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
setInterfaceType (type) {
|
|
219
|
+
const state = getState()
|
|
220
|
+
if (type === state.interfaceType) {
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
setState({ interfaceType: type })
|
|
224
|
+
if (type === TOUCH_INTERFACE) {
|
|
225
|
+
touchHandler.updateTargetPosition()
|
|
226
|
+
} else {
|
|
227
|
+
touchHandler.hide()
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
done () {
|
|
232
|
+
manager.emit(ADAPTER_EVENTS.EDIT_FINISH, store.toGeoJSON(olFeature))
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
// Committed-verdict write (events.js): routed through the live-stroke controller so its
|
|
236
|
+
// cached state stays in sync — see wirePointLiveValidity's own comment on why this never
|
|
237
|
+
// touches the feature's style.
|
|
238
|
+
setInvalid (invalid) {
|
|
239
|
+
live.liveStroke.set(invalid)
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
// Nothing to restore here: the pre-edit feature is kept as tempFeature in the reducer and
|
|
243
|
+
// events.js re-adds it on cancel.
|
|
244
|
+
cancel () {},
|
|
245
|
+
|
|
246
|
+
undo: doUndo,
|
|
247
|
+
|
|
248
|
+
// Intentionally a no-op — deleting a Point's one coordinate isn't a "delete vertex"
|
|
249
|
+
// action the way it is for a ring; whole-feature deletion is deleteFeature()'s job.
|
|
250
|
+
// Matches MaplibreDrawAdapter.deleteVertex's existing no-op precedent.
|
|
251
|
+
deleteVertex () {},
|
|
252
|
+
|
|
253
|
+
// MoveControls' D-pad, routed here via mapProvider.activeMoveTarget (events.js) — claimed
|
|
254
|
+
// once at construction (pointSelectionState's VERTEX_SELECTION) and held for the session.
|
|
255
|
+
nudgeSelectedVertex: keyboardHandler.nudgeByDelta,
|
|
256
|
+
|
|
257
|
+
destroy () {
|
|
258
|
+
live.destroy()
|
|
259
|
+
olFeature.setStyle(originalFeatureStyle)
|
|
260
|
+
selection.destroy()
|
|
261
|
+
mapSync.destroy()
|
|
262
|
+
pointDrag.destroy()
|
|
263
|
+
touchHandler.destroy()
|
|
264
|
+
keyboardHandler.destroy()
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|