element-plus/packages/utils/util.ts
2022-02-09 16:59:08 +08:00

170 lines
4.0 KiB
TypeScript

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<T>(arr: Array<T>): Record<string, T> {
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<unknown>
} {
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<T extends (...args: any) => any>(fn: T): T {
let locked = false
return function (this: ThisParameterType<T>, ...args: any[]) {
if (locked) return
locked = true
window.requestAnimationFrame(() => {
Reflect.apply(fn, this, args)
locked = false
})
} as T
}
export const clearTimer = (timer: Ref<Nullable<number>>) => {
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<HTMLElement | ComponentPublicInstance>
* @returns (val: T) => void
*/
export const refAttacher = <T extends HTMLElement | ComponentPublicInstance>(
ref: Ref<T>
) => {
return (val: T) => {
ref.value = val
}
}
export const merge = <T extends Record<string, any>>(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
}