DataUtils.ts 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import { isPlainObject } from 'is-plain-object'
  2. import copy from 'fast-copy'
  3. export { default as deepEqual } from 'fast-deep-equal'
  4. import deepmerge from 'deepmerge'
  5. /* eslint-disable @typescript-eslint/no-explicit-any */
  6. /**
  7. * Deep copy function for TypeScript.
  8. *
  9. * @param T Generic type of target/copied value.
  10. * @param target Target value to be copied.
  11. * @see Source project, ts-deeply https://github.com/ykdr2017/ts-deepcopy
  12. * @see Code pen https://codepen.io/erikvullings/pen/ejyBYg
  13. */
  14. export const deepCopy = copy
  15. type Patch<T> = Partial<{ [P in keyof T]: T | Partial<T> | Patch<T[P]> }>
  16. export function deepMerge<T>(a: T, b: Patch<T>): T {
  17. // @ts-expect-error ???
  18. return deepmerge(a, b, {
  19. arrayMerge: (destinationArray, sourceArray, options) => sourceArray,
  20. })
  21. }
  22. /**
  23. * Modulate a value between two ranges.
  24. *
  25. * @param value
  26. * @param rangeA From [low, high]
  27. * @param rangeB To [low, high]
  28. * @param clamp
  29. */
  30. export function modulate(value: number, rangeA: number[], rangeB: number[], clamp = false): number {
  31. const [fromLow, fromHigh] = rangeA
  32. const [v0, v1] = rangeB
  33. const result = v0 + ((value - fromLow) / (fromHigh - fromLow)) * (v1 - v0)
  34. return clamp
  35. ? v0 < v1
  36. ? Math.max(Math.min(result, v1), v0)
  37. : Math.max(Math.min(result, v0), v1)
  38. : result
  39. }
  40. /**
  41. * Clamp a value into a range.
  42. *
  43. * @param n
  44. * @param min
  45. */
  46. export function clamp(n: number, min: number): number
  47. export function clamp(n: number, min: number, max: number): number
  48. export function clamp(n: number, min: number, max?: number): number {
  49. return Math.max(min, typeof max !== 'undefined' ? Math.min(n, max) : n)
  50. }
  51. const serializableTypes = new Set(['string', 'number', 'boolean', 'undefined'])
  52. export function isSerializable(value: any): boolean {
  53. if (serializableTypes.has(typeof value) || value === null) return true
  54. if (Array.isArray(value)) return value.every(isSerializable)
  55. if (isPlainObject(value)) return Object.values(value).every(isSerializable)
  56. return false
  57. }
  58. export function fileToBase64(file: Blob): Promise<string | ArrayBuffer | null> {
  59. return new Promise((resolve, reject) => {
  60. if (file) {
  61. const reader = new FileReader()
  62. reader.readAsDataURL(file)
  63. reader.onload = () => resolve(reader.result)
  64. reader.onerror = error => reject(error)
  65. reader.onabort = error => reject(error)
  66. }
  67. })
  68. }
  69. export function getSizeFromSrc(dataURL: string): Promise<number[]> {
  70. return new Promise(resolve => {
  71. const img = new Image()
  72. img.onload = () => resolve([img.width, img.height])
  73. img.src = dataURL
  74. })
  75. }
  76. export function getFirstFromSet<T = unknown>(set: Set<T>): T {
  77. return set.values().next().value
  78. }