UNPKG

claritykit-svelte

Version:

A comprehensive Svelte component library focused on accessibility, ADHD-optimized design, developer experience, and full SSR compatibility

175 lines (174 loc) 5.49 kB
/** * Debounce function to limit the rate at which a function can fire * @param func The function to debounce * @param wait The number of milliseconds to delay * @param immediate If true, trigger the function on the leading edge instead of the trailing * @returns The debounced function */ export function debounce(func, wait, immediate = false) { let timeout = null; let lastArgs = null; let lastThis = null; let lastCallTime = null; let result; const later = () => { const last = lastCallTime ? Date.now() - lastCallTime : 0; if (last < wait && last >= 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate && lastArgs && lastThis) { result = func.apply(lastThis, lastArgs); lastArgs = null; lastThis = null; } } }; return function debounced(...args) { lastArgs = args; lastThis = this; lastCallTime = Date.now(); const callNow = immediate && !timeout; if (!timeout) { timeout = setTimeout(later, wait); } if (callNow) { result = func.apply(this, args); } return result; }; } /** * Cancel a debounced function * @param debouncedFunc The debounced function to cancel */ export function cancelDebounce(debouncedFunc) { if (debouncedFunc && typeof debouncedFunc === 'function' && debouncedFunc.cancel) { debouncedFunc.cancel(); } } /** * Enhanced debounce with cancel and flush methods * @param func The function to debounce * @param wait The number of milliseconds to delay * @param options Options for the debounce behavior * @returns The debounced function with cancel and flush methods */ export function enhancedDebounce(func, wait, options = {}) { const { leading = false, trailing = true, maxWait } = options; let timeout = null; let maxTimeout = null; let lastArgs = null; let lastThis = null; let lastCallTime = null; let lastInvokeTime = 0; let result; const invokeFunc = (time) => { const args = lastArgs; const thisArg = lastThis; lastArgs = null; lastThis = null; lastInvokeTime = time; if (args && thisArg !== undefined) { result = func.apply(thisArg, args); } return result; }; const leadingEdge = (time) => { lastInvokeTime = time; timeout = setTimeout(timerExpired, wait); return leading ? invokeFunc(time) : result; }; const remainingWait = (time) => { const timeSinceLastCall = time - (lastCallTime || 0); const timeSinceLastInvoke = time - lastInvokeTime; const timeWaiting = wait - timeSinceLastCall; return maxWait !== undefined ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting; }; const shouldInvoke = (time) => { const timeSinceLastCall = time - (lastCallTime || 0); const timeSinceLastInvoke = time - lastInvokeTime; return (lastCallTime === null || timeSinceLastCall >= wait || timeSinceLastCall < 0 || (maxWait !== undefined && timeSinceLastInvoke >= maxWait)); }; const timerExpired = () => { const time = Date.now(); if (shouldInvoke(time)) { return trailingEdge(time); } timeout = setTimeout(timerExpired, remainingWait(time)); }; const trailingEdge = (time) => { timeout = null; if (trailing && lastArgs) { return invokeFunc(time); } lastArgs = null; lastThis = null; return result; }; const cancel = () => { if (timeout) { clearTimeout(timeout); timeout = null; } if (maxTimeout) { clearTimeout(maxTimeout); maxTimeout = null; } lastInvokeTime = 0; lastArgs = null; lastCallTime = null; lastThis = null; }; const flush = () => { return timeout === null ? result : trailingEdge(Date.now()); }; const pending = () => { return timeout !== null; }; function debounced(...args) { const time = Date.now(); const isInvoking = shouldInvoke(time); lastArgs = args; lastThis = this; lastCallTime = time; if (isInvoking) { if (timeout === null) { return leadingEdge(lastCallTime); } if (maxWait !== undefined) { timeout = setTimeout(timerExpired, wait); return invokeFunc(lastCallTime); } } if (timeout === null) { timeout = setTimeout(timerExpired, wait); } return result; } debounced.cancel = cancel; debounced.flush = flush; debounced.pending = pending; return debounced; } /** * Throttle function to limit the rate at which a function can fire * @param func The function to throttle * @param wait The number of milliseconds to throttle * @param options Options for the throttle behavior * @returns The throttled function */ export function throttle(func, wait, options = {}) { const { leading = true, trailing = true } = options; return enhancedDebounce(func, wait, { leading, trailing, maxWait: wait }); }