UNPKG

theme-editor-svelte

Version:
71 lines (70 loc) 2.45 kB
/* eslint-disable no-undef */ import { Debounced, watch } from 'runed'; import diff, {} from 'microdiff'; import { applyPatch, get, invertPatch } from './utils.js'; /** * Tracks the change history of a value, providing undo and redo capabilities. * * @see {@link https://runed.dev/docs/utilities/state-history} */ export class StateHistoryDiff { #redoStack = $state([]); #ignoreUpdate = false; #set; log = $state([]); snapshot = $state(null); canUndo = $derived(this.log.length > 1); canRedo = $derived(this.#redoStack.length > 0); constructor(value, set, options) { this.#redoStack = []; this.#set = set; this.undo = this.undo.bind(this); this.redo = this.redo.bind(this); this.snapshot = get(value); const addEvent = (event) => { this.log.push(event); const capacity$ = options?.capacity ? get(options?.capacity) : undefined; if (capacity$ && this.log.length > capacity$) { this.log = this.log.slice(-capacity$); } }; const debouncedValue = new Debounced(() => get(value), 300); watch(() => debouncedValue.current, (v) => { if (this.#ignoreUpdate) { this.#ignoreUpdate = false; return; } const difference = diff($state.snapshot(this.snapshot), v); if (!difference.length) return; this.snapshot = v; addEvent({ difference, timestamp: new Date().getTime() }); console.log('Added Event', difference); this.#redoStack = []; }); watch(() => get(options?.capacity ?? 0), (c) => { if (!c) return; this.log = this.log.slice(-c); }); } undo() { const curr = this.log.at(-1); if (!curr) return; this.#ignoreUpdate = true; this.#redoStack.push(curr); this.log.pop(); const newSnapshot = applyPatch($state.snapshot(this.snapshot), invertPatch(curr.difference)); this.#set(newSnapshot); } redo() { const nextEvent = this.#redoStack.pop(); if (!nextEvent) return; this.#ignoreUpdate = true; this.log.push(nextEvent); const newSnapshot = applyPatch($state.snapshot(this.snapshot), nextEvent.difference); this.#set(newSnapshot); } }