@defra/interactive-map 0.0.40-alpha → 0.0.42-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/button-definition.md +7 -8
- package/docs/api/control-definition.md +17 -1
- package/docs/api/panel-definition.md +5 -0
- package/docs/api/slots.md +50 -6
- package/docs/assets/images/slot-map.svg +61 -262
- package/docs/examples/add-polygons.mdx +8 -2
- package/docs/examples/add-symbols.mdx +8 -2
- package/docs/examples/draw-tools.mdx +3 -3
- package/docs/plugins/datasets.md +23 -15
- package/docs/plugins/draw.md +79 -16
- package/docs/plugins/map-key.md +147 -0
- package/docs/plugins/search.md +1 -1
- package/docs/plugins.md +5 -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/umd/im-map-styles-plugin.js +1 -1
- package/plugins/beta/map-styles/dist/umd/index.js +1 -1
- package/plugins/beta/use-location/dist/esm/im-use-location-plugin.js +1 -1
- package/plugins/beta/use-location/dist/umd/im-use-location-plugin.js +1 -1
- package/plugins/beta/use-location/src/manifest.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.test.js +175 -0
- package/plugins/datasets/src/adapters/esri/registry/esriDataset.test.js +55 -0
- package/plugins/datasets/src/adapters/loadLayerAdapter.test.js +61 -0
- package/plugins/datasets/src/adapters/maplibre/maplibreLayerAdapter.test.js +14 -0
- 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 -4
- 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 +13 -1
- package/plugins/datasets/src/registry/dataset.test.js +61 -0
- package/plugins/datasets/src/registry/datasetRegistry.js +12 -6
- package/plugins/datasets/src/registry/datasetRegistry.test.js +16 -4
- 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 +10 -2
- package/plugins/draw/src/DrawInit.test.jsx +51 -10
- package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.js +91 -11
- 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/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 +4 -27
- 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 +3 -53
- 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 +10 -3
- 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.test.js +12 -0
- package/plugins/draw/src/adapters/openlayers/draw/drawInput.js +8 -2
- package/plugins/draw/src/adapters/openlayers/edit/EditMode.test.js +31 -0
- package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.js +9 -5
- package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.test.js +33 -0
- 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 +10 -3
- package/plugins/draw/src/adapters/openlayers/edit/nudge.test.js +24 -1
- 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/createNewShape.js +41 -0
- package/plugins/draw/src/api/createNewShape.test.js +63 -0
- package/plugins/draw/src/api/editFeature.js +7 -4
- package/plugins/draw/src/api/editFeature.test.js +16 -0
- package/plugins/draw/src/api/newLine.js +2 -40
- package/plugins/draw/src/api/newPoint.js +42 -0
- package/plugins/draw/src/api/newPoint.test.js +117 -0
- package/plugins/draw/src/api/newPolygon.js +2 -40
- 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 +17 -10
- package/plugins/draw/src/events.test.js +14 -0
- 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/useHighlightSync.js +33 -12
- package/plugins/interact/src/hooks/useHighlightSync.test.js +96 -25
- package/plugins/interact/src/manifest.js +1 -1
- 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 +68 -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 +43 -0
- package/plugins/{datasets → map-key}/src/components/Key/Key.module.scss +21 -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 +26 -0
- package/plugins/map-key/src/components/Key/KeyItem.test.jsx +67 -0
- package/plugins/map-key/src/components/Key/KeySvg.jsx +41 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvg.test.jsx +14 -12
- package/plugins/map-key/src/components/Key/KeySvgLine.jsx +22 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgLine.test.jsx +1 -1
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgPattern.jsx +7 -2
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgPattern.test.jsx +8 -2
- package/plugins/map-key/src/components/Key/KeySvgRect.jsx +26 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgRect.test.jsx +1 -1
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgSymbol.jsx +8 -3
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgSymbol.test.jsx +17 -6
- package/plugins/map-key/src/components/Key/MapKey.jsx +53 -0
- package/plugins/map-key/src/components/Key/MapKey.test.jsx +147 -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 +2 -3
- 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/src/App/components/Actions/Actions.test.jsx +47 -0
- package/src/App/components/Hints/Hints.test.jsx +13 -0
- package/src/App/components/KeyboardHelp/KeyboardHelp.jsx +25 -23
- package/src/App/components/KeyboardHelp/KeyboardHelp.test.jsx +22 -5
- package/src/App/components/MoveControls/MoveControls.module.scss +41 -10
- package/src/App/components/Panel/Panel.jsx +65 -8
- package/src/App/components/Panel/Panel.module.scss +49 -15
- package/src/App/components/Panel/Panel.test.jsx +114 -1
- package/src/App/components/Tabs/Tabs.jsx +43 -17
- package/src/App/components/Tabs/Tabs.module.scss +32 -5
- package/src/App/components/Tabs/Tabs.test.jsx +40 -10
- package/src/App/components/Tooltip/Tooltip.test.jsx +12 -14
- 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/layout/Layout.jsx +9 -10
- package/src/App/layout/Layout.test.jsx +2 -0
- package/src/App/layout/layout.module.scss +31 -17
- package/src/App/registry/pluginRegistry.js +1 -1
- package/src/App/registry/pluginRegistry.test.js +12 -0
- package/src/App/renderer/HtmlElementHost.jsx +18 -5
- package/src/App/renderer/HtmlElementHost.test.jsx +54 -0
- package/src/App/renderer/groupByKey.js +28 -0
- package/src/App/renderer/groupByKey.test.js +39 -0
- package/src/App/renderer/groupIntoTabs.js +42 -0
- package/src/App/renderer/groupIntoTabs.test.js +95 -0
- package/src/App/renderer/mapButtons.js +97 -129
- package/src/App/renderer/mapButtons.test.js +67 -83
- package/src/App/renderer/mapControls.js +4 -1
- package/src/App/renderer/mapControls.test.js +32 -0
- package/src/App/renderer/mapPanels.js +53 -11
- package/src/App/renderer/mapPanels.test.js +95 -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 +1 -0
- package/src/App/store/appDispatchMiddleware.js +1 -1
- package/src/App/store/appDispatchMiddleware.test.js +8 -0
- package/src/config/appConfig.js +2 -2
- package/src/config/events.js +2 -0
- 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/types.js +31 -3
- 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/KeySvgRect.jsx +0 -22
- 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
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { flattenStyleProperties } from '../utils/flattenStyleProperties.js'
|
|
2
|
+
import { stripInternalProperties } from '../utils/stripInternalProperties.js'
|
|
3
|
+
import { logger } from '../../../../src/services/logger.js'
|
|
4
|
+
|
|
5
|
+
const SUPPORTED_GEOMETRY_TYPES = new Set(['Point', 'LineString', 'Polygon'])
|
|
6
|
+
|
|
7
|
+
// type: 'Feature' is defaulted below (both adapters mishandle it missing), but a missing or
|
|
8
|
+
// unsupported geometry is a genuine caller mistake, so that's rejected instead.
|
|
9
|
+
const isValidFeature = (feature) =>
|
|
10
|
+
SUPPORTED_GEOMETRY_TYPES.has(feature.geometry?.type) && feature.geometry?.coordinates !== undefined
|
|
2
11
|
|
|
3
12
|
export const addFeature = ({ mapProvider, services }, feature) => {
|
|
4
13
|
const { draw } = mapProvider
|
|
@@ -10,6 +19,7 @@ export const addFeature = ({ mapProvider, services }, feature) => {
|
|
|
10
19
|
|
|
11
20
|
const { stroke, fill, strokeWidth, properties, ...featureRest } = feature
|
|
12
21
|
const flatFeature = {
|
|
22
|
+
type: 'Feature',
|
|
13
23
|
...featureRest,
|
|
14
24
|
properties: {
|
|
15
25
|
...properties,
|
|
@@ -17,6 +27,11 @@ export const addFeature = ({ mapProvider, services }, feature) => {
|
|
|
17
27
|
}
|
|
18
28
|
}
|
|
19
29
|
|
|
30
|
+
if (!isValidFeature(flatFeature)) {
|
|
31
|
+
logger.warn('addFeature: ignoring invalid GeoJSON feature', flatFeature)
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
20
35
|
draw.add(flatFeature)
|
|
21
|
-
eventBus.emit('draw:add', flatFeature)
|
|
36
|
+
eventBus.emit('draw:add', stripInternalProperties(flatFeature))
|
|
22
37
|
}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { addFeature } from './addFeature.js'
|
|
2
2
|
import { flattenStyleProperties } from '../utils/flattenStyleProperties.js'
|
|
3
|
+
import { logger } from '../../../../src/services/logger.js'
|
|
3
4
|
|
|
4
5
|
jest.mock('../utils/flattenStyleProperties.js', () => ({
|
|
5
6
|
flattenStyleProperties: jest.fn(() => ({ _flat: true }))
|
|
6
7
|
}))
|
|
8
|
+
jest.mock('../../../../src/services/logger.js', () => ({
|
|
9
|
+
logger: { warn: jest.fn(), error: jest.fn() }
|
|
10
|
+
}))
|
|
7
11
|
|
|
8
12
|
const setup = (draw) => {
|
|
9
13
|
const eventBus = { emit: jest.fn() }
|
|
@@ -19,15 +23,27 @@ describe('addFeature', () => {
|
|
|
19
23
|
expect(eventBus.emit).not.toHaveBeenCalled()
|
|
20
24
|
})
|
|
21
25
|
|
|
22
|
-
test('flattens style properties and adds the feature', () => {
|
|
26
|
+
test('flattens style properties, defaults type to Feature, and adds the feature', () => {
|
|
23
27
|
const draw = { add: jest.fn() }
|
|
24
28
|
const { context, eventBus } = setup(draw)
|
|
25
|
-
const feature = {
|
|
29
|
+
const feature = {
|
|
30
|
+
id: 'a',
|
|
31
|
+
geometry: { type: 'Point', coordinates: [0, 0] },
|
|
32
|
+
stroke: 'red',
|
|
33
|
+
fill: 'blue',
|
|
34
|
+
strokeWidth: 2,
|
|
35
|
+
properties: { name: 'x' }
|
|
36
|
+
}
|
|
26
37
|
|
|
27
38
|
addFeature(context, feature)
|
|
28
39
|
|
|
29
40
|
expect(flattenStyleProperties).toHaveBeenCalledWith({ stroke: 'red', fill: 'blue', strokeWidth: 2 })
|
|
30
|
-
const expected = {
|
|
41
|
+
const expected = {
|
|
42
|
+
type: 'Feature',
|
|
43
|
+
id: 'a',
|
|
44
|
+
geometry: { type: 'Point', coordinates: [0, 0] },
|
|
45
|
+
properties: { name: 'x', _flat: true }
|
|
46
|
+
}
|
|
31
47
|
expect(draw.add).toHaveBeenCalledWith(expected)
|
|
32
48
|
expect(eventBus.emit).toHaveBeenCalledWith('draw:add', expected)
|
|
33
49
|
})
|
|
@@ -35,7 +51,48 @@ describe('addFeature', () => {
|
|
|
35
51
|
test('handles a feature without existing properties', () => {
|
|
36
52
|
const draw = { add: jest.fn() }
|
|
37
53
|
const { context } = setup(draw)
|
|
38
|
-
addFeature(context, { id: 'b', geometry: {} })
|
|
39
|
-
expect(draw.add).toHaveBeenCalledWith({
|
|
54
|
+
addFeature(context, { id: 'b', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] } })
|
|
55
|
+
expect(draw.add).toHaveBeenCalledWith({
|
|
56
|
+
type: 'Feature',
|
|
57
|
+
id: 'b',
|
|
58
|
+
geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] },
|
|
59
|
+
properties: { _flat: true }
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test('does not override an explicitly supplied type', () => {
|
|
64
|
+
const draw = { add: jest.fn() }
|
|
65
|
+
const { context } = setup(draw)
|
|
66
|
+
addFeature(context, { id: 'c', type: 'Feature', geometry: { type: 'Polygon', coordinates: [[]] } })
|
|
67
|
+
expect(draw.add).toHaveBeenCalledWith(expect.objectContaining({ type: 'Feature' }))
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// A missing/unsupported geometry is a genuine caller mistake — warned and dropped rather
|
|
71
|
+
// than handed to an adapter that would throw or silently misread it.
|
|
72
|
+
describe('invalid geometry', () => {
|
|
73
|
+
test('rejects a feature with no geometry at all', () => {
|
|
74
|
+
const draw = { add: jest.fn() }
|
|
75
|
+
const { context, eventBus } = setup(draw)
|
|
76
|
+
addFeature(context, { id: 'd' })
|
|
77
|
+
expect(draw.add).not.toHaveBeenCalled()
|
|
78
|
+
expect(eventBus.emit).not.toHaveBeenCalled()
|
|
79
|
+
expect(logger.warn).toHaveBeenCalledWith('addFeature: ignoring invalid GeoJSON feature', expect.objectContaining({ id: 'd' }))
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('rejects an unsupported geometry type', () => {
|
|
83
|
+
const draw = { add: jest.fn() }
|
|
84
|
+
const { context } = setup(draw)
|
|
85
|
+
addFeature(context, { id: 'e', geometry: { type: 'MultiPoint', coordinates: [[0, 0]] } })
|
|
86
|
+
expect(draw.add).not.toHaveBeenCalled()
|
|
87
|
+
expect(logger.warn).toHaveBeenCalled()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('rejects a geometry with no coordinates', () => {
|
|
91
|
+
const draw = { add: jest.fn() }
|
|
92
|
+
const { context } = setup(draw)
|
|
93
|
+
addFeature(context, { id: 'f', geometry: { type: 'Point' } })
|
|
94
|
+
expect(draw.add).not.toHaveBeenCalled()
|
|
95
|
+
expect(logger.warn).toHaveBeenCalled()
|
|
96
|
+
})
|
|
40
97
|
})
|
|
41
98
|
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { flattenStyleProperties } from '../utils/flattenStyleProperties.js'
|
|
2
|
+
|
|
3
|
+
// Shared body for newLine/newPolygon — the two are identical except which draw_* mode they
|
|
4
|
+
// enter, so that's the only thing parameterised here. Kept out of newPoint.js: a Point also
|
|
5
|
+
// needs symbol-key extraction (see newPoint.js), a genuine difference, not just the mode string.
|
|
6
|
+
export const createNewShape = (mode) => ({ appState, appConfig, pluginConfig, pluginState, mapState, mapProvider, services }, featureId, options = {}) => {
|
|
7
|
+
const { dispatch } = pluginState
|
|
8
|
+
const { draw } = mapProvider
|
|
9
|
+
const { eventBus } = services
|
|
10
|
+
|
|
11
|
+
if (!draw) {
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
eventBus.emit('draw:started', { mode })
|
|
16
|
+
|
|
17
|
+
const snapLayers = options.snapLayers === undefined ? (pluginConfig.snapLayers ?? null) : options.snapLayers
|
|
18
|
+
draw.setSnapLayers(snapLayers)
|
|
19
|
+
dispatch({ type: 'SET_HAS_SNAP_LAYERS', payload: snapLayers?.length > 0 })
|
|
20
|
+
|
|
21
|
+
const { stroke, fill, strokeWidth, properties: customProperties, onGeometryChange, ...modeOptions } = options
|
|
22
|
+
// Per-call callback overrides the plugin-level one; events.js reads this on every commit.
|
|
23
|
+
draw._geometryValidator = onGeometryChange ?? pluginConfig.onGeometryChange
|
|
24
|
+
const properties = {
|
|
25
|
+
...customProperties,
|
|
26
|
+
...flattenStyleProperties({ stroke, fill, strokeWidth })
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
draw.changeMode(mode, {
|
|
30
|
+
container: appState.layoutRefs.viewportRef.current,
|
|
31
|
+
addVertexButtonId: `${appConfig.id}-draw-add-point`,
|
|
32
|
+
interfaceType: appState.interfaceType,
|
|
33
|
+
crossHair: mapState.crossHair,
|
|
34
|
+
getSnapEnabled: () => draw.isSnapEnabled(),
|
|
35
|
+
featureId,
|
|
36
|
+
...modeOptions,
|
|
37
|
+
properties
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
dispatch({ type: 'SET_MODE', payload: mode })
|
|
41
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createNewShape } from './createNewShape.js'
|
|
2
|
+
import { flattenStyleProperties } from '../utils/flattenStyleProperties.js'
|
|
3
|
+
|
|
4
|
+
jest.mock('../utils/flattenStyleProperties.js', () => ({
|
|
5
|
+
flattenStyleProperties: jest.fn(() => ({ _flat: true }))
|
|
6
|
+
}))
|
|
7
|
+
|
|
8
|
+
const makeContext = (overrides = {}) => {
|
|
9
|
+
const dispatch = jest.fn()
|
|
10
|
+
const eventBus = { emit: jest.fn() }
|
|
11
|
+
const draw = { setSnapLayers: jest.fn(), changeMode: jest.fn(), isSnapEnabled: jest.fn(() => false) }
|
|
12
|
+
const context = {
|
|
13
|
+
appState: { layoutRefs: { viewportRef: { current: 'viewport' } }, interfaceType: 'mouse' },
|
|
14
|
+
appConfig: { id: 'app' },
|
|
15
|
+
pluginConfig: {},
|
|
16
|
+
pluginState: { dispatch },
|
|
17
|
+
mapState: { crossHair: true },
|
|
18
|
+
mapProvider: { draw },
|
|
19
|
+
services: { eventBus },
|
|
20
|
+
...overrides
|
|
21
|
+
}
|
|
22
|
+
return { context, dispatch, eventBus, draw }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
beforeEach(() => jest.clearAllMocks())
|
|
26
|
+
|
|
27
|
+
describe('createNewShape', () => {
|
|
28
|
+
test('does nothing when there is no draw instance', () => {
|
|
29
|
+
const { context, eventBus } = makeContext({ mapProvider: { draw: null } })
|
|
30
|
+
createNewShape('draw_line')(context, 'f1')
|
|
31
|
+
expect(eventBus.emit).not.toHaveBeenCalled()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// newLine.js/newPolygon.js only ever differ by this one argument — everything else about
|
|
35
|
+
// the returned function's behaviour is already covered end-to-end by their own tests.
|
|
36
|
+
test('threads the given mode through every call site: started event, changeMode, SET_MODE', () => {
|
|
37
|
+
const { context, dispatch, eventBus, draw } = makeContext()
|
|
38
|
+
createNewShape('draw_custom')(context, 'f1')
|
|
39
|
+
|
|
40
|
+
expect(eventBus.emit).toHaveBeenCalledWith('draw:started', { mode: 'draw_custom' })
|
|
41
|
+
expect(draw.changeMode).toHaveBeenCalledWith('draw_custom', expect.objectContaining({ featureId: 'f1' }))
|
|
42
|
+
expect(dispatch).toHaveBeenCalledWith({ type: 'SET_MODE', payload: 'draw_custom' })
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('flattens style properties into the mode-change payload', () => {
|
|
46
|
+
const { context, draw } = makeContext()
|
|
47
|
+
createNewShape('draw_line')(context, 'f1', { stroke: 'red', fill: 'blue', strokeWidth: 3, properties: { name: 'x' } })
|
|
48
|
+
|
|
49
|
+
expect(flattenStyleProperties).toHaveBeenCalledWith({ stroke: 'red', fill: 'blue', strokeWidth: 3 })
|
|
50
|
+
expect(draw.changeMode.mock.calls[0][1].properties).toEqual({ name: 'x', _flat: true })
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('two factory instances stay independent — one mode never leaks into the other', () => {
|
|
54
|
+
const { context: lineCtx, draw: lineDraw } = makeContext()
|
|
55
|
+
const { context: polyCtx, draw: polyDraw } = makeContext()
|
|
56
|
+
|
|
57
|
+
createNewShape('draw_line')(lineCtx, 'f1')
|
|
58
|
+
createNewShape('draw_polygon')(polyCtx, 'f2')
|
|
59
|
+
|
|
60
|
+
expect(lineDraw.changeMode).toHaveBeenCalledWith('draw_line', expect.anything())
|
|
61
|
+
expect(polyDraw.changeMode).toHaveBeenCalledWith('draw_polygon', expect.anything())
|
|
62
|
+
})
|
|
63
|
+
})
|
|
@@ -18,14 +18,17 @@ export const editFeature = ({ appState, appConfig, mapState, pluginConfig, plugi
|
|
|
18
18
|
// Per-call callback overrides the plugin-level one; events.js reads this on every commit.
|
|
19
19
|
draw._geometryValidator = options.onGeometryChange ?? pluginConfig.onGeometryChange
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
// A Point has no ring of vertices to edit — edit_point relocates the single coordinate
|
|
22
|
+
// instead of edit_vertex's ring-of-vertices machinery.
|
|
23
|
+
const editModeMap = { LineString: 'edit_line', Polygon: 'edit_polygon', Point: 'edit_point' }
|
|
24
|
+
const mode = existingFeature.geometry.type === 'Point' ? 'edit_point' : 'edit_vertex'
|
|
22
25
|
eventBus.emit('draw:editstart', { mode: editModeMap[existingFeature.geometry.type] })
|
|
23
26
|
|
|
24
27
|
const snapLayers = options.snapLayers === undefined ? (pluginConfig.snapLayers ?? null) : options.snapLayers
|
|
25
28
|
draw.setSnapLayers(snapLayers)
|
|
26
29
|
dispatch({ type: 'SET_HAS_SNAP_LAYERS', payload: snapLayers?.length > 0 })
|
|
27
30
|
|
|
28
|
-
draw.changeMode(
|
|
31
|
+
draw.changeMode(mode, {
|
|
29
32
|
container: appState.layoutRefs.viewportRef.current,
|
|
30
33
|
deleteVertexButtonId: `${appConfig.id}-draw-delete-point`,
|
|
31
34
|
undoButtonId: `${appConfig.id}-draw-undo`,
|
|
@@ -42,11 +45,11 @@ export const editFeature = ({ appState, appConfig, mapState, pluginConfig, plugi
|
|
|
42
45
|
payload: { feature, tempFeature: feature }
|
|
43
46
|
})
|
|
44
47
|
|
|
45
|
-
dispatch({ type: 'SET_MODE', payload:
|
|
48
|
+
dispatch({ type: 'SET_MODE', payload: mode })
|
|
46
49
|
|
|
47
50
|
// Seed the Done-button gate and the stroke from the feature's starting validity
|
|
48
51
|
// so an already invalid feature opens dashed and cannot be "finished" until fixed.
|
|
49
|
-
const { valid } = validateGeometry(feature, { phase: 'edit-start', mode
|
|
52
|
+
const { valid } = validateGeometry(feature, { phase: 'edit-start', mode }, { onGeometryChange: draw._geometryValidator })
|
|
50
53
|
dispatch({ type: 'SET_GEOMETRY_VALID', payload: valid })
|
|
51
54
|
draw.setInvalid?.(!valid)
|
|
52
55
|
|
|
@@ -74,6 +74,22 @@ describe('editFeature', () => {
|
|
|
74
74
|
expect(eventBus.emit).toHaveBeenCalledWith('draw:editstart', { mode: 'edit_line' })
|
|
75
75
|
})
|
|
76
76
|
|
|
77
|
+
// A Point has no ring of vertices — editing one goes through edit_point (relocate the
|
|
78
|
+
// single coordinate), not edit_vertex, across every mode-name site this function touches.
|
|
79
|
+
test('uses edit_point mode for a point feature', () => {
|
|
80
|
+
const { context, dispatch, eventBus, draw } = makeContext()
|
|
81
|
+
draw.get.mockReturnValue({ id: 'f1', geometry: { type: 'Point', coordinates: [0, 0] } })
|
|
82
|
+
|
|
83
|
+
editFeature(context, 'f1')
|
|
84
|
+
|
|
85
|
+
expect(eventBus.emit).toHaveBeenCalledWith('draw:editstart', { mode: 'edit_point' })
|
|
86
|
+
expect(draw.changeMode).toHaveBeenCalledWith('edit_point', expect.objectContaining({ featureId: 'f1' }))
|
|
87
|
+
expect(dispatch).toHaveBeenCalledWith({ type: 'SET_MODE', payload: 'edit_point' })
|
|
88
|
+
// Point geometry is always valid (validation/rules.js has no Point branch) — the gate
|
|
89
|
+
// opens immediately, unlike a self-intersecting/zero-area polygon.
|
|
90
|
+
expect(dispatch).toHaveBeenCalledWith({ type: 'SET_GEOMETRY_VALID', payload: true })
|
|
91
|
+
})
|
|
92
|
+
|
|
77
93
|
test('stores a per-call onGeometryChange validator, overriding the plugin-level one', () => {
|
|
78
94
|
const pluginOnGeometryChange = jest.fn()
|
|
79
95
|
const onGeometryChange = jest.fn()
|
|
@@ -1,41 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createNewShape } from './createNewShape.js'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
// mode string — kept as two small, explicit entry points rather than one
|
|
5
|
-
// mode-parameterised factory. Revisit if a third geometry type is ever added.
|
|
6
|
-
export const newLine = ({ appState, appConfig, pluginConfig, pluginState, mapState, mapProvider, services }, featureId, options = {}) => {
|
|
7
|
-
const { dispatch } = pluginState
|
|
8
|
-
const { draw } = mapProvider
|
|
9
|
-
const { eventBus } = services
|
|
10
|
-
|
|
11
|
-
if (!draw) {
|
|
12
|
-
return
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
eventBus.emit('draw:started', { mode: 'draw_line' })
|
|
16
|
-
|
|
17
|
-
const snapLayers = options.snapLayers === undefined ? (pluginConfig.snapLayers ?? null) : options.snapLayers
|
|
18
|
-
draw.setSnapLayers(snapLayers)
|
|
19
|
-
dispatch({ type: 'SET_HAS_SNAP_LAYERS', payload: snapLayers?.length > 0 })
|
|
20
|
-
|
|
21
|
-
const { stroke, fill, strokeWidth, properties: customProperties, onGeometryChange, ...modeOptions } = options
|
|
22
|
-
// Per-call callback overrides the plugin-level one; events.js reads this on every commit.
|
|
23
|
-
draw._geometryValidator = onGeometryChange ?? pluginConfig.onGeometryChange
|
|
24
|
-
const properties = {
|
|
25
|
-
...customProperties,
|
|
26
|
-
...flattenStyleProperties({ stroke, fill, strokeWidth })
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
draw.changeMode('draw_line', {
|
|
30
|
-
container: appState.layoutRefs.viewportRef.current,
|
|
31
|
-
addVertexButtonId: `${appConfig.id}-draw-add-point`,
|
|
32
|
-
interfaceType: appState.interfaceType,
|
|
33
|
-
crossHair: mapState.crossHair,
|
|
34
|
-
getSnapEnabled: () => draw.isSnapEnabled(),
|
|
35
|
-
featureId,
|
|
36
|
-
...modeOptions,
|
|
37
|
-
properties
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
dispatch({ type: 'SET_MODE', payload: 'draw_line' })
|
|
41
|
-
}
|
|
3
|
+
export const newLine = createNewShape('draw_line')
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { SYMBOL_KEYS } from '../utils/symbolKeys.js'
|
|
2
|
+
|
|
3
|
+
export const newPoint = ({ appState, appConfig, pluginConfig, pluginState, mapState, mapProvider, services }, featureId, options = {}) => {
|
|
4
|
+
const { dispatch } = pluginState
|
|
5
|
+
const { draw } = mapProvider
|
|
6
|
+
const { eventBus } = services
|
|
7
|
+
|
|
8
|
+
if (!draw) {
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
eventBus.emit('draw:started', { mode: 'draw_point' })
|
|
13
|
+
|
|
14
|
+
const snapLayers = options.snapLayers === undefined ? (pluginConfig.snapLayers ?? null) : options.snapLayers
|
|
15
|
+
draw.setSnapLayers(snapLayers)
|
|
16
|
+
dispatch({ type: 'SET_HAS_SNAP_LAYERS', payload: snapLayers?.length > 0 })
|
|
17
|
+
|
|
18
|
+
const { properties: customProperties, onGeometryChange, ...modeOptions } = options
|
|
19
|
+
// Per-call callback overrides the plugin-level one; events.js reads this on every commit.
|
|
20
|
+
draw._geometryValidator = onGeometryChange ?? pluginConfig.onGeometryChange
|
|
21
|
+
|
|
22
|
+
const symbolStyle = {}
|
|
23
|
+
SYMBOL_KEYS.forEach((key) => {
|
|
24
|
+
if (modeOptions[key] !== undefined) {
|
|
25
|
+
symbolStyle[key] = modeOptions[key]
|
|
26
|
+
delete modeOptions[key]
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
draw.changeMode('draw_point', {
|
|
31
|
+
container: appState.layoutRefs.viewportRef.current,
|
|
32
|
+
addVertexButtonId: `${appConfig.id}-draw-add-point`,
|
|
33
|
+
interfaceType: appState.interfaceType,
|
|
34
|
+
crossHair: mapState.crossHair,
|
|
35
|
+
getSnapEnabled: () => draw.isSnapEnabled(),
|
|
36
|
+
featureId,
|
|
37
|
+
...modeOptions,
|
|
38
|
+
properties: { ...customProperties, ...symbolStyle }
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
dispatch({ type: 'SET_MODE', payload: 'draw_point' })
|
|
42
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { newPoint } from './newPoint.js'
|
|
2
|
+
|
|
3
|
+
const makeContext = (overrides = {}) => {
|
|
4
|
+
const dispatch = jest.fn()
|
|
5
|
+
const eventBus = { emit: jest.fn() }
|
|
6
|
+
const draw = { setSnapLayers: jest.fn(), changeMode: jest.fn(), isSnapEnabled: jest.fn(() => false) }
|
|
7
|
+
const context = {
|
|
8
|
+
appState: { layoutRefs: { viewportRef: { current: 'viewport' } }, interfaceType: 'mouse' },
|
|
9
|
+
appConfig: { id: 'app' },
|
|
10
|
+
pluginConfig: { snapLayers: ['pc-layer'] },
|
|
11
|
+
pluginState: { dispatch },
|
|
12
|
+
mapState: { crossHair: true },
|
|
13
|
+
mapProvider: { draw },
|
|
14
|
+
services: { eventBus },
|
|
15
|
+
...overrides
|
|
16
|
+
}
|
|
17
|
+
return { context, dispatch, eventBus, draw }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
beforeEach(() => jest.clearAllMocks())
|
|
21
|
+
|
|
22
|
+
describe('newPoint', () => {
|
|
23
|
+
test('does nothing when there is no draw instance', () => {
|
|
24
|
+
const { context, eventBus } = makeContext({ mapProvider: { draw: null } })
|
|
25
|
+
newPoint(context, 'f1')
|
|
26
|
+
expect(eventBus.emit).not.toHaveBeenCalled()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('starts point placement, splitting symbol-config keys out of properties', () => {
|
|
30
|
+
const { context, dispatch, eventBus, draw } = makeContext()
|
|
31
|
+
|
|
32
|
+
newPoint(context, 'f1', {
|
|
33
|
+
symbol: 'pin',
|
|
34
|
+
symbolBackgroundColor: { outdoor: '#1d70b8' },
|
|
35
|
+
properties: { name: 'x' },
|
|
36
|
+
extra: 'opt'
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
expect(eventBus.emit).toHaveBeenCalledWith('draw:started', { mode: 'draw_point' })
|
|
40
|
+
|
|
41
|
+
expect(draw.changeMode).toHaveBeenCalledWith('draw_point', expect.objectContaining({
|
|
42
|
+
container: 'viewport',
|
|
43
|
+
addVertexButtonId: 'app-draw-add-point',
|
|
44
|
+
interfaceType: 'mouse',
|
|
45
|
+
crossHair: true,
|
|
46
|
+
featureId: 'f1',
|
|
47
|
+
extra: 'opt',
|
|
48
|
+
properties: { name: 'x', symbol: 'pin', symbolBackgroundColor: { outdoor: '#1d70b8' } }
|
|
49
|
+
}))
|
|
50
|
+
|
|
51
|
+
const opts = draw.changeMode.mock.calls[0][1]
|
|
52
|
+
expect(opts.getSnapEnabled()).toBe(false)
|
|
53
|
+
expect(draw.isSnapEnabled).toHaveBeenCalled()
|
|
54
|
+
// Symbol keys are pulled off modeOptions entirely, not just copied into properties.
|
|
55
|
+
expect(opts).not.toHaveProperty('symbol')
|
|
56
|
+
expect(opts).not.toHaveProperty('symbolBackgroundColor')
|
|
57
|
+
|
|
58
|
+
expect(dispatch).toHaveBeenCalledWith({ type: 'SET_MODE', payload: 'draw_point' })
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('carries every recognised symbol-config key through to properties', () => {
|
|
62
|
+
const { context, draw } = makeContext()
|
|
63
|
+
const symbolStyle = {
|
|
64
|
+
symbol: 'pin',
|
|
65
|
+
symbolSvgContent: '<path/>',
|
|
66
|
+
symbolBackgroundColor: '#fff',
|
|
67
|
+
symbolForegroundColor: '#000',
|
|
68
|
+
symbolHaloWidth: 2,
|
|
69
|
+
symbolGraphic: 'g',
|
|
70
|
+
symbolViewBox: '0 0 10 10',
|
|
71
|
+
symbolAnchor: [0.5, 0.9]
|
|
72
|
+
}
|
|
73
|
+
newPoint(context, 'f1', symbolStyle)
|
|
74
|
+
expect(draw.changeMode.mock.calls[0][1].properties).toEqual(symbolStyle)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('omits symbol-config keys entirely when none are supplied', () => {
|
|
78
|
+
const { context, draw } = makeContext()
|
|
79
|
+
newPoint(context, 'f1')
|
|
80
|
+
expect(draw.changeMode.mock.calls[0][1].properties).toEqual({})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test('prefers explicit option snapLayers and flags them', () => {
|
|
84
|
+
const { context, draw, dispatch } = makeContext()
|
|
85
|
+
newPoint(context, 'f1', { snapLayers: ['opt'] })
|
|
86
|
+
expect(draw.setSnapLayers).toHaveBeenCalledWith(['opt'])
|
|
87
|
+
expect(dispatch).toHaveBeenCalledWith({ type: 'SET_HAS_SNAP_LAYERS', payload: true })
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('falls back to the plugin config snapLayers', () => {
|
|
91
|
+
const { context, draw } = makeContext()
|
|
92
|
+
newPoint(context, 'f1')
|
|
93
|
+
expect(draw.setSnapLayers).toHaveBeenCalledWith(['pc-layer'])
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test('falls back to null when neither option nor config is set', () => {
|
|
97
|
+
const { context, draw, dispatch } = makeContext({ pluginConfig: {} })
|
|
98
|
+
newPoint(context, 'f1')
|
|
99
|
+
expect(draw.setSnapLayers).toHaveBeenCalledWith(null)
|
|
100
|
+
expect(dispatch).toHaveBeenCalledWith({ type: 'SET_HAS_SNAP_LAYERS', payload: false })
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test('stores a per-call onGeometryChange validator without leaking it into mode options', () => {
|
|
104
|
+
const { context, draw } = makeContext()
|
|
105
|
+
const onGeometryChange = jest.fn()
|
|
106
|
+
newPoint(context, 'f1', { onGeometryChange })
|
|
107
|
+
expect(draw._geometryValidator).toBe(onGeometryChange)
|
|
108
|
+
expect(draw.changeMode.mock.calls[0][1]).not.toHaveProperty('onGeometryChange')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test('falls back to the plugin-level onGeometryChange validator', () => {
|
|
112
|
+
const pluginOnGeometryChange = jest.fn()
|
|
113
|
+
const { context, draw } = makeContext({ pluginConfig: { onGeometryChange: pluginOnGeometryChange } })
|
|
114
|
+
newPoint(context, 'f1')
|
|
115
|
+
expect(draw._geometryValidator).toBe(pluginOnGeometryChange)
|
|
116
|
+
})
|
|
117
|
+
})
|
|
@@ -1,41 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createNewShape } from './createNewShape.js'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
// mode string — kept as two small, explicit entry points rather than one
|
|
5
|
-
// mode-parameterised factory. Revisit if a third geometry type is ever added.
|
|
6
|
-
export const newPolygon = ({ appState, appConfig, pluginConfig, pluginState, mapState, mapProvider, services }, featureId, options = {}) => {
|
|
7
|
-
const { dispatch } = pluginState
|
|
8
|
-
const { draw } = mapProvider
|
|
9
|
-
const { eventBus } = services
|
|
10
|
-
|
|
11
|
-
if (!draw) {
|
|
12
|
-
return
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
eventBus.emit('draw:started', { mode: 'draw_polygon' })
|
|
16
|
-
|
|
17
|
-
const snapLayers = options.snapLayers === undefined ? (pluginConfig.snapLayers ?? null) : options.snapLayers
|
|
18
|
-
draw.setSnapLayers(snapLayers)
|
|
19
|
-
dispatch({ type: 'SET_HAS_SNAP_LAYERS', payload: snapLayers?.length > 0 })
|
|
20
|
-
|
|
21
|
-
const { stroke, fill, strokeWidth, properties: customProperties, onGeometryChange, ...modeOptions } = options
|
|
22
|
-
// Per-call callback overrides the plugin-level one; events.js reads this on every commit.
|
|
23
|
-
draw._geometryValidator = onGeometryChange ?? pluginConfig.onGeometryChange
|
|
24
|
-
const properties = {
|
|
25
|
-
...customProperties,
|
|
26
|
-
...flattenStyleProperties({ stroke, fill, strokeWidth })
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
draw.changeMode('draw_polygon', {
|
|
30
|
-
container: appState.layoutRefs.viewportRef.current,
|
|
31
|
-
addVertexButtonId: `${appConfig.id}-draw-add-point`,
|
|
32
|
-
interfaceType: appState.interfaceType,
|
|
33
|
-
crossHair: mapState.crossHair,
|
|
34
|
-
getSnapEnabled: () => draw.isSnapEnabled(),
|
|
35
|
-
featureId,
|
|
36
|
-
...modeOptions,
|
|
37
|
-
properties
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
dispatch({ type: 'SET_MODE', payload: 'draw_polygon' })
|
|
41
|
-
}
|
|
3
|
+
export const newPolygon = createNewShape('draw_polygon')
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { flattenStyleProperties } from '../utils/flattenStyleProperties.js'
|
|
2
|
+
import { logger } from '../../../../src/services/logger.js'
|
|
3
|
+
|
|
4
|
+
const STYLE_KEYS = new Set(['stroke', 'fill', 'strokeWidth'])
|
|
5
|
+
|
|
6
|
+
// A partial patch — unlike addFeature.js building a whole new feature from scratch,
|
|
7
|
+
// flattenStyleProperties would otherwise write stroke/fill/strokeWidth as explicit
|
|
8
|
+
// `undefined` for any of the three the caller didn't mention, blanking out whatever that
|
|
9
|
+
// feature already had.
|
|
10
|
+
const definedOnly = (properties) => {
|
|
11
|
+
const result = {}
|
|
12
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
13
|
+
if (value !== undefined) {
|
|
14
|
+
result[key] = value
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return result
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const setStyle = ({ mapProvider, services }, featureId, styleChanges = {}) => {
|
|
21
|
+
const { draw } = mapProvider
|
|
22
|
+
const { eventBus } = services
|
|
23
|
+
|
|
24
|
+
if (!draw) {
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!draw.get(featureId)) {
|
|
29
|
+
logger.warn('setStyle: no feature found for id', featureId)
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const styleProps = {}
|
|
34
|
+
const symbolProps = {}
|
|
35
|
+
for (const [key, value] of Object.entries(styleChanges)) {
|
|
36
|
+
(STYLE_KEYS.has(key) ? styleProps : symbolProps)[key] = value
|
|
37
|
+
}
|
|
38
|
+
const properties = definedOnly({ ...flattenStyleProperties(styleProps), ...symbolProps })
|
|
39
|
+
|
|
40
|
+
draw.setStyle(featureId, properties)
|
|
41
|
+
eventBus.emit('draw:stylechange', { featureId, properties })
|
|
42
|
+
return true
|
|
43
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { setStyle } from './setStyle.js'
|
|
2
|
+
import { logger } from '../../../../src/services/logger.js'
|
|
3
|
+
|
|
4
|
+
jest.mock('../../../../src/services/logger.js', () => ({
|
|
5
|
+
logger: { warn: jest.fn(), error: jest.fn() }
|
|
6
|
+
}))
|
|
7
|
+
|
|
8
|
+
const setup = (draw) => {
|
|
9
|
+
const eventBus = { emit: jest.fn() }
|
|
10
|
+
return { context: { mapProvider: { draw }, services: { eventBus } }, eventBus }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
beforeEach(() => jest.clearAllMocks())
|
|
14
|
+
|
|
15
|
+
describe('setStyle', () => {
|
|
16
|
+
test('does nothing when there is no draw instance', () => {
|
|
17
|
+
const { context, eventBus } = setup(undefined)
|
|
18
|
+
expect(setStyle(context, 'a', { stroke: 'red' })).toBe(false)
|
|
19
|
+
expect(eventBus.emit).not.toHaveBeenCalled()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('warns and does nothing for an id with no existing feature', () => {
|
|
23
|
+
const draw = { get: jest.fn(() => null), setStyle: jest.fn() }
|
|
24
|
+
const { context, eventBus } = setup(draw)
|
|
25
|
+
expect(setStyle(context, 'missing', { stroke: 'red' })).toBe(false)
|
|
26
|
+
expect(draw.setStyle).not.toHaveBeenCalled()
|
|
27
|
+
expect(eventBus.emit).not.toHaveBeenCalled()
|
|
28
|
+
expect(logger.warn).toHaveBeenCalledWith('setStyle: no feature found for id', 'missing')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('flattens stroke/fill/strokeWidth and forwards symbol keys as-is', () => {
|
|
32
|
+
const draw = { get: jest.fn(() => ({ id: 'a' })), setStyle: jest.fn() }
|
|
33
|
+
const { context, eventBus } = setup(draw)
|
|
34
|
+
|
|
35
|
+
const result = setStyle(context, 'a', {
|
|
36
|
+
stroke: { outdoor: '#1d70b8', dark: '#4c9ed9' },
|
|
37
|
+
symbol: 'square',
|
|
38
|
+
symbolBackgroundColor: { outdoor: '#ca3535', dark: '#ffffff' }
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const expected = {
|
|
42
|
+
stroke: '#1d70b8',
|
|
43
|
+
strokeOutdoor: '#1d70b8',
|
|
44
|
+
strokeDark: '#4c9ed9',
|
|
45
|
+
symbol: 'square',
|
|
46
|
+
symbolBackgroundColor: { outdoor: '#ca3535', dark: '#ffffff' }
|
|
47
|
+
}
|
|
48
|
+
expect(draw.setStyle).toHaveBeenCalledWith('a', expected)
|
|
49
|
+
expect(eventBus.emit).toHaveBeenCalledWith('draw:stylechange', { featureId: 'a', properties: expected })
|
|
50
|
+
expect(result).toBe(true)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// A partial patch — omitting fill/strokeWidth must leave whatever the feature already has
|
|
54
|
+
// alone, not blank them out to undefined.
|
|
55
|
+
test('only includes the style keys actually supplied, dropping the unset ones', () => {
|
|
56
|
+
const draw = { get: jest.fn(() => ({ id: 'a' })), setStyle: jest.fn() }
|
|
57
|
+
const { context } = setup(draw)
|
|
58
|
+
setStyle(context, 'a', { stroke: '#e6c700' })
|
|
59
|
+
expect(draw.setStyle).toHaveBeenCalledWith('a', { stroke: '#e6c700' })
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('drops keys explicitly passed as undefined, from both style and symbol props', () => {
|
|
63
|
+
const draw = { get: jest.fn(() => ({ id: 'a' })), setStyle: jest.fn() }
|
|
64
|
+
const { context } = setup(draw)
|
|
65
|
+
setStyle(context, 'a', { stroke: 'red', fill: undefined, symbol: 'pin', symbolBackgroundColor: undefined })
|
|
66
|
+
expect(draw.setStyle).toHaveBeenCalledWith('a', { stroke: 'red', symbol: 'pin' })
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('defaults styleChanges to {} and still resolves cleanly', () => {
|
|
70
|
+
const draw = { get: jest.fn(() => ({ id: 'a' })), setStyle: jest.fn() }
|
|
71
|
+
const { context } = setup(draw)
|
|
72
|
+
setStyle(context, 'a')
|
|
73
|
+
expect(draw.setStyle).toHaveBeenCalledWith('a', {})
|
|
74
|
+
})
|
|
75
|
+
})
|