import { extend, hasOwn, isFunction, looseEqual } from '@vue/shared' import { isEqualWith } from 'lodash-unified' import { isNumber, throwError } from '@element-plus/utils-v2' import type { ComponentPublicInstance, CSSProperties, Ref } from 'vue' import type { Nullable } from './types' const SCOPE = 'Util' export function toObject(arr: Array): Record { const res = {} for (let i = 0; i < arr.length; i++) { if (arr[i]) { extend(res, arr[i]) } } return res } export const getValueByPath = (obj, paths = ''): unknown => { let ret: unknown = obj paths.split('.').map((path) => { ret = ret?.[path] }) return ret } export function getPropByPath( obj: any, path: string, strict: boolean ): { o: unknown k: string v: Nullable } { let tempObj = obj let key, value if (obj && hasOwn(obj, path)) { key = path value = tempObj?.[path] } else { path = path.replace(/\[(\w+)\]/g, '.$1') path = path.replace(/^\./, '') const keyArr = path.split('.') let i = 0 for (i; i < keyArr.length - 1; i++) { if (!tempObj && !strict) break const key = keyArr[i] if (key in tempObj) { tempObj = tempObj[key] } else { if (strict) { throwError(SCOPE, 'Please transfer a valid prop path to form item!') } break } } key = keyArr[i] value = tempObj?.[keyArr[i]] } return { o: tempObj, k: key, v: value, } } // use isEqual instead // export const valueEquals export const escapeRegexpString = (value = ''): string => String(value).replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') // Use native Array.find, Array.findIndex instead // coerce truthy value to array export const coerceTruthyValueToArray = (arr) => { if (!arr && arr !== 0) { return [] } return Array.isArray(arr) ? arr : [arr] } export const autoprefixer = function (style: CSSProperties): CSSProperties { const rules = ['transform', 'transition', 'animation'] const prefixes = ['ms-', 'webkit-'] rules.forEach((rule) => { const value = style[rule] if (rule && value) { prefixes.forEach((prefix) => { style[prefix + rule] = value }) } }) return style } // reexport from lodash & vue shared export { looseEqual, extend } export function rafThrottle any>(fn: T): T { let locked = false return function (this: ThisParameterType, ...args: any[]) { if (locked) return locked = true window.requestAnimationFrame(() => { Reflect.apply(fn, this, args) locked = false }) } as T } export const clearTimer = (timer: Ref>) => { if (isNumber(timer.value)) clearTimeout(timer.value) timer.value = null } export function arrayFlat(arr: unknown[]) { return arr.reduce((acm: unknown[], item) => { const val = Array.isArray(item) ? arrayFlat(item) : item return acm.concat(val) }, []) } /** * Enhance `lodash.isEqual` for it always return false even two functions have completely same statements. * @param obj The value to compare * @param other The other value to compare * @returns Returns `true` if the values are equivalent, else `false`. * @example * lodash.isEqual(() => 1, () => 1) // false * isEqualWith(() => 1, () => 1) // true */ export function isEqualWithFunction(obj: any, other: any) { return isEqualWith(obj, other, (objVal, otherVal) => { return isFunction(objVal) && isFunction(otherVal) ? `${objVal}` === `${otherVal}` : undefined }) } /** * Generate function for attach ref for the h renderer * @param ref Ref * @returns (val: T) => void */ export const refAttacher = ( ref: Ref ) => { return (val: T) => { ref.value = val } } export const merge = >(a: T, b: T) => { const keys = [ ...new Set([...Object.keys(a), ...Object.keys(b)]), ] as (keyof T)[] const obj = {} as T for (const key of keys) { obj[key] = b[key] ?? a[key] } return obj }