houser-js-utils
Version:
A comprehensive collection of TypeScript utility functions for common development tasks including array manipulation, string processing, date handling, random number generation, validation, and much more.
1,565 lines • 308 kB
JavaScript
var P = Object.defineProperty;
var O = (e, t, r) => t in e ? P(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r;
var D = (e, t, r) => O(e, typeof t != "symbol" ? t + "" : t, r);
const Z = {
/**
* Gets the ARIA described by attribute of an element
* @param element - The DOM element to check
* @returns The value of aria-describedby attribute or null if not set
* @example
* ```typescript
* const element = document.querySelector('.input');
* const describedBy = AccessibilityUtils.getAriaDescribedBy(element);
* ```
*/
getAriaDescribedBy(e) {
return e.getAttribute("aria-describedby");
},
/**
* Sets the ARIA described by attribute of an element
* @param element - The DOM element to modify
* @param describedBy - The ID(s) of the element(s) that describe this element
* @example
* ```typescript
* const input = document.querySelector('.input');
* const helpText = document.querySelector('.help-text');
* AccessibilityUtils.setAriaDescribedBy(input, helpText.id);
* ```
*/
setAriaDescribedBy(e, t) {
e.setAttribute("aria-describedby", t);
},
/**
* Gets the ARIA expanded state of an element
* @param element - Element to check
* @returns ARIA expanded state
*/
getAriaExpanded(e) {
const t = e.getAttribute("aria-expanded");
return t === null ? null : t === "true";
},
/**
* Sets the ARIA expanded state of an element
* @param element - Element to modify
* @param expanded - ARIA expanded state to set
*/
setAriaExpanded(e, t) {
e.setAttribute("aria-expanded", t.toString());
},
/**
* Gets the ARIA hidden state of an element
* @param element - Element to check
* @returns ARIA hidden state
*/
getAriaHidden(e) {
const t = e.getAttribute("aria-hidden");
return t === null ? null : t === "true";
},
/**
* Sets the ARIA hidden state of an element
* @param element - Element to modify
* @param hidden - ARIA hidden state to set
*/
setAriaHidden(e, t) {
e.setAttribute("aria-hidden", t.toString());
},
/**
* Gets the ARIA invalid state of an element
* @param element - Element to check
* @returns ARIA invalid state
*/
getAriaInvalid(e) {
const t = e.getAttribute("aria-invalid");
return t === null ? null : t === "true";
},
/**
* Sets the ARIA invalid state of an element
* @param element - Element to modify
* @param invalid - ARIA invalid state to set
*/
setAriaInvalid(e, t) {
e.setAttribute("aria-invalid", t.toString());
},
/**
* Gets the ARIA label of an element
* @param element - Element to check
* @returns ARIA label
*/
getAriaLabel(e) {
return e.getAttribute("aria-label");
},
/**
* Sets the ARIA label of an element
* @param element - Element to modify
* @param label - ARIA label to set
*/
setAriaLabel(e, t) {
e.setAttribute("aria-label", t);
},
/**
* Gets the ARIA required state of an element
* @param element - Element to check
* @returns ARIA required state
*/
getAriaRequired(e) {
const t = e.getAttribute("aria-required");
return t === null ? null : t === "true";
},
/**
* Sets the ARIA required state of an element
* @param element - Element to modify
* @param required - ARIA required state to set
*/
setAriaRequired(e, t) {
e.setAttribute("aria-required", t.toString());
},
/**
* Gets the ARIA role of an element
* @param element - Element to check
* @returns ARIA role
*/
getAriaRole(e) {
return e.getAttribute("role");
},
/**
* Sets the ARIA role of an element
* @param element - Element to modify
* @param role - ARIA role to set
*/
setAriaRole(e, t) {
e.setAttribute("role", t);
},
/**
* Removes focus from an element
* @param element - Element to blur
*/
blurElement(e) {
e instanceof HTMLElement && e.blur();
},
/**
* Sets focus to an element
* @param element - Element to focus
*/
focusElement(e) {
e instanceof HTMLElement && e.focus();
},
/**
* Sets focus to the first focusable element in a container
* @param container - Container element
*/
focusFirstElement(e) {
const t = this.getFocusableElements(e);
t.length > 0 && t[0].focus();
},
/**
* Sets focus to the last focusable element in a container
* @param container - Container element
*/
focusLastElement(e) {
const t = this.getFocusableElements(e);
t.length > 0 && t[t.length - 1].focus();
},
/**
* Sets focus to the next focusable element
* @param currentElement - Current element
*/
focusNextElement(e) {
const t = this.getFocusableElements(document.body), r = t.indexOf(e);
r < t.length - 1 && t[r + 1].focus();
},
/**
* Sets focus to the previous focusable element
* @param currentElement - Current element
*/
focusPreviousElement(e) {
const t = this.getFocusableElements(document.body), r = t.indexOf(e);
r > 0 && t[r - 1].focus();
},
/**
* Gets all focusable elements within a container
* @param container - Container element
* @returns Array of focusable elements
*/
getFocusableElements(e) {
const t = e.querySelectorAll("*");
return Array.from(t).filter((r) => this.isFocusable(r));
},
/**
* Gets the current focus element
* @returns Currently focused element
*/
getFocusedElement() {
return document.activeElement;
},
/**
* Checks if an element is focusable
* @param element - Element to check
* @returns True if element is focusable
*/
isFocusable(e) {
if (!(e instanceof HTMLElement) || e.tabIndex < 0 || "disabled" in e && e.disabled)
return !1;
switch (e.tagName.toLowerCase()) {
case "a":
case "button":
case "input":
case "select":
case "textarea":
return !0;
default:
return !1;
}
},
/**
* Traps focus within a container element, typically used for modals or dialogs
* @param container - The container element to trap focus within
* @returns A function that removes the focus trap when called
* @example
* ```typescript
* const modal = document.querySelector('.modal');
* const removeTrap = AccessibilityUtils.trapFocus(modal);
*
* // Later, when the modal is closed:
* removeTrap();
* ```
*/
trapFocus(e) {
const t = this.getFocusableElements(e), r = t[0], n = t[t.length - 1], a = (o) => {
!(o instanceof KeyboardEvent) || o.key !== "Tab" || (o.shiftKey ? document.activeElement === r && (o.preventDefault(), n.focus()) : document.activeElement === n && (o.preventDefault(), r.focus()));
};
return e.addEventListener("keydown", a), () => e.removeEventListener("keydown", a);
}
}, H = {
/**
* Collection of value parsers for AG Grid
*/
parsers: {
/**
* Parses a string value to a number
* @param params - Object containing the new value to parse
* @returns Parsed number or null if invalid
* @example
* ```typescript
* const value = AGGridUtils.parsers.number({ newValue: '123.45' });
* // Returns: 123.45
* ```
*/
number: (e) => {
if (typeof e.newValue == "string" && e.newValue.trim() === "")
return null;
const t = Number(e.newValue);
return isNaN(t) ? null : t;
},
/**
* Trims a string value
* @param params - Object containing the new value to trim
* @returns Trimmed string or null if empty
* @example
* ```typescript
* const value = AGGridUtils.parsers.string({ newValue: ' hello ' });
* // Returns: 'hello'
* ```
*/
string: (e) => String(e.newValue).trim()
},
/**
* Collection of value setters for AG Grid
*/
setters: {
/**
* Creates a setter that rounds numbers up to the nearest integer
* @param field - The field name to set the value on
* @returns A value setter function
* @example
* ```typescript
* const setter = AGGridUtils.setters.numberCeil('price');
* setter({ data: {}, newValue: 123.45 });
* // Sets data.price to 124
* ```
*/
numberCeil: (e) => (t) => (t.data[e] = Math.ceil(t.newValue), !0),
/**
* Creates a setter that rounds numbers down to the nearest integer
* @param field - The field name to set the value on
* @returns A value setter function
* @example
* ```typescript
* const setter = AGGridUtils.setters.numberFloor('price');
* setter({ data: {}, newValue: 123.45 });
* // Sets data.price to 123
* ```
*/
numberFloor: (e) => (t) => (t.data[e] = Math.floor(t.newValue), !0),
/**
* Creates a setter that formats numbers to one decimal place if they're integers
* @param field - The field name to set the value on
* @returns A value setter function
* @example
* ```typescript
* const setter = AGGridUtils.setters.numberFloat('price');
* setter({ data: {}, newValue: 123 });
* // Sets data.price to '123.0'
* ```
*/
numberFloat: (e) => (t) => {
const r = Number(t.newValue);
return t.data[e] = Number.isInteger(r) ? r.toFixed(1) : r, !0;
}
}
}, _ = {
/**
* Animates an element using CSS transitions
* @param element - The HTML element to animate
* @param properties - CSS properties to animate
* @param duration - Animation duration in milliseconds (default: 300)
* @param easing - CSS easing function (default: 'ease')
* @returns Promise that resolves when animation completes
* @example
* ```typescript
* const element = document.querySelector('.box');
* await AnimationUtils.animate(element, {
* transform: 'translateX(100px)',
* opacity: '0.5'
* }, 500, 'ease-in-out');
* ```
*/
animate(e, t, r = 300, n = "ease") {
return new Promise((a) => {
Object.keys(t).forEach((s) => {
t[s] !== void 0 && parseFloat(
getComputedStyle(e)[s]
);
}), e.style.transition = `all ${r}ms ${n}`, Object.keys(t).forEach((s) => {
const i = t[s];
i !== void 0 && (e.style[s] = i);
});
const o = () => {
e.removeEventListener("transitionend", o), e.style.transition = "", a();
};
e.addEventListener("transitionend", o), setTimeout(o, r);
});
},
/**
* Animates an element using requestAnimationFrame for smoother animations
* @param element - The HTML element to animate
* @param properties - CSS properties to animate
* @param duration - Animation duration in milliseconds (default: 300)
* @param easing - Custom easing function (default: linear)
* @returns Promise that resolves when animation completes
* @example
* ```typescript
* const element = document.querySelector('.box');
* await AnimationUtils.animateWithRAF(element, {
* transform: 'translateX(100px)'
* }, 500, (t) => t * t); // Quadratic easing
* ```
*/
animateWithRAF(e, t, r = 300, n = (a) => a) {
return new Promise((a) => {
const o = performance.now(), s = {}, i = {};
Object.keys(t).forEach((u) => {
const m = t[u];
if (m !== void 0) {
const h = parseFloat(
getComputedStyle(e)[u]
), d = parseFloat(m);
!isNaN(h) && !isNaN(d) && (s[u] = h, i[u] = d);
}
});
const c = (u) => {
const m = u - o, h = Math.min(m / r, 1), d = n(h);
Object.keys(s).forEach((f) => {
const w = s[f], g = i[f], M = w + (g - w) * d;
e.style[f] = `${M}px`;
}), h < 1 ? requestAnimationFrame(c) : a();
};
requestAnimationFrame(c);
});
},
/**
* Creates a keyframe animation using the Web Animations API
* @param element - The HTML element to animate
* @param keyframes - Array of keyframe objects
* @param options - Animation options
* @returns Animation object
* @example
* ```typescript
* const element = document.querySelector('.box');
* const animation = AnimationUtils.createKeyframeAnimation(element, [
* { transform: 'translateX(0)', opacity: 1 },
* { transform: 'translateX(100px)', opacity: 0.5 }
* ], {
* duration: 500,
* easing: 'ease-in-out'
* });
* ```
*/
createKeyframeAnimation(e, t, r = {}) {
return e.animate(t, {
duration: 300,
easing: "ease",
fill: "forwards",
...r
});
},
/**
* Creates a spring physics-based animation
* @param element - The HTML element to animate
* @param properties - CSS properties to animate
* @param options - Spring animation configuration
* @returns Promise that resolves when animation completes
* @example
* ```typescript
* const element = document.querySelector('.box');
* await AnimationUtils.createSpringAnimation(element, {
* transform: 'translateY(200px)'
* }, {
* stiffness: 170,
* damping: 26,
* mass: 1
* });
* ```
*/
createSpringAnimation(e, t, r = {}) {
const { stiffness: n = 170, damping: a = 26, mass: o = 1, duration: s = 300 } = r;
return new Promise((i) => {
const c = performance.now(), u = {}, m = {}, h = {};
Object.keys(t).forEach((f) => {
const w = t[f];
if (w !== void 0) {
const g = parseFloat(
getComputedStyle(e)[f]
), M = parseFloat(w);
!isNaN(g) && !isNaN(M) && (u[f] = g, m[f] = M, h[f] = 0);
}
});
const d = (f) => {
const w = f - c;
if (w >= s) {
Object.keys(m).forEach((g) => {
e.style[g] = `${m[g]}px`;
}), i();
return;
}
Object.keys(u).forEach((g) => {
const M = u[g], E = m[g], A = parseFloat(
e.style[g] || M
), S = h[g], R = E - A, U = n * R, k = a * S, x = (U - k) / o;
h[g] = S + x * (w / 1e3);
const L = A + h[g] * (w / 1e3);
e.style[g] = `${L}px`;
}), requestAnimationFrame(d);
};
requestAnimationFrame(d);
});
},
/**
* Gets all animations currently running on an element
* @param element - The HTML element to check
* @returns Array of Animation objects
* @example
* ```typescript
* const element = document.querySelector('.box');
* const animations = AnimationUtils.getAnimations(element);
* ```
*/
getAnimations(e) {
return e.getAnimations();
},
/**
* Gets the current animation state of an element
* @param element - The HTML element to check
* @returns Current animation state: 'idle', 'running', or 'paused'
* @example
* ```typescript
* const element = document.querySelector('.box');
* const state = AnimationUtils.getAnimationState(element);
* if (state === 'running') {
* console.log('Element is currently animating');
* }
* ```
*/
getAnimationState(e) {
const t = e.getAnimations();
return t.length === 0 ? "idle" : t.some((r) => r.playState === "running") ? "running" : "paused";
},
/**
* Checks if an element has any active animations
* @param element - The HTML element to check
* @returns True if the element has any animations
* @example
* ```typescript
* const element = document.querySelector('.box');
* if (AnimationUtils.hasAnimations(element)) {
* console.log('Element has active animations');
* }
* ```
*/
hasAnimations(e) {
return e.getAnimations().length > 0;
},
/**
* Pauses all animations on an element
* @param element - The HTML element to pause animations on
* @example
* ```typescript
* const element = document.querySelector('.box');
* AnimationUtils.pauseAnimations(element);
* ```
*/
pauseAnimations(e) {
e.getAnimations().forEach((t) => t.pause());
},
/**
* Resumes all paused animations on an element
* @param element - The HTML element to resume animations on
* @example
* ```typescript
* const element = document.querySelector('.box');
* AnimationUtils.resumeAnimations(element);
* ```
*/
resumeAnimations(e) {
e.getAnimations().forEach((t) => t.play());
},
/**
* Reverses the direction of all animations on an element
* @param element - The HTML element to reverse animations on
* @example
* ```typescript
* const element = document.querySelector('.box');
* AnimationUtils.reverseAnimations(element);
* ```
*/
reverseAnimations(e) {
e.getAnimations().forEach((t) => t.reverse());
},
/**
* Stops and removes all animations from an element
* @param element - The HTML element to stop animations on
* @example
* ```typescript
* const element = document.querySelector('.box');
* AnimationUtils.stopAnimations(element);
* ```
*/
stopAnimations(e) {
e.getAnimations().forEach((t) => t.cancel());
},
/**
* Waits for all animations on an element to complete
* @param element - The HTML element to wait for
* @returns Promise that resolves when all animations complete
* @example
* ```typescript
* const element = document.querySelector('.box');
* await AnimationUtils.waitForAnimations(element);
* console.log('All animations completed');
* ```
*/
async waitForAnimations(e) {
const t = e.getAnimations();
await Promise.all(t.map((r) => r.finished));
}
}, W = {
/**
* Calculates the average of all numbers in an array
* @param arr - Array of numbers to calculate average from
* @returns The average of all numbers, or 0 if array is empty
* @example
* ```typescript
* const avg = ArrayUtils.average([1, 2, 3, 4, 5]); // Returns 3
* ```
*/
average(e) {
return !Array.isArray(e) || e.length === 0 ? 0 : this.sumArray(e) / e.length;
},
/**
* Compares two arrays for equality by sorting and comparing elements
* @param a1 - First array to compare
* @param a2 - Second array to compare
* @returns True if arrays contain the same elements in any order
* @example
* ```typescript
* const equal = ArrayUtils.arrayEquals([1, 2, 3], [3, 2, 1]); // Returns true
* ```
*/
arrayEquals(e, t) {
if (!Array.isArray(e) || !Array.isArray(t) || e.length !== t.length) return !1;
const r = [...e].sort(this.sortCompare), n = [...t].sort(this.sortCompare);
return r.every((a, o) => a === n[o]);
},
/**
* Splits an array into chunks of specified size
* @param arr - Array to split into chunks
* @param size - Size of each chunk (must be positive)
* @returns Array of arrays, each of size 'size'
* @throws Error if size is not positive
*/
chunks(e, t) {
if (!Number.isInteger(t) || t <= 0)
throw new Error("Chunk size must be a positive integer");
const r = [];
for (let n = 0; n < e.length; n += t)
r.push(e.slice(n, n + t));
return r;
},
/**
* Compares two arrays for equality based on the given options
* @param arr1 - First array to compare
* @param arr2 - Second array to compare
* @param options - Comparison options
* @returns True if arrays are equal based on the options
*/
compareArrays(e, t, r = {}) {
if (!Array.isArray(e) || !Array.isArray(t)) return !1;
const { ordered: n = !1, deep: a = !1 } = r;
if (e.length !== t.length) return !1;
if (n)
return e.every(
(o, s) => a ? this.deepEqual(o, t[s]) : o === t[s]
);
{
const o = new Array(t.length).fill(!1);
return e.every((s) => {
const i = t.findIndex(
(c, u) => !o[u] && (a ? this.deepEqual(s, c) : s === c)
);
return i === -1 ? !1 : (o[i] = !0, !0);
});
}
},
/**
* Returns a new array with unique values
* @param array - Array to make unique
* @returns Array with duplicate values removed
*/
deduplicate(e) {
return Array.isArray(e) ? Array.from(new Set(e)) : [];
},
/**
* Deep equality comparison between two values
* @param a - First value
* @param b - Second value
* @returns True if values are deeply equal
*/
deepEqual(e, t) {
if (e === t) return !0;
if (typeof e != typeof t) return !1;
if (typeof e == "object" && e !== null && t !== null) {
if (Array.isArray(e) !== Array.isArray(t)) return !1;
if (Array.isArray(e))
return e.length !== t.length ? !1 : e.every((a, o) => this.deepEqual(a, t[o]));
const r = Object.keys(e), n = Object.keys(t);
return r.length !== n.length ? !1 : r.every(
(a) => this.deepEqual(
e[a],
t[a]
)
);
}
return !1;
},
/**
* Returns elements from array a that are not in array b based on id property
* @param a - First array
* @param b - Second array
* @param getId - Optional function to extract id from items
* @returns Array of elements from a that are not in b
*/
difference(e, t, r = (n) => n.id) {
const n = new Set(t.map(r));
return e.filter((a) => !n.has(r(a)));
},
/**
* Returns the maximum value in an array
* @param arr - Array of comparable values
* @returns Maximum value or undefined if array is empty
*/
findMax(e) {
if (!(!Array.isArray(e) || e.length === 0))
return e.reduce((t, r) => r > t ? r : t, e[0]);
},
/**
* Returns the minimum value in an array
* @param arr - Array of comparable values
* @returns Minimum value or undefined if array is empty
*/
findMin(e) {
if (!(!Array.isArray(e) || e.length === 0))
return e.reduce((t, r) => r < t ? r : t, e[0]);
},
/**
* Finds and updates an item in a collection based on id
* @param collection - Collection to update
* @param item - Item to update
* @returns Updated collection
* @throws Error if item is not found
*/
findAndUpdate(e, t) {
const r = e.findIndex((n) => n.id === t.id);
if (r === -1)
throw new Error(`Item with id ${t.id} not found in collection`);
return e.map((n, a) => a === r ? t : n);
},
/**
* Flattens a nested array to a specified depth
* @param arr - Array to flatten
* @param depth - Maximum depth to flatten (default: Infinity)
* @returns Flattened array
*/
flatten(e, t = 1 / 0) {
return t <= 0 ? e.slice() : Array.isArray(e) ? e.reduce((r, n) => (Array.isArray(n) ? r.push(...this.flatten(n, t - 1)) : r.push(n), r), []) : [e];
},
/**
* Groups array elements by a key or function
* @param arr - Array to group
* @param keyOrFn - Key to group by or function that returns the group key
* @returns Object with grouped arrays
*/
groupBy(e, t) {
return Array.isArray(e) ? e.reduce((r, n) => {
const a = typeof t == "function" ? t(n) : String(n[t]);
return r[a] = r[a] || [], r[a].push(n), r;
}, {}) : {};
},
/**
* Checks if two arrays have any common elements
* @param array1 - First array
* @param array2 - Second array
* @returns True if arrays share at least one common element
*/
hasCommonElement(e, t) {
if (!e?.length || !t?.length) return !1;
const [r, n] = e.length < t.length ? [e, t] : [t, e], a = new Set(r);
return n.some((o) => a.has(o));
},
/**
* Returns the intersection of two arrays
* @param arr1 - First array
* @param arr2 - Second array
* @returns Array containing elements present in both arrays
*/
intersection(e, t) {
if (!Array.isArray(e) || !Array.isArray(t)) return [];
const r = new Set(t);
return e.filter((n) => r.has(n));
},
/**
* Moves an item from one position to another in an array
* @param arr - Array to modify
* @param from - Source index
* @param to - Destination index
* @returns New array with item moved
* @throws Error if indices are out of bounds
*/
moveItem(e, t, r) {
if (!Array.isArray(e))
throw new Error("First argument must be an array");
if (t < 0 || t >= e.length)
throw new Error(`Source index ${t} is out of bounds`);
if (r < 0 || r >= e.length)
throw new Error(`Destination index ${r} is out of bounds`);
const n = [...e], [a] = n.splice(t, 1);
return n.splice(r, 0, a), n;
},
/**
* Returns a random element from the array
* @param arr - Array to get element from
* @returns Random element or undefined if array is empty
*/
random(e) {
if (!(!Array.isArray(e) || e.length === 0))
return e[Math.floor(Math.random() * e.length)];
},
/**
* Removes elements from an array that match a predicate
* @param arr - Array to remove elements from
* @param predicate - Function that returns true for elements to remove
* @returns New array with matching elements removed
*/
remove(e, t) {
return Array.isArray(e) ? e.filter((r) => !t(r)) : [];
},
/**
* Returns a new array with elements in random order
* @param arr - Array to shuffle
* @returns New array with elements in random order
*/
shuffle(e) {
if (!Array.isArray(e)) return [];
const t = [...e];
for (let r = t.length - 1; r > 0; r--) {
const n = Math.floor(Math.random() * (r + 1));
[t[r], t[n]] = [t[n], t[r]];
}
return t;
},
/**
* Compares two values for sorting
* @param x - First value to compare
* @param y - Second value to compare
* @returns -1 if x < y, 0 if equal, 1 if x > y
*/
sortCompare(e, t) {
const r = ["string", "number", "bool"];
return typeof e != typeof t ? r.indexOf(typeof t) - r.indexOf(typeof e) : e === t ? 0 : e > t ? 1 : -1;
},
/**
* Returns the sum of all numbers in an array
* @param arr - Array of numbers
* @returns Sum of all numbers
*/
sumArray(e) {
return Array.isArray(e) ? e.reduce((t, r) => t + (Number(r) || 0), 0) : 0;
},
/**
* Returns the first n elements of an array
* @param arr - Array to get elements from
* @param n - Number of elements to get (default: 1)
* @returns Array containing the first n elements
*/
takeFirst(e, t = 1) {
return Array.isArray(e) ? e.slice(0, t) : [];
},
/**
* Returns the last n elements of an array
* @param arr - Array to get elements from
* @param n - Number of elements to get (default: 1)
* @returns Array containing the last n elements
*/
takeLast(e, t = 1) {
return Array.isArray(e) ? e.slice(-t) : [];
},
/**
* Returns the union of multiple arrays
* @param arrays - Arrays to union
* @returns Array containing unique elements from all arrays
*/
union(...e) {
return this.deduplicate(e.flat());
}
}, T = (e) => {
const { r: t, g: r, b: n } = e;
if (t < 0 || t > 255 || r < 0 || r > 255 || n < 0 || n > 255)
throw new Error("RGB values must be between 0 and 255");
}, B = (e) => {
const { h: t, s: r, l: n } = e;
if (t < 0 || t > 360 || r < 0 || r > 100 || n < 0 || n > 100)
throw new Error(
"HSL values must be between 0-360 for hue and 0-100 for saturation and lightness"
);
}, F = (e) => {
const { c: t, m: r, y: n, k: a } = e;
if (t < 0 || t > 100 || r < 0 || r > 100 || n < 0 || n > 100 || a < 0 || a > 100)
throw new Error("CMYK values must be between 0 and 100");
}, Y = {
/**
* Adjusts the brightness of a color
* @param color - RGB color values
* @param amount - Amount to adjust (-100 to 100)
* @returns Adjusted RGB color values
*/
adjustBrightness(e, t) {
T(e);
const r = (n) => Math.max(0, Math.min(255, n + t));
return {
r: r(e.r),
g: r(e.g),
b: r(e.b)
};
},
/**
* Adjusts the saturation of a color by a specified amount
* @param color - RGB color values
* @param amount - Amount to adjust (-100 to 100)
* @returns Adjusted RGB color values
*/
adjustSaturation(e, t) {
T(e);
const r = this.rgbToHsl(e);
return r.s = Math.max(0, Math.min(100, r.s + t)), this.hslToRgb(r);
},
/**
* Blends two colors together
* @param color1 - First RGB color
* @param color2 - Second RGB color
* @param ratio - Blend ratio (0 to 1)
* @returns Blended RGB color
*/
blendColors(e, t, r) {
if (T(e), T(t), r < 0 || r > 1)
throw new Error("Blend ratio must be between 0 and 1");
const n = (a, o) => Math.round(a * (1 - r) + o * r);
return {
r: n(e.r, t.r),
g: n(e.g, t.g),
b: n(e.b, t.b)
};
},
/**
* Converts a CMYK color to RGB
* @param cmyk - CMYK color values
* @returns RGB color values
*/
cmykToRgb(e) {
F(e);
const { c: t, m: r, y: n, k: a } = e, o = 255 * (1 - t / 100) * (1 - a / 100), s = 255 * (1 - r / 100) * (1 - a / 100), i = 255 * (1 - n / 100) * (1 - a / 100);
return { r: Math.round(o), g: Math.round(s), b: Math.round(i) };
},
/**
* Calculates the contrast ratio between two colors
* @param color1 - First RGB color
* @param color2 - Second RGB color
* @returns Contrast ratio (1 to 21)
*/
contrastRatio(e, t) {
T(e), T(t);
const r = (i) => {
const [c, u, m] = [i.r, i.g, i.b].map((h) => (h = h / 255, h <= 0.03928 ? h / 12.92 : Math.pow((h + 0.055) / 1.055, 2.4)));
return 0.2126 * c + 0.7152 * u + 0.0722 * m;
}, n = r(e), a = r(t), o = Math.max(n, a), s = Math.min(n, a);
return (o + 0.05) / (s + 0.05);
},
/**
* Converts a hex color to RGB
* @param hex - Hex color string (e.g., "#FF0000")
* @returns RGB color values
*/
hexToRgb(e) {
if (!this.isValidHexColor(e))
throw new Error("Invalid hex color format");
const t = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);
if (!t) throw new Error("Invalid hex color");
return {
r: parseInt(t[1], 16),
g: parseInt(t[2], 16),
b: parseInt(t[3], 16)
};
},
/**
* Converts an HSL color to RGB
* @param hsl - HSL color values
* @returns RGB color values
*/
hslToRgb(e) {
B(e);
const { h: t, s: r, l: n } = e, a = r / 100, o = n / 100, s = (1 - Math.abs(2 * o - 1)) * a, i = s * (1 - Math.abs(t / 60 % 2 - 1)), c = o - s / 2;
let u = 0, m = 0, h = 0;
return t >= 0 && t < 60 ? (u = s, m = i, h = 0) : t >= 60 && t < 120 ? (u = i, m = s, h = 0) : t >= 120 && t < 180 ? (u = 0, m = s, h = i) : t >= 180 && t < 240 ? (u = 0, m = i, h = s) : t >= 240 && t < 300 ? (u = i, m = 0, h = s) : t >= 300 && t < 360 && (u = s, m = 0, h = i), {
r: Math.round((u + c) * 255),
g: Math.round((m + c) * 255),
b: Math.round((h + c) * 255)
};
},
/**
* Inverts a color
* @param color - RGB color values
* @returns Inverted RGB color values
*/
invertColor(e) {
return T(e), {
r: 255 - e.r,
g: 255 - e.g,
b: 255 - e.b
};
},
/**
* Validates if a string is a valid hex color
* @param hex - Hex color string to validate
* @returns True if the string is a valid hex color
*/
isValidHexColor(e) {
return /^#?([a-f\d]{3}|[a-f\d]{6})$/i.test(e);
},
/**
* Generates a random color in the specified format
* @param type - Color format (rgb, hex, hsl, or cmyk)
* @returns Random color string in the specified format
*/
randomColor(e = "hex") {
const t = (r) => Math.floor(Math.random() * r);
switch (e) {
case "rgb":
const r = t(256), n = t(256), a = t(256);
return `rgb(${r}, ${n}, ${a})`;
case "hex":
const o = t(256), s = t(256), i = t(256);
return `#${[o, s, i].map((A) => A.toString(16).padStart(2, "0")).join("")}`;
case "hsl":
const c = t(361), u = t(101), m = t(101);
return `hsl(${c}, ${u}%, ${m}%)`;
case "cmyk":
const h = t(101), d = t(101), f = t(101), w = t(101);
return `cmyk(${h}%, ${d}%, ${f}%, ${w}%)`;
default:
const g = t(256), M = t(256), E = t(256);
return `#${[g, M, E].map((A) => A.toString(16).padStart(2, "0")).join("")}`;
}
},
/**
* Converts RGB color to CMYK
* @param rgb - RGB color values
* @returns CMYK color values
*/
rgbToCmyk(e) {
T(e);
const { r: t, g: r, b: n } = e, a = t / 255, o = r / 255, s = n / 255, i = 1 - Math.max(a, o, s), c = (1 - a - i) / (1 - i) || 0, u = (1 - o - i) / (1 - i) || 0, m = (1 - s - i) / (1 - i) || 0;
return {
c: Math.round(c * 100),
m: Math.round(u * 100),
y: Math.round(m * 100),
k: Math.round(i * 100)
};
},
/**
* Converts RGB color to hex
* @param rgb - RGB color values
* @returns Hex color string
*/
rgbToHex(e) {
T(e);
const { r: t, g: r, b: n } = e;
return "#" + [t, r, n].map((a) => a.toString(16).padStart(2, "0")).join("");
},
/**
* Converts RGB color to HSL
* @param rgb - RGB color values
* @returns HSL color values
*/
rgbToHsl(e) {
T(e);
const { r: t, g: r, b: n } = e, a = t / 255, o = r / 255, s = n / 255, i = Math.max(a, o, s), c = Math.min(a, o, s);
let u = 0, m = 0;
const h = (i + c) / 2;
if (i !== c) {
const d = i - c;
switch (m = h > 0.5 ? d / (2 - i - c) : d / (i + c), i) {
case a:
u = (o - s) / d + (o < s ? 6 : 0);
break;
case o:
u = (s - a) / d + 2;
break;
case s:
u = (a - o) / d + 4;
break;
}
u /= 6;
}
return {
h: Math.round(u * 360),
s: Math.round(m * 100),
l: Math.round(h * 100)
};
}
}, $ = 1e3, K = $ * 60, j = K * 60, G = j * 24, J = {
/**
* Adds specified time units to a date.
* @param args - Object containing time units to add
* @param date - Date to add to (defaults to current date)
* @returns New date with added time units
* @throws {Error} If any time unit is negative
* @example
* ```typescript
* DateUtils.add({ days: 7, hours: 2 }); // Add 7 days and 2 hours to now
* DateUtils.add({ months: 1 }, new Date('2023-01-15')); // Add 1 month to specific date
* ```
*/
add(e, t = /* @__PURE__ */ new Date()) {
this.validateTimeUnits(e);
const r = this.convertToDate(t);
return this.adjustDate(e, r, 1);
},
/**
* Internal helper for modifying a date by adding or subtracting time units
* @param args - Object containing time units
* @param date - Date to modify
* @param direction - 1 for addition, -1 for subtraction
* @returns New date with modified time units
*/
adjustDate(e, t, r) {
const n = new Date(t.valueOf());
return e.years && n.setFullYear(n.getFullYear() + e.years * r), e.months && n.setMonth(n.getMonth() + e.months * r), e.weeks && n.setDate(n.getDate() + e.weeks * 7 * r), e.days && n.setDate(n.getDate() + e.days * r), e.hours && n.setHours(n.getHours() + e.hours * r), e.minutes && n.setMinutes(n.getMinutes() + e.minutes * r), e.seconds && n.setSeconds(n.getSeconds() + e.seconds * r), n;
},
/**
* Calculates age based on birth date
* @param date - Birth date to calculate age from
* @returns Age in years or null if date is invalid
*/
calculateAge(e) {
if (!e)
return null;
const t = /* @__PURE__ */ new Date(), r = e instanceof Date ? e : new Date(e);
let n = t.getFullYear() - r.getFullYear();
const a = t.getMonth() - r.getMonth(), o = t.getDate() - r.getDate();
return (a < 0 || a === 0 && o < 0) && n--, n;
},
/**
* Calculates the number of days between two dates
* @param first - First date
* @param second - Second date
* @returns Number of days between dates
*/
calculateDaysBetween(e, t) {
const r = this.convertToDate(e), n = this.convertToDate(t), a = Date.UTC(r.getFullYear(), r.getMonth(), r.getDate()), o = Date.UTC(n.getFullYear(), n.getMonth(), n.getDate());
return Math.abs(Math.floor((o - a) / G));
},
/**
* Compares two dates and returns the result based on order parameter
* @param a - First date to compare
* @param b - Second date to compare
* @param order - Sort order ('asc' or 'desc')
* @returns Comparison result (-1, 0, or 1)
*/
compareDates(e, t, r = "asc") {
const n = this.normalizeDate(e), a = this.normalizeDate(t);
if (!n && !a) return 0;
if (!n) return r === "asc" ? 1 : -1;
if (!a) return r === "asc" ? -1 : 1;
const o = n.getTime() - a.getTime();
return r === "asc" ? o : -o;
},
/**
* Converts various date formats to a Date object
* @param dateStr - Date to convert (Date, number, or string)
* @returns Date object
* @throws Error if date string is invalid
*/
convertToDate(e) {
if (e instanceof Date)
return new Date(e.valueOf());
if (typeof e == "number")
return e.toString().length === 10 ? new Date(e * $) : new Date(e);
if (typeof e == "string") {
const t = new Date(e);
if (isNaN(t.getTime()))
throw new Error(`Invalid date string: ${e}`);
return t;
}
throw new Error("Invalid date argument");
},
/**
* Copies time from one date to another
* @param fromDate - Source date to copy time from
* @param toDate - Target date to copy time to
* @returns New date with copied time
*/
copyTimeToDate(e, t) {
const r = this.convertToDate(e), n = this.convertToDate(t);
return n.setHours(
r.getHours(),
r.getMinutes(),
r.getSeconds(),
r.getMilliseconds()
), n;
},
/**
* Returns the age in years between two dates
* @param birthDate - Birth date
* @param referenceDate - Reference date (defaults to current date)
* @returns Age in years
*/
getAge(e, t = /* @__PURE__ */ new Date()) {
const r = this.convertToDate(e), n = this.convertToDate(t);
let a = n.getFullYear() - r.getFullYear();
const o = n.getMonth() - r.getMonth();
return (o < 0 || o === 0 && n.getDate() < r.getDate()) && a--, a;
},
/**
* Returns the number of days in a given month
* @param year - Year to check
* @param month - Month to check (0-11)
* @returns Number of days in the month
*/
getDaysInMonth(e, t) {
return new Date(e, t + 1, 0).getDate();
},
/**
* Returns the number of days between two dates, inclusive
* @param startDate - Start date
* @param endDate - End date
* @returns Number of days between dates, inclusive
*/
getDaysBetweenInclusive(e, t) {
return this.calculateDaysBetween(e, t) + 1;
},
/**
* Returns the first day of the month
* @param date - Date to get first day of month for (defaults to current date)
* @returns Date set to first day of month
*/
getFirstDayOfMonth(e = /* @__PURE__ */ new Date()) {
const t = this.convertToDate(e);
return t.setDate(1), this.getStartOfDay(t);
},
/**
* Returns the first day of the quarter
* @param date - Date to get first day of quarter for (defaults to current date)
* @returns Date set to first day of quarter
*/
getFirstDayOfQuarter(e = /* @__PURE__ */ new Date()) {
const t = this.convertToDate(e), r = this.getQuarter(t);
return t.setMonth((r - 1) * 3, 1), this.getStartOfDay(t);
},
/**
* Returns the first day of the week (Sunday)
* @param date - Date to get first day of week for (defaults to current date)
* @returns Date set to first day of week
*/
getFirstDayOfWeek(e = /* @__PURE__ */ new Date()) {
const t = this.convertToDate(e), r = t.getDay();
return t.setDate(t.getDate() - r), this.getStartOfDay(t);
},
/**
* Returns the first day of the year
* @param date - Date to get first day of year for (defaults to current date)
* @returns Date set to first day of year
*/
getFirstDayOfYear(e = /* @__PURE__ */ new Date()) {
const t = this.convertToDate(e);
return t.setMonth(0, 1), this.getStartOfDay(t);
},
/**
* Returns the end of the day (23:59:59.999)
* @param date - Date to get end of day for (defaults to current date)
* @returns Date set to end of day
*/
getEndOfDay(e) {
const t = this.convertToDate(e || /* @__PURE__ */ new Date());
return t.setHours(23, 59, 59, 999), t;
},
/**
* Returns the last day of the month
* @param date - Date to get last day of month for (defaults to current date)
* @returns Date set to last day of month
*/
getLastDayOfMonth(e = /* @__PURE__ */ new Date()) {
const t = this.convertToDate(e);
return t.setMonth(t.getMonth() + 1), t.setDate(0), this.getEndOfDay(t);
},
/**
* Returns the last day of the quarter
* @param date - Date to get last day of quarter for (defaults to current date)
* @returns Date set to last day of quarter
*/
getLastDayOfQuarter(e = /* @__PURE__ */ new Date()) {
const t = this.convertToDate(e), r = this.getQuarter(t);
return t.setMonth(r * 3, 0), this.getEndOfDay(t);
},
/**
* Returns the last day of the week (Saturday)
* @param date - Date to get last day of week for (defaults to current date)
* @returns Date set to last day of week
*/
getLastDayOfWeek(e = /* @__PURE__ */ new Date()) {
const t = this.convertToDate(e), r = t.getDay();
return t.setDate(t.getDate() + (6 - r)), this.getEndOfDay(t);
},
/**
* Returns the last day of the year
* @param date - Date to get last day of year for (defaults to current date)
* @returns Date set to last day of year
*/
getLastDayOfYear(e = /* @__PURE__ */ new Date()) {
const t = this.convertToDate(e);
return t.setMonth(11, 31), this.getEndOfDay(t);
},
/**
* Returns the quarter (1-4) of the year
* @param date - Date to get quarter for (defaults to current date)
* @returns Quarter number
*/
getQuarter(e = /* @__PURE__ */ new Date()) {
const t = this.convertToDate(e).getMonth();
return Math.floor(t / 3) + 1;
},
/**
* Returns the start of the day (00:00:00.000)
* @param date - Date to get start of day for (defaults to current date)
* @returns Date set to start of day
*/
getStartOfDay(e) {
const t = this.convertToDate(e || /* @__PURE__ */ new Date());
return t.setHours(0, 0, 0, 0), t;
},
/**
* Returns the week number of the year (1-53)
* @param date - Date to get week number for (defaults to current date)
* @returns Week number
*/
getWeekNumber(e = /* @__PURE__ */ new Date()) {
const t = this.convertToDate(e), r = new Date(t.getFullYear(), 0, 1), n = (t.getTime() - r.getTime()) / G;
return Math.ceil((n + r.getDay() + 1) / 7);
},
/**
* Checks if a date is after another date
* @param date1 - First date to compare
* @param date2 - Second date to compare
* @returns True if date1 is after date2
*/
isAfter(e, t) {
return this.convertToDate(e).valueOf() > this.convertToDate(t).valueOf();
},
/**
* Checks if a date is before another date
* @param date1 - First date to compare
* @param date2 - Second date to compare
* @returns True if date1 is before date2
*/
isBefore(e, t) {
return this.convertToDate(e).valueOf() < this.convertToDate(t).valueOf();
},
/**
* Checks if a date is in the past
* @param date - Date to check
* @returns True if date is in the past
*/
isDateInThePast(e) {
return this.convertToDate(e) <= /* @__PURE__ */ new Date();
},
/**
* Checks if two dates are the same day
* @param date1 - First date to compare
* @param date2 - Second date to compare
* @returns True if dates are the same day
*/
isSameDay(e, t) {
const r = this.convertToDate(e), n = this.convertToDate(t);
return r.getDate() === n.getDate() && r.getMonth() === n.getMonth() && r.getFullYear() === n.getFullYear();
},
/**
* Checks if two dates have the same time
* @param date1 - First date to compare
* @param date2 - Second date to compare
* @returns True if dates have the same time
*/
isSameTime(e, t) {
return this.convertToDate(e).valueOf() === this.convertToDate(t).valueOf();
},
/**
* Checks if a date is today
* @param date - Date to check
* @returns True if date is today
*/
isToday(e) {
const t = this.convertToDate(e);
return !t || t.toLocaleDateString() === (/* @__PURE__ */ new Date()).toLocaleDateString();
},
/**
* Checks if a date is tomorrow
* @param date - Date to check
* @returns True if date is tomorrow
*/
isTomorrow(e) {
const t = /* @__PURE__ */ new Date();
return t.setDate(t.getDate() + 1), this.isSameDay(e, t);
},
/**
* Checks if a date is valid
* @param date - Date to check
* @returns True if date is valid
*/
isValidDate(e) {
try {
return !Number.isNaN(this.convertToDate(e).getTime());
} catch {
return !1;
}
},
/**
* Checks if a date falls on a weekday
* @param date - Date to check
* @returns True if the date is a weekday
*/
isWeekday(e) {
return !this.isWeekend(e);
},
/**
* Checks if a date falls on a weekend
* @param date - Date to check
* @returns True if the date is a weekend
*/
isWeekend(e) {
const t = this.convertToDate(e).getUTCDay();
return t === 0 || t === 6;
},
/**
* Checks if a date is yesterday
* @param date - Date to check
* @returns True if date is yesterday
*/
isYesterday(e) {
const t = /* @__PURE__ */ new Date();
return t.setDate(t.getDate() - 1), this.isSameDay(e, t);
},
/**
* Normalizes a date to UTC midnight
* @param date - Date to normalize
* @returns Normalized date or null if invalid
*/
normalizeDate(e) {
if (!e) return null;
try {
if (typeof e == "string") {
const r = e.split("/");
if (r.length === 2 && r[0].length === 2 && r[1].length === 4) {
const n = parseInt(r[0], 10), a = parseInt(r[1], 10);
if (!isNaN(n) && !isNaN(a) && n >= 1 && n <= 12)
return new Date(Date.UTC(a, n - 1, 1));
}
}
const t = this.convertToDate(e);
if (!isNaN(t.valueOf()))
return new Date(
Date.UTC(
t.getUTCFullYear(),
t.getUTCMonth(),
t.getUTCDate()
)
);
} catch {
}
return null;
},
/**
* Subtracts specified time units from a date
* @param args - Object containing time units to subtract
* @param date - Date to subtract from (defaults to current date)
* @returns New date with subtracted time units
* @throws Error if any time unit is negative
*/
subtract(e, t = /* @__PURE__ */ new Date()) {
this.validateTimeUnits(e);
const r = this.convertToDate(t);
return this.adjustDate(e, r, -1);
},
/**
* Converts a date to a local ISO string
* @param date - Date to convert
* @returns Local ISO string representation
*/
toLocalISOString(e) {
const t = (r) => String(r).padStart(2, "0");
return `${e.getFullYear()}-${t(e.getMonth() + 1)}-${t(
e.getDate()
)}T${t(e.getHours())}:${t(e.getMinutes())}:${t(
e.getSeconds()
)}.${String(e.getMilliseconds()).padStart(3, "0")}`;
},
/**
* Validates time unit arguments
* @param args - Time unit arguments to validate
* @throws Error if any time unit is negative
*/
validateTimeUnits(e) {
for (const [t, r] of Object.entries(e))
if (r !== void 0 && r < 0)
throw new Error(
`Negative values are not allowed for time unit: ${t}`
);
}
}, Q = {
/**
* Adds a listener for online/offline status changes
* @param callback - Function to call when status changes
* @returns Function to remove the listener
*/
addOnlineStatusListener(e) {
const t = () => e(navigator.onLine);
return window.addEventListener("online", t), window.addEventListener("offline", t), () => {
window.removeEventListener("online", t), window.removeEventListener("offline", t);
};
},
/**
* Gets the battery level of the device
* @returns Promise resolving to battery level (0-1) or null if not available
*/
async getBatteryLevel() {
if (!("getBattery" in navigator)) return null;
try {
return (await navigator.getBattery()).level;
} catch {
return null;
}
},
/**
* Gets the connection type of the device
* @returns Connection type or null if not available
*/
getConnectionType() {
return "connection" in navigator && navigator.connection?.effectiveType || null;
},
/**
* Gets the device type (mobile, tablet, desktop)
* @returns Device type
*/
getDeviceType() {
const e = navigator.userAgent;
return /(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(e) ? "tablet" : /Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(
e
) ? "mobile" : "desktop";
},
/**
* Gets the number of CPU cores available
* @returns Number of CPU cores
*/
getHardwareConcurrency() {
return navigator.hardwareConcurrency || 1;
},
/**
* Gets the device language
* @returns Device language
*/
getLanguage() {
return navigator.language;
},
/**
* Gets the device memory in GB
* @returns Device memory or null if not available
*/
getMemory() {
return "deviceMemory" in navigator && navigator.deviceMemory || null;
},
/**
* Gets the device orientation
* @returns Device orientation
*/
getOrientation() {
return window.innerHeight > window.innerWidth ? "portrait" : "landscape";
},
/**
* Gets the device pixel ratio
* @returns Device pixel ratio
*/
getPixelRatio() {
return window.devicePixelRatio || 1;
},
/**
* Gets the device platform
* @returns Device platform
*/
getPlatform() {
return "userAgentData" in navigator ? navigator.userAgentData.platform || "" : navigator.userAgent;
},
/**
* Gets the screen dimensions
* @returns Object containing screen width and height
*/
getScreenDimensions() {
return {
width: window.screen.width,
height: window.screen.height
};
},
/**
* Gets the device vendor
* @returns Device vendor
*/
getVendor() {
return "userAgentData" in navigator ? navigator.userAgentData.brands[0]?.brand || "" : navigator.userAgent;
},
/**
* Checks if a string is a valid MAC address
* @param mac - MAC address to validate
* @returns True if valid, false ot