UNPKG

label-studio

Version:

Data Labeling Tool that is backend agnostic and can be embedded into your applications

104 lines (84 loc) 2.82 kB
import { types, resolvePath, getEnv, onSnapshot, getSnapshot, applySnapshot } from "mobx-state-tree"; /** * Time Traveller */ const TimeTraveller = types .model("TimeTraveller", { history: types.array(types.frozen()), undoIdx: -1, targetPath: "", skipNextUndoState: types.optional(types.boolean, false), createdIdx: 1, isFrozen: types.optional(types.boolean, false), frozenIdx: -1, }) .views(self => ({ get canUndo() { // [TODO] since we initialize state a bit incorrectly we end up with 2 items in history // before even any action takes place. To protect those items we keep them in history forever return self.undoIdx > 1; }, get canRedo() { return self.undoIdx < self.history.length - 1; }, })) .actions(self => { let targetStore; let snapshotDisposer; return { freeze() { self.isFrozen = true; self.skipNextUndoState = true; self.frozenIdx = self.undoIdx; }, addUndoState(recorder) { if (self.skipNextUndoState) { /** * Skip recording if this state was caused by undo / redo */ self.skipNextUndoState = false; return; } self.history.splice(self.undoIdx); self.history.push(recorder); self.undoIdx = self.history.length; }, afterCreate() { targetStore = self.targetPath ? resolvePath(self, self.targetPath) : getEnv(self).targetStore; if (!targetStore) throw new Error( "Failed to find target store for TimeTraveller. Please provide `targetPath` property, or a `targetStore` in the environment", ); // TODO: check if targetStore doesn't contain self // if (contains(targetStore, self)) throw new Error("TimeTraveller shouldn't be recording itself. Please specify a sibling as taret, not some parent") // start listening to changes snapshotDisposer = onSnapshot(targetStore, snapshot => this.addUndoState(snapshot)); // record an initial state if no known if (self.history.length === 0) { self.addUndoState(getSnapshot(targetStore)); } self.createdIdx = self.undoIdx; }, beforeDestroy() { snapshotDisposer(); }, undo() { if (self.isFrozen && self.frozenIdx < self.undoIdx) return; let newIdx = self.undoIdx - 1; self.set(newIdx); }, redo() { let newIdx = self.undoIdx + 1; self.set(newIdx); }, set(idx) { self.undoIdx = idx; self.skipNextUndoState = true; applySnapshot(targetStore, self.history[idx]); }, reset() { self.set(self.createdIdx); }, }; }); export default TimeTraveller;