Skip to content

Commit

Permalink
Merge pull request #369 from 1CRM/record-init-clone
Browse files Browse the repository at this point in the history
Deeply clone value object in Record when set it as initial object
  • Loading branch information
alexivanenko authored Feb 12, 2024
2 parents f513eb2 + 28ce262 commit cba1e0c
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 7 deletions.
17 changes: 11 additions & 6 deletions packages/oceanfront/src/lib/records.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Ref, reactive, ref, toRaw, markRaw, watchEffect } from 'vue'
import { Ref, reactive, ref, markRaw, watchEffect } from 'vue'
import { ConfigManager, Config } from './config'
import { ItemList } from './items_list'
import { looseEqual, readonlyUnref } from './util'
import { deepEqual, deepToRaw, readonlyUnref } from './util'

export interface FieldRecordState {
pending?: boolean
Expand Down Expand Up @@ -31,6 +31,7 @@ export interface FormRecord {
locked?: boolean
pending?: boolean
reset(): void
reinit(): void
updated?: boolean
value: Record<string, any>
metadata: Record<string, FieldMetadata>
Expand Down Expand Up @@ -60,8 +61,8 @@ class BasicRecord<T extends object = Record<string, any>>
}

constructor(initial?: T) {
const init = toRaw(initial || {}) as T
this._initial = ref(Object.assign({}, init)) as Ref<T>
const init = deepToRaw(initial || {}) as T
this._initial = ref(structuredClone(init)) as Ref<T>
this._rules = ref([])
this._state = ref({ locked: false })
this._value = reactive(init) as T
Expand All @@ -73,7 +74,7 @@ class BasicRecord<T extends object = Record<string, any>>
const init = this._initial.value
const vals = this._value
let invalid = false
this._state.value.updated = !looseEqual(init, vals)
this._state.value.updated = !deepEqual(init, vals)
for (const rule of this._rules.value) {
if (!rule(vals)) {
invalid = true
Expand Down Expand Up @@ -121,7 +122,11 @@ class BasicRecord<T extends object = Record<string, any>>
}

reset() {
this.value = this._initial.value
this.value = structuredClone(deepToRaw(this._initial.value))
}

reinit() {
this._initial.value = structuredClone(deepToRaw(this.value))
}

get updated(): boolean {
Expand Down
50 changes: 49 additions & 1 deletion packages/oceanfront/src/lib/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
toRef,
triggerRef,
unref,
isReactive,
toRaw,
} from 'vue'
import { hasOwn } from '@vue/shared'

Expand Down Expand Up @@ -348,7 +350,7 @@ class PositionObserverImpl implements PositionObserver {
this._native.observe(target, { box: 'border-box' })
} else if (!this._polling) {
// first timeout is short to handle initial re-layout
this._polling = window.setTimeout(this._poll.bind(this, 50))
this._polling = window.setTimeout(this._poll.bind(this), 50)
}
if (this._options.immediate) {
triggerRef(this._positions)
Expand Down Expand Up @@ -424,3 +426,49 @@ export const scaleClass = (size: string | undefined) => {
return {}
}
}

export const deepToRaw = (obj: any) => {
if (typeof obj !== 'object' || obj === null) {
return obj // Return the value if obj is not an object
}

const copy: any = Array.isArray(obj) ? [] : {}

for (const attr in obj) {
if (obj.hasOwnProperty(attr)) {
if (isReactive(obj[attr])) {
copy[attr] = deepToRaw(toRaw(obj[attr]))
} else if (typeof obj[attr] === 'object' && obj[attr] !== null) {
copy[attr] = deepToRaw(obj[attr])
} else {
copy[attr] = obj[attr]
}
}
}

return copy
}

export const deepEqual = (a: any, b: any): boolean => {
if (a === b) return true

if (
typeof a !== 'object' ||
a === null ||
typeof b !== 'object' ||
b === null
) {
return false
}

const keysA = Object.keys(a),
keysB = Object.keys(b)

if (keysA.length !== keysB.length) return false

for (const key of keysA) {
if (!keysB.includes(key) || !deepEqual(a[key], b[key])) return false
}

return true
}

0 comments on commit cba1e0c

Please sign in to comment.