diff --git a/docs/modules/editable-layers/api-reference/edit-modes/transform-modes.md b/docs/modules/editable-layers/api-reference/edit-modes/transform-modes.md index 59969676..334f8af9 100644 --- a/docs/modules/editable-layers/api-reference/edit-modes/transform-modes.md +++ b/docs/modules/editable-layers/api-reference/edit-modes/transform-modes.md @@ -58,3 +58,8 @@ A single mode that provides translating, rotating, and scaling capabilities. Tra [Source code](https://github.com/visgl/deck.gl-community/blob/master/modules/editable-layers/src/edit-modes/transform-mode.ts) +## DeleteMode + +User can delete features by clicking on them. Only the most recently added feature will be deleted if multiple features overlap. + +[Source code](https://github.com/visgl/deck.gl-community/blob/master/modules/editable-layers/src/edit-modes/delete-mode.ts) diff --git a/modules/editable-layers/src/edit-modes/delete-mode.ts b/modules/editable-layers/src/edit-modes/delete-mode.ts new file mode 100644 index 00000000..eaeead7a --- /dev/null +++ b/modules/editable-layers/src/edit-modes/delete-mode.ts @@ -0,0 +1,28 @@ +import {FeatureCollection} from '../utils/geojson-types'; + +import {GeoJsonEditMode} from './geojson-edit-mode'; +import {ClickEvent, ModeProps} from './types'; +export class DeleteMode extends GeoJsonEditMode { + handleClick(_event: ClickEvent, props: ModeProps): void { + const selectedFeatureIndexes = props.lastPointerMoveEvent.picks.map((pick) => pick.index); + if (selectedFeatureIndexes.length > 0) { + const indexToDelete = selectedFeatureIndexes[0]; + + const features = props.data.features.filter((_, index) => index !== indexToDelete); + const updatedData = { + ...props.data, + features + }; + + const editAction = { + updatedData, + editType: 'deleteFeature', + editContext: { + featureIndexes: selectedFeatureIndexes + } + }; + + props.onEdit(editAction); + } + } +} diff --git a/modules/editable-layers/src/editable-layers/editable-geojson-layer.ts b/modules/editable-layers/src/editable-layers/editable-geojson-layer.ts index 8f18b900..e6473cc0 100644 --- a/modules/editable-layers/src/editable-layers/editable-geojson-layer.ts +++ b/modules/editable-layers/src/editable-layers/editable-geojson-layer.ts @@ -40,6 +40,7 @@ import {Draw90DegreePolygonMode} from '../edit-modes/draw-90degree-polygon-mode' import {DrawPolygonByDraggingMode} from '../edit-modes/draw-polygon-by-dragging-mode'; import {SnappableMode} from '../edit-modes/snappable-mode'; import {TransformMode} from '../edit-modes/transform-mode'; +import {DeleteMode} from '../edit-modes/delete-mode'; import {GeoJsonEditModeType} from '../edit-modes/geojson-edit-mode'; import {Color} from '../utils/types'; @@ -250,6 +251,7 @@ const modeNameMapping = { split: SplitPolygonMode, extrude: ExtrudeMode, elevation: ElevationMode, + delete: DeleteMode, // Draw modes drawPoint: DrawPointMode, diff --git a/modules/editable-layers/test/edit-modes/lib/delete-mode.spec.ts b/modules/editable-layers/test/edit-modes/lib/delete-mode.spec.ts new file mode 100644 index 00000000..379e997e --- /dev/null +++ b/modules/editable-layers/test/edit-modes/lib/delete-mode.spec.ts @@ -0,0 +1,72 @@ +import {describe, it, expect, vi} from 'vitest'; +import {DeleteMode} from '../../../src/edit-modes/delete-mode'; +import {ClickEvent, ModeProps} from '../../../src/edit-modes/types'; +import {FeatureCollection} from '../../../src/utils/geojson-types'; + +describe('DeleteMode', () => { + it('should not call onEdit when no features are selected', () => { + const deleteMode = new DeleteMode(); + const props: ModeProps = { + data: {type: 'FeatureCollection', features: []}, + selectedIndexes: [], + lastPointerMoveEvent: {picks: []}, + onEdit: vi.fn() + }; + + deleteMode.handleClick({} as ClickEvent, props); + + expect(props.onEdit).not.toHaveBeenCalled(); + }); + + it('should call onEdit with correct parameters when one feature is selected', () => { + const deleteMode = new DeleteMode(); + const props: ModeProps = { + data: { + type: 'FeatureCollection', + features: [ + {type: 'Feature', geometry: {type: 'Point', coordinates: [0, 0]}, properties: {}} + ] + }, + selectedIndexes: [], + lastPointerMoveEvent: {picks: [{index: 0}]}, + onEdit: vi.fn() + }; + + deleteMode.handleClick({} as ClickEvent, props); + + expect(props.onEdit).toHaveBeenCalledWith({ + updatedData: {type: 'FeatureCollection', features: []}, + editType: 'deleteFeature', + editContext: {featureIndexes: [0]} + }); + }); + + it('should call onEdit with correct parameters when multiple features are selected', () => { + const deleteMode = new DeleteMode(); + const props: ModeProps = { + data: { + type: 'FeatureCollection', + features: [ + {type: 'Feature', geometry: {type: 'Point', coordinates: [0, 0]}, properties: {}}, + {type: 'Feature', geometry: {type: 'Point', coordinates: [1, 1]}, properties: {}} + ] + }, + selectedIndexes: [], + lastPointerMoveEvent: {picks: [{index: 0}, {index: 1}]}, + onEdit: vi.fn() + }; + + deleteMode.handleClick({} as ClickEvent, props); + + expect(props.onEdit).toHaveBeenCalledWith({ + updatedData: { + type: 'FeatureCollection', + features: [ + {type: 'Feature', geometry: {type: 'Point', coordinates: [1, 1]}, properties: {}} + ] + }, + editType: 'deleteFeature', + editContext: {featureIndexes: [0, 1]} + }); + }); +});