refactor: project saving logic (#5225)

* fix: project data saving not visually shown immediately

* feat: useSavable improvements

* feat: migrate where possible to useSavable

* fix: gitignore

* feat: use es-toolkit
This commit is contained in:
Calum H.
2026-01-28 16:46:14 +00:00
committed by GitHub
parent e57c15b3ce
commit 400c571fe6
15 changed files with 699 additions and 507 deletions

View File

@@ -1,37 +1,56 @@
import { isEqual } from 'es-toolkit'
import type { ComputedRef, Ref } from 'vue'
import { computed, ref } from 'vue'
export function useSavable<T extends Record<string, unknown>>(
data: () => T,
save: (changes: Partial<T>) => void,
save: (changes: Partial<T>) => void | Promise<void>,
): {
saved: ComputedRef<T>
current: Ref<T>
changes: ComputedRef<Partial<T>>
hasChanges: ComputedRef<boolean>
saving: Ref<boolean>
reset: () => void
save: () => void
save: () => Promise<void>
} {
const savedValues = computed(data)
const currentValues = ref({ ...data() }) as Ref<T>
const saving = ref(false)
const changes = computed<Partial<T>>(() => {
const values: Partial<T> = {}
const keys = Object.keys(currentValues.value) as (keyof T)[]
for (const key of keys) {
if (savedValues.value[key] !== currentValues.value[key]) {
if (!isEqual(savedValues.value[key], currentValues.value[key])) {
values[key] = currentValues.value[key]
}
}
return values
})
const hasChanges = computed(() => Object.keys(changes.value).length > 0)
const reset = () => {
currentValues.value = data()
}
const saveInternal = () => (changes.value ? save(changes.value) : {})
const saveInternal = async () => {
if (!hasChanges.value) return
saving.value = true
try {
await save(changes.value)
} finally {
saving.value = false
}
}
return {
saved: savedValues,
current: currentValues,
changes,
hasChanges,
saving,
reset,
save: saveInternal,
}