2021-05-10 12:16:57 +00:00
|
|
|
import { Data } from "types"
|
2021-05-13 08:34:56 +00:00
|
|
|
import { BaseCommand } from "./commands/command"
|
2021-05-16 07:09:46 +00:00
|
|
|
import state from "./state"
|
2021-05-10 12:16:57 +00:00
|
|
|
|
2021-05-13 06:44:52 +00:00
|
|
|
// A singleton to manage history changes.
|
|
|
|
|
2021-05-16 07:09:46 +00:00
|
|
|
class BaseHistory<T> {
|
2021-05-10 12:16:57 +00:00
|
|
|
private stack: BaseCommand<T>[] = []
|
|
|
|
private pointer = -1
|
|
|
|
private maxLength = 100
|
|
|
|
private _enabled = true
|
|
|
|
|
|
|
|
execute = (data: T, command: BaseCommand<T>) => {
|
|
|
|
if (this.disabled) return
|
|
|
|
this.stack = this.stack.slice(0, this.pointer + 1)
|
|
|
|
this.stack.push(command)
|
|
|
|
command.redo(data, true)
|
|
|
|
this.pointer++
|
|
|
|
|
|
|
|
if (this.stack.length > this.maxLength) {
|
|
|
|
this.stack = this.stack.slice(this.stack.length - this.maxLength)
|
|
|
|
this.pointer = this.maxLength - 1
|
|
|
|
}
|
|
|
|
|
|
|
|
this.save(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
undo = (data: T) => {
|
|
|
|
if (this.disabled) return
|
|
|
|
if (this.pointer === -1) return
|
|
|
|
const command = this.stack[this.pointer]
|
|
|
|
command.undo(data)
|
|
|
|
this.pointer--
|
|
|
|
this.save(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
redo = (data: T) => {
|
|
|
|
if (this.disabled) return
|
|
|
|
if (this.pointer === this.stack.length - 1) return
|
|
|
|
const command = this.stack[this.pointer + 1]
|
|
|
|
command.redo(data, false)
|
|
|
|
this.pointer++
|
|
|
|
this.save(data)
|
|
|
|
}
|
|
|
|
|
2021-05-16 07:09:46 +00:00
|
|
|
load(data: T, id = "code_slate_0.0.1") {
|
2021-05-10 12:16:57 +00:00
|
|
|
if (typeof window === "undefined") return
|
|
|
|
if (typeof localStorage === "undefined") return
|
|
|
|
|
2021-05-16 07:09:46 +00:00
|
|
|
const savedData = localStorage.getItem(id)
|
|
|
|
|
|
|
|
if (savedData !== null) {
|
|
|
|
Object.assign(data, this.restoreSavedData(JSON.parse(savedData)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
save = (data: T, id = "code_slate_0.0.1") => {
|
|
|
|
if (typeof window === "undefined") return
|
|
|
|
if (typeof localStorage === "undefined") return
|
|
|
|
|
|
|
|
localStorage.setItem(id, JSON.stringify(this.prepareDataForSave(data)))
|
2021-05-10 12:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
disable = () => {
|
|
|
|
this._enabled = false
|
|
|
|
}
|
|
|
|
|
|
|
|
enable = () => {
|
|
|
|
this._enabled = true
|
|
|
|
}
|
|
|
|
|
2021-05-16 07:09:46 +00:00
|
|
|
prepareDataForSave(data: T): any {
|
|
|
|
return { ...data }
|
|
|
|
}
|
|
|
|
|
|
|
|
restoreSavedData(data: any): T {
|
|
|
|
return { ...data }
|
|
|
|
}
|
|
|
|
|
2021-05-10 12:16:57 +00:00
|
|
|
get disabled() {
|
|
|
|
return !this._enabled
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-16 07:09:46 +00:00
|
|
|
// App-specific
|
|
|
|
|
|
|
|
class History extends BaseHistory<Data> {
|
|
|
|
constructor() {
|
|
|
|
super()
|
|
|
|
}
|
|
|
|
|
|
|
|
prepareDataForSave(data: Data): any {
|
|
|
|
const dataToSave: any = { ...data }
|
|
|
|
|
|
|
|
dataToSave.selectedIds = Array.from(data.selectedIds.values())
|
|
|
|
|
|
|
|
return dataToSave
|
|
|
|
}
|
|
|
|
|
|
|
|
restoreSavedData(data: any): Data {
|
|
|
|
const restoredData = { ...data }
|
|
|
|
|
|
|
|
restoredData.selectedIds = new Set(restoredData.selectedIds)
|
|
|
|
|
|
|
|
// Also restore camera position, which is saved separately in this app
|
|
|
|
const cameraInfo = localStorage.getItem("code_slate_camera")
|
|
|
|
|
|
|
|
if (cameraInfo !== null) {
|
2021-05-16 08:33:08 +00:00
|
|
|
Object.assign(restoredData.camera, JSON.parse(cameraInfo))
|
2021-05-16 07:09:46 +00:00
|
|
|
|
|
|
|
// And update the CSS property
|
|
|
|
document.documentElement.style.setProperty(
|
|
|
|
"--camera-zoom",
|
2021-05-16 08:33:08 +00:00
|
|
|
restoredData.camera.zoom.toString()
|
2021-05-16 07:09:46 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return restoredData
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default new History()
|