* fix: project data saving not visually shown immediately * feat: useSavable improvements * feat: migrate where possible to useSavable * fix: gitignore * feat: use es-toolkit
58 lines
1.3 KiB
TypeScript
58 lines
1.3 KiB
TypeScript
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 | Promise<void>,
|
|
): {
|
|
saved: ComputedRef<T>
|
|
current: Ref<T>
|
|
changes: ComputedRef<Partial<T>>
|
|
hasChanges: ComputedRef<boolean>
|
|
saving: Ref<boolean>
|
|
reset: () => 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 (!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 = 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,
|
|
}
|
|
}
|