import { SerializableItem } from "./serializableitem"; export interface Checkpoint<DataType> { id: number; description: string; data: DataType; } export class History<HistoryDataType> { public maxCheckpoints: number; private current: number; private data: SerializableItem<unknown, HistoryDataType>; public checkpoints: Checkpoint<HistoryDataType>[]; private next: number; constructor( data: SerializableItem<unknown, HistoryDataType>, maxCheckpoints = 20, initialMessage = "New History" ) { this.data = data; this.maxCheckpoints = maxCheckpoints; this.checkpoints = []; this.current = -1; this.next = 0; this.createCheckpoint(initialMessage); } public createCheckpoint(description: string) { const checkpointData = this.data.toHistorySerializableObject(); const checkpoint = { id: ++this.next, description: description, data: JSON.parse(JSON.stringify(checkpointData)), // deepcopy }; // Remove potential history which is not relevant anymore (maybe caused by undo ops) if (this.current < this.checkpoints.length - 1 && this.current > 0) { this.checkpoints.length = this.current + 1; } this.checkpoints.push(checkpoint); this.current++; } public get currentCheckpoint() { return this.checkpoints[this.current]; } public historyDescription(): Array<string> { return this.checkpoints.map((savepoint) => savepoint.description); } public resetToCheckpoint(id: number): Checkpoint<HistoryDataType> { const index = this.checkpoints.findIndex( (checkpoint) => checkpoint.id == id ); if (index > 0) { this.current = index; return this.checkpoints[index]; } } public undo(): Checkpoint<HistoryDataType> { if (this.hasUndoCheckpoints()) { this.current--; } return this.checkpoints[this.current]; } public redo(): Checkpoint<HistoryDataType> { if (this.hasRedoCheckpoints()) { this.current++; } return this.checkpoints[this.current]; } public hasUndoCheckpoints(): boolean { return this.current > 0; } public hasRedoCheckpoints(): boolean { return this.current < this.checkpoints.length; } }