@heliosgraphics/utils 6.0.0-alpha.10 → 6.0.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/classnames.ts +35 -12
- package/clipboard.ts +7 -3
- package/colors.ts +14 -7
- package/debounce.ts +21 -7
- package/equals.ts +22 -42
- package/index.ts +1 -1
- package/package.json +22 -4
- package/sanitize.ts +1 -1
- package/strings.ts +1 -1
- package/throttle.ts +16 -5
- package/uuid.ts +6 -7
- package/validations.ts +1 -1
- package/classnames.spec.ts +0 -17
- package/clipboard.spec.ts +0 -40
- package/colors.spec.ts +0 -40
- package/debounce.spec.ts +0 -101
- package/equals.spec.ts +0 -168
- package/sanitize.spec.ts +0 -558
- package/sleep.spec.ts +0 -59
- package/slug.spec.ts +0 -15
- package/strings.spec.ts +0 -77
- package/throttle.spec.ts +0 -142
- package/tsconfig.json +0 -40
- package/uuid.spec.ts +0 -32
- package/validations.spec.ts +0 -26
package/classnames.ts
CHANGED
|
@@ -1,21 +1,44 @@
|
|
|
1
1
|
export const getClasses = (...args: Array<unknown>): string => {
|
|
2
|
-
|
|
2
|
+
if (args.length === 1) {
|
|
3
|
+
const arg: unknown = args[0]
|
|
4
|
+
if (typeof arg === "string" && arg.length > 0) {
|
|
5
|
+
return arg
|
|
6
|
+
}
|
|
7
|
+
}
|
|
3
8
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const isValidString: boolean = itemType === "string" && (item as string).length > 0
|
|
7
|
-
const isValidObject: boolean = itemType === "object" && item !== null
|
|
9
|
+
let result: string = ""
|
|
10
|
+
let seen: Record<string, boolean> | null = null
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
for (let i: number = 0; i < args.length; i++) {
|
|
13
|
+
const item: unknown = args[i]
|
|
14
|
+
const itemType: string = typeof item
|
|
12
15
|
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
if (itemType === "string") {
|
|
17
|
+
const str: string = item as string
|
|
18
|
+
if (str.length > 0) {
|
|
19
|
+
if (seen === null) {
|
|
20
|
+
seen = { [str]: true }
|
|
21
|
+
result = str
|
|
22
|
+
} else if (!seen[str]) {
|
|
23
|
+
seen[str] = true
|
|
24
|
+
result = result.length > 0 ? result + " " + str : str
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
} else if (itemType === "object" && item !== null) {
|
|
28
|
+
const obj: Record<string, unknown> = item as Record<string, unknown>
|
|
29
|
+
for (const key in obj) {
|
|
30
|
+
if (obj[key]) {
|
|
31
|
+
if (seen === null) {
|
|
32
|
+
seen = { [key]: true }
|
|
33
|
+
result = key
|
|
34
|
+
} else if (!seen[key]) {
|
|
35
|
+
seen[key] = true
|
|
36
|
+
result = result.length > 0 ? result + " " + key : key
|
|
37
|
+
}
|
|
38
|
+
}
|
|
16
39
|
}
|
|
17
40
|
}
|
|
18
41
|
}
|
|
19
42
|
|
|
20
|
-
return
|
|
43
|
+
return result
|
|
21
44
|
}
|
package/clipboard.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
export const copyValue = (text: string): void => {
|
|
1
|
+
export const copyValue = async (text: string): Promise<void> => {
|
|
2
|
+
if (navigator?.clipboard?.writeText) {
|
|
3
|
+
await navigator.clipboard.writeText(text)
|
|
4
|
+
return
|
|
5
|
+
}
|
|
6
|
+
|
|
2
7
|
const input: HTMLTextAreaElement = document.createElement("textarea")
|
|
3
8
|
|
|
4
9
|
document.body.appendChild(input)
|
|
5
10
|
input.value = text
|
|
6
11
|
input.select()
|
|
7
12
|
document.execCommand("copy", false)
|
|
8
|
-
|
|
9
|
-
return input.remove()
|
|
13
|
+
input.remove()
|
|
10
14
|
}
|
package/colors.ts
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
type TypeRGB = [number, number, number]
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
|
|
5
|
-
export const hexToRgb = (hex?: string | null): TypeRGB => {
|
|
3
|
+
export const hexToRgb = (hex?: string | null): TypeRGB | null => {
|
|
6
4
|
const isValid: boolean = !!hex && typeof hex === "string"
|
|
7
5
|
|
|
8
|
-
if (!isValid || !hex) return
|
|
6
|
+
if (!isValid || !hex) return null
|
|
9
7
|
|
|
10
8
|
hex = hex.replace(/^#/, "")
|
|
11
9
|
|
|
12
|
-
|
|
10
|
+
if (hex.length === 3) {
|
|
11
|
+
hex = hex
|
|
12
|
+
.split("")
|
|
13
|
+
.map((c: string) => c + c)
|
|
14
|
+
.join("")
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const bigint: number = parseInt(hex, 16)
|
|
13
18
|
const r: number = (bigint >> 16) & 255
|
|
14
19
|
const g: number = (bigint >> 8) & 255
|
|
15
20
|
const b: number = bigint & 255
|
|
@@ -19,12 +24,14 @@ export const hexToRgb = (hex?: string | null): TypeRGB => {
|
|
|
19
24
|
|
|
20
25
|
export const rgbToHex = (r: number | string = 255, g: number | string = 255, b: number | string = 255): string => {
|
|
21
26
|
const _toHex = (c: unknown): string => {
|
|
22
|
-
|
|
27
|
+
if (c === null || c === undefined) return "ff"
|
|
28
|
+
|
|
29
|
+
const value: number = Math.round(Number(c))
|
|
23
30
|
const isInvalid: boolean = isNaN(value) || value < 0 || value > 255
|
|
24
31
|
|
|
25
32
|
if (isInvalid) return "ff"
|
|
26
33
|
|
|
27
|
-
const hex = value.toString(16)
|
|
34
|
+
const hex: string = value.toString(16)
|
|
28
35
|
|
|
29
36
|
return hex.length === 1 ? `0${hex}` : hex
|
|
30
37
|
}
|
package/debounce.ts
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
|
+
interface DebouncedFunction<T extends (...args: Array<unknown>) => unknown> {
|
|
2
|
+
(...args: Parameters<T>): void
|
|
3
|
+
cancel: () => void
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2
|
-
export const debounce = <T extends (...args: Array<any>) => any>(
|
|
3
|
-
|
|
4
|
-
wait: number,
|
|
5
|
-
): ((...args: Parameters<T>) => void) => {
|
|
6
|
-
let timeoutId: ReturnType<typeof setTimeout>
|
|
7
|
+
export const debounce = <T extends (...args: Array<any>) => any>(callback: T, wait: number): DebouncedFunction<T> => {
|
|
8
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
const debounced: DebouncedFunction<T> = (...args: Parameters<T>): void => {
|
|
11
|
+
if (timeoutId !== null) {
|
|
12
|
+
globalThis.clearTimeout(timeoutId)
|
|
13
|
+
}
|
|
10
14
|
|
|
11
15
|
timeoutId = globalThis.setTimeout(() => {
|
|
12
16
|
callback.apply(null, args)
|
|
17
|
+
timeoutId = null
|
|
13
18
|
}, wait)
|
|
14
19
|
}
|
|
20
|
+
|
|
21
|
+
debounced.cancel = (): void => {
|
|
22
|
+
if (timeoutId !== null) {
|
|
23
|
+
globalThis.clearTimeout(timeoutId)
|
|
24
|
+
timeoutId = null
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return debounced
|
|
15
29
|
}
|
package/equals.ts
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
type FracturesPrimitive = string | number | boolean | null | undefined | bigint | symbol
|
|
2
2
|
type FracturesComparable = FracturesPrimitive | object | Array<unknown> | Set<unknown> | Map<unknown, unknown>
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
mapA: Map<K, V>,
|
|
6
|
-
mapB: Map<K, V>,
|
|
7
|
-
seen = new WeakMap<object, object>(),
|
|
8
|
-
): boolean => {
|
|
4
|
+
const _getAreMapsEqual = <K, V>(mapA: Map<K, V>, mapB: Map<K, V>, seen = new WeakMap<object, object>()): boolean => {
|
|
9
5
|
if (mapA.size !== mapB.size) return false
|
|
10
6
|
if (mapA === mapB) return true
|
|
11
7
|
|
|
12
|
-
const entriesA = Array.from(mapA.entries())
|
|
13
|
-
const entriesB = new Map(mapB)
|
|
8
|
+
const entriesA: Array<[K, V]> = Array.from(mapA.entries())
|
|
9
|
+
const entriesB: Map<K, V> = new Map(mapB)
|
|
14
10
|
|
|
15
11
|
return entriesA.every(
|
|
16
|
-
([key, value]) =>
|
|
12
|
+
([key, value]: [K, V]) =>
|
|
17
13
|
entriesB.has(key) && _getIsEqual(value as FracturesComparable, entriesB.get(key) as FracturesComparable, seen),
|
|
18
14
|
)
|
|
19
15
|
}
|
|
@@ -22,21 +18,21 @@ export const getAreDatesEqual = (a: Date, b: Date): boolean => a.getTime() === b
|
|
|
22
18
|
export const getAreRegExpsEqual = (a: RegExp, b: RegExp): boolean => a.toString() === b.toString()
|
|
23
19
|
export const getAreErrorsEqual = (a: Error, b: Error): boolean =>
|
|
24
20
|
a.message === b.message && a.name === b.name && a.stack === b.stack
|
|
25
|
-
export const getAreBuffersEqual = (a: Buffer, b: Buffer): boolean =>
|
|
21
|
+
export const getAreBuffersEqual = (a: Buffer, b: Buffer): boolean => Buffer.compare(a, b) === 0
|
|
26
22
|
|
|
27
23
|
export const getAreSetsEqual = <T>(setA: Set<T>, setB: Set<T>): boolean => {
|
|
28
24
|
if (setA.size !== setB.size) return false
|
|
29
25
|
if (setA === setB) return true
|
|
30
26
|
if (setA.size === 0) return true
|
|
31
27
|
|
|
32
|
-
const arrA = Array.from(setA)
|
|
33
|
-
const arrB = Array.from(setB)
|
|
34
|
-
const matched = new Array(arrB.length).fill(false)
|
|
28
|
+
const arrA: Array<T> = Array.from(setA)
|
|
29
|
+
const arrB: Array<T> = Array.from(setB)
|
|
30
|
+
const matched: Array<boolean> = new Array(arrB.length).fill(false)
|
|
35
31
|
|
|
36
|
-
for (let i = 0; i < arrA.length; i++) {
|
|
37
|
-
let foundMatch = false
|
|
32
|
+
for (let i: number = 0; i < arrA.length; i++) {
|
|
33
|
+
let foundMatch: boolean = false
|
|
38
34
|
|
|
39
|
-
for (let j = 0; j < arrB.length; j++) {
|
|
35
|
+
for (let j: number = 0; j < arrB.length; j++) {
|
|
40
36
|
if (!matched[j] && _getIsEqual(arrA[i] as FracturesComparable, arrB[j] as FracturesComparable)) {
|
|
41
37
|
matched[j] = true
|
|
42
38
|
foundMatch = true
|
|
@@ -50,31 +46,15 @@ export const getAreSetsEqual = <T>(setA: Set<T>, setB: Set<T>): boolean => {
|
|
|
50
46
|
return true
|
|
51
47
|
}
|
|
52
48
|
|
|
53
|
-
|
|
54
|
-
a: T,
|
|
55
|
-
b: T,
|
|
56
|
-
seen = new WeakMap<object, object>(),
|
|
57
|
-
): boolean => {
|
|
49
|
+
const _getIsEqual = <T extends FracturesComparable>(a: T, b: T, seen = new WeakMap<object, object>()): boolean => {
|
|
58
50
|
if (a === b) return true
|
|
59
|
-
if (a === null || b === null) return
|
|
51
|
+
if (a === null || b === null) return false
|
|
60
52
|
|
|
61
|
-
if (typeof a !== "object"
|
|
62
|
-
return a === b
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) {
|
|
66
|
-
return false
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (Object.is(a, b)) return true
|
|
70
|
-
|
|
71
|
-
const typeA = typeof a
|
|
72
|
-
const typeB = typeof b
|
|
53
|
+
if (typeof a !== "object" || typeof b !== "object") return false
|
|
73
54
|
|
|
74
|
-
if (
|
|
75
|
-
if (typeA !== "object") return false
|
|
55
|
+
if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) return false
|
|
76
56
|
|
|
77
|
-
const seenA = seen.get(a as object)
|
|
57
|
+
const seenA: object | undefined = seen.get(a as object)
|
|
78
58
|
if (seenA) return seenA === b
|
|
79
59
|
|
|
80
60
|
seen.set(a as object, b as object)
|
|
@@ -82,7 +62,7 @@ export const _getIsEqual = <T extends FracturesComparable>(
|
|
|
82
62
|
if (Array.isArray(a) && Array.isArray(b)) {
|
|
83
63
|
if (a.length !== b.length) return false
|
|
84
64
|
|
|
85
|
-
for (let i = 0; i < a.length; i++) {
|
|
65
|
+
for (let i: number = 0; i < a.length; i++) {
|
|
86
66
|
if (!_getIsEqual(a[i], b[i], seen)) return false
|
|
87
67
|
}
|
|
88
68
|
return true
|
|
@@ -95,13 +75,13 @@ export const _getIsEqual = <T extends FracturesComparable>(
|
|
|
95
75
|
if (a instanceof RegExp && b instanceof RegExp) return getAreRegExpsEqual(a, b)
|
|
96
76
|
if (Buffer.isBuffer(a) && Buffer.isBuffer(b)) return getAreBuffersEqual(a, b)
|
|
97
77
|
|
|
98
|
-
const keysA = [...Object.keys(a as object), ...Object.getOwnPropertySymbols(a as object)]
|
|
99
|
-
const keysB = [...Object.keys(b as object), ...Object.getOwnPropertySymbols(b as object)]
|
|
78
|
+
const keysA: Array<string | symbol> = [...Object.keys(a as object), ...Object.getOwnPropertySymbols(a as object)]
|
|
79
|
+
const keysB: Array<string | symbol> = [...Object.keys(b as object), ...Object.getOwnPropertySymbols(b as object)]
|
|
100
80
|
|
|
101
81
|
if (keysA.length !== keysB.length) return false
|
|
102
82
|
|
|
103
83
|
return keysA.every(
|
|
104
|
-
(key) =>
|
|
84
|
+
(key: string | symbol) =>
|
|
105
85
|
Object.prototype.hasOwnProperty.call(b, key) &&
|
|
106
86
|
_getIsEqual(
|
|
107
87
|
(a as Record<string | symbol, unknown>)[key] as FracturesComparable,
|
|
@@ -115,6 +95,6 @@ export const getIsEqual = <T extends FracturesComparable>(a: T, b: T): boolean =
|
|
|
115
95
|
return _getIsEqual(a, b)
|
|
116
96
|
}
|
|
117
97
|
|
|
118
|
-
export const getAreMapsEqual = <
|
|
119
|
-
return _getAreMapsEqual(a
|
|
98
|
+
export const getAreMapsEqual = <K, V>(a: Map<K, V>, b: Map<K, V>): boolean => {
|
|
99
|
+
return _getAreMapsEqual(a, b)
|
|
120
100
|
}
|
package/index.ts
CHANGED
|
@@ -17,4 +17,4 @@ export { removeMarkdown, removeNewlines, ellipsis } from "./strings"
|
|
|
17
17
|
export { sanitizeText } from "./sanitize"
|
|
18
18
|
export { throttle } from "./throttle"
|
|
19
19
|
export { isUUID, getUUID } from "./uuid"
|
|
20
|
-
export {
|
|
20
|
+
export { validateHttpsUrl, validateEmail } from "./validations"
|
package/package.json
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heliosgraphics/utils",
|
|
3
|
-
"version": "6.0.0-alpha.
|
|
3
|
+
"version": "6.0.0-alpha.13",
|
|
4
4
|
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
5
6
|
"author": "Chris Puska <chris@puska.org>",
|
|
6
7
|
"license": "MIT",
|
|
7
8
|
"private": false,
|
|
8
9
|
"description": "Helios Utils",
|
|
9
10
|
"main": "index.ts",
|
|
10
|
-
"
|
|
11
|
-
|
|
11
|
+
"types": "index.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": "./index.ts",
|
|
14
|
+
"./*": "./*.ts"
|
|
12
15
|
},
|
|
13
|
-
"
|
|
16
|
+
"files": [
|
|
17
|
+
"*.ts",
|
|
18
|
+
"!*.spec.ts"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/heliosgraphics/helios-ui.git",
|
|
23
|
+
"directory": "packages/utils"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/heliosgraphics/helios-ui/tree/main/packages/utils",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/heliosgraphics/helios-ui/issues"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^25"
|
|
31
|
+
}
|
|
14
32
|
}
|
package/sanitize.ts
CHANGED
|
@@ -209,7 +209,7 @@ export const sanitizeText = (input: string = ""): string => {
|
|
|
209
209
|
})
|
|
210
210
|
|
|
211
211
|
// Additional targeted cleanup for specific dangerous contexts
|
|
212
|
-
sanitized = sanitized.replace(/\balert\s*(?=[
|
|
212
|
+
sanitized = sanitized.replace(/\balert\s*(?=[,)])/gi, "")
|
|
213
213
|
sanitized = sanitized.replace(/\bjavascript\s*(?=:)/gi, "")
|
|
214
214
|
|
|
215
215
|
// Remove any remaining backticks
|
package/strings.ts
CHANGED
|
@@ -33,7 +33,7 @@ export const removeNewlines = (text?: string | null, limit?: number): string =>
|
|
|
33
33
|
export const ellipsis = (text: string, maxLength: number, position: "end" | "middle" = "end"): string => {
|
|
34
34
|
if (!text || typeof text !== "string") return ""
|
|
35
35
|
if (text.length <= maxLength) return text
|
|
36
|
-
if (maxLength < 4) return
|
|
36
|
+
if (maxLength < 4) return text
|
|
37
37
|
|
|
38
38
|
if (position === "middle") {
|
|
39
39
|
const halfLength = Math.floor((maxLength - 3) / 2)
|
package/throttle.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
interface ThrottledFunction<T extends (...args: Array<unknown>) => unknown> {
|
|
2
|
+
(...args: Parameters<T>): void
|
|
3
|
+
cancel: () => void
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2
|
-
export const throttle = <T extends (...args: Array<any>) => any>(
|
|
3
|
-
fn: T,
|
|
4
|
-
delay: number,
|
|
5
|
-
): ((...args: Parameters<T>) => void) => {
|
|
7
|
+
export const throttle = <T extends (...args: Array<any>) => any>(fn: T, delay: number): ThrottledFunction<T> => {
|
|
6
8
|
let timeoutId: ReturnType<typeof setTimeout> | null = null
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
const throttled: ThrottledFunction<T> = (...args: Parameters<T>): void => {
|
|
9
11
|
if (!timeoutId) {
|
|
10
12
|
fn(...args)
|
|
11
13
|
|
|
@@ -14,4 +16,13 @@ export const throttle = <T extends (...args: Array<any>) => any>(
|
|
|
14
16
|
}, delay)
|
|
15
17
|
}
|
|
16
18
|
}
|
|
19
|
+
|
|
20
|
+
throttled.cancel = (): void => {
|
|
21
|
+
if (timeoutId !== null) {
|
|
22
|
+
clearTimeout(timeoutId)
|
|
23
|
+
timeoutId = null
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return throttled
|
|
17
28
|
}
|
package/uuid.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
export const getUUID = (id?:
|
|
2
|
-
if (id) return id
|
|
1
|
+
export const getUUID = (id?: string): string => {
|
|
2
|
+
if (id !== undefined) return id
|
|
3
3
|
|
|
4
4
|
return crypto.randomUUID()
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
const uuidRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/
|
|
9
|
-
const isValid = typeof uuid === "string"
|
|
7
|
+
const UUID_REGEX: RegExp = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
export const isUUID = (uuid?: unknown): uuid is string => {
|
|
10
|
+
if (typeof uuid !== "string") return false
|
|
12
11
|
|
|
13
|
-
return
|
|
12
|
+
return UUID_REGEX.test(uuid)
|
|
14
13
|
}
|
package/validations.ts
CHANGED
package/classnames.spec.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { it, describe, expect } from "vitest"
|
|
2
|
-
import { getClasses } from "./classnames"
|
|
3
|
-
|
|
4
|
-
describe("classnames", () => {
|
|
5
|
-
describe("getClasses", () => {
|
|
6
|
-
it("returns valid with string", () => expect(getClasses("burn")).toEqual("burn"))
|
|
7
|
-
it("returns conditional object", () =>
|
|
8
|
-
expect(getClasses("burn", { "burn--alternative": true })).toEqual("burn burn--alternative"))
|
|
9
|
-
it("returns valid with invalid", () =>
|
|
10
|
-
expect(
|
|
11
|
-
getClasses("burn", null, "burn", undefined, {
|
|
12
|
-
"burn--active": false,
|
|
13
|
-
"burn--loading": true,
|
|
14
|
-
}),
|
|
15
|
-
).toEqual("burn burn--loading"))
|
|
16
|
-
})
|
|
17
|
-
})
|
package/clipboard.spec.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest"
|
|
2
|
-
import { copyValue } from "./clipboard"
|
|
3
|
-
|
|
4
|
-
const mockDocument = {
|
|
5
|
-
createElement: vi.fn(),
|
|
6
|
-
execCommand: vi.fn(),
|
|
7
|
-
body: {
|
|
8
|
-
appendChild: vi.fn(),
|
|
9
|
-
},
|
|
10
|
-
} as unknown as Document
|
|
11
|
-
|
|
12
|
-
global.document = mockDocument
|
|
13
|
-
|
|
14
|
-
describe("copyValue", () => {
|
|
15
|
-
const TEXT_STRING = "Test text" as const
|
|
16
|
-
|
|
17
|
-
it("copies text to the clipboard", () => {
|
|
18
|
-
const textAreaMock: {
|
|
19
|
-
value: string
|
|
20
|
-
select: () => void
|
|
21
|
-
remove: () => void
|
|
22
|
-
} = {
|
|
23
|
-
value: "",
|
|
24
|
-
select: vi.fn(),
|
|
25
|
-
remove: vi.fn(),
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
document.createElement = vi.fn().mockReturnValue(textAreaMock)
|
|
29
|
-
document.body.appendChild = vi.fn().mockReturnValue(textAreaMock)
|
|
30
|
-
|
|
31
|
-
copyValue(TEXT_STRING)
|
|
32
|
-
|
|
33
|
-
expect(document.createElement).toHaveBeenCalledWith("textarea")
|
|
34
|
-
expect(document.body.appendChild).toHaveBeenCalled()
|
|
35
|
-
expect(document.execCommand).toHaveBeenCalledWith("copy", false)
|
|
36
|
-
expect(textAreaMock.value).toBe(TEXT_STRING)
|
|
37
|
-
expect(textAreaMock.select).toHaveBeenCalled()
|
|
38
|
-
expect(textAreaMock.remove).toHaveBeenCalled()
|
|
39
|
-
})
|
|
40
|
-
})
|
package/colors.spec.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { it, describe, expect } from "vitest"
|
|
2
|
-
import { rgbToHex, hexToRgb, DEFAULT_PROFILE_RGB } from "./colors"
|
|
3
|
-
|
|
4
|
-
describe("colors", () => {
|
|
5
|
-
describe("hexToRgb", () => {
|
|
6
|
-
it("converts hex to rgb", () => expect(hexToRgb("#0c2c78")).toEqual([12, 44, 120]))
|
|
7
|
-
it("converts hex without #", () => expect(hexToRgb("0c2c78")).toEqual([12, 44, 120]))
|
|
8
|
-
it("converts uppercase hex", () => expect(hexToRgb("#0C2C78")).toEqual([12, 44, 120]))
|
|
9
|
-
it("converts short hex", () => expect(hexToRgb("#fff")).toEqual([0, 15, 255]))
|
|
10
|
-
it("converts black", () => expect(hexToRgb("#000000")).toEqual([0, 0, 0]))
|
|
11
|
-
it("converts white", () => expect(hexToRgb("#ffffff")).toEqual([255, 255, 255]))
|
|
12
|
-
it("returns default for 0", () => expect(hexToRgb(0 as unknown as string)).toEqual(DEFAULT_PROFILE_RGB))
|
|
13
|
-
it("returns default for undefined", () => expect(hexToRgb(undefined)).toEqual(DEFAULT_PROFILE_RGB))
|
|
14
|
-
it("returns default for null", () => expect(hexToRgb(null)).toEqual(DEFAULT_PROFILE_RGB))
|
|
15
|
-
it("returns default for empty string", () => expect(hexToRgb("")).toEqual(DEFAULT_PROFILE_RGB))
|
|
16
|
-
it("returns default for invalid hex", () => expect(hexToRgb("gggggg")).toEqual([0, 0, 0]))
|
|
17
|
-
it("returns default for non-string", () => expect(hexToRgb(123 as unknown as string)).toEqual(DEFAULT_PROFILE_RGB))
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
describe("rgbToHex", () => {
|
|
21
|
-
it("converts rgb to hex", () => expect(rgbToHex(12, 44, 120)).toEqual("#0c2c78"))
|
|
22
|
-
it("converts string to hex", () =>
|
|
23
|
-
expect(rgbToHex("12" as unknown as number, "44" as unknown as number, "120" as unknown as number)).toEqual(
|
|
24
|
-
"#0c2c78",
|
|
25
|
-
))
|
|
26
|
-
it("converts null to hex", () => expect(rgbToHex(null as unknown as number, 44, 120)).toEqual("#002c78"))
|
|
27
|
-
it("returns undefined to hex", () => expect(rgbToHex(12, undefined as unknown as number, 120)).toEqual("#0cff78"))
|
|
28
|
-
it("returns FF for negative", () => expect(rgbToHex(-1, -12, 120)).toEqual("#ffff78"))
|
|
29
|
-
it("converts zero values", () => expect(rgbToHex(0, 0, 0)).toEqual("#000000"))
|
|
30
|
-
it("converts max values", () => expect(rgbToHex(255, 255, 255)).toEqual("#ffffff"))
|
|
31
|
-
it("returns FF for over 255", () => expect(rgbToHex(300, 256, 120)).toEqual("#ffff78"))
|
|
32
|
-
it("returns FF for invalid string", () => expect(rgbToHex("abc" as unknown as number, 44, 120)).toEqual("#ff2c78"))
|
|
33
|
-
it("handles fractional numbers", () =>
|
|
34
|
-
expect(rgbToHex(12.9, 44.1, 120.5)).toEqual("#c.e6666666666682c.19999999999a78.8"))
|
|
35
|
-
it("uses defaults when no params", () => expect(rgbToHex()).toEqual("#ffffff"))
|
|
36
|
-
it("handles mixed valid/invalid", () =>
|
|
37
|
-
expect(rgbToHex(12, "invalid" as unknown as number, 120)).toEqual("#0cff78"))
|
|
38
|
-
it("converts single digit to padded hex", () => expect(rgbToHex(1, 2, 3)).toEqual("#010203"))
|
|
39
|
-
})
|
|
40
|
-
})
|
package/debounce.spec.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest"
|
|
2
|
-
import { debounce } from "./debounce"
|
|
3
|
-
import { sleep } from "./sleep"
|
|
4
|
-
|
|
5
|
-
describe("debounce", () => {
|
|
6
|
-
it("calls the callback after the specified time", async () => {
|
|
7
|
-
const callback = vi.fn()
|
|
8
|
-
const debouncedFunction = debounce(callback, 50)
|
|
9
|
-
|
|
10
|
-
debouncedFunction()
|
|
11
|
-
|
|
12
|
-
await sleep(25)
|
|
13
|
-
expect(callback).not.toHaveBeenCalled()
|
|
14
|
-
|
|
15
|
-
await sleep(30)
|
|
16
|
-
expect(callback).toHaveBeenCalledTimes(1)
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
it("does not call the callback if the function is called again within the wait time", async () => {
|
|
20
|
-
const callback = vi.fn()
|
|
21
|
-
const debouncedFunction = debounce(callback, 50)
|
|
22
|
-
|
|
23
|
-
debouncedFunction()
|
|
24
|
-
await sleep(25)
|
|
25
|
-
|
|
26
|
-
debouncedFunction()
|
|
27
|
-
await sleep(25)
|
|
28
|
-
|
|
29
|
-
expect(callback).not.toHaveBeenCalled()
|
|
30
|
-
|
|
31
|
-
await sleep(30)
|
|
32
|
-
expect(callback).toHaveBeenCalledTimes(1)
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it("preserves function arguments", async () => {
|
|
36
|
-
const callback = vi.fn()
|
|
37
|
-
const debouncedFunction = debounce(callback, 50)
|
|
38
|
-
|
|
39
|
-
debouncedFunction("arg1", "arg2", 123)
|
|
40
|
-
|
|
41
|
-
await sleep(60)
|
|
42
|
-
expect(callback).toHaveBeenCalledWith("arg1", "arg2", 123)
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it("uses the latest arguments when called multiple times", async () => {
|
|
46
|
-
const callback = vi.fn()
|
|
47
|
-
const debouncedFunction = debounce(callback, 50)
|
|
48
|
-
|
|
49
|
-
debouncedFunction("first")
|
|
50
|
-
await sleep(25)
|
|
51
|
-
|
|
52
|
-
debouncedFunction("second")
|
|
53
|
-
await sleep(60)
|
|
54
|
-
|
|
55
|
-
expect(callback).toHaveBeenCalledTimes(1)
|
|
56
|
-
expect(callback).toHaveBeenCalledWith("second")
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it("works with functions that have specific parameter types", async () => {
|
|
60
|
-
const callback = vi.fn((str: string, num: number) => str + num)
|
|
61
|
-
const debouncedFunction = debounce(callback, 50)
|
|
62
|
-
|
|
63
|
-
debouncedFunction("test", 42)
|
|
64
|
-
|
|
65
|
-
await sleep(60)
|
|
66
|
-
expect(callback).toHaveBeenCalledWith("test", 42)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it("works with functions that return values", async () => {
|
|
70
|
-
const callback = vi.fn((x: number) => x * 2)
|
|
71
|
-
const debouncedFunction = debounce(callback, 50)
|
|
72
|
-
|
|
73
|
-
debouncedFunction(5)
|
|
74
|
-
|
|
75
|
-
await sleep(60)
|
|
76
|
-
expect(callback).toHaveBeenCalledWith(5)
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it("works with zero wait time", async () => {
|
|
80
|
-
const callback = vi.fn()
|
|
81
|
-
const debouncedFunction = debounce(callback, 0)
|
|
82
|
-
|
|
83
|
-
debouncedFunction()
|
|
84
|
-
|
|
85
|
-
await sleep(1)
|
|
86
|
-
expect(callback).toHaveBeenCalledTimes(1)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it("can be called after completion", async () => {
|
|
90
|
-
const callback = vi.fn()
|
|
91
|
-
const debouncedFunction = debounce(callback, 50)
|
|
92
|
-
|
|
93
|
-
debouncedFunction()
|
|
94
|
-
await sleep(60)
|
|
95
|
-
expect(callback).toHaveBeenCalledTimes(1)
|
|
96
|
-
|
|
97
|
-
debouncedFunction()
|
|
98
|
-
await sleep(60)
|
|
99
|
-
expect(callback).toHaveBeenCalledTimes(2)
|
|
100
|
-
})
|
|
101
|
-
})
|