2023-03-09 09:50:26 +01:00
|
|
|
import { helpers } from "@budibase/shared-core"
|
2024-04-08 09:29:20 +02:00
|
|
|
import dayjs from "dayjs"
|
2023-11-20 21:52:29 +01:00
|
|
|
|
2023-03-09 09:50:26 +01:00
|
|
|
export const deepGet = helpers.deepGet
|
|
|
|
|
2022-01-18 10:39:19 +01:00
|
|
|
/**
|
|
|
|
* Generates a DOM safe UUID.
|
|
|
|
* Starting with a letter is important to make it DOM safe.
|
|
|
|
* @return {string} a random DOM safe UUID
|
|
|
|
*/
|
|
|
|
export function uuid() {
|
|
|
|
return "cxxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, c => {
|
|
|
|
const r = (Math.random() * 16) | 0
|
|
|
|
const v = c === "x" ? r : (r & 0x3) | 0x8
|
|
|
|
return v.toString(16)
|
|
|
|
})
|
|
|
|
}
|
2021-04-20 21:06:27 +02:00
|
|
|
|
2022-01-18 10:39:19 +01:00
|
|
|
/**
|
|
|
|
* Capitalises a string
|
|
|
|
* @param string the string to capitalise
|
|
|
|
* @return {string} the capitalised string
|
|
|
|
*/
|
|
|
|
export const capitalise = string => {
|
|
|
|
if (!string) {
|
|
|
|
return string
|
|
|
|
}
|
|
|
|
return string.substring(0, 1).toUpperCase() + string.substring(1)
|
2021-04-20 21:06:27 +02:00
|
|
|
}
|
2021-06-23 12:47:07 +02:00
|
|
|
|
2022-01-18 10:39:19 +01:00
|
|
|
/**
|
|
|
|
* Computes a short hash of a string
|
|
|
|
* @param string the string to compute a hash of
|
|
|
|
* @return {string} the hash string
|
|
|
|
*/
|
|
|
|
export const hashString = string => {
|
|
|
|
if (!string) {
|
|
|
|
return "0"
|
|
|
|
}
|
|
|
|
let hash = 0
|
|
|
|
for (let i = 0; i < string.length; i++) {
|
|
|
|
let char = string.charCodeAt(i)
|
|
|
|
hash = (hash << 5) - hash + char
|
|
|
|
hash = hash & hash // Convert to 32bit integer
|
|
|
|
}
|
|
|
|
return hash.toString()
|
|
|
|
}
|
2021-12-06 13:37:50 +01:00
|
|
|
|
2021-12-10 15:18:01 +01:00
|
|
|
/**
|
|
|
|
* Sets a key within an object. The key supports dot syntax for retrieving deep
|
|
|
|
* fields - e.g. "a.b.c".
|
|
|
|
* Exact matches of keys with dots in them take precedence over nested keys of
|
|
|
|
* the same path - e.g. setting "a.b" of { "a.b": "foo", a: { b: "bar" } }
|
|
|
|
* will override the value "foo" rather than "bar".
|
2021-12-10 16:27:04 +01:00
|
|
|
* If a deep path is specified and the parent keys don't exist then these will
|
|
|
|
* be created.
|
2021-12-10 15:18:01 +01:00
|
|
|
* @param obj the object
|
|
|
|
* @param key the key
|
|
|
|
* @param value the value
|
|
|
|
*/
|
|
|
|
export const deepSet = (obj, key, value) => {
|
|
|
|
if (!obj || !key) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
|
|
obj[key] = value
|
|
|
|
return
|
|
|
|
}
|
|
|
|
const split = key.split(".")
|
|
|
|
for (let i = 0; i < split.length - 1; i++) {
|
2021-12-10 16:27:04 +01:00
|
|
|
const nextKey = split[i]
|
|
|
|
if (obj && obj[nextKey] == null) {
|
|
|
|
obj[nextKey] = {}
|
|
|
|
}
|
|
|
|
obj = obj?.[nextKey]
|
|
|
|
}
|
|
|
|
if (!obj) {
|
|
|
|
return
|
2021-12-10 15:18:01 +01:00
|
|
|
}
|
|
|
|
obj[split[split.length - 1]] = value
|
2021-12-06 13:37:50 +01:00
|
|
|
}
|
2022-01-31 10:32:06 +01:00
|
|
|
|
2022-01-31 19:58:19 +01:00
|
|
|
/**
|
|
|
|
* Deeply clones an object. Functions are not supported.
|
|
|
|
* @param obj the object to clone
|
|
|
|
*/
|
2022-01-31 10:32:06 +01:00
|
|
|
export const cloneDeep = obj => {
|
2023-02-23 14:55:18 +01:00
|
|
|
if (!obj) {
|
|
|
|
return obj
|
|
|
|
}
|
2022-01-31 10:32:06 +01:00
|
|
|
return JSON.parse(JSON.stringify(obj))
|
|
|
|
}
|
2022-02-24 22:48:23 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Copies a value to the clipboard
|
|
|
|
* @param value the value to copy
|
|
|
|
*/
|
|
|
|
export const copyToClipboard = value => {
|
|
|
|
return new Promise(res => {
|
|
|
|
if (navigator.clipboard && window.isSecureContext) {
|
|
|
|
// Try using the clipboard API first
|
|
|
|
navigator.clipboard.writeText(value).then(res)
|
|
|
|
} else {
|
|
|
|
// Fall back to the textarea hack
|
|
|
|
let textArea = document.createElement("textarea")
|
|
|
|
textArea.value = value
|
|
|
|
textArea.style.position = "fixed"
|
|
|
|
textArea.style.left = "-9999px"
|
|
|
|
textArea.style.top = "-9999px"
|
|
|
|
document.body.appendChild(textArea)
|
|
|
|
textArea.focus()
|
|
|
|
textArea.select()
|
|
|
|
document.execCommand("copy")
|
|
|
|
textArea.remove()
|
|
|
|
res()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2024-04-08 09:29:20 +02:00
|
|
|
|
|
|
|
export const parseDate = (value, { timeOnly, dateOnly } = {}) => {
|
|
|
|
// If empty then invalid
|
|
|
|
if (!value) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
// Certain string values need transformed
|
2024-04-10 19:46:41 +02:00
|
|
|
if (typeof value === "string") {
|
2024-04-08 09:29:20 +02:00
|
|
|
if (timeOnly || !isNaN(new Date(`0-${value}`))) {
|
|
|
|
value = `0-${value}`
|
|
|
|
}
|
|
|
|
|
|
|
|
// If date only, check for cases where we received a UTC string
|
|
|
|
else if (dateOnly && value.endsWith("Z")) {
|
|
|
|
value = value.split("Z")[0]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse value and check for validity
|
|
|
|
const parsedDate = dayjs(value)
|
|
|
|
if (!parsedDate.isValid()) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
// By rounding to the nearest second we avoid locking up in an endless
|
|
|
|
// loop in the builder, caused by potentially enriching {{ now }} to every
|
|
|
|
// millisecond.
|
|
|
|
return dayjs(Math.floor(parsedDate.valueOf() / 1000) * 1000)
|
|
|
|
}
|