@defra/interactive-map 0.0.40-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/assets/images/slot-map.svg +61 -262
- package/docs/examples/draw-tools.mdx +3 -3
- package/docs/plugins/draw.md +79 -16
- 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/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.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/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 +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/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 +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/MoveControls/MoveControls.module.scss +41 -10
- 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/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/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 +1 -0
- package/src/App/store/appDispatchMiddleware.js +1 -1
- package/src/App/store/appDispatchMiddleware.test.js +8 -0
- 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 +11 -1
- 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
|
@@ -2,9 +2,13 @@ import Style from 'ol/style/Style.js'
|
|
|
2
2
|
import Fill from 'ol/style/Fill.js'
|
|
3
3
|
import Stroke from 'ol/style/Stroke.js'
|
|
4
4
|
import CircleStyle from 'ol/style/Circle.js'
|
|
5
|
+
import Icon from 'ol/style/Icon.js'
|
|
5
6
|
import MultiPoint from 'ol/geom/MultiPoint.js'
|
|
6
7
|
import { SIZES } from '../defaults.js'
|
|
7
8
|
import { getPlacedSketchCoords } from '../utils/sketchHelpers.js'
|
|
9
|
+
import { getCachedSymbolImage } from '../../../../../../providers/beta/openlayers/src/utils/symbolImages.js'
|
|
10
|
+
import { symbolRegistry } from '../../../../../../src/services/symbolRegistry.js'
|
|
11
|
+
import { getSymbolAnchor } from '../../../../../../src/utils/symbolUtils.js'
|
|
8
12
|
|
|
9
13
|
const HALO_RADIUS_OFFSET = 3
|
|
10
14
|
|
|
@@ -74,12 +78,74 @@ const createSketchLineStyles = (colors) => ({
|
|
|
74
78
|
})
|
|
75
79
|
})
|
|
76
80
|
|
|
81
|
+
// Placeholder for a committed point feature that has no symbol config, or whose symbol
|
|
82
|
+
// image hasn't rasterised/cached yet (point/pointSymbolImages.js resolves it asynchronously
|
|
83
|
+
// after the feature is added to the store — this is what renders in the gap, and what
|
|
84
|
+
// permanently renders for a point with no symbol properties at all). Stroke/Fill styles
|
|
85
|
+
// have no effect on a Point geometry, so without this branch a committed point renders
|
|
86
|
+
// invisibly.
|
|
87
|
+
const createPointPlaceholderStyle = (colors) => new Style({
|
|
88
|
+
image: new CircleStyle({
|
|
89
|
+
radius: SIZES.vertexRadius + HALO_RADIUS_OFFSET,
|
|
90
|
+
fill: new Fill({ color: colors.shapeFill }),
|
|
91
|
+
stroke: new Stroke({ color: colors.shapeStroke, width: 2 })
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// ol/style/Icon defaults (anchorOrigin: 'top-left', anchorXUnits/anchorYUnits: 'fraction')
|
|
96
|
+
// already match symbolAnchor's own [x,y]-fraction-from-top-left convention, so unlike the
|
|
97
|
+
// MapLibre adapter (whose icon-anchor only has 9 discrete positions) no offset compensation
|
|
98
|
+
// is needed here — the raw anchor is usable as-is. Icon style instances are cached per
|
|
99
|
+
// imageId+anchor+pixelRatio so a style function running every render frame doesn't rebuild
|
|
100
|
+
// one each time.
|
|
101
|
+
const createPointStyles = (colors) => {
|
|
102
|
+
const pointStyle = createPointPlaceholderStyle(colors)
|
|
103
|
+
const iconStyleCache = new Map()
|
|
104
|
+
|
|
105
|
+
// imageIdProp lets edit_point's own per-feature style override (point/editPointMode.js)
|
|
106
|
+
// ask for the precomputed "selected" (black ring) variant instead of the normal one — the
|
|
107
|
+
// same variant the interact plugin's own highlight already uses, via the same
|
|
108
|
+
// symbolSelectedImageId point/pointSymbolImages.js resolves for every point.
|
|
109
|
+
const getPointIconStyle = (properties, imageIdProp = 'symbolImageId') => {
|
|
110
|
+
const imageId = properties[imageIdProp]
|
|
111
|
+
const canvas = imageId && getCachedSymbolImage(imageId)
|
|
112
|
+
if (!canvas) {
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
const symbolDef = symbolRegistry.getSymbolDef(properties)
|
|
116
|
+
const [anchorX, anchorY] = getSymbolAnchor(properties, symbolDef)
|
|
117
|
+
// The cached canvas was rasterised at viewBox × pixelRatio device pixels for crispness
|
|
118
|
+
// (point/pointSymbolImages.js) — unlike MapLibre's map.addImage(id, data, {pixelRatio}),
|
|
119
|
+
// which uses the registered pixelRatio to auto-derive the displayed CSS size, ol/style/
|
|
120
|
+
// Icon draws its source at native pixel size by default, so `scale` has to cancel that
|
|
121
|
+
// back out here or the icon renders pixelRatio× too big.
|
|
122
|
+
const pixelRatio = properties.symbolPixelRatio || 1
|
|
123
|
+
const cacheKey = `${imageId}|${anchorX}|${anchorY}|${pixelRatio}`
|
|
124
|
+
let iconStyle = iconStyleCache.get(cacheKey)
|
|
125
|
+
if (!iconStyle) {
|
|
126
|
+
iconStyle = new Style({ image: new Icon({ img: canvas, anchor: [anchorX, anchorY], scale: 1 / pixelRatio }) })
|
|
127
|
+
iconStyleCache.set(cacheKey, iconStyle)
|
|
128
|
+
}
|
|
129
|
+
return iconStyle
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
pointStyleFor: (feature) => getPointIconStyle(feature.getProperties()) ?? pointStyle,
|
|
134
|
+
// Falls back through selected → normal → placeholder, so edit_point still shows
|
|
135
|
+
// something sensible before the selected variant has resolved.
|
|
136
|
+
selectedPointStyleFor: (feature) => {
|
|
137
|
+
const properties = feature.getProperties()
|
|
138
|
+
return getPointIconStyle(properties, 'symbolSelectedImageId') ?? getPointIconStyle(properties) ?? pointStyle
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
77
143
|
/**
|
|
78
144
|
* Create all draw-ol style instances for the given resolved color set.
|
|
79
145
|
*
|
|
80
146
|
* @param {object} colors - Output of resolveColors()
|
|
81
147
|
* @returns {{ vertexStyle, selectedVertexStyle, midpointStyle, selectedMidpointStyle,
|
|
82
|
-
* editFeatureStyle, createSketchStyle, createFeatureStyle }}
|
|
148
|
+
* editFeatureStyle, selectedPointStyleFor, createSketchStyle, createFeatureStyle }}
|
|
83
149
|
*/
|
|
84
150
|
export const createStyles = (colors) => {
|
|
85
151
|
const { vertexImage, vertexStyle, selectedVertexStyle } = createVertexStyles(colors)
|
|
@@ -130,7 +196,12 @@ export const createStyles = (colors) => {
|
|
|
130
196
|
return type === geometryType ? [lineStyle, sketchVertexStyle] : [lineStyle]
|
|
131
197
|
}
|
|
132
198
|
|
|
199
|
+
const { pointStyleFor, selectedPointStyleFor } = createPointStyles(colors)
|
|
200
|
+
|
|
133
201
|
const createFeatureStyle = () => (feature) => {
|
|
202
|
+
if (feature.getGeometry().getType() === 'Point') {
|
|
203
|
+
return [pointStyleFor(feature)]
|
|
204
|
+
}
|
|
134
205
|
const p = feature.getProperties()
|
|
135
206
|
const id = colors.mapStyleId
|
|
136
207
|
const stroke = (id && p[`stroke${capitalize(id)}`]) || p.stroke || colors.shapeStroke
|
|
@@ -149,6 +220,7 @@ export const createStyles = (colors) => {
|
|
|
149
220
|
selectedMidpointStyle,
|
|
150
221
|
editFeatureStyle,
|
|
151
222
|
editFeatureStyleInvalid,
|
|
223
|
+
selectedPointStyleFor,
|
|
152
224
|
createSketchStyle,
|
|
153
225
|
createFeatureStyle
|
|
154
226
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import Point from 'ol/geom/Point.js'
|
|
2
2
|
import LineString from 'ol/geom/LineString.js'
|
|
3
3
|
import Feature from 'ol/Feature.js'
|
|
4
|
+
import Icon from 'ol/style/Icon.js'
|
|
4
5
|
import { createStyles } from './styles.js'
|
|
5
6
|
import { SIZES } from '../defaults.js'
|
|
6
|
-
import { polygonFeature, lineFeature } from '../__helpers__/harness.js'
|
|
7
|
+
import { polygonFeature, lineFeature, pointFeature } from '../__helpers__/harness.js'
|
|
8
|
+
import { getOrCreateSymbolImage, clearSymbolImageCache } from '../../../../../../providers/beta/openlayers/src/utils/symbolImages.js'
|
|
7
9
|
|
|
8
10
|
const colors = {
|
|
9
11
|
editStroke: '#e5',
|
|
@@ -117,7 +119,11 @@ describe('sketch styles while drawing', () => {
|
|
|
117
119
|
})
|
|
118
120
|
|
|
119
121
|
describe('createFeatureStyle (inactive shapes)', () => {
|
|
120
|
-
const styleFor = (properties) =>
|
|
122
|
+
const styleFor = (properties) => {
|
|
123
|
+
const feature = polygonFeature([[0, 0], [1, 0], [1, 1], [0, 0]])
|
|
124
|
+
feature.setProperties(properties)
|
|
125
|
+
return styles.createFeatureStyle()(feature)[0]
|
|
126
|
+
}
|
|
121
127
|
|
|
122
128
|
test('falls back to the configured shape colours', () => {
|
|
123
129
|
const style = styleFor({})
|
|
@@ -137,7 +143,120 @@ describe('createFeatureStyle (inactive shapes)', () => {
|
|
|
137
143
|
|
|
138
144
|
test('without a map style id only the generic properties apply', () => {
|
|
139
145
|
const plain = createStyles({ ...colors, mapStyleId: null })
|
|
140
|
-
const
|
|
146
|
+
const feature = polygonFeature([[0, 0], [1, 0], [1, 1], [0, 0]])
|
|
147
|
+
feature.setProperties({ strokeRoad: '#r1' })
|
|
148
|
+
const style = plain.createFeatureStyle()(feature)[0]
|
|
141
149
|
expect(style.getStroke().getColor()).toBe(colors.shapeStroke)
|
|
142
150
|
})
|
|
151
|
+
|
|
152
|
+
test('a committed Point feature gets the placeholder circle style, not Stroke/Fill', () => {
|
|
153
|
+
const style = styles.createFeatureStyle()(pointFeature([5, 5]))[0]
|
|
154
|
+
expect(style.getImage()).toBeTruthy()
|
|
155
|
+
expect(style.getStroke()).toBeFalsy()
|
|
156
|
+
expect(style.getFill()).toBeFalsy()
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
test('a Point feature with a resolved+cached symbolImageId renders a real Icon, not the placeholder', () => {
|
|
160
|
+
HTMLCanvasElement.prototype.getContext = jest.fn(function () {
|
|
161
|
+
this._ctx ??= { putImageData: jest.fn() }
|
|
162
|
+
return this._ctx
|
|
163
|
+
})
|
|
164
|
+
clearSymbolImageCache()
|
|
165
|
+
const canvas = getOrCreateSymbolImage('symbol-pin-1x', { width: 10, height: 10 })
|
|
166
|
+
|
|
167
|
+
const feature = pointFeature([5, 5])
|
|
168
|
+
feature.setProperties({ symbol: 'pin', symbolImageId: 'symbol-pin-1x' })
|
|
169
|
+
|
|
170
|
+
const style = styles.createFeatureStyle()(feature)[0]
|
|
171
|
+
expect(style.getImage()).toBeInstanceOf(Icon)
|
|
172
|
+
expect(style.getImage().getImage(1)).toBe(canvas)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
test('reuses the same Icon Style instance across renders for the same imageId/anchor/pixelRatio', () => {
|
|
176
|
+
HTMLCanvasElement.prototype.getContext = jest.fn(function () {
|
|
177
|
+
this._ctx ??= { putImageData: jest.fn() }
|
|
178
|
+
return this._ctx
|
|
179
|
+
})
|
|
180
|
+
clearSymbolImageCache()
|
|
181
|
+
getOrCreateSymbolImage('symbol-pin-cache', { width: 10, height: 10 })
|
|
182
|
+
|
|
183
|
+
const feature = pointFeature([5, 5])
|
|
184
|
+
feature.setProperties({ symbol: 'pin', symbolImageId: 'symbol-pin-cache' })
|
|
185
|
+
|
|
186
|
+
const styleFn = styles.createFeatureStyle()
|
|
187
|
+
const first = styleFn(feature)[0]
|
|
188
|
+
const second = styleFn(feature)[0] // a style function runs every render frame
|
|
189
|
+
expect(second).toBe(first)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
test('scales the Icon down by the rasterisation pixelRatio, so a high-DPI raster still displays at its intended CSS size', () => {
|
|
193
|
+
HTMLCanvasElement.prototype.getContext = jest.fn(function () {
|
|
194
|
+
this._ctx ??= { putImageData: jest.fn() }
|
|
195
|
+
return this._ctx
|
|
196
|
+
})
|
|
197
|
+
clearSymbolImageCache()
|
|
198
|
+
getOrCreateSymbolImage('symbol-pin-4x', { width: 176, height: 176 }) // 44 viewBox × pixelRatio 4
|
|
199
|
+
|
|
200
|
+
const feature = pointFeature([5, 5])
|
|
201
|
+
feature.setProperties({ symbol: 'pin', symbolImageId: 'symbol-pin-4x', symbolPixelRatio: 4 })
|
|
202
|
+
|
|
203
|
+
const style = styles.createFeatureStyle()(feature)[0]
|
|
204
|
+
expect(style.getImage().getScale()).toBe(0.25)
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
test('defaults to scale 1 when symbolPixelRatio is missing', () => {
|
|
208
|
+
HTMLCanvasElement.prototype.getContext = jest.fn(function () {
|
|
209
|
+
this._ctx ??= { putImageData: jest.fn() }
|
|
210
|
+
return this._ctx
|
|
211
|
+
})
|
|
212
|
+
clearSymbolImageCache()
|
|
213
|
+
getOrCreateSymbolImage('symbol-pin-no-ratio', { width: 44, height: 44 })
|
|
214
|
+
|
|
215
|
+
const feature = pointFeature([5, 5])
|
|
216
|
+
feature.setProperties({ symbol: 'pin', symbolImageId: 'symbol-pin-no-ratio' })
|
|
217
|
+
|
|
218
|
+
const style = styles.createFeatureStyle()(feature)[0]
|
|
219
|
+
expect(style.getImage().getScale()).toBe(1)
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
// edit_point's per-feature style override (point/editPointMode.js) uses this to keep
|
|
223
|
+
// showing the same "selected" (black ring) look the interact plugin's own highlight
|
|
224
|
+
// already uses for the whole edit session — see selectedPointStyleFor's own comment.
|
|
225
|
+
test('selectedPointStyleFor renders the selected variant, falling back to normal then the placeholder', () => {
|
|
226
|
+
HTMLCanvasElement.prototype.getContext = jest.fn(function () {
|
|
227
|
+
this._ctx ??= { putImageData: jest.fn() }
|
|
228
|
+
return this._ctx
|
|
229
|
+
})
|
|
230
|
+
clearSymbolImageCache()
|
|
231
|
+
const selectedCanvas = getOrCreateSymbolImage('symbol-pin-selected', { width: 10, height: 10 })
|
|
232
|
+
|
|
233
|
+
const selected = pointFeature([5, 5])
|
|
234
|
+
selected.setProperties({ symbol: 'pin', symbolImageId: 'symbol-pin-normal', symbolSelectedImageId: 'symbol-pin-selected' })
|
|
235
|
+
const selectedStyle = styles.selectedPointStyleFor(selected)
|
|
236
|
+
expect(selectedStyle.getImage()).toBeInstanceOf(Icon)
|
|
237
|
+
expect(selectedStyle.getImage().getImage(1)).toBe(selectedCanvas)
|
|
238
|
+
|
|
239
|
+
// No selected variant resolved yet → falls back to the normal icon
|
|
240
|
+
const normalCanvas = getOrCreateSymbolImage('symbol-pin-normal-only', { width: 10, height: 10 })
|
|
241
|
+
const normalOnly = pointFeature([5, 5])
|
|
242
|
+
normalOnly.setProperties({ symbol: 'pin', symbolImageId: 'symbol-pin-normal-only' })
|
|
243
|
+
const fallbackStyle = styles.selectedPointStyleFor(normalOnly)
|
|
244
|
+
expect(fallbackStyle.getImage().getImage(1)).toBe(normalCanvas)
|
|
245
|
+
|
|
246
|
+
// Neither resolved → the placeholder circle
|
|
247
|
+
const placeholderStyle = styles.selectedPointStyleFor(pointFeature([5, 5]))
|
|
248
|
+
expect(placeholderStyle.getStroke()).toBeFalsy()
|
|
249
|
+
expect(placeholderStyle.getImage()).not.toBeInstanceOf(Icon)
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
test('a Point feature whose symbolImageId has not been rasterised/cached yet falls back to the placeholder', () => {
|
|
253
|
+
clearSymbolImageCache()
|
|
254
|
+
const feature = pointFeature([5, 5])
|
|
255
|
+
feature.setProperties({ symbol: 'pin', symbolImageId: 'symbol-not-cached' })
|
|
256
|
+
|
|
257
|
+
const style = styles.createFeatureStyle()(feature)[0]
|
|
258
|
+
expect(style.getImage()).not.toBeInstanceOf(Icon)
|
|
259
|
+
expect(style.getStroke()).toBeFalsy()
|
|
260
|
+
expect(style.getFill()).toBeFalsy()
|
|
261
|
+
})
|
|
143
262
|
})
|
|
@@ -4,6 +4,8 @@ import { createFeatureStore } from '../core/featureStore.js'
|
|
|
4
4
|
import { ADAPTER_EVENTS } from '../../../adapterEvents.js'
|
|
5
5
|
import { STYLES_CHANGED_EVENT } from '../core/internalEvents.js'
|
|
6
6
|
import { createFakeMap, createFakeManager, polygonFeature, lineFeature } from '../__helpers__/harness.js'
|
|
7
|
+
import Feature from 'ol/Feature.js'
|
|
8
|
+
import Polygon from 'ol/geom/Polygon.js'
|
|
7
9
|
|
|
8
10
|
jest.mock('./drawInput.js', () => ({
|
|
9
11
|
createDrawInput: jest.fn(() => ({
|
|
@@ -174,6 +176,16 @@ describe('drawing lifecycle', () => {
|
|
|
174
176
|
expect(manager.styles.createSketchStyle).not.toHaveBeenCalledWith('Polygon', true)
|
|
175
177
|
})
|
|
176
178
|
|
|
179
|
+
test('a Polygon sketch with no ring yet (freshly started, before the first coordinate) never throws or goes dashed', () => {
|
|
180
|
+
const { manager, interaction } = setup('Polygon')
|
|
181
|
+
const sketch = new Feature(new Polygon([]))
|
|
182
|
+
sketch.setId('shape-1')
|
|
183
|
+
interaction.dispatchEvent({ type: 'drawstart', feature: sketch })
|
|
184
|
+
// coords[0] is undefined with zero rings — displayedSketch's `?? []` fallback covers it.
|
|
185
|
+
expect(() => sketch.getGeometry().setCoordinates([])).not.toThrow()
|
|
186
|
+
expect(manager.styles.createSketchStyle).not.toHaveBeenCalledWith('Polygon', true)
|
|
187
|
+
})
|
|
188
|
+
|
|
177
189
|
test('2 placed vertices + cursor never go dashed (below the 4-point threshold)', () => {
|
|
178
190
|
const { manager, interaction } = setup('Polygon')
|
|
179
191
|
const sketch = polygonFeature([[0, 0], [5, 5], [0, 0]])
|
|
@@ -3,7 +3,8 @@ import { createVertexPlacement } from './vertexPlacement.js'
|
|
|
3
3
|
const ARROW_KEYS = new Set(['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'])
|
|
4
4
|
|
|
5
5
|
// Mirrors DrawInit.jsx's fixAtCenter()/hide() gate, but reasserted on every interface-type transition this module detects — matching MapLibre's own onSetup/onInterfaceTypeChange redundancy.
|
|
6
|
-
|
|
6
|
+
// Exported alongside wireInputEvents for the same reason — point/drawPointMode.js reuses it directly.
|
|
7
|
+
export const applyCrossHairVisibility = (crossHair, type) => {
|
|
7
8
|
if (!crossHair) { return }
|
|
8
9
|
if (['touch', 'keyboard'].includes(type)) {
|
|
9
10
|
crossHair.fixAtCenter()
|
|
@@ -12,7 +13,12 @@ const applyCrossHairVisibility = (crossHair, type) => {
|
|
|
12
13
|
}
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
// Exported so point/drawPointMode.js can reuse this DOM-wiring layer directly — it's
|
|
17
|
+
// already generic (crosshair/interface-type tracking, add-vertex-button/Enter/undo
|
|
18
|
+
// listeners), parametrised entirely by the callbacks passed in, with no ring-shaped
|
|
19
|
+
// assumptions baked in. draw_point supplies point-shaped versions of those callbacks
|
|
20
|
+
// (no rubber band, no undo) rather than this file growing a second copy of the wiring.
|
|
21
|
+
export const wireInputEvents = ({
|
|
16
22
|
container, addVertexButtonId, olView, onUndo,
|
|
17
23
|
getInterfaceType, setInterfaceType, clearLastCoord,
|
|
18
24
|
updateRubberbanding, placeVertex
|
|
@@ -71,6 +71,13 @@ test('a mid-drag crossing turns the stroke dashed live, and back when it clears'
|
|
|
71
71
|
expect(currentStyle(olFeature)).toBe(manager.styles.editFeatureStyle)
|
|
72
72
|
})
|
|
73
73
|
|
|
74
|
+
test('a Polygon with no ring yet (transiently empty mid-drag) never throws computing numVertices', () => {
|
|
75
|
+
const { olFeature } = setup()
|
|
76
|
+
// OL's own Polygon geometry supports zero rings natively — coordinates[0] is undefined,
|
|
77
|
+
// exercising updateLiveValidity's `?? 1` fallback rather than a real drag scenario.
|
|
78
|
+
expect(() => olFeature.getGeometry().setCoordinates([])).not.toThrow()
|
|
79
|
+
})
|
|
80
|
+
|
|
74
81
|
test('a validity flip repaints the map without touching the feature — Modify tracks its own drag via feature #change, which setStyle() would trip', () => {
|
|
75
82
|
const { map, olFeature } = setup()
|
|
76
83
|
const renderCallsBefore = map.render.mock.calls.length
|
|
@@ -88,6 +95,30 @@ test('validity flips while editing also gate the Done button', () => {
|
|
|
88
95
|
expect.objectContaining({ valid: true }))
|
|
89
96
|
})
|
|
90
97
|
|
|
98
|
+
test('a LineString edit computes numVertices directly from coordinates.length (no ring -1 adjustment)', () => {
|
|
99
|
+
const map = createFakeMap()
|
|
100
|
+
const manager = createFakeManager()
|
|
101
|
+
manager.store = createFeatureStore()
|
|
102
|
+
manager.store.add({ type: 'Feature', id: 'l1', properties: {}, geometry: { type: 'LineString', coordinates: [[0, 0], [100, 0], [100, 100]] } })
|
|
103
|
+
const container = createContainer()
|
|
104
|
+
map.getViewport().appendChild(container)
|
|
105
|
+
const mode = createEditMode({
|
|
106
|
+
map,
|
|
107
|
+
manager,
|
|
108
|
+
options: { featureId: 'l1', container, interfaceType: 'mouse', deleteVertexButtonId: 'del-v', snap: null }
|
|
109
|
+
})
|
|
110
|
+
liveModes.push(mode)
|
|
111
|
+
const olFeature = manager.store.getOL('l1')
|
|
112
|
+
|
|
113
|
+
expect(manager.emit).toHaveBeenCalledWith(ADAPTER_EVENTS.VERTEX_CHANGE, { numVertices: 3 })
|
|
114
|
+
|
|
115
|
+
// LIVE_RULES (self-intersect/area) only ever apply to Polygon (see rules.js's getPolygon
|
|
116
|
+
// guard) — a self-crossing LineString still drives updateLiveValidity, but never flips
|
|
117
|
+
// the stroke invalid, unlike the Polygon case above.
|
|
118
|
+
olFeature.getGeometry().setCoordinates([[0, 0], [100, 100], [100, 0], [0, 100]])
|
|
119
|
+
expect(currentStyle(olFeature)).toBe(manager.styles.editFeatureStyle)
|
|
120
|
+
})
|
|
121
|
+
|
|
91
122
|
test('the user callback runs throttled during an edit drag', () => {
|
|
92
123
|
jest.useFakeTimers()
|
|
93
124
|
const { manager, olFeature } = setup()
|
|
@@ -104,12 +104,13 @@ const buildKeydownHandler = ({ map, getState, setState, nudge, keyMove, onUndo,
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
const buildKeyupHandler = ({
|
|
107
|
+
const buildKeyupHandler = ({ keyMove, onVertexMoved, onDeleted, isFocused }) => (e) => {
|
|
108
108
|
if (isFocused()) {
|
|
109
109
|
return
|
|
110
110
|
}
|
|
111
111
|
if (ARROW_KEYS.has(e.key) && keyMove.start && keyMove.index != null) {
|
|
112
|
-
snap
|
|
112
|
+
// Not hiding the snap indicator here — nudge.js's own snap.apply() already left it showing
|
|
113
|
+
// correctly, and it should stay that way after the key is released.
|
|
113
114
|
onVertexMoved({ vertexIndex: keyMove.index, previousCoord: keyMove.start })
|
|
114
115
|
keyMove.start = null
|
|
115
116
|
keyMove.index = null
|
|
@@ -133,16 +134,19 @@ const buildKeyupHandler = ({ snap, keyMove, onVertexMoved, onDeleted, isFocused
|
|
|
133
134
|
* convert it. Only pressing a plain/Shift arrow converts it.
|
|
134
135
|
*
|
|
135
136
|
* @param {{ map, getState, setState, snap, onVertexMoved, onInserted, onDeleted, onUndo, onKeyboardActive }} options
|
|
137
|
+
* @param {(olFeature, index: number, coord: number[]) => void} [options.moveCoord] - coordinate
|
|
138
|
+
* writer forwarded to wireNudge, defaulting there to moveVertex; point/editPointMode.js
|
|
139
|
+
* injects point/pointOps.js's movePoint instead.
|
|
136
140
|
* @returns {{ nudgeByDelta: (dx: number, dy: number, isLargeStep: boolean) => void, destroy: () => void }}
|
|
137
141
|
*/
|
|
138
142
|
export const createKeyboardHandler = (options) => {
|
|
139
|
-
const { map, snap, getState, setState, onVertexMoved, onInserted, onDeleted, onUndo, onKeyboardActive } = options
|
|
140
|
-
const { nudge, keyMove, nudgeByDelta } = wireNudge({ map, snap, getState, setState, onInserted, onVertexMoved })
|
|
143
|
+
const { map, snap, getState, setState, onVertexMoved, onInserted, onDeleted, onUndo, onKeyboardActive, moveCoord } = options
|
|
144
|
+
const { nudge, keyMove, nudgeByDelta } = wireNudge({ map, snap, getState, setState, onInserted, onVertexMoved, moveCoord })
|
|
141
145
|
const appViewport = map.getViewport().closest('[role="application"]') ?? map.getViewport()
|
|
142
146
|
const isFocused = () => isInteractiveElementFocused(appViewport)
|
|
143
147
|
|
|
144
148
|
const onKeydown = buildKeydownHandler({ map, getState, setState, nudge, keyMove, onUndo, onKeyboardActive, isFocused })
|
|
145
|
-
const onKeyup = buildKeyupHandler({
|
|
149
|
+
const onKeyup = buildKeyupHandler({ keyMove, onVertexMoved, onDeleted, isFocused })
|
|
146
150
|
|
|
147
151
|
globalThis.addEventListener('keydown', onKeydown, { capture: true })
|
|
148
152
|
globalThis.addEventListener('keyup', onKeyup, { capture: true })
|
|
@@ -115,6 +115,39 @@ test('a plain arrow nudges the selected vertex, and keyup commits a single move
|
|
|
115
115
|
expect(onVertexMoved).toHaveBeenCalledTimes(1)
|
|
116
116
|
})
|
|
117
117
|
|
|
118
|
+
// Regression: keyup used to force-hide the snap indicator unconditionally, so it vanished the
|
|
119
|
+
// instant the key was released even when the vertex landed exactly on a snap target — making it
|
|
120
|
+
// impossible to tell whether the nudge had actually snapped. nudge.js's own snap.apply() already
|
|
121
|
+
// leaves the indicator correctly reflecting reality; keyup must leave it alone (mirrors the ML
|
|
122
|
+
// adapter, which never hides its own indicator on keyup either).
|
|
123
|
+
test('keyup does not hide the snap indicator', () => {
|
|
124
|
+
const snap = { apply: jest.fn((c) => c), hideIndicator: jest.fn(), snapRadius: 12 }
|
|
125
|
+
const map = createFakeMap({ center: [98, 98] })
|
|
126
|
+
const state = {
|
|
127
|
+
olFeature: polygonFeature(RING),
|
|
128
|
+
selectedVertexIndex: 1,
|
|
129
|
+
selectedVertexType: 'vertex',
|
|
130
|
+
vertices: [[0, 0], [100, 0], [100, 100]],
|
|
131
|
+
midpoints: [[50, 0], [100, 50], [50, 50]]
|
|
132
|
+
}
|
|
133
|
+
const setState = jest.fn((updates) => Object.assign(state, updates))
|
|
134
|
+
const handler = createKeyboardHandler({
|
|
135
|
+
map,
|
|
136
|
+
getState: () => state,
|
|
137
|
+
setState,
|
|
138
|
+
snap,
|
|
139
|
+
onVertexMoved: jest.fn(),
|
|
140
|
+
onInserted: jest.fn(),
|
|
141
|
+
onDeleted: jest.fn(),
|
|
142
|
+
onUndo: jest.fn(),
|
|
143
|
+
onKeyboardActive: jest.fn()
|
|
144
|
+
})
|
|
145
|
+
liveHandlers.push(handler)
|
|
146
|
+
key('keydown', { key: 'ArrowRight' })
|
|
147
|
+
key('keyup', { key: 'ArrowRight' })
|
|
148
|
+
expect(snap.hideIndicator).not.toHaveBeenCalled()
|
|
149
|
+
})
|
|
150
|
+
|
|
118
151
|
test('arrows without a selection do nothing', () => {
|
|
119
152
|
const { setState } = setup()
|
|
120
153
|
key('keydown', { key: 'ArrowRight' })
|
|
@@ -44,16 +44,18 @@ export const buildModifyCondition = ({ map, getState }) => (mapBrowserEvent) =>
|
|
|
44
44
|
* each completed drag via `onModifyEnd(prevCoords)` with the vertices
|
|
45
45
|
* snapshotted at drag start.
|
|
46
46
|
*
|
|
47
|
+
* @param {(mapBrowserEvent) => boolean} [condition] - overrides buildModifyCondition's
|
|
48
|
+
* fixed-radius vertex hit-test.
|
|
47
49
|
* @returns {{ destroy: () => void }}
|
|
48
50
|
*/
|
|
49
|
-
export const createModifyInteraction = ({ map, olFeature, getState, onModifyEnd }) => {
|
|
50
|
-
const
|
|
51
|
+
export const createModifyInteraction = ({ map, olFeature, getState, onModifyEnd, condition }) => {
|
|
52
|
+
const modifyCondition = condition ?? buildModifyCondition({ map, getState })
|
|
51
53
|
|
|
52
54
|
const modifyInteraction = new Modify({
|
|
53
55
|
features: new Collection([olFeature]),
|
|
54
56
|
style: () => [], // vertex circles rendered by vertexLayer instead
|
|
55
57
|
pixelTolerance: PIXEL_TOLERANCE,
|
|
56
|
-
condition
|
|
58
|
+
condition: modifyCondition
|
|
57
59
|
})
|
|
58
60
|
map.addInteraction(modifyInteraction)
|
|
59
61
|
|
|
@@ -77,4 +77,15 @@ describe('createModifyInteraction', () => {
|
|
|
77
77
|
modify.destroy()
|
|
78
78
|
expect(map.removeInteraction).toHaveBeenCalledWith(interaction)
|
|
79
79
|
})
|
|
80
|
+
|
|
81
|
+
// point/editPointMode.js injects its own condition (built around map.forEachFeatureAtPixel)
|
|
82
|
+
// in place of buildModifyCondition's fixed-radius hit-test, since a symbol icon can render
|
|
83
|
+
// far bigger than PIXEL_TOLERANCE covers.
|
|
84
|
+
test('an injected condition overrides buildModifyCondition\'s own default', () => {
|
|
85
|
+
const map = createFakeMap()
|
|
86
|
+
const state = { interfaceType: 'mouse', vertices: [[0, 0]], midpoints: [] }
|
|
87
|
+
const customCondition = jest.fn(() => true)
|
|
88
|
+
createModifyInteraction({ map, olFeature: polygonFeature(RING), getState: () => state, onModifyEnd: jest.fn(), condition: customCondition })
|
|
89
|
+
expect(map.interactions[0].condition_).toBe(customCondition)
|
|
90
|
+
})
|
|
80
91
|
})
|
|
@@ -30,9 +30,12 @@ export const resolveSnappedCoord = (snap, map, current, nudgedCoord, snappedCoor
|
|
|
30
30
|
* `keyMove` tracks the coordinate at the start of a nudge sequence so a single
|
|
31
31
|
* move_vertex undo op can be pushed on keyup (see keyboardHandler).
|
|
32
32
|
*
|
|
33
|
+
* @param {(olFeature, index: number, coord: number[]) => void} [options.moveCoord] - coordinate
|
|
34
|
+
* writer, defaulting to moveVertex; point/editPointMode.js injects point/pointOps.js's
|
|
35
|
+
* movePoint instead, since a Point has no ring index to address.
|
|
33
36
|
* @returns {{ nudge: (e: KeyboardEvent) => void, keyMove: { start, index }, nudgeByDelta: (dx: number, dy: number, isLargeStep: boolean) => void }}
|
|
34
37
|
*/
|
|
35
|
-
export const wireNudge = ({ map, snap, getState, setState, onInserted, onVertexMoved }) => {
|
|
38
|
+
export const wireNudge = ({ map, snap, getState, setState, onInserted, onVertexMoved, moveCoord = moveVertex }) => {
|
|
36
39
|
const keyMove = { start: null, index: null }
|
|
37
40
|
|
|
38
41
|
// Shared core: moves the selected vertex by an already pixel-scaled delta,
|
|
@@ -46,9 +49,13 @@ export const wireNudge = ({ map, snap, getState, setState, onInserted, onVertexM
|
|
|
46
49
|
const current = vertices[selectedVertexIndex]
|
|
47
50
|
const nudgedCoord = nudgeCoord(map, current, dx, dy)
|
|
48
51
|
const snappedCoord = snap ? snap.apply(nudgedCoord) : nudgedCoord
|
|
49
|
-
snap?.hideIndicator()
|
|
50
52
|
const newCoord = resolveSnappedCoord(snap, map, current, nudgedCoord, snappedCoord, dx, dy)
|
|
51
|
-
|
|
53
|
+
// resolveSnappedCoord can override snappedCoord with an escape jump clear of the snap
|
|
54
|
+
// radius — when it does, the indicator snap.apply() just showed is now stale, so correct it.
|
|
55
|
+
if (snap && newCoord !== snappedCoord) {
|
|
56
|
+
snap.hideIndicator()
|
|
57
|
+
}
|
|
58
|
+
moveCoord(olFeature, selectedVertexIndex, newCoord)
|
|
52
59
|
setState({ vertices: vertices.map((c, i) => i === selectedVertexIndex ? newCoord : c) })
|
|
53
60
|
return { previousCoord: current, vertexIndex: selectedVertexIndex }
|
|
54
61
|
}
|
|
@@ -134,7 +134,17 @@ describe('snapping while nudging', () => {
|
|
|
134
134
|
Object.assign(state, { selectedVertexIndex: 1, selectedVertexType: 'vertex' })
|
|
135
135
|
nudge({ key: 'ArrowRight' })
|
|
136
136
|
expect(ring(olFeature)[1]).toEqual([16, 0])
|
|
137
|
-
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
// Regression: a stray hideIndicator() right after snap.apply() undid its own show on every
|
|
140
|
+
// single nudge, so the indicator could never actually be seen. keyboardHandler.js's own keyup
|
|
141
|
+
// no longer hides it either — see its own comment — so once shown here, it stays shown.
|
|
142
|
+
test('does not hide the indicator mid-sequence — snap.apply() already shows/hides it', () => {
|
|
143
|
+
const snap = snapReturning((c) => [c[0] + 1, c[1]])
|
|
144
|
+
const { state, nudge } = setup({ snap })
|
|
145
|
+
Object.assign(state, { selectedVertexIndex: 1, selectedVertexType: 'vertex' })
|
|
146
|
+
nudge({ key: 'ArrowRight' })
|
|
147
|
+
expect(snap.hideIndicator).not.toHaveBeenCalled()
|
|
138
148
|
})
|
|
139
149
|
|
|
140
150
|
test('escapes the snap radius when snapping blocks the nudge', () => {
|
|
@@ -144,6 +154,19 @@ describe('snapping while nudging', () => {
|
|
|
144
154
|
nudge({ key: 'ArrowRight' })
|
|
145
155
|
expect(ring(olFeature)[1]).toEqual([23, 0]) // snapRadius + 1 past the original position
|
|
146
156
|
})
|
|
157
|
+
|
|
158
|
+
// Regression: nudging away from a target you're sitting on escapes the snap radius (above),
|
|
159
|
+
// but snap.apply() had already shown the indicator at the target it's about to flee — left
|
|
160
|
+
// uncorrected, the indicator stayed pinned there for one extra press even though the vertex
|
|
161
|
+
// had already jumped clear, only catching up on the next press once querying from the
|
|
162
|
+
// escaped position genuinely found nothing.
|
|
163
|
+
test('hides the indicator when a nudge escapes the snap radius', () => {
|
|
164
|
+
const snap = snapReturning(() => [10, 0]) // snap holds the vertex in place
|
|
165
|
+
const { state, nudge } = setup({ snap })
|
|
166
|
+
Object.assign(state, { selectedVertexIndex: 1, selectedVertexType: 'vertex' })
|
|
167
|
+
nudge({ key: 'ArrowRight' })
|
|
168
|
+
expect(snap.hideIndicator).toHaveBeenCalled()
|
|
169
|
+
})
|
|
147
170
|
})
|
|
148
171
|
|
|
149
172
|
describe('resolveSnappedCoord', () => {
|
|
@@ -30,21 +30,22 @@ const handleTouchStart = (e, { map, targetEl, cssToOl, getState, drag }) => {
|
|
|
30
30
|
e.preventDefault()
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const handleTouchMove = (e, { map, targetEl, olToCSS, getState, setState, snap, drag }) => {
|
|
33
|
+
const handleTouchMove = (e, { map, targetEl, olToCSS, getState, setState, snap, drag, moveCoord }) => {
|
|
34
34
|
if (!isOnTouchTarget(e.target) || drag.dragStartIndex == null) {
|
|
35
35
|
return
|
|
36
36
|
}
|
|
37
37
|
e.preventDefault()
|
|
38
38
|
const tOl = map.getEventPixel({ clientX: e.touches[0].clientX, clientY: e.touches[0].clientY })
|
|
39
39
|
const rawCoord = pixelToCoord(map, { x: tOl[0] - drag.vertexTouchDelta.x, y: tOl[1] - drag.vertexTouchDelta.y })
|
|
40
|
+
// snap.apply() already shows/hides the indicator based on whether a candidate was found.
|
|
40
41
|
const newCoord = snap ? snap.apply(rawCoord) : rawCoord
|
|
41
|
-
snap?.hideIndicator()
|
|
42
42
|
const { olFeature, vertices } = getState()
|
|
43
43
|
if (!olFeature) {
|
|
44
44
|
return
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
moveCoord(olFeature, drag.dragStartIndex, newCoord)
|
|
47
47
|
setState({ vertices: vertices.map((c, i) => i === drag.dragStartIndex ? newCoord : c) })
|
|
48
|
+
// Tracks the raw touch point, not newCoord — the vertex is what jumps to the snap candidate.
|
|
48
49
|
showTouchTarget(targetEl, olToCSS({ x: tOl[0] - drag.targetTouchDelta.x, y: tOl[1] - drag.targetTouchDelta.y }))
|
|
49
50
|
}
|
|
50
51
|
|
|
@@ -63,7 +64,7 @@ const handleTap = (e, { map, getState, onTap, drag }) => {
|
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
const handleTouchEnd = (e, ctx) => {
|
|
66
|
-
const { getState, onVertexMoved,
|
|
67
|
+
const { getState, onVertexMoved, drag, updateTargetPosition } = ctx
|
|
67
68
|
if (drag.dragStartIndex == null) {
|
|
68
69
|
handleTap(e, ctx)
|
|
69
70
|
drag.tapStart = null
|
|
@@ -74,8 +75,9 @@ const handleTouchEnd = (e, ctx) => {
|
|
|
74
75
|
if (vertices[drag.dragStartIndex] && drag.dragStartCoord) {
|
|
75
76
|
onVertexMoved({ vertexIndex: drag.dragStartIndex, previousCoord: drag.dragStartCoord })
|
|
76
77
|
}
|
|
77
|
-
|
|
78
|
+
// Not hiding the indicator here — it should stay showing after the drag ends.
|
|
78
79
|
drag.dragStartCoord = null; drag.dragStartIndex = null; drag.vertexTouchDelta = null; drag.targetTouchDelta = null
|
|
80
|
+
updateTargetPosition() // re-sync the target to the vertex's final position
|
|
79
81
|
e.preventDefault()
|
|
80
82
|
}
|
|
81
83
|
|
|
@@ -97,6 +99,8 @@ const wireTouchEvents = (deps) => {
|
|
|
97
99
|
container.addEventListener('touchstart', onTouchstart, { passive: false })
|
|
98
100
|
container.addEventListener('touchmove', onTouchmove, { passive: false })
|
|
99
101
|
container.addEventListener('touchend', onTouchend, { passive: false })
|
|
102
|
+
// touchcancel fires instead of touchend when the browser itself interrupts the gesture.
|
|
103
|
+
container.addEventListener('touchcancel', onTouchend, { passive: false })
|
|
100
104
|
|
|
101
105
|
return {
|
|
102
106
|
isDragging: () => drag.dragStartIndex != null,
|
|
@@ -104,6 +108,7 @@ const wireTouchEvents = (deps) => {
|
|
|
104
108
|
container.removeEventListener('touchstart', onTouchstart)
|
|
105
109
|
container.removeEventListener('touchmove', onTouchmove)
|
|
106
110
|
container.removeEventListener('touchend', onTouchend)
|
|
111
|
+
container.removeEventListener('touchcancel', onTouchend)
|
|
107
112
|
}
|
|
108
113
|
}
|
|
109
114
|
}
|
|
@@ -114,15 +119,16 @@ const wireTouchEvents = (deps) => {
|
|
|
114
119
|
* without finger occlusion. Tap on a vertex or midpoint selects it via onTap.
|
|
115
120
|
*
|
|
116
121
|
* @param {{ map, container, getState, setState, onVertexMoved, onTap, colors }} options
|
|
122
|
+
* @param {(olFeature, index: number, coord: number[]) => void} [options.moveCoord] - coordinate
|
|
123
|
+
* writer, defaulting to moveVertex; point/editPointMode.js injects point/pointOps.js's
|
|
124
|
+
* movePoint instead, since a Point has no ring index to address.
|
|
117
125
|
* @returns {{ updateTargetPosition, updateColors, hide, destroy }}
|
|
118
126
|
*/
|
|
119
|
-
export const createTouchHandler = ({ map, container, getState, setState, onVertexMoved, onTap, colors, snap }) => {
|
|
127
|
+
export const createTouchHandler = ({ map, container, getState, setState, onVertexMoved, onTap, colors, snap, moveCoord = moveVertex }) => {
|
|
120
128
|
const targetEl = createTouchTarget(container)
|
|
121
129
|
applyTouchTargetColors(targetEl, colors)
|
|
122
130
|
|
|
123
|
-
// OL pixel space
|
|
124
|
-
// Container CSS space is larger when a CSS transform scales up ol-viewport
|
|
125
|
-
// (e.g. scale(1.5) at medium map size makes OL pixels 1.5× smaller than CSS pixels).
|
|
131
|
+
// Converts between OL pixel space and CSS space, which differ when a CSS transform scales up ol-viewport.
|
|
126
132
|
const cssTx = { scale: 1, ox: 0, oy: 0 }
|
|
127
133
|
const olToCSS = (p) => ({ x: p.x * cssTx.scale + cssTx.ox, y: p.y * cssTx.scale + cssTx.oy })
|
|
128
134
|
const cssToOl = (p) => ({ x: (p.x - cssTx.ox) / cssTx.scale, y: (p.y - cssTx.oy) / cssTx.scale })
|
|
@@ -140,8 +146,6 @@ export const createTouchHandler = ({ map, container, getState, setState, onVerte
|
|
|
140
146
|
})
|
|
141
147
|
}
|
|
142
148
|
|
|
143
|
-
const touchEvents = wireTouchEvents({ container, map, targetEl, olToCSS, cssToOl, getState, setState, onVertexMoved, onTap, snap })
|
|
144
|
-
|
|
145
149
|
const updateTargetPosition = () => {
|
|
146
150
|
const { selectedVertexIndex, vertices, interfaceType } = getState()
|
|
147
151
|
if (selectedVertexIndex < 0 || !vertices[selectedVertexIndex] || interfaceType !== 'touch') {
|
|
@@ -156,8 +160,9 @@ export const createTouchHandler = ({ map, container, getState, setState, onVerte
|
|
|
156
160
|
showTouchTarget(targetEl, olToCSS(px))
|
|
157
161
|
}
|
|
158
162
|
|
|
159
|
-
|
|
160
|
-
|
|
163
|
+
const touchEvents = wireTouchEvents({ container, map, targetEl, olToCSS, cssToOl, getState, setState, onVertexMoved, onTap, snap, moveCoord, updateTargetPosition })
|
|
164
|
+
|
|
165
|
+
// Reposition on every render (skipped mid-drag, which handles position directly) to stay anchored during pinch-zoom and pan.
|
|
161
166
|
const onPostrender = () => {
|
|
162
167
|
const { selectedVertexIndex, interfaceType } = getState()
|
|
163
168
|
if (selectedVertexIndex >= 0 && !touchEvents.isDragging() && interfaceType === 'touch') {
|