temporarily write into an object
vue-writable-object.ts edited
47 lines 1.2 kB view raw
1import { computed, readonly, type Ref, ref, toRef } from 'vue'; 2 3import isEqual from 'dequal'; 4 5const clone = <T>(value: T): T => { 6 if (value !== null && typeof value === 'object') { 7 // nested reactivity means that structuredClone() isn't possible, the next 8 // closest thing is to stringify the object into JSON and parse it back. 9 return JSON.parse(JSON.stringify(value)) as T; 10 } 11 12 return value; 13}; 14 15const writableObject = <T extends Record<string, unknown>, K extends keyof T>( 16 upstream: Ref<T>, 17 keys: K[] 18) => { 19 const scratch = ref( 20 ((): T => { 21 const ro = readonly(upstream); 22 const cloned: Record<string, unknown> = {}; 23 24 for (const key in upstream.value) { 25 if (keys.includes(key as any)) { 26 cloned[key] = clone(upstream.value[key]); 27 } else { 28 cloned[key] = toRef(() => ro.value[key]); 29 } 30 } 31 32 return cloned as T; 33 })() 34 ) as Ref<T>; 35 36 return { 37 scratch: scratch as Readonly<Ref<T>>, 38 hasChanges: computed(() => { 39 return !isEqual(upstream.value, scratch.value); 40 }), 41 resetChanges: () => { 42 for (const key in upstream.value) { 43 scratch.value[key] = clone(upstream.value[key]); 44 } 45 }, 46 }; 47};