@ledgerhq/lumen-utils-shared 0.0.17 → 0.0.18
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/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +108 -61
- package/dist/lib/debounce/debounce.d.ts +57 -0
- package/dist/lib/debounce/debounce.d.ts.map +1 -0
- package/dist/lib/debounce/index.d.ts +2 -0
- package/dist/lib/debounce/index.d.ts.map +1 -0
- package/dist/lib/throttle/index.d.ts +2 -0
- package/dist/lib/throttle/index.d.ts.map +1 -0
- package/dist/lib/throttle/throttle.d.ts +41 -0
- package/dist/lib/throttle/throttle.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/lib/debounce/debounce.ts +216 -0
- package/src/lib/debounce/index.ts +1 -0
- package/src/lib/throttle/index.ts +1 -0
- package/src/lib/throttle/throttle.ts +56 -0
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { createContext as
|
|
3
|
-
import { clsx as
|
|
4
|
-
import { twMerge as
|
|
5
|
-
function
|
|
1
|
+
import { jsx as W } from "react/jsx-runtime";
|
|
2
|
+
import { createContext as k, useMemo as A, useContext as F } from "react";
|
|
3
|
+
import { clsx as _ } from "clsx";
|
|
4
|
+
import { twMerge as j } from "tailwind-merge";
|
|
5
|
+
function q(t) {
|
|
6
6
|
return t ? t.split(/[-_ ]+/).map((n) => n.charAt(0).toUpperCase() + n.slice(1).toLowerCase()).join("") : "";
|
|
7
7
|
}
|
|
8
|
-
const
|
|
9
|
-
function
|
|
8
|
+
const z = 48, N = 17, R = 2;
|
|
9
|
+
function G(t) {
|
|
10
10
|
const n = t.replace(/\D/g, "").length;
|
|
11
11
|
return Math.max(
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
N,
|
|
13
|
+
z - n * R
|
|
14
14
|
);
|
|
15
15
|
}
|
|
16
|
-
function
|
|
16
|
+
function D(t, n = !0) {
|
|
17
17
|
if (!t) return "";
|
|
18
|
-
const
|
|
19
|
-
return
|
|
18
|
+
const l = t.includes("."), [o, r] = t.split("."), c = o.replace(/\B(?=(\d{3})+(?!\d))/g, " ");
|
|
19
|
+
return l && n ? r ? `${c}.${r}` : `${c}.` : c;
|
|
20
20
|
}
|
|
21
|
-
function
|
|
21
|
+
function H(t, n = {}) {
|
|
22
22
|
const {
|
|
23
|
-
allowDecimals:
|
|
23
|
+
allowDecimals: l = !0,
|
|
24
24
|
thousandsSeparator: o = !0,
|
|
25
25
|
maxIntegerLength: r = 9,
|
|
26
26
|
maxDecimalLength: c = 9
|
|
27
|
-
} = n,
|
|
28
|
-
let e =
|
|
29
|
-
if (e = e.replace(/^0+(?=\d)/, ""), !
|
|
30
|
-
return e = e.replace(/\D/g, ""), r > 0 && e.length > r && (e = e.slice(0, r)), o ?
|
|
27
|
+
} = n, i = t.replace(",", ".").replace(/[^\d.]/g, "");
|
|
28
|
+
let e = i;
|
|
29
|
+
if (e = e.replace(/^0+(?=\d)/, ""), !l)
|
|
30
|
+
return e = e.replace(/\D/g, ""), r > 0 && e.length > r && (e = e.slice(0, r)), o ? D(e) : e;
|
|
31
31
|
e === "." && (e = "0.");
|
|
32
|
-
const
|
|
33
|
-
if (
|
|
34
|
-
let s = e.slice(0,
|
|
35
|
-
r > 0 && s.length > r && (s = s.slice(0, r)),
|
|
36
|
-
const
|
|
37
|
-
e =
|
|
32
|
+
const f = e.indexOf(".");
|
|
33
|
+
if (f !== -1) {
|
|
34
|
+
let s = e.slice(0, f), a = e.slice(f + 1).replace(/\./g, "");
|
|
35
|
+
r > 0 && s.length > r && (s = s.slice(0, r)), a = a.slice(0, c), s === "" && (s = "0");
|
|
36
|
+
const g = i.endsWith(".") || e.endsWith(".");
|
|
37
|
+
e = a.length > 0 ? `${s}.${a}` : g ? `${s}.` : s;
|
|
38
38
|
} else
|
|
39
39
|
r > 0 && e.length > r && (e = e.slice(0, r));
|
|
40
|
-
return o ?
|
|
40
|
+
return o ? D(e) : e;
|
|
41
41
|
}
|
|
42
|
-
function
|
|
43
|
-
const
|
|
44
|
-
const e =
|
|
45
|
-
() =>
|
|
46
|
-
Object.values(
|
|
42
|
+
function J(t, n) {
|
|
43
|
+
const l = k(n), o = ({ children: c, value: i }) => {
|
|
44
|
+
const e = A(
|
|
45
|
+
() => i,
|
|
46
|
+
Object.values(i ?? {})
|
|
47
47
|
);
|
|
48
|
-
return /* @__PURE__ */
|
|
48
|
+
return /* @__PURE__ */ W(l.Provider, { value: e, children: c });
|
|
49
49
|
};
|
|
50
50
|
o.displayName = t + "Provider";
|
|
51
51
|
function r({
|
|
52
52
|
consumerName: c,
|
|
53
|
-
contextRequired:
|
|
53
|
+
contextRequired: i
|
|
54
54
|
}) {
|
|
55
|
-
const e =
|
|
55
|
+
const e = F(l);
|
|
56
56
|
if (e)
|
|
57
57
|
return e;
|
|
58
|
-
if (
|
|
58
|
+
if (i)
|
|
59
59
|
throw new Error(
|
|
60
60
|
`${c} must be used within ${t}`
|
|
61
61
|
);
|
|
@@ -63,53 +63,100 @@ function _(t, n) {
|
|
|
63
63
|
}
|
|
64
64
|
return [o, r];
|
|
65
65
|
}
|
|
66
|
-
function
|
|
67
|
-
return
|
|
66
|
+
function K(...t) {
|
|
67
|
+
return j(_(t));
|
|
68
68
|
}
|
|
69
|
-
const
|
|
70
|
-
function
|
|
69
|
+
const I = (t) => typeof t == "string" || typeof t == "number", Q = (t) => Array.isArray(t) ? t.every(I) : I(t);
|
|
70
|
+
function M(t, n) {
|
|
71
71
|
if (typeof t == "function")
|
|
72
72
|
return t(n);
|
|
73
73
|
typeof t == "object" && t !== null && "current" in t && (t.current = n);
|
|
74
74
|
}
|
|
75
|
-
function
|
|
75
|
+
function w(...t) {
|
|
76
76
|
const n = /* @__PURE__ */ new Map();
|
|
77
|
-
return (
|
|
78
|
-
if (
|
|
77
|
+
return (l) => {
|
|
78
|
+
if (l === null) {
|
|
79
79
|
t.forEach((o) => {
|
|
80
80
|
const r = n.get(o);
|
|
81
|
-
typeof r == "function" && r(),
|
|
81
|
+
typeof r == "function" && r(), M(o, null);
|
|
82
82
|
}), n.clear();
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
85
|
t.forEach((o) => {
|
|
86
|
-
const r =
|
|
86
|
+
const r = M(o, l);
|
|
87
87
|
typeof r == "function" && n.set(o, r);
|
|
88
88
|
});
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
|
-
function
|
|
92
|
-
return
|
|
91
|
+
function Y(...t) {
|
|
92
|
+
return A(() => w(...t), t);
|
|
93
93
|
}
|
|
94
|
-
const
|
|
95
|
-
let
|
|
94
|
+
const tt = (t, n) => {
|
|
95
|
+
let l = t;
|
|
96
96
|
for (const o of n) {
|
|
97
|
-
if (
|
|
97
|
+
if (l == null)
|
|
98
98
|
return;
|
|
99
|
-
|
|
99
|
+
l = l[o];
|
|
100
100
|
}
|
|
101
|
-
return
|
|
101
|
+
return l;
|
|
102
102
|
};
|
|
103
|
+
function V(t, n, l = {}) {
|
|
104
|
+
const { leading: o = !1, trailing: r = !0, maxWait: c } = l;
|
|
105
|
+
let i = null, e = null, f = null, s = 0, a = null, g = null, d;
|
|
106
|
+
const x = c !== void 0, S = x ? Math.max(c, n) : 0, T = (u) => {
|
|
107
|
+
s = u;
|
|
108
|
+
const m = a, h = g;
|
|
109
|
+
return a = null, g = null, d = t.apply(h, m), d;
|
|
110
|
+
}, E = (u) => {
|
|
111
|
+
const m = f ? u - f : 0;
|
|
112
|
+
return n - m;
|
|
113
|
+
}, v = (u) => {
|
|
114
|
+
const m = f ? u - f : n, h = u - s;
|
|
115
|
+
return f === null || m >= n || m < 0 || x && h >= S;
|
|
116
|
+
}, C = (u) => (i = null, r && a ? T(u) : (a = null, g = null, d)), p = () => {
|
|
117
|
+
const u = Date.now();
|
|
118
|
+
if (v(u)) {
|
|
119
|
+
C(u);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
i = setTimeout(p, E(u));
|
|
123
|
+
}, P = () => {
|
|
124
|
+
const u = Date.now();
|
|
125
|
+
e = null, a && (T(u), i !== null && (clearTimeout(i), i = setTimeout(p, n)));
|
|
126
|
+
}, b = (u) => (s = u, i = setTimeout(p, n), x && (e = setTimeout(P, S)), o ? T(u) : d), $ = () => {
|
|
127
|
+
i !== null && clearTimeout(i), e !== null && clearTimeout(e), s = 0, a = null, g = null, f = null, i = null, e = null;
|
|
128
|
+
}, L = () => i === null && e === null ? d : C(Date.now()), O = () => i !== null || e !== null, y = function(...u) {
|
|
129
|
+
const m = Date.now(), h = v(m);
|
|
130
|
+
if (a = u, g = this, f = m, h) {
|
|
131
|
+
if (i === null)
|
|
132
|
+
return b(m);
|
|
133
|
+
if (x)
|
|
134
|
+
return clearTimeout(i), i = setTimeout(p, n), T(m);
|
|
135
|
+
}
|
|
136
|
+
return i === null && (i = setTimeout(p, n)), d;
|
|
137
|
+
};
|
|
138
|
+
return y.cancel = $, y.flush = L, y.pending = O, y;
|
|
139
|
+
}
|
|
140
|
+
function et(t, n, l = {}) {
|
|
141
|
+
const { leading: o = !0, trailing: r = !0 } = l;
|
|
142
|
+
return V(t, n, {
|
|
143
|
+
leading: o,
|
|
144
|
+
trailing: r,
|
|
145
|
+
maxWait: n
|
|
146
|
+
});
|
|
147
|
+
}
|
|
103
148
|
export {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
149
|
+
M as assignRef,
|
|
150
|
+
K as cn,
|
|
151
|
+
J as createSafeContext,
|
|
152
|
+
V as debounce,
|
|
153
|
+
D as formatThousands,
|
|
154
|
+
G as getFontSize,
|
|
155
|
+
tt as getObjectPath,
|
|
156
|
+
Q as isTextChildren,
|
|
157
|
+
w as mergeRefs,
|
|
158
|
+
H as textFormatter,
|
|
159
|
+
et as throttle,
|
|
160
|
+
q as toPascalCase,
|
|
161
|
+
Y as useMergedRef
|
|
115
162
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export type DebounceOptions = {
|
|
2
|
+
/**
|
|
3
|
+
* Invoke on the leading edge of the timeout.
|
|
4
|
+
* @default false
|
|
5
|
+
*/
|
|
6
|
+
leading?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Invoke on the trailing edge of the timeout.
|
|
9
|
+
* @default true
|
|
10
|
+
*/
|
|
11
|
+
trailing?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Maximum time func is allowed to be delayed before it's invoked.
|
|
14
|
+
*/
|
|
15
|
+
maxWait?: number;
|
|
16
|
+
};
|
|
17
|
+
export type DebouncedFunction<T extends (...args: never[]) => unknown> = {
|
|
18
|
+
(...args: Parameters<T>): ReturnType<T> | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Cancels any pending invocation.
|
|
21
|
+
*/
|
|
22
|
+
cancel: () => void;
|
|
23
|
+
/**
|
|
24
|
+
* Immediately invokes any pending invocation.
|
|
25
|
+
*/
|
|
26
|
+
flush: () => ReturnType<T> | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Returns true if there is a pending invocation.
|
|
29
|
+
*/
|
|
30
|
+
pending: () => boolean;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Creates a debounced function that delays invoking `func` until after `wait`
|
|
34
|
+
* milliseconds have elapsed since the last time the debounced function was
|
|
35
|
+
* invoked. The debounced function comes with `cancel`, `flush`, and `pending`
|
|
36
|
+
* methods.
|
|
37
|
+
*
|
|
38
|
+
* By default, only `trailing` is enabled:
|
|
39
|
+
* - `leading`: Invoke immediately on the first call
|
|
40
|
+
* - `trailing`: Invoke after `wait` ms of no calls
|
|
41
|
+
* - `maxWait`: Maximum time to wait before forcing invocation
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* // Debounce search input to avoid excessive API calls
|
|
45
|
+
* const debouncedSearch = debounce(search, 300);
|
|
46
|
+
* input.addEventListener('input', debouncedSearch);
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* // Invoke immediately, then debounce subsequent calls
|
|
50
|
+
* const debounced = debounce(fn, 200, { leading: true });
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* // Guarantee invocation at least every 1000ms
|
|
54
|
+
* const debounced = debounce(fn, 200, { maxWait: 1000 });
|
|
55
|
+
*/
|
|
56
|
+
export declare function debounce<T extends (...args: never[]) => unknown>(func: T, wait: number, options?: DebounceOptions): DebouncedFunction<T>;
|
|
57
|
+
//# sourceMappingURL=debounce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debounce.d.ts","sourceRoot":"","sources":["../../../src/lib/debounce/debounce.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,IAAI;IACvE,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IACpD;;OAEG;IACH,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB;;OAEG;IACH,KAAK,EAAE,MAAM,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IACvC;;OAEG;IACH,OAAO,EAAE,MAAM,OAAO,CAAC;CACxB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,EAC9D,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,eAAoB,GAC5B,iBAAiB,CAAC,CAAC,CAAC,CA0JtB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/debounce/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/throttle/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { DebouncedFunction } from '../debounce';
|
|
2
|
+
export type ThrottleOptions = {
|
|
3
|
+
/**
|
|
4
|
+
* Invoke on the leading edge of the timeout.
|
|
5
|
+
* @default true
|
|
6
|
+
*/
|
|
7
|
+
leading?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Invoke on the trailing edge of the timeout.
|
|
10
|
+
* @default true
|
|
11
|
+
*/
|
|
12
|
+
trailing?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export type ThrottledFunction<T extends (...args: never[]) => unknown> = DebouncedFunction<T>;
|
|
15
|
+
/**
|
|
16
|
+
* Creates a throttled function that only invokes `func` at most once per
|
|
17
|
+
* every `wait` milliseconds. The throttled function comes with `cancel`,
|
|
18
|
+
* `flush`, and `pending` methods.
|
|
19
|
+
*
|
|
20
|
+
* Throttle is implemented using debounce with `maxWait` equal to `wait`,
|
|
21
|
+
* which guarantees the function is invoked at regular intervals.
|
|
22
|
+
*
|
|
23
|
+
* By default, both `leading` and `trailing` are enabled:
|
|
24
|
+
* - `leading`: Invoke immediately on the first call
|
|
25
|
+
* - `trailing`: Invoke after `wait` ms if called again during the wait period
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // Throttle scroll handler to run at most once every 100ms
|
|
29
|
+
* const throttledScroll = throttle(handleScroll, 100);
|
|
30
|
+
* window.addEventListener('scroll', throttledScroll);
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* // Only invoke on trailing edge (like debounce but with max frequency)
|
|
34
|
+
* const throttled = throttle(fn, 200, { leading: false });
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* // Only invoke on leading edge (ignore calls during wait period)
|
|
38
|
+
* const throttled = throttle(fn, 200, { trailing: false });
|
|
39
|
+
*/
|
|
40
|
+
export declare function throttle<T extends (...args: never[]) => unknown>(func: T, wait: number, options?: ThrottleOptions): ThrottledFunction<T>;
|
|
41
|
+
//# sourceMappingURL=throttle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle.d.ts","sourceRoot":"","sources":["../../../src/lib/throttle/throttle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE1D,MAAM,MAAM,eAAe,GAAG;IAC5B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,IACnE,iBAAiB,CAAC,CAAC,CAAC,CAAC;AAEvB;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,EAC9D,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,eAAoB,GAC5B,iBAAiB,CAAC,CAAC,CAAC,CAQtB"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
export type DebounceOptions = {
|
|
2
|
+
/**
|
|
3
|
+
* Invoke on the leading edge of the timeout.
|
|
4
|
+
* @default false
|
|
5
|
+
*/
|
|
6
|
+
leading?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Invoke on the trailing edge of the timeout.
|
|
9
|
+
* @default true
|
|
10
|
+
*/
|
|
11
|
+
trailing?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Maximum time func is allowed to be delayed before it's invoked.
|
|
14
|
+
*/
|
|
15
|
+
maxWait?: number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type DebouncedFunction<T extends (...args: never[]) => unknown> = {
|
|
19
|
+
(...args: Parameters<T>): ReturnType<T> | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Cancels any pending invocation.
|
|
22
|
+
*/
|
|
23
|
+
cancel: () => void;
|
|
24
|
+
/**
|
|
25
|
+
* Immediately invokes any pending invocation.
|
|
26
|
+
*/
|
|
27
|
+
flush: () => ReturnType<T> | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Returns true if there is a pending invocation.
|
|
30
|
+
*/
|
|
31
|
+
pending: () => boolean;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Creates a debounced function that delays invoking `func` until after `wait`
|
|
36
|
+
* milliseconds have elapsed since the last time the debounced function was
|
|
37
|
+
* invoked. The debounced function comes with `cancel`, `flush`, and `pending`
|
|
38
|
+
* methods.
|
|
39
|
+
*
|
|
40
|
+
* By default, only `trailing` is enabled:
|
|
41
|
+
* - `leading`: Invoke immediately on the first call
|
|
42
|
+
* - `trailing`: Invoke after `wait` ms of no calls
|
|
43
|
+
* - `maxWait`: Maximum time to wait before forcing invocation
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* // Debounce search input to avoid excessive API calls
|
|
47
|
+
* const debouncedSearch = debounce(search, 300);
|
|
48
|
+
* input.addEventListener('input', debouncedSearch);
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* // Invoke immediately, then debounce subsequent calls
|
|
52
|
+
* const debounced = debounce(fn, 200, { leading: true });
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* // Guarantee invocation at least every 1000ms
|
|
56
|
+
* const debounced = debounce(fn, 200, { maxWait: 1000 });
|
|
57
|
+
*/
|
|
58
|
+
export function debounce<T extends (...args: never[]) => unknown>(
|
|
59
|
+
func: T,
|
|
60
|
+
wait: number,
|
|
61
|
+
options: DebounceOptions = {},
|
|
62
|
+
): DebouncedFunction<T> {
|
|
63
|
+
const { leading = false, trailing = true, maxWait } = options;
|
|
64
|
+
|
|
65
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
66
|
+
let maxTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
67
|
+
let lastCallTime: number | null = null;
|
|
68
|
+
let lastInvokeTime = 0;
|
|
69
|
+
let lastArgs: Parameters<T> | null = null;
|
|
70
|
+
let lastThis: unknown = null;
|
|
71
|
+
let result: ReturnType<T> | undefined;
|
|
72
|
+
|
|
73
|
+
const hasMaxWait = maxWait !== undefined;
|
|
74
|
+
const maxWaitMs = hasMaxWait ? Math.max(maxWait, wait) : 0;
|
|
75
|
+
|
|
76
|
+
const invokeFunc = (time: number): ReturnType<T> => {
|
|
77
|
+
lastInvokeTime = time;
|
|
78
|
+
const args = lastArgs!;
|
|
79
|
+
const thisArg = lastThis;
|
|
80
|
+
lastArgs = null;
|
|
81
|
+
lastThis = null;
|
|
82
|
+
result = func.apply(thisArg, args) as ReturnType<T>;
|
|
83
|
+
return result;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const remainingWait = (time: number): number => {
|
|
87
|
+
const timeSinceLastCall = lastCallTime ? time - lastCallTime : 0;
|
|
88
|
+
return wait - timeSinceLastCall;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const shouldInvoke = (time: number): boolean => {
|
|
92
|
+
const timeSinceLastCall = lastCallTime ? time - lastCallTime : wait;
|
|
93
|
+
const timeSinceLastInvoke = time - lastInvokeTime;
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
lastCallTime === null ||
|
|
97
|
+
timeSinceLastCall >= wait ||
|
|
98
|
+
timeSinceLastCall < 0 ||
|
|
99
|
+
(hasMaxWait && timeSinceLastInvoke >= maxWaitMs)
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const trailingEdge = (time: number): ReturnType<T> | undefined => {
|
|
104
|
+
timeoutId = null;
|
|
105
|
+
|
|
106
|
+
if (trailing && lastArgs) {
|
|
107
|
+
return invokeFunc(time);
|
|
108
|
+
}
|
|
109
|
+
lastArgs = null;
|
|
110
|
+
lastThis = null;
|
|
111
|
+
return result;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const timerExpired = (): void => {
|
|
115
|
+
const time = Date.now();
|
|
116
|
+
|
|
117
|
+
if (shouldInvoke(time)) {
|
|
118
|
+
trailingEdge(time);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Restart the timer with remaining wait
|
|
123
|
+
timeoutId = setTimeout(timerExpired, remainingWait(time));
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const maxTimerExpired = (): void => {
|
|
127
|
+
const time = Date.now();
|
|
128
|
+
maxTimeoutId = null;
|
|
129
|
+
|
|
130
|
+
if (lastArgs) {
|
|
131
|
+
invokeFunc(time);
|
|
132
|
+
// Reset the regular timer since we just invoked
|
|
133
|
+
if (timeoutId !== null) {
|
|
134
|
+
clearTimeout(timeoutId);
|
|
135
|
+
timeoutId = setTimeout(timerExpired, wait);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const leadingEdge = (time: number): ReturnType<T> | undefined => {
|
|
141
|
+
lastInvokeTime = time;
|
|
142
|
+
timeoutId = setTimeout(timerExpired, wait);
|
|
143
|
+
|
|
144
|
+
if (hasMaxWait) {
|
|
145
|
+
maxTimeoutId = setTimeout(maxTimerExpired, maxWaitMs);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (leading) {
|
|
149
|
+
return invokeFunc(time);
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const cancel = (): void => {
|
|
155
|
+
if (timeoutId !== null) {
|
|
156
|
+
clearTimeout(timeoutId);
|
|
157
|
+
}
|
|
158
|
+
if (maxTimeoutId !== null) {
|
|
159
|
+
clearTimeout(maxTimeoutId);
|
|
160
|
+
}
|
|
161
|
+
lastInvokeTime = 0;
|
|
162
|
+
lastArgs = null;
|
|
163
|
+
lastThis = null;
|
|
164
|
+
lastCallTime = null;
|
|
165
|
+
timeoutId = null;
|
|
166
|
+
maxTimeoutId = null;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const flush = (): ReturnType<T> | undefined => {
|
|
170
|
+
if (timeoutId === null && maxTimeoutId === null) {
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
return trailingEdge(Date.now());
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const pending = (): boolean => {
|
|
177
|
+
return timeoutId !== null || maxTimeoutId !== null;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const debounced = function (
|
|
181
|
+
this: unknown,
|
|
182
|
+
...args: Parameters<T>
|
|
183
|
+
): ReturnType<T> | undefined {
|
|
184
|
+
const time = Date.now();
|
|
185
|
+
const isInvoking = shouldInvoke(time);
|
|
186
|
+
|
|
187
|
+
lastArgs = args;
|
|
188
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
189
|
+
lastThis = this;
|
|
190
|
+
lastCallTime = time;
|
|
191
|
+
|
|
192
|
+
if (isInvoking) {
|
|
193
|
+
if (timeoutId === null) {
|
|
194
|
+
return leadingEdge(time);
|
|
195
|
+
}
|
|
196
|
+
if (hasMaxWait) {
|
|
197
|
+
// Reset the timer on each call when maxWait is set
|
|
198
|
+
clearTimeout(timeoutId);
|
|
199
|
+
timeoutId = setTimeout(timerExpired, wait);
|
|
200
|
+
return invokeFunc(time);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (timeoutId === null) {
|
|
205
|
+
timeoutId = setTimeout(timerExpired, wait);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return result;
|
|
209
|
+
} as DebouncedFunction<T>;
|
|
210
|
+
|
|
211
|
+
debounced.cancel = cancel;
|
|
212
|
+
debounced.flush = flush;
|
|
213
|
+
debounced.pending = pending;
|
|
214
|
+
|
|
215
|
+
return debounced;
|
|
216
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './debounce';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './throttle';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { debounce, DebouncedFunction } from '../debounce';
|
|
2
|
+
|
|
3
|
+
export type ThrottleOptions = {
|
|
4
|
+
/**
|
|
5
|
+
* Invoke on the leading edge of the timeout.
|
|
6
|
+
* @default true
|
|
7
|
+
*/
|
|
8
|
+
leading?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Invoke on the trailing edge of the timeout.
|
|
11
|
+
* @default true
|
|
12
|
+
*/
|
|
13
|
+
trailing?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type ThrottledFunction<T extends (...args: never[]) => unknown> =
|
|
17
|
+
DebouncedFunction<T>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates a throttled function that only invokes `func` at most once per
|
|
21
|
+
* every `wait` milliseconds. The throttled function comes with `cancel`,
|
|
22
|
+
* `flush`, and `pending` methods.
|
|
23
|
+
*
|
|
24
|
+
* Throttle is implemented using debounce with `maxWait` equal to `wait`,
|
|
25
|
+
* which guarantees the function is invoked at regular intervals.
|
|
26
|
+
*
|
|
27
|
+
* By default, both `leading` and `trailing` are enabled:
|
|
28
|
+
* - `leading`: Invoke immediately on the first call
|
|
29
|
+
* - `trailing`: Invoke after `wait` ms if called again during the wait period
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Throttle scroll handler to run at most once every 100ms
|
|
33
|
+
* const throttledScroll = throttle(handleScroll, 100);
|
|
34
|
+
* window.addEventListener('scroll', throttledScroll);
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* // Only invoke on trailing edge (like debounce but with max frequency)
|
|
38
|
+
* const throttled = throttle(fn, 200, { leading: false });
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // Only invoke on leading edge (ignore calls during wait period)
|
|
42
|
+
* const throttled = throttle(fn, 200, { trailing: false });
|
|
43
|
+
*/
|
|
44
|
+
export function throttle<T extends (...args: never[]) => unknown>(
|
|
45
|
+
func: T,
|
|
46
|
+
wait: number,
|
|
47
|
+
options: ThrottleOptions = {},
|
|
48
|
+
): ThrottledFunction<T> {
|
|
49
|
+
const { leading = true, trailing = true } = options;
|
|
50
|
+
|
|
51
|
+
return debounce(func, wait, {
|
|
52
|
+
leading,
|
|
53
|
+
trailing,
|
|
54
|
+
maxWait: wait,
|
|
55
|
+
});
|
|
56
|
+
}
|