bak
This commit is contained in:
1 parent
e4aa0dce86
commit
cc6b11e61d
23 files changed
+2047
-524
No files matched your search
@@ -0,0 +1,144 @@
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watch, type ComputedRef } from 'vue'
|
||||
import { useSceneStore } from '@/stores/scene'
|
||||
import type { Scene } from '@/types/scene'
|
||||
|
||||
export const HISTORY_MAX = 100
|
||||
export const HISTORY_DEBOUNCE_MS = 400
|
||||
|
||||
type SceneStore = ReturnType<typeof useSceneStore>
|
||||
|
||||
export interface HistoryApi {
|
||||
undo: () => void
|
||||
redo: () => void
|
||||
canUndo: ComputedRef<boolean>
|
||||
canRedo: ComputedRef<boolean>
|
||||
resetTo: (scene?: Scene) => void
|
||||
commitNow: () => void
|
||||
start: () => void
|
||||
stop: () => void
|
||||
}
|
||||
|
||||
export interface CreateHistoryOptions {
|
||||
max?: number
|
||||
debounceMs?: number
|
||||
}
|
||||
|
||||
export function createHistory(store: SceneStore, opts: CreateHistoryOptions = {}): HistoryApi {
|
||||
const max = opts.max ?? HISTORY_MAX
|
||||
const debounceMs = opts.debounceMs ?? HISTORY_DEBOUNCE_MS
|
||||
|
||||
const stack = ref<string[]>([])
|
||||
const index = ref(-1)
|
||||
let suspend = false
|
||||
let debounceTimer: ReturnType<typeof setTimeout> | null = null
|
||||
let unsub: (() => void) | null = null
|
||||
|
||||
const snap = () => JSON.stringify(store.scene)
|
||||
|
||||
function pushNow() {
|
||||
if (suspend) return
|
||||
const next = snap()
|
||||
if (stack.value[index.value] === next) return
|
||||
stack.value = stack.value.slice(0, index.value + 1)
|
||||
stack.value.push(next)
|
||||
if (stack.value.length > max) {
|
||||
const overflow = stack.value.length - max
|
||||
stack.value.splice(0, overflow)
|
||||
index.value -= overflow
|
||||
}
|
||||
index.value = stack.value.length - 1
|
||||
}
|
||||
|
||||
function schedulePush() {
|
||||
if (suspend) return
|
||||
if (debounceTimer) clearTimeout(debounceTimer)
|
||||
debounceTimer = setTimeout(() => {
|
||||
debounceTimer = null
|
||||
pushNow()
|
||||
}, debounceMs)
|
||||
}
|
||||
|
||||
function apply(json: string) {
|
||||
suspend = true
|
||||
store.scene = JSON.parse(json)
|
||||
store.setPlayhead(store.playhead)
|
||||
if (store.selectedLayerId && !store.scene.layers.some((l) => l.id === store.selectedLayerId)) {
|
||||
store.selectLayer(null)
|
||||
}
|
||||
setTimeout(() => {
|
||||
suspend = false
|
||||
}, 0)
|
||||
}
|
||||
|
||||
function flushPending() {
|
||||
if (debounceTimer) {
|
||||
clearTimeout(debounceTimer)
|
||||
debounceTimer = null
|
||||
pushNow()
|
||||
}
|
||||
}
|
||||
|
||||
function undo() {
|
||||
flushPending()
|
||||
if (index.value <= 0) return
|
||||
index.value -= 1
|
||||
apply(stack.value[index.value])
|
||||
}
|
||||
|
||||
function redo() {
|
||||
if (index.value >= stack.value.length - 1) return
|
||||
index.value += 1
|
||||
apply(stack.value[index.value])
|
||||
}
|
||||
|
||||
function resetTo(scene?: Scene) {
|
||||
if (debounceTimer) {
|
||||
clearTimeout(debounceTimer)
|
||||
debounceTimer = null
|
||||
}
|
||||
const json = scene ? JSON.stringify(scene) : snap()
|
||||
stack.value = [json]
|
||||
index.value = 0
|
||||
}
|
||||
|
||||
const canUndo = computed(() => index.value > 0)
|
||||
const canRedo = computed(() => index.value < stack.value.length - 1)
|
||||
|
||||
function start() {
|
||||
stack.value = [snap()]
|
||||
index.value = 0
|
||||
unsub = watch(
|
||||
() => store.scene,
|
||||
() => schedulePush(),
|
||||
{ deep: true, flush: 'post' },
|
||||
)
|
||||
}
|
||||
|
||||
function stop() {
|
||||
if (unsub) unsub()
|
||||
unsub = null
|
||||
if (debounceTimer) {
|
||||
clearTimeout(debounceTimer)
|
||||
debounceTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
undo,
|
||||
redo,
|
||||
canUndo,
|
||||
canRedo,
|
||||
resetTo,
|
||||
commitNow: pushNow,
|
||||
start,
|
||||
stop,
|
||||
}
|
||||
}
|
||||
|
||||
export function useHistory(): HistoryApi {
|
||||
const store = useSceneStore()
|
||||
const api = createHistory(store)
|
||||
onMounted(api.start)
|
||||
onBeforeUnmount(api.stop)
|
||||
return api
|
||||
}
|
||||
Reference in new issue
Block a user