@ledgerhq/lumen-utils-shared 0.0.17-1 → 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 CHANGED
@@ -5,4 +5,6 @@ export * from './lib/cn';
5
5
  export * from './lib/isTextChildren';
6
6
  export * from './lib/useMergeRef';
7
7
  export * from './lib/getObjectPath';
8
+ export * from './lib/throttle';
9
+ export * from './lib/debounce';
8
10
  //# sourceMappingURL=index.d.ts.map
@@ -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 h } from "react/jsx-runtime";
2
- import { createContext as x, useMemo as m, useContext as y } from "react";
3
- import { clsx as P } from "clsx";
4
- import { twMerge as S } from "tailwind-merge";
5
- function w(t) {
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 C = 48, A = 17, M = 2;
9
- function E(t) {
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
- A,
13
- C - n * M
12
+ N,
13
+ z - n * R
14
14
  );
15
15
  }
16
- function f(t, n = !0) {
16
+ function D(t, n = !0) {
17
17
  if (!t) return "";
18
- const i = t.includes("."), [o, r] = t.split("."), c = o.replace(/\B(?=(\d{3})+(?!\d))/g, " ");
19
- return i && n ? r ? `${c}.${r}` : `${c}.` : c;
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 T(t, n = {}) {
21
+ function H(t, n = {}) {
22
22
  const {
23
- allowDecimals: i = !0,
23
+ allowDecimals: l = !0,
24
24
  thousandsSeparator: o = !0,
25
25
  maxIntegerLength: r = 9,
26
26
  maxDecimalLength: c = 9
27
- } = n, a = t.replace(",", ".").replace(/[^\d.]/g, "");
28
- let e = a;
29
- if (e = e.replace(/^0+(?=\d)/, ""), !i)
30
- return e = e.replace(/\D/g, ""), r > 0 && e.length > r && (e = e.slice(0, r)), o ? f(e) : e;
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 l = e.indexOf(".");
33
- if (l !== -1) {
34
- let s = e.slice(0, l), u = e.slice(l + 1).replace(/\./g, "");
35
- r > 0 && s.length > r && (s = s.slice(0, r)), u = u.slice(0, c), s === "" && (s = "0");
36
- const d = a.endsWith(".") || e.endsWith(".");
37
- e = u.length > 0 ? `${s}.${u}` : d ? `${s}.` : s;
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 ? f(e) : e;
40
+ return o ? D(e) : e;
41
41
  }
42
- function _(t, n) {
43
- const i = x(n), o = ({ children: c, value: a }) => {
44
- const e = m(
45
- () => a,
46
- Object.values(a ?? {})
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__ */ h(i.Provider, { value: e, children: c });
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: a
53
+ contextRequired: i
54
54
  }) {
55
- const e = y(i);
55
+ const e = F(l);
56
56
  if (e)
57
57
  return e;
58
- if (a)
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 j(...t) {
67
- return S(P(t));
66
+ function K(...t) {
67
+ return j(_(t));
68
68
  }
69
- const p = (t) => typeof t == "string" || typeof t == "number", F = (t) => Array.isArray(t) ? t.every(p) : p(t);
70
- function g(t, n) {
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 $(...t) {
75
+ function w(...t) {
76
76
  const n = /* @__PURE__ */ new Map();
77
- return (i) => {
78
- if (i === null) {
77
+ return (l) => {
78
+ if (l === null) {
79
79
  t.forEach((o) => {
80
80
  const r = n.get(o);
81
- typeof r == "function" && r(), g(o, null);
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 = g(o, i);
86
+ const r = M(o, l);
87
87
  typeof r == "function" && n.set(o, r);
88
88
  });
89
89
  };
90
90
  }
91
- function I(...t) {
92
- return m(() => $(...t), t);
91
+ function Y(...t) {
92
+ return A(() => w(...t), t);
93
93
  }
94
- const z = (t, n) => {
95
- let i = t;
94
+ const tt = (t, n) => {
95
+ let l = t;
96
96
  for (const o of n) {
97
- if (i == null)
97
+ if (l == null)
98
98
  return;
99
- i = i[o];
99
+ l = l[o];
100
100
  }
101
- return i;
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
- g as assignRef,
105
- j as cn,
106
- _ as createSafeContext,
107
- f as formatThousands,
108
- E as getFontSize,
109
- z as getObjectPath,
110
- F as isTextChildren,
111
- $ as mergeRefs,
112
- T as textFormatter,
113
- w as toPascalCase,
114
- I as useMergedRef
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,2 @@
1
+ export * from './debounce';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -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,2 @@
1
+ export * from './throttle';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ledgerhq/lumen-utils-shared",
3
- "version": "0.0.17-1",
3
+ "version": "0.0.18",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
package/src/index.ts CHANGED
@@ -5,3 +5,5 @@ export * from './lib/cn';
5
5
  export * from './lib/isTextChildren';
6
6
  export * from './lib/useMergeRef';
7
7
  export * from './lib/getObjectPath';
8
+ export * from './lib/throttle';
9
+ export * from './lib/debounce';
@@ -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
+ }