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
JavaScript
/**
* 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
});
}