mapbox / mapbox-gl-draw

Draw tools for mapbox-gl-js
https://www.mapbox.com/mapbox-gl-js/example/mapbox-gl-draw/
ISC License
955 stars 593 forks source link

Add undo/redo functionality #791

Open sleep-drifter opened 6 years ago

sleep-drifter commented 6 years ago

Feature Request:

Wanted to see if there is any desire to bring back redo/undo functionality (#21). I find it to be one of the most useful drawing "tools" in the Studio dataset editor and would love to have it in the draw API.

Thanks!

mcwhittemore commented 6 years ago

@mwujek - My gut instinct is that this is best done in user land with that module documented in this repo. That way Draw continues to mange one thing but users can easily get undo/redo into their applications. What do you think?

Are there any good plugable undo/redo modules out there? I haven't looked into this in a while.

sleep-drifter commented 6 years ago

@mcwhittemore thanks for the quick response. I'll look into 3rd party plugins after taking a look at the module you mentioned. Where can exactly can I find that?

mcwhittemore commented 6 years ago

Where can exactly can I find that?

There is not Draw undo/redo module. I was noting that having one to point to would be good.

I'll look into 3rd party plugins

If you can comment back here with your findings that would be great.

clementblaudeauEarthCube commented 6 years ago

I would be very interested by such a feature. Any updates?

andycochran commented 6 years ago

The easy part is undo/redo – @tmcw, #21

Does anyone have an example of implementing undo/redo?

tmcw commented 6 years ago

After that, I wrote an article about implementing undo and redo in JavaScript. That approach is essentially what's implemented in Studio, iD, and elsewhere.

usb248 commented 4 years ago

Any updates ?

teatalayP commented 2 years ago

Come on guys!

daniel-krastev commented 1 year ago

If anyone is still looking for this, I couldn't find anything, so implemented a naive solution as a starter (undo only). I have a simple stack class, that looks like this:

export class Stack<T> {
    private stack: T[] = []

    // Push an item onto the stack
    push(item: T): void {
        this.stack.push(item)
    }

    // Pop the top item from the stack and return it
    pop(): T | undefined {
        return this.stack.pop()
    }

    // Check if the stack is empty
    isEmpty(): boolean {
        return this.stack.length === 0
    }

    // Get the current size of the stack
    size(): number {
        return this.stack.length
    }

    // Peek at the top item of the stack without removing it
    peek(): T | undefined {
        return this.stack[this.stack.length - 1]
    }
}

You can init it and add it to the draw events you want to undo:

drawingsStack = new Stack<FeatureCollection>()

const drawFunc: Listener = (e) => {
    // any other logic you might have
    drawingsStack.push(d.getAll())
}
_map.on('draw.create', drawFunc)
_map.on('draw.update', drawFunc)
_map.on('draw.delete', drawFunc)

Then on the map div you add a listener. I am using Svelte. ($mbdraw is the MapboxDraw instance)

<div
    style="width: {width}px; height: {height}px;"
    {...$$props}
    id="map_container"
    on:keydown|preventDefault={(event) => {
        if (event.ctrlKey && event.key === 'z') {
            if (drawingModeEnabled) {
                undo()
            }
        }
    }}
/>

<script lang="ts">
    const undo = () => {
        $mbdraw.deleteAll()
        if (drawingsStack.isEmpty()) {
            return
        }
        $mbdraw.add(drawingsStack.pop() as FeatureCollection)
    }
</script>