Cmd-Z (#141)
This commit is contained in:
parent
4ca8f65887
commit
9e5c5daf64
@ -20,6 +20,34 @@ const LOCAL_STORAGE_KEY_STATE = "excalidraw-state";
|
|||||||
|
|
||||||
const elements = Array.of<ExcalidrawElement>();
|
const elements = Array.of<ExcalidrawElement>();
|
||||||
|
|
||||||
|
let skipHistory = false;
|
||||||
|
const stateHistory: string[] = [];
|
||||||
|
function generateHistoryCurrentEntry() {
|
||||||
|
return JSON.stringify(
|
||||||
|
elements.map(element => ({ ...element, isSelected: false }))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function pushHistoryEntry(newEntry: string) {
|
||||||
|
if (
|
||||||
|
stateHistory.length > 0 &&
|
||||||
|
stateHistory[stateHistory.length - 1] === newEntry
|
||||||
|
) {
|
||||||
|
// If the last entry is the same as this one, ignore it
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stateHistory.push(newEntry);
|
||||||
|
}
|
||||||
|
function restoreHistoryEntry(entry: string) {
|
||||||
|
const newElements = JSON.parse(entry);
|
||||||
|
elements.splice(0, elements.length);
|
||||||
|
newElements.forEach((newElement: ExcalidrawElement) => {
|
||||||
|
generateDraw(newElement);
|
||||||
|
elements.push(newElement);
|
||||||
|
});
|
||||||
|
// When restoring, we shouldn't add an history entry otherwise we'll be stuck with it and can't go back
|
||||||
|
skipHistory = true;
|
||||||
|
}
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript/47593316#47593316
|
// https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript/47593316#47593316
|
||||||
const LCG = (seed: number) => () =>
|
const LCG = (seed: number) => () =>
|
||||||
((2 ** 31 - 1) & (seed = Math.imul(48271, seed))) / 2 ** 31;
|
((2 ** 31 - 1) & (seed = Math.imul(48271, seed))) / 2 ** 31;
|
||||||
@ -901,6 +929,17 @@ class App extends React.Component<{}, AppState> {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
} else if (shapesShortcutKeys.includes(event.key.toLowerCase())) {
|
} else if (shapesShortcutKeys.includes(event.key.toLowerCase())) {
|
||||||
this.setState({ elementType: findElementByKey(event.key) });
|
this.setState({ elementType: findElementByKey(event.key) });
|
||||||
|
} else if (event.metaKey && event.code === "KeyZ") {
|
||||||
|
let lastEntry = stateHistory.pop();
|
||||||
|
// If nothing was changed since last, take the previous one
|
||||||
|
if (generateHistoryCurrentEntry() === lastEntry) {
|
||||||
|
lastEntry = stateHistory.pop();
|
||||||
|
}
|
||||||
|
if (lastEntry !== undefined) {
|
||||||
|
restoreHistoryEntry(lastEntry);
|
||||||
|
}
|
||||||
|
this.forceUpdate();
|
||||||
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1301,6 +1340,8 @@ class App extends React.Component<{}, AppState> {
|
|||||||
});
|
});
|
||||||
lastX = x;
|
lastX = x;
|
||||||
lastY = y;
|
lastY = y;
|
||||||
|
// We don't want to save history when resizing an element
|
||||||
|
skipHistory = true;
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1319,6 +1360,8 @@ class App extends React.Component<{}, AppState> {
|
|||||||
});
|
});
|
||||||
lastX = x;
|
lastX = x;
|
||||||
lastY = y;
|
lastY = y;
|
||||||
|
// We don't want to save history when dragging an element to initially size it
|
||||||
|
skipHistory = true;
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1347,6 +1390,8 @@ class App extends React.Component<{}, AppState> {
|
|||||||
if (this.state.elementType === "selection") {
|
if (this.state.elementType === "selection") {
|
||||||
setSelection(draggingElement);
|
setSelection(draggingElement);
|
||||||
}
|
}
|
||||||
|
// We don't want to save history when moving an element
|
||||||
|
skipHistory = true;
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1384,6 +1429,8 @@ class App extends React.Component<{}, AppState> {
|
|||||||
window.addEventListener("mousemove", onMouseMove);
|
window.addEventListener("mousemove", onMouseMove);
|
||||||
window.addEventListener("mouseup", onMouseUp);
|
window.addEventListener("mouseup", onMouseUp);
|
||||||
|
|
||||||
|
// We don't want to save history on mouseDown, only on mouseUp when it's fully configured
|
||||||
|
skipHistory = true;
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -1407,6 +1454,10 @@ class App extends React.Component<{}, AppState> {
|
|||||||
viewBackgroundColor: this.state.viewBackgroundColor
|
viewBackgroundColor: this.state.viewBackgroundColor
|
||||||
});
|
});
|
||||||
save(this.state);
|
save(this.state);
|
||||||
|
if (!skipHistory) {
|
||||||
|
pushHistoryEntry(generateHistoryCurrentEntry());
|
||||||
|
}
|
||||||
|
skipHistory = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user