Open hamedf62 opened 4 years ago
Hi.
The undo / redo function.
I don't know if he would have to come with the library.
Or develop by yourself. I study it.
I thought it would be possible if create and save events logs, and parse it back when ctrl+z pushed.
Good luck 🙏
Yes, it is needed undo redo feature.
Hello @jerosoler , can I know whether you have plans to start work on it? Whether you will add undo-redo features in this amazing library?
Hello,
I have not started working. But here is a guide on how to do it. Since it is a function you can have many options.
Here I have done a little test removing and creating connections.
It seems to work well.
Since it can be implemented thanks to events.
HTML
<button onclick="undo()">Undo</button>
<button onclick="redo()">Redo</button>
Javascript
const history = [];
let historyPosition = -1;
let fixHistory = false;
function undo() {
if(historyPosition < 0) {
console.log("Finish");
return;
}
const redoElement = history[historyPosition];
const redoElementName = Object.keys( redoElement );
switch(redoElementName[0]){
case 'connectionCreated':
var d = history[historyPosition]["connectionCreated"][0];
fixHistory = true;
editor.removeSingleConnection(d.output_id, d.input_id, d.output_class, d.input_class);
break;
case 'connectionRemoved':
var d = history[historyPosition]["connectionRemoved"][0];
fixHistory = true;
editor.addConnection(d.output_id, d.input_id, d.output_class, d.input_class);
break;
}
historyPosition--;
}
function redo() {
if(historyPosition === history.length-1) {
console.log("Last");
return;
}
historyPosition++;
const redoElement = history[historyPosition];
const redoElementName = Object.keys( redoElement );
switch(redoElementName[0]){
case 'connectionCreated':
var d = history[historyPosition]["connectionCreated"][0];
fixHistory = true;
editor.addConnection(d.output_id, d.input_id, d.output_class, d.input_class);
break;
case 'connectionRemoved':
var d = history[historyPosition]["connectionRemoved"][0];
fixHistory = true;
editor.removeSingleConnection(d.output_id, d.input_id, d.output_class, d.input_class);
break;
}
}
editor.on('connectionCreated', function({ output_id, input_id, output_class, input_class }) {
if(!fixHistory) {
history.push({ connectionCreated: [{ output_id, input_id, output_class, input_class }] });
historyPosition++;
} else {
fixHistory = false;
}
})
editor.on('connectionRemoved', function({ output_id, input_id, output_class, input_class }) {
if(!fixHistory) {
history.push({ connectionRemoved: [{ output_id, input_id, output_class, input_class }] });
historyPosition++;
} else {
fixHistory = false;
}
})
Jero
Thanks a lot @jerosoler , I will give a try to the example code you have provided above.
hey @jerosoler , can we do similar functionality with nodes? i.e redo and undo with nodes. if yes, can we tell me how?
How to do the same for nodes and node values
Hello @jerosoler,
I'm working on undo and redo actions but I used an approach a little bit different than the example. I'm trying to achieve this by Export() and Import() methods. When I add a Node by a button o screen it works well because I can Export() flow before the node creation.
But I'm trying to figure out how can I Export() flow before the events occurs.
I tried:
But the events trigger after all action done. Is there anyway I can execute some code before the flow execute his actions?
Thank you and congrats about this library
I understand that if you are saving the values. It already has the old value.
All changes can be detected using a javascript proxy. Example:
const editor = new Drawflow(id);
const handler = {
get: function(target, property, receiver) {
console.log(JSON.stringify(target));
return Reflect.get(...arguments)
},
}
editor.drawflow = new Proxy(editor.drawflow, handler);
editor.start();
I've been playing around with this idea a bit, an example could be:
var id = document.getElementById("drawflow");
const editor = new Drawflow(id);
const history = [];
let historyStatus = null;
let historyActive = false;
let historyStop = false;
function undo() {
if(historyStatus == 1) {
return;
}
if(historyStatus === null) {
historyStatus = history.length;
}
if(historyStatus === history.length) {
historyActive = true;
history.push(editor.export())
history.splice(-1); // Fix editor export two events;
}
historyStatus = historyStatus-1;
historyActive = true;
editor.import(JSON.parse(history[historyStatus]));
editor.drawflow = new Proxy(editor.drawflow, handler);
}
function redo() {
if(historyStatus !== null && historyStatus < history.length-1) {
historyStatus = historyStatus+1;
historyActive = true;
editor.import(JSON.parse(history[historyStatus]));
editor.drawflow = new Proxy(editor.drawflow, handler);
}
}
editor.on("click", () => {
historyStop = true;
})
editor.on("mouseUp", () => {
historyStop = false;
historyActive = true;
history.push(editor.export())
history.splice(-1);
if(history.at(-2) === history.at(-1)) {
console.log("delete");
history.splice(-1);
} else {
/// ??? DELETE History at restore point????
historyStatus = history.length-1
}
})
const handler = {
get: function(target, property, receiver) {
console.log(JSON.stringify(target));
if(!historyActive && !historyStop) {
history.push(JSON.stringify(target));
}
historyActive = false;
return Reflect.get(...arguments)
},
}
editor.drawflow = new Proxy(editor.drawflow, handler);
editor.start();
history.splice(-2); // Fix history
editor.addNode('aaa', 0, 1, 100, 300, 'aaa', {}, 'aaa' );
editor.addNode('bbb', 1, 0, 200, 400, 'bbb', {}, 'bbb' );
editor.addNode('ccc', 1, 0, 500, 500, 'ccc', {}, 'ccc' );
Thank you a lot @jerosoler!!
could you add this functionality along with the library
dear @jerosoler
it would be good idea to add CTRL+Z and CTRL+Y in next versions to accelerate editing.
specially if you want to correct wrongly added node or connection have to delete it manually.
** for delete a node in Mac have to push down both fn+Del to do. i thought its possible to use only Del key.
thank you