diff --git a/packages/plugins/Draw/CHANGELOG.md b/packages/plugins/Draw/CHANGELOG.md index 89c6673e5..898878e8f 100644 --- a/packages/plugins/Draw/CHANGELOG.md +++ b/packages/plugins/Draw/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## unpublished + +- Feature: Add a "Translate" mode that allows moving drawn features as they are. + ## 3.0.0 - Breaking: Upgrade peerDependency `ol` from `^9.2.4` to `^10.3.1`. diff --git a/packages/plugins/Draw/src/locales.ts b/packages/plugins/Draw/src/locales.ts index 762d4fda9..ef89c61ea 100644 --- a/packages/plugins/Draw/src/locales.ts +++ b/packages/plugins/Draw/src/locales.ts @@ -10,6 +10,7 @@ export const resourcesDe = { write: 'Zeichnen und Schreiben', writeAndMeasure: 'Zeichnen, Schreiben und Messen', edit: 'Bearbeiten', + translate: 'Verschieben', delete: 'Löschen', }, drawMode: { @@ -53,6 +54,7 @@ export const resourcesEn = { write: 'Draw and write', writeAndMeasure: 'Draw, write and measure', edit: 'Edit', + translate: 'Translate', delete: 'Delete', }, drawMode: { diff --git a/packages/plugins/Draw/src/store/actions.ts b/packages/plugins/Draw/src/store/actions.ts index b1dc2000e..7a5c5cc0f 100644 --- a/packages/plugins/Draw/src/store/actions.ts +++ b/packages/plugins/Draw/src/store/actions.ts @@ -9,6 +9,7 @@ import { createTextStyle } from '../utils/createTextStyle' import createDrawStyle from '../utils/createDrawStyle' import createInteractions from './createInteractions' import createModifyInteractions from './createInteractions/createModifyInteractions' +import createTranslateInteractions from './createInteractions/createTranslateInteractions' import modifyDrawStyle from './createInteractions/modifyDrawStyle' import modifyTextStyle from './createInteractions/modifyTextStyle' @@ -22,6 +23,7 @@ export const makeActions = () => { const actions: PolarActionTree = { createInteractions, createModifyInteractions, + createTranslateInteractions, modifyDrawStyle, modifyTextStyle, setupModule({ diff --git a/packages/plugins/Draw/src/store/createInteractions/createModifyInteractions.ts b/packages/plugins/Draw/src/store/createInteractions/createModifyInteractions.ts index 9334a65a6..015ef1ef2 100644 --- a/packages/plugins/Draw/src/store/createInteractions/createModifyInteractions.ts +++ b/packages/plugins/Draw/src/store/createInteractions/createModifyInteractions.ts @@ -1,40 +1,30 @@ import { Modify, Select, Snap } from 'ol/interaction' import Interaction from 'ol/interaction/Interaction' import { PolarActionContext } from '@polar/lib-custom-types' -import { Collection, Feature, Map, MapBrowserEvent } from 'ol' +import { Collection, Feature, Map } from 'ol' import { CreateInteractionsPayload, DrawGetters, DrawState } from '../../types' +import { makeLocalSelector } from './localSelector' const createModify = ( map: Map, drawLayer: CreateInteractionsPayload['drawLayer'] ) => { - let active = false + const activeContainer = { active: false } const features: Collection = new Collection() const modify = new Modify({ features }) modify.on('modifystart', () => { - active = true + activeContainer.active = true }) modify.on('modifyend', () => { - active = false + activeContainer.active = false }) - const localSelector = (e: MapBrowserEvent) => { - if (!active) { - map.forEachFeatureAtPixel( - e.pixel, - (f) => { - if (f !== features.item(0)) { - features.setAt(0, f as Feature) - } - return true - }, - { - layerFilter: (l) => l === drawLayer, - } - ) - } - } - + const localSelector = makeLocalSelector( + map, + activeContainer, + features, + drawLayer + ) map.on('pointermove', localSelector) // @ts-expect-error | "un on removal" riding piggyback as _onRemove modify._onRemove = () => map.un('pointermove', localSelector) diff --git a/packages/plugins/Draw/src/store/createInteractions/createTranslateInteractions.ts b/packages/plugins/Draw/src/store/createInteractions/createTranslateInteractions.ts new file mode 100644 index 000000000..b8931f725 --- /dev/null +++ b/packages/plugins/Draw/src/store/createInteractions/createTranslateInteractions.ts @@ -0,0 +1,43 @@ +import { Translate, Snap } from 'ol/interaction' +import Interaction from 'ol/interaction/Interaction' +import { PolarActionContext } from '@polar/lib-custom-types' +import { Collection, Feature, Map } from 'ol' +import { CreateInteractionsPayload, DrawGetters, DrawState } from '../../types' +import { makeLocalSelector } from './localSelector' + +const createTranslate = ( + map: Map, + drawLayer: CreateInteractionsPayload['drawLayer'] +) => { + const activeContainer = { active: false } + const features: Collection = new Collection() + const translate = new Translate({ features }) + translate.on('translatestart', () => { + activeContainer.active = true + }) + translate.on('translateend', () => { + activeContainer.active = false + }) + + const localSelector = makeLocalSelector( + map, + activeContainer, + features, + drawLayer + ) + map.on('pointermove', localSelector) + // @ts-expect-error | "un on removal" riding piggyback as _onRemove + translate._onRemove = () => map.un('pointermove', localSelector) + + return translate +} + +export default function ( + { rootGetters }: PolarActionContext, + { drawSource, drawLayer }: CreateInteractionsPayload +): Interaction[] { + return [ + createTranslate(rootGetters.map, drawLayer), + new Snap({ source: drawSource }), + ] +} diff --git a/packages/plugins/Draw/src/store/createInteractions/index.ts b/packages/plugins/Draw/src/store/createInteractions/index.ts index 7526ddb03..33c1dd5ef 100644 --- a/packages/plugins/Draw/src/store/createInteractions/index.ts +++ b/packages/plugins/Draw/src/store/createInteractions/index.ts @@ -38,7 +38,6 @@ export default function ( map.getView().getProjection(), configuration?.style ) - const draw = new Draw({ source: drawSource, type: drawMode, @@ -47,10 +46,11 @@ export default function ( // @ts-expect-error | internal hack to detect it in @polar/plugin-pins and @polar/plugin-gfi draw._isDrawPlugin = true draw.on('drawend', (e) => e.feature.setStyle(style)) - return [draw, new Snap({ source: drawSource })] } else if (mode === 'edit') { return dispatch('createModifyInteractions', { drawSource, drawLayer }) + } else if (mode === 'translate') { + return dispatch('createTranslateInteractions', { drawSource, drawLayer }) } else if (mode === 'delete') { return createDeleteInteractions(drawSource, drawLayer) } diff --git a/packages/plugins/Draw/src/store/createInteractions/localSelector.ts b/packages/plugins/Draw/src/store/createInteractions/localSelector.ts new file mode 100644 index 000000000..d282b2903 --- /dev/null +++ b/packages/plugins/Draw/src/store/createInteractions/localSelector.ts @@ -0,0 +1,28 @@ +import { Collection, Feature, Map, MapBrowserEvent } from 'ol' +import { CreateInteractionsPayload } from '../../types' + +/* sets the topmost hovered feature as singleton feature in collection */ +export const makeLocalSelector = + ( + map: Map, + activeContainer: { active: boolean }, + features: Collection, + drawLayer: CreateInteractionsPayload['drawLayer'] + ) => + // bound event processor + (e: MapBrowserEvent) => { + if (!activeContainer.active) { + map.forEachFeatureAtPixel( + e.pixel, + (f) => { + if (f !== features.item(0)) { + features.setAt(0, f as Feature) + } + return true + }, + { + layerFilter: (l) => l === drawLayer, + } + ) + } + } diff --git a/packages/plugins/Draw/src/store/index.ts b/packages/plugins/Draw/src/store/index.ts index 4080cb094..52e5b4559 100644 --- a/packages/plugins/Draw/src/store/index.ts +++ b/packages/plugins/Draw/src/store/index.ts @@ -83,6 +83,7 @@ export const makeStoreModule = () => { none: 'plugins.draw.mode.none', draw: `plugins.draw.mode.${drawLabel}`, edit: 'plugins.draw.mode.edit', + translate: 'plugins.draw.mode.translate', delete: 'plugins.draw.mode.delete', } }, diff --git a/packages/plugins/Draw/src/types.ts b/packages/plugins/Draw/src/types.ts index 7cb490904..a1208b452 100644 --- a/packages/plugins/Draw/src/types.ts +++ b/packages/plugins/Draw/src/types.ts @@ -18,7 +18,7 @@ export interface PolarVectorOptions { style?: StyleLike } -export type Mode = 'none' | 'draw' | 'edit' | 'delete' +export type Mode = 'none' | 'draw' | 'edit' | 'translate' | 'delete' export interface CreateInteractionsPayload { drawSource: VectorSource