UNPKG

my-utils-kit

Version:

A lightweight and type-safe utility library for working with strings, objects, array Performance methods in TypeScript. Includes helpful methods for deep cloning, object transformations, safe access, query string handling, and more — designed for modern J

257 lines (209 loc) 6.05 kB
export function debounce<T extends (...args: any[]) => any>( fn: T, delay: number, immediate = false ): (...args: Parameters<T>) => void { let timeout: ReturnType<typeof setTimeout> | null; return function (this: ThisParameterType<T>, ...args: Parameters<T>): void { const context = this; const callNow = immediate && !timeout; if (timeout) clearTimeout(timeout); timeout = setTimeout(() => { timeout = null; if (!immediate) fn.apply(context, args); }, delay); if (callNow) fn.apply(context, args); }; } export function throttle<T extends (...args: any[]) => any>( fn: T, delay: number, immediate = false ): (...args: Parameters<T>) => void { let timer: ReturnType<typeof setTimeout> | null = null; let lastArgs: Parameters<T> | null = null; return function (this: ThisParameterType<T>, ...args: Parameters<T>) { const context = this; if (!timer) { if (immediate) { fn.apply(context, args); } else { lastArgs = args; } timer = setTimeout(() => { if (!immediate && lastArgs) { fn.apply(context, lastArgs); lastArgs = null; } timer = null; }, delay); } }; } export function memoize<T extends (...args: any[]) => any>(fn: T): T { const cache: Map<string, ReturnType<T>> = new Map(); return function (this: any, ...args: Parameters<T>): ReturnType<T> { const key = JSON.stringify(args); if (cache.has(key)) { console.log("Returning from cache:", key); return cache.get(key)!; } const result = fn.apply(this, args); cache.set(key, result); console.log("Calculating new result:", key); return result; } as T; } export const asyncMemoize = async <T extends (...args: any[]) => Promise<any>>( fn: T ): Promise<T> => { const cache = new Map<string, any>(); return async function (...args: Parameters<T>) { const key = JSON.stringify(args); if (cache.has(key)) { return cache.get(key); } const result = await fn(...args); cache.set(key, result); return result; } as T; }; export class OperationBatcher { private operations: (() => Promise<any>)[] = []; private executing = false; enqueueOperation(fn: () => Promise<any>) { this.operations.push(fn); if (!this.executing) { this.execute(); } } private async execute() { this.executing = true; while (this.operations.length > 0) { const operation = this.operations.shift(); if (operation) { try { await operation(); } catch (err) { console.error("Operation failed", err); } } } this.executing = false; } } export function runCallbackOnUserEvent( callback: () => void, options?: { events?: (keyof DocumentEventMap)[]; timeout?: number; } ): void { if (typeof callback !== "function") { throw new Error("Callback must be a function"); } const { events = ["mousemove", "scroll", "touchstart"], timeout } = options || {}; let executed = false; function runOnce(): void { if (executed) return; executed = true; for (const event of events) { document.removeEventListener(event, runOnce); } callback(); } for (const event of events) { document.addEventListener(event, runOnce, { once: true, passive: true }); } if (typeof timeout === "number" && timeout > 0) { setTimeout(runOnce, timeout); } } export function loadScriptOnUserEvent( src: string, options?: { events?: (keyof DocumentEventMap)[]; timeout?: number; } ): void { if (typeof src !== "string" || !src.trim()) { throw new Error("Script source must be a non-empty string"); } const { events = ["mousemove", "scroll", "touchstart"], timeout } = options || {}; let executed = false; function loadScript(): void { if (executed) return; executed = true; for (const event of events) { document.removeEventListener(event, loadScript); } const script = document.createElement("script"); script.src = src; script.async = true; document.head.appendChild(script); } for (const event of events) { document.addEventListener(event, loadScript, { once: true, passive: true }); } if (typeof timeout === "number" && timeout > 0) { setTimeout(loadScript, timeout); } } export function observeElementOnIntersect( selector: string, options: IntersectionObserverInit, callback: (entry: IntersectionObserverEntry) => void ): void { if (typeof selector !== "string") { throw new Error("Selector must be a string"); } const elements = document.querySelectorAll<HTMLElement>(selector); if (!elements.length) return; const observer = new IntersectionObserver((entries, obs) => { for (const entry of entries) { if (entry.isIntersecting) { callback(entry); obs.unobserve(entry.target); } } }, options); elements.forEach((el) => observer.observe(el)); } type BreakpointRange = { min: number; max: number; }; type Breakpoints = Record<string, BreakpointRange>; export function getScreenSize( ranges: Breakpoints = { xs: { min: 0, max: 480 }, sm: { min: 481, max: 640 }, md: { min: 641, max: 768 }, lg: { min: 769, max: 1024 }, xl: { min: 1025, max: 1280 }, "2xl": { min: 1281, max: 1536 }, "3xl": { min: 1537, max: Infinity } } ): string | undefined { const width = window.innerWidth; for (const [label, { min, max }] of Object.entries(ranges)) { if (width >= min && width <= max) { return label; } } return undefined; // fallback } export function watchScreenSize( callback: (size: string | undefined) => void, ranges?: Breakpoints ): () => void { function handler() { const size = getScreenSize(ranges || undefined); callback(size); } window.addEventListener('resize', handler); handler(); // Call immediately on setup return () => window.removeEventListener('resize', handler); }