@vnejs/helpers.debounce 0.2.1
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 +9 -0
- package/dist/index.js +59 -0
- package/package.json +28 -0
- package/src/index.ts +74 -0
- package/src/tests/debounce.test.ts +60 -0
- package/tsconfig.json +15 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type DebounceFn = (...args: never[]) => unknown;
|
|
2
|
+
export type DebounceOptions<T extends DebounceFn> = {
|
|
3
|
+
fn: T;
|
|
4
|
+
wait?: number;
|
|
5
|
+
maxWait?: number;
|
|
6
|
+
leading?: boolean;
|
|
7
|
+
trailing?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare const debounce: <T extends DebounceFn>({ fn, wait, maxWait, leading, trailing, }: DebounceOptions<T>) => ((...args: Parameters<T>) => void);
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export const debounce = ({ fn, wait = 0, maxWait, leading = false, trailing = true, }) => {
|
|
2
|
+
let waitTimer;
|
|
3
|
+
let maxTimer;
|
|
4
|
+
let lastArgs;
|
|
5
|
+
let isPending = false;
|
|
6
|
+
const clearTimers = () => {
|
|
7
|
+
if (waitTimer !== undefined)
|
|
8
|
+
clearTimeout(waitTimer);
|
|
9
|
+
if (maxTimer !== undefined)
|
|
10
|
+
clearTimeout(maxTimer);
|
|
11
|
+
waitTimer = undefined;
|
|
12
|
+
maxTimer = undefined;
|
|
13
|
+
};
|
|
14
|
+
const invoke = () => {
|
|
15
|
+
const args = lastArgs;
|
|
16
|
+
lastArgs = undefined;
|
|
17
|
+
isPending = false;
|
|
18
|
+
clearTimers();
|
|
19
|
+
if (args)
|
|
20
|
+
fn(...args);
|
|
21
|
+
};
|
|
22
|
+
return (...args) => {
|
|
23
|
+
const isLeadingCall = leading && !isPending;
|
|
24
|
+
lastArgs = args;
|
|
25
|
+
isPending = true;
|
|
26
|
+
if (isLeadingCall) {
|
|
27
|
+
fn(...args);
|
|
28
|
+
lastArgs = undefined;
|
|
29
|
+
}
|
|
30
|
+
if (waitTimer !== undefined)
|
|
31
|
+
clearTimeout(waitTimer);
|
|
32
|
+
waitTimer = setTimeout(() => {
|
|
33
|
+
waitTimer = undefined;
|
|
34
|
+
if (trailing && lastArgs)
|
|
35
|
+
invoke();
|
|
36
|
+
else {
|
|
37
|
+
isPending = false;
|
|
38
|
+
lastArgs = undefined;
|
|
39
|
+
if (maxTimer !== undefined)
|
|
40
|
+
clearTimeout(maxTimer);
|
|
41
|
+
maxTimer = undefined;
|
|
42
|
+
}
|
|
43
|
+
}, wait);
|
|
44
|
+
if (maxWait !== undefined && maxTimer === undefined) {
|
|
45
|
+
maxTimer = setTimeout(() => {
|
|
46
|
+
maxTimer = undefined;
|
|
47
|
+
if (trailing && lastArgs)
|
|
48
|
+
invoke();
|
|
49
|
+
else {
|
|
50
|
+
isPending = false;
|
|
51
|
+
lastArgs = undefined;
|
|
52
|
+
if (waitTimer !== undefined)
|
|
53
|
+
clearTimeout(waitTimer);
|
|
54
|
+
waitTimer = undefined;
|
|
55
|
+
}
|
|
56
|
+
}, maxWait);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vnejs/helpers.debounce",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"src",
|
|
10
|
+
"tsconfig.json"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "npx @vnejs/monorepo test",
|
|
14
|
+
"build": "npx @vnejs/monorepo package",
|
|
15
|
+
"publish:major:plugin": "npm run publish:major",
|
|
16
|
+
"publish:minor:plugin": "npm run publish:minor",
|
|
17
|
+
"publish:patch:plugin": "npm run publish:patch",
|
|
18
|
+
"publish:major": "npx @vnejs/monorepo publish major --access public",
|
|
19
|
+
"publish:minor": "npx @vnejs/monorepo publish minor --access public",
|
|
20
|
+
"publish:patch": "npx @vnejs/monorepo publish patch --access public"
|
|
21
|
+
},
|
|
22
|
+
"author": "",
|
|
23
|
+
"license": "ISC",
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@vnejs/configs.ts-common": "~0.2.0",
|
|
26
|
+
"@vnejs/configs.vitest": "~0.2.0"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export type DebounceFn = (...args: never[]) => unknown;
|
|
2
|
+
|
|
3
|
+
export type DebounceOptions<T extends DebounceFn> = {
|
|
4
|
+
fn: T;
|
|
5
|
+
wait?: number;
|
|
6
|
+
maxWait?: number;
|
|
7
|
+
leading?: boolean;
|
|
8
|
+
trailing?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const debounce = <T extends DebounceFn>({
|
|
12
|
+
fn,
|
|
13
|
+
wait = 0,
|
|
14
|
+
maxWait,
|
|
15
|
+
leading = false,
|
|
16
|
+
trailing = true,
|
|
17
|
+
}: DebounceOptions<T>): ((...args: Parameters<T>) => void) => {
|
|
18
|
+
let waitTimer: ReturnType<typeof setTimeout> | undefined;
|
|
19
|
+
let maxTimer: ReturnType<typeof setTimeout> | undefined;
|
|
20
|
+
let lastArgs: Parameters<T> | undefined;
|
|
21
|
+
let isPending = false;
|
|
22
|
+
|
|
23
|
+
const clearTimers = () => {
|
|
24
|
+
if (waitTimer !== undefined) clearTimeout(waitTimer);
|
|
25
|
+
if (maxTimer !== undefined) clearTimeout(maxTimer);
|
|
26
|
+
waitTimer = undefined;
|
|
27
|
+
maxTimer = undefined;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const invoke = () => {
|
|
31
|
+
const args = lastArgs;
|
|
32
|
+
lastArgs = undefined;
|
|
33
|
+
isPending = false;
|
|
34
|
+
clearTimers();
|
|
35
|
+
if (args) fn(...args);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return (...args: Parameters<T>) => {
|
|
39
|
+
const isLeadingCall = leading && !isPending;
|
|
40
|
+
|
|
41
|
+
lastArgs = args;
|
|
42
|
+
isPending = true;
|
|
43
|
+
|
|
44
|
+
if (isLeadingCall) {
|
|
45
|
+
fn(...args);
|
|
46
|
+
lastArgs = undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (waitTimer !== undefined) clearTimeout(waitTimer);
|
|
50
|
+
waitTimer = setTimeout(() => {
|
|
51
|
+
waitTimer = undefined;
|
|
52
|
+
if (trailing && lastArgs) invoke();
|
|
53
|
+
else {
|
|
54
|
+
isPending = false;
|
|
55
|
+
lastArgs = undefined;
|
|
56
|
+
if (maxTimer !== undefined) clearTimeout(maxTimer);
|
|
57
|
+
maxTimer = undefined;
|
|
58
|
+
}
|
|
59
|
+
}, wait);
|
|
60
|
+
|
|
61
|
+
if (maxWait !== undefined && maxTimer === undefined) {
|
|
62
|
+
maxTimer = setTimeout(() => {
|
|
63
|
+
maxTimer = undefined;
|
|
64
|
+
if (trailing && lastArgs) invoke();
|
|
65
|
+
else {
|
|
66
|
+
isPending = false;
|
|
67
|
+
lastArgs = undefined;
|
|
68
|
+
if (waitTimer !== undefined) clearTimeout(waitTimer);
|
|
69
|
+
waitTimer = undefined;
|
|
70
|
+
}
|
|
71
|
+
}, maxWait);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { debounce } from "../index.js";
|
|
4
|
+
|
|
5
|
+
describe("debounce", () => {
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
vi.useRealTimers();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("trailing call fires after wait", () => {
|
|
11
|
+
vi.useFakeTimers();
|
|
12
|
+
const fn = vi.fn();
|
|
13
|
+
const debounced = debounce({ fn, wait: 100 });
|
|
14
|
+
|
|
15
|
+
debounced();
|
|
16
|
+
debounced();
|
|
17
|
+
expect(fn).not.toHaveBeenCalled();
|
|
18
|
+
|
|
19
|
+
vi.advanceTimersByTime(100);
|
|
20
|
+
expect(fn).toHaveBeenCalledOnce();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("leading call fires immediately", () => {
|
|
24
|
+
vi.useFakeTimers();
|
|
25
|
+
const fn = vi.fn();
|
|
26
|
+
const debounced = debounce({ fn, wait: 100, leading: true, trailing: false });
|
|
27
|
+
|
|
28
|
+
debounced();
|
|
29
|
+
expect(fn).toHaveBeenCalledOnce();
|
|
30
|
+
|
|
31
|
+
debounced();
|
|
32
|
+
vi.advanceTimersByTime(100);
|
|
33
|
+
expect(fn).toHaveBeenCalledOnce();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("leading and trailing fire for a burst", () => {
|
|
37
|
+
vi.useFakeTimers();
|
|
38
|
+
const fn = vi.fn();
|
|
39
|
+
const debounced = debounce({ fn, wait: 100, leading: true, trailing: true });
|
|
40
|
+
|
|
41
|
+
debounced();
|
|
42
|
+
debounced();
|
|
43
|
+
expect(fn).toHaveBeenCalledOnce();
|
|
44
|
+
|
|
45
|
+
vi.advanceTimersByTime(100);
|
|
46
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("maxWait flushes before wait silence", () => {
|
|
50
|
+
vi.useFakeTimers();
|
|
51
|
+
const fn = vi.fn();
|
|
52
|
+
const debounced = debounce({ fn, wait: 100, maxWait: 150 });
|
|
53
|
+
|
|
54
|
+
debounced();
|
|
55
|
+
vi.advanceTimersByTime(90);
|
|
56
|
+
debounced();
|
|
57
|
+
vi.advanceTimersByTime(90);
|
|
58
|
+
expect(fn).toHaveBeenCalledOnce();
|
|
59
|
+
});
|
|
60
|
+
});
|
package/tsconfig.json
ADDED