@khanacademy/wonder-blocks-timing 4.0.1 → 5.0.0
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/CHANGELOG.md +18 -0
- package/dist/components/with-action-scheduler.d.ts +4 -1
- package/dist/es/index.js +93 -105
- package/dist/hooks/use-interval.d.ts +27 -5
- package/dist/hooks/use-timeout.d.ts +27 -5
- package/dist/index.d.ts +2 -5
- package/dist/index.js +92 -105
- package/dist/util/animation-frame.d.ts +6 -6
- package/dist/util/interval.d.ts +8 -8
- package/dist/util/policies.d.ts +12 -8
- package/dist/util/timeout.d.ts +8 -7
- package/dist/util/types.d.ts +18 -10
- package/package.json +3 -3
- package/src/components/with-action-scheduler.tsx +9 -1
- package/src/hooks/__tests__/use-interval.test.ts +453 -56
- package/src/hooks/__tests__/use-timeout.test.ts +412 -58
- package/src/hooks/use-interval.ts +90 -25
- package/src/hooks/use-timeout.ts +90 -25
- package/src/index.ts +4 -14
- package/src/util/__tests__/animation-frame.test.ts +9 -10
- package/src/util/animation-frame.ts +12 -16
- package/src/util/interval.ts +13 -18
- package/src/util/policies.ts +13 -8
- package/src/util/timeout.ts +14 -18
- package/src/util/types.ts +19 -11
- package/tsconfig-build.json +2 -4
- package/tsconfig-build.tsbuildinfo +1 -1
- package/dist/components/action-scheduler-provider.js.flow +0 -33
- package/dist/components/with-action-scheduler.js.flow +0 -22
- package/dist/hooks/internal/use-updating-ref.d.ts +0 -13
- package/dist/hooks/internal/use-updating-ref.js.flow +0 -20
- package/dist/hooks/use-interval.js.flow +0 -17
- package/dist/hooks/use-scheduled-interval.d.ts +0 -2
- package/dist/hooks/use-scheduled-interval.js.flow +0 -12
- package/dist/hooks/use-scheduled-timeout.d.ts +0 -2
- package/dist/hooks/use-scheduled-timeout.js.flow +0 -12
- package/dist/hooks/use-timeout.js.flow +0 -17
- package/dist/index.js.flow +0 -31
- package/dist/util/action-scheduler.js.flow +0 -38
- package/dist/util/animation-frame.js.flow +0 -70
- package/dist/util/interval.js.flow +0 -69
- package/dist/util/policies.js.flow +0 -14
- package/dist/util/timeout.js.flow +0 -71
- package/dist/util/types.js.flow +0 -232
- package/dist/util/types.typestest.js.flow +0 -6
- package/src/hooks/__tests__/use-scheduled-interval.test.ts +0 -460
- package/src/hooks/__tests__/use-scheduled-timeout.test.ts +0 -478
- package/src/hooks/internal/use-updating-ref.ts +0 -23
- package/src/hooks/use-scheduled-interval.ts +0 -72
- package/src/hooks/use-scheduled-timeout.ts +0 -79
package/src/hooks/use-timeout.ts
CHANGED
|
@@ -1,36 +1,101 @@
|
|
|
1
|
-
import {useEffect} from "react";
|
|
1
|
+
import {useEffect, useMemo, useRef} from "react";
|
|
2
|
+
import {ClearPolicy, ActionPolicy} from "../util/policies";
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import type {ITimeout, HookOptions} from "../util/types";
|
|
5
|
+
|
|
6
|
+
import Timeout from "../util/timeout";
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
|
-
*
|
|
9
|
+
* Hook providing access to a scheduled timeout.
|
|
7
10
|
*
|
|
8
|
-
* @param action
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
+
* @param action The action to be invoked when the timeout period has
|
|
12
|
+
* passed. By default, this will not cause the timeout to restart if it changes.
|
|
13
|
+
* This makes it easier to use with inline lambda functions rather than
|
|
14
|
+
* requiring consumers to wrap their action in a `useCallback`. To change
|
|
15
|
+
* this behavior, see the `actionPolicy` option.
|
|
16
|
+
* @param timeoutMs The timeout period. If this changes, the timeout will
|
|
17
|
+
* be reset per the `schedulePolicy` option.
|
|
18
|
+
* @param options Options for the hook.
|
|
19
|
+
* @param options.actionPolicy Determines how the action is handled when it
|
|
20
|
+
* changes. By default, the action is replaced but the timeout is not reset,
|
|
21
|
+
* and the updated action will be invoked when the timeout next fires.
|
|
22
|
+
* If you want to reset the timeout when the action changes, use
|
|
23
|
+
* `ActionPolicy.Reset`.
|
|
24
|
+
* @param options.clearPolicy Determines how the timeout is cleared when the
|
|
25
|
+
* component is unmounted or the timeout is recreated. By default, the
|
|
26
|
+
* timeout is cleared immediately. If you want to let the timeout run to
|
|
27
|
+
* completion, use `ClearPolicy.Resolve`. This is NOT applied if the timeout
|
|
28
|
+
* is cleared manually via the `clear()` method on the returned API.
|
|
29
|
+
* @param options.schedulePolicy Determines when the timeout is scheduled.
|
|
30
|
+
* By default, the timeout is scheduled immediately. If you want to delay
|
|
31
|
+
* scheduling the timeout, use `SchedulePolicy.OnDemand`.
|
|
32
|
+
* @returns An `ITimeout` API for interacting with the given timeout. This
|
|
33
|
+
* API is a no-op if called when not mounted. This means that any calls prior
|
|
34
|
+
* to mounting or after unmounting will not have any effect.
|
|
11
35
|
*/
|
|
12
36
|
export function useTimeout(
|
|
13
37
|
action: () => unknown,
|
|
14
38
|
timeoutMs: number,
|
|
15
|
-
|
|
16
|
-
) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
39
|
+
options: HookOptions = {},
|
|
40
|
+
): ITimeout {
|
|
41
|
+
const {actionPolicy, clearPolicy, schedulePolicy} = options;
|
|
42
|
+
const actionProxyRef = useRef<() => unknown>(action);
|
|
43
|
+
const timeoutRef = useRef<ITimeout | null>(null);
|
|
20
44
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
45
|
+
// Since we are passing our proxy function to the timeout instance,
|
|
46
|
+
// it's check that the action is a function will never fail. So, we have to
|
|
47
|
+
// do that check ourselves, and we do it here.
|
|
48
|
+
if (typeof action !== "function") {
|
|
49
|
+
throw new Error("Action must be a function");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// If we're rendered with an updated action, we want to update the ref
|
|
53
|
+
// so the existing timeout gets the new action, and then reset the
|
|
54
|
+
// timeout if our action policy calls for it.
|
|
55
|
+
if (action !== actionProxyRef.current) {
|
|
56
|
+
actionProxyRef.current = action;
|
|
57
|
+
if (actionPolicy === ActionPolicy.Reset) {
|
|
58
|
+
timeoutRef.current?.set();
|
|
30
59
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// This effect updates the timeout when the timeoutMs, clearPolicy,
|
|
63
|
+
// or schedulePolicy changes.
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
// Make a new timeout.
|
|
66
|
+
timeoutRef.current = new Timeout(
|
|
67
|
+
() => {
|
|
68
|
+
actionProxyRef.current?.();
|
|
69
|
+
},
|
|
70
|
+
timeoutMs,
|
|
71
|
+
schedulePolicy,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Clear the interval when the effect is cleaned up, if necessary,
|
|
75
|
+
// making sure to use the clear policy.
|
|
76
|
+
return () => {
|
|
77
|
+
timeoutRef.current?.clear(clearPolicy);
|
|
78
|
+
timeoutRef.current = null;
|
|
79
|
+
};
|
|
80
|
+
}, [timeoutMs, clearPolicy, schedulePolicy]);
|
|
81
|
+
|
|
82
|
+
// This is the API we expose to the consumer. We expose this rather than
|
|
83
|
+
// the interval instance itself so that the API we give back is stable
|
|
84
|
+
// even if the underlying interval instance changes.
|
|
85
|
+
const externalApi = useMemo(
|
|
86
|
+
() => ({
|
|
87
|
+
set: () => {
|
|
88
|
+
timeoutRef.current?.set();
|
|
89
|
+
},
|
|
90
|
+
clear: (policy?: ClearPolicy) => {
|
|
91
|
+
timeoutRef.current?.clear(policy);
|
|
92
|
+
},
|
|
93
|
+
get isSet() {
|
|
94
|
+
return timeoutRef.current?.isSet ?? false;
|
|
95
|
+
},
|
|
96
|
+
}),
|
|
97
|
+
[],
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
return externalApi;
|
|
36
101
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
IAnimationFrame,
|
|
3
|
-
IInterval,
|
|
4
|
-
IScheduleActions,
|
|
5
|
-
ITimeout,
|
|
6
|
-
WithActionScheduler,
|
|
7
|
-
WithActionSchedulerProps,
|
|
8
|
-
WithoutActionScheduler,
|
|
9
|
-
} from "./util/types";
|
|
10
|
-
|
|
11
1
|
export type {
|
|
12
2
|
IAnimationFrame,
|
|
13
3
|
IInterval,
|
|
@@ -16,12 +6,12 @@ export type {
|
|
|
16
6
|
WithActionScheduler,
|
|
17
7
|
WithActionSchedulerProps,
|
|
18
8
|
WithoutActionScheduler,
|
|
19
|
-
|
|
9
|
+
HookOptions,
|
|
10
|
+
Options,
|
|
11
|
+
} from "./util/types";
|
|
20
12
|
|
|
21
|
-
export {SchedulePolicy, ClearPolicy} from "./util/policies";
|
|
13
|
+
export {SchedulePolicy, ClearPolicy, ActionPolicy} from "./util/policies";
|
|
22
14
|
export {default as ActionSchedulerProvider} from "./components/action-scheduler-provider";
|
|
23
15
|
export {default as withActionScheduler} from "./components/with-action-scheduler";
|
|
24
16
|
export {useInterval} from "./hooks/use-interval";
|
|
25
17
|
export {useTimeout} from "./hooks/use-timeout";
|
|
26
|
-
export {useScheduledInterval} from "./hooks/use-scheduled-interval";
|
|
27
|
-
export {useScheduledTimeout} from "./hooks/use-scheduled-timeout";
|
|
@@ -9,8 +9,7 @@ describe("AnimationFrame", () => {
|
|
|
9
9
|
// it here and map it to timeouts, that way we can use the fake timer
|
|
10
10
|
// API to test our animation frame things.
|
|
11
11
|
jest.spyOn(global, "requestAnimationFrame").mockImplementation(
|
|
12
|
-
|
|
13
|
-
(fn: any, ...args: any) => setTimeout(fn, 0),
|
|
12
|
+
(fn: any, ...args: any) => setTimeout(fn, 0) as any,
|
|
14
13
|
);
|
|
15
14
|
jest.spyOn(global, "cancelAnimationFrame").mockImplementation(
|
|
16
15
|
(id: any, ...args: any) => clearTimeout(id),
|
|
@@ -46,13 +45,14 @@ describe("AnimationFrame", () => {
|
|
|
46
45
|
|
|
47
46
|
it("requests an animation frame when schedule policy is SchedulePolicy.Immediately", () => {
|
|
48
47
|
// Arrange
|
|
48
|
+
const spy = jest.spyOn(global, "requestAnimationFrame");
|
|
49
49
|
|
|
50
50
|
// Act
|
|
51
51
|
// eslint-disable-next-line no-new
|
|
52
52
|
new AnimationFrame(() => {}, SchedulePolicy.Immediately);
|
|
53
53
|
|
|
54
54
|
// Assert
|
|
55
|
-
expect(
|
|
55
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
56
56
|
});
|
|
57
57
|
});
|
|
58
58
|
|
|
@@ -98,27 +98,25 @@ describe("AnimationFrame", () => {
|
|
|
98
98
|
describe("#set", () => {
|
|
99
99
|
it("should call requestAnimationFrame", () => {
|
|
100
100
|
// Arrange
|
|
101
|
+
const spy = jest.spyOn(global, "requestAnimationFrame");
|
|
101
102
|
const animationFrame = new AnimationFrame(() => {});
|
|
102
103
|
|
|
103
104
|
// Act
|
|
104
105
|
animationFrame.set();
|
|
105
106
|
|
|
106
107
|
// Assert
|
|
107
|
-
expect(
|
|
108
|
-
1,
|
|
109
|
-
expect.any(Function),
|
|
110
|
-
);
|
|
108
|
+
expect(spy).toHaveBeenNthCalledWith(1, expect.any(Function));
|
|
111
109
|
});
|
|
112
110
|
|
|
113
111
|
it("should invoke requestAnimationFrame to call the given action", () => {
|
|
114
112
|
// Arrange
|
|
113
|
+
const spy = jest.spyOn(global, "requestAnimationFrame");
|
|
115
114
|
const action = jest.fn();
|
|
116
115
|
const animationFrame = new AnimationFrame((time: any) =>
|
|
117
116
|
action(time),
|
|
118
117
|
);
|
|
119
118
|
animationFrame.set();
|
|
120
|
-
|
|
121
|
-
const scheduledAction = requestAnimationFrame.mock.calls[0][0];
|
|
119
|
+
const scheduledAction = spy.mock.calls[0][0];
|
|
122
120
|
|
|
123
121
|
// Act
|
|
124
122
|
scheduledAction(2001);
|
|
@@ -144,6 +142,7 @@ describe("AnimationFrame", () => {
|
|
|
144
142
|
|
|
145
143
|
it("should set the timeout again if it has already executed", () => {
|
|
146
144
|
// Arrange
|
|
145
|
+
const spy = jest.spyOn(global, "requestAnimationFrame");
|
|
147
146
|
const action = jest.fn();
|
|
148
147
|
const animationFrame = new AnimationFrame(
|
|
149
148
|
action,
|
|
@@ -156,7 +155,7 @@ describe("AnimationFrame", () => {
|
|
|
156
155
|
animationFrame.set();
|
|
157
156
|
|
|
158
157
|
// Assert
|
|
159
|
-
expect(
|
|
158
|
+
expect(spy).toHaveBeenCalledTimes(2);
|
|
160
159
|
});
|
|
161
160
|
});
|
|
162
161
|
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SchedulePolicy as SchedulePolicies,
|
|
3
|
-
ClearPolicy as ClearPolicies,
|
|
4
|
-
} from "./policies";
|
|
1
|
+
import {SchedulePolicy, ClearPolicy} from "./policies";
|
|
5
2
|
|
|
6
|
-
import type {IAnimationFrame
|
|
3
|
+
import type {IAnimationFrame} from "./types";
|
|
7
4
|
|
|
8
5
|
/**
|
|
9
6
|
* Encapsulates everything associated with calling requestAnimationFrame/
|
|
@@ -22,8 +19,8 @@ export default class AnimationFrame implements IAnimationFrame {
|
|
|
22
19
|
* Creates an animation frame request that will invoke the given action.
|
|
23
20
|
* The request is not made until set is called.
|
|
24
21
|
*
|
|
25
|
-
* @param
|
|
26
|
-
* @param
|
|
22
|
+
* @param action The action to be invoked.
|
|
23
|
+
* @param [schedulePolicy] When SchedulePolicy.Immediately,
|
|
27
24
|
* the interval is set immediately on instantiation; otherwise, `set` must be
|
|
28
25
|
* called to set the interval.
|
|
29
26
|
* Defaults to `SchedulePolicy.Immediately`.
|
|
@@ -31,7 +28,7 @@ export default class AnimationFrame implements IAnimationFrame {
|
|
|
31
28
|
*/
|
|
32
29
|
constructor(
|
|
33
30
|
action: (time: DOMHighResTimeStamp) => unknown,
|
|
34
|
-
schedulePolicy: SchedulePolicy =
|
|
31
|
+
schedulePolicy: SchedulePolicy = SchedulePolicy.Immediately,
|
|
35
32
|
) {
|
|
36
33
|
if (typeof action !== "function") {
|
|
37
34
|
throw new Error("Action must be a function");
|
|
@@ -39,7 +36,7 @@ export default class AnimationFrame implements IAnimationFrame {
|
|
|
39
36
|
|
|
40
37
|
this._action = action;
|
|
41
38
|
|
|
42
|
-
if (schedulePolicy ===
|
|
39
|
+
if (schedulePolicy === SchedulePolicy.Immediately) {
|
|
43
40
|
this.set();
|
|
44
41
|
}
|
|
45
42
|
}
|
|
@@ -66,10 +63,10 @@ export default class AnimationFrame implements IAnimationFrame {
|
|
|
66
63
|
*/
|
|
67
64
|
set(): void {
|
|
68
65
|
if (this.isSet) {
|
|
69
|
-
this.clear(
|
|
66
|
+
this.clear(ClearPolicy.Cancel);
|
|
70
67
|
}
|
|
71
68
|
this._animationFrameId = requestAnimationFrame((time) =>
|
|
72
|
-
this.clear(
|
|
69
|
+
this.clear(ClearPolicy.Resolve, time),
|
|
73
70
|
);
|
|
74
71
|
}
|
|
75
72
|
|
|
@@ -79,19 +76,18 @@ export default class AnimationFrame implements IAnimationFrame {
|
|
|
79
76
|
* If the request is pending, this cancels that pending request without
|
|
80
77
|
* invoking the action. If no request is pending, this does nothing.
|
|
81
78
|
*
|
|
82
|
-
* @param
|
|
79
|
+
* @param [policy] When ClearPolicy.Resolve, if the request
|
|
83
80
|
* was set when called, the request action is invoked after cancelling
|
|
84
81
|
* the request; otherwise, the pending action is cancelled.
|
|
85
82
|
* Defaults to `ClearPolicy.Cancel`.
|
|
86
|
-
* @param
|
|
83
|
+
* @param [time] Timestamp to pass to the action when
|
|
87
84
|
* ClearPolicy.Resolve is specified. Ignored when ClearPolicy.Cancel is
|
|
88
85
|
* specified.
|
|
89
86
|
*
|
|
90
|
-
* @returns {void}
|
|
91
87
|
* @memberof AnimationFrame
|
|
92
88
|
*/
|
|
93
89
|
clear(
|
|
94
|
-
policy: ClearPolicy =
|
|
90
|
+
policy: ClearPolicy = ClearPolicy.Cancel,
|
|
95
91
|
time?: DOMHighResTimeStamp,
|
|
96
92
|
): void {
|
|
97
93
|
const animationFrameId = this._animationFrameId;
|
|
@@ -100,7 +96,7 @@ export default class AnimationFrame implements IAnimationFrame {
|
|
|
100
96
|
return;
|
|
101
97
|
}
|
|
102
98
|
cancelAnimationFrame(animationFrameId);
|
|
103
|
-
if (policy ===
|
|
99
|
+
if (policy === ClearPolicy.Resolve) {
|
|
104
100
|
this._action(time || performance.now());
|
|
105
101
|
}
|
|
106
102
|
}
|
package/src/util/interval.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SchedulePolicy as SchedulePolicies,
|
|
3
|
-
ClearPolicy as ClearPolicies,
|
|
4
|
-
} from "./policies";
|
|
1
|
+
import {SchedulePolicy, ClearPolicy} from "./policies";
|
|
5
2
|
|
|
6
|
-
import type {IInterval
|
|
3
|
+
import type {IInterval} from "./types";
|
|
7
4
|
|
|
8
5
|
/**
|
|
9
6
|
* Encapsulates everything associated with calling setInterval/clearInterval,
|
|
@@ -15,7 +12,7 @@ import type {IInterval, SchedulePolicy, ClearPolicy} from "./types";
|
|
|
15
12
|
* @implements {IInterval}
|
|
16
13
|
*/
|
|
17
14
|
export default class Interval implements IInterval {
|
|
18
|
-
_intervalId:
|
|
15
|
+
_intervalId: ReturnType<typeof setInterval> | null | undefined;
|
|
19
16
|
_action: () => unknown;
|
|
20
17
|
_intervalMs: number;
|
|
21
18
|
|
|
@@ -23,10 +20,10 @@ export default class Interval implements IInterval {
|
|
|
23
20
|
* Creates an interval that will invoke the given action after
|
|
24
21
|
* the given period. The interval does not start until set is called.
|
|
25
22
|
*
|
|
26
|
-
* @param
|
|
23
|
+
* @param action The action to be invoked each time the
|
|
27
24
|
* interval period has passed.
|
|
28
|
-
* @param
|
|
29
|
-
* @param
|
|
25
|
+
* @param intervalMs The interval period.
|
|
26
|
+
* @param [schedulePolicy] When SchedulePolicy.Immediately,
|
|
30
27
|
* the interval is set immediately on instantiation; otherwise, `set` must be
|
|
31
28
|
* called to set the interval.
|
|
32
29
|
* Defaults to `SchedulePolicy.Immediately`.
|
|
@@ -35,7 +32,7 @@ export default class Interval implements IInterval {
|
|
|
35
32
|
constructor(
|
|
36
33
|
action: () => unknown,
|
|
37
34
|
intervalMs: number,
|
|
38
|
-
schedulePolicy: SchedulePolicy =
|
|
35
|
+
schedulePolicy: SchedulePolicy = SchedulePolicy.Immediately,
|
|
39
36
|
) {
|
|
40
37
|
if (typeof action !== "function") {
|
|
41
38
|
throw new Error("Action must be a function");
|
|
@@ -48,7 +45,7 @@ export default class Interval implements IInterval {
|
|
|
48
45
|
this._action = action;
|
|
49
46
|
this._intervalMs = intervalMs;
|
|
50
47
|
|
|
51
|
-
if (schedulePolicy ===
|
|
48
|
+
if (schedulePolicy === SchedulePolicy.Immediately) {
|
|
52
49
|
this.set();
|
|
53
50
|
}
|
|
54
51
|
}
|
|
@@ -56,7 +53,7 @@ export default class Interval implements IInterval {
|
|
|
56
53
|
/**
|
|
57
54
|
* Determine if the interval is active or not.
|
|
58
55
|
*
|
|
59
|
-
* @returns
|
|
56
|
+
* @returns true if the interval is active, otherwise false.
|
|
60
57
|
* @memberof Interval
|
|
61
58
|
*/
|
|
62
59
|
get isSet(): boolean {
|
|
@@ -73,9 +70,8 @@ export default class Interval implements IInterval {
|
|
|
73
70
|
*/
|
|
74
71
|
set(): void {
|
|
75
72
|
if (this.isSet) {
|
|
76
|
-
this.clear(
|
|
73
|
+
this.clear(ClearPolicy.Cancel);
|
|
77
74
|
}
|
|
78
|
-
// @ts-expect-error [FEI-5019] - TS2322 - Type 'Timer' is not assignable to type 'number'.
|
|
79
75
|
this._intervalId = setInterval(() => this._action(), this._intervalMs);
|
|
80
76
|
}
|
|
81
77
|
|
|
@@ -85,22 +81,21 @@ export default class Interval implements IInterval {
|
|
|
85
81
|
* If the interval is active, this cancels that interval. If no interval is
|
|
86
82
|
* pending, this does nothing.
|
|
87
83
|
*
|
|
88
|
-
* @param
|
|
84
|
+
* @param [policy] When ClearPolicy.Resolve, if the request
|
|
89
85
|
* was set when called, the request action is invoked after cancelling
|
|
90
86
|
* the request; otherwise, the pending action is cancelled.
|
|
91
87
|
* Defaults to `ClearPolicy.Cancel`.
|
|
92
88
|
*
|
|
93
|
-
* @returns {void}
|
|
94
89
|
* @memberof Interval
|
|
95
90
|
*/
|
|
96
|
-
clear(policy: ClearPolicy =
|
|
91
|
+
clear(policy: ClearPolicy = ClearPolicy.Cancel): void {
|
|
97
92
|
const intervalId = this._intervalId;
|
|
98
93
|
this._intervalId = null;
|
|
99
94
|
if (intervalId == null) {
|
|
100
95
|
return;
|
|
101
96
|
}
|
|
102
97
|
clearInterval(intervalId);
|
|
103
|
-
if (policy ===
|
|
98
|
+
if (policy === ClearPolicy.Resolve) {
|
|
104
99
|
this._action();
|
|
105
100
|
}
|
|
106
101
|
}
|
package/src/util/policies.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
export
|
|
2
|
-
Immediately
|
|
3
|
-
OnDemand
|
|
4
|
-
}
|
|
1
|
+
export enum SchedulePolicy {
|
|
2
|
+
Immediately = "schedule-immediately",
|
|
3
|
+
OnDemand = "schedule-on-demand",
|
|
4
|
+
}
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
Resolve
|
|
8
|
-
Cancel
|
|
9
|
-
}
|
|
6
|
+
export enum ClearPolicy {
|
|
7
|
+
Resolve = "resolve-on-clear",
|
|
8
|
+
Cancel = "cancel-on-clear",
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export enum ActionPolicy {
|
|
12
|
+
Reset = "reset",
|
|
13
|
+
Passive = "passive",
|
|
14
|
+
}
|
package/src/util/timeout.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SchedulePolicy as SchedulePolicies,
|
|
3
|
-
ClearPolicy as ClearPolicies,
|
|
4
|
-
} from "./policies";
|
|
1
|
+
import {SchedulePolicy, ClearPolicy} from "./policies";
|
|
5
2
|
|
|
6
|
-
import type {ITimeout
|
|
3
|
+
import type {ITimeout} from "./types";
|
|
7
4
|
|
|
8
5
|
/**
|
|
9
6
|
* Encapsulates everything associated with calling setTimeout/clearTimeout, and
|
|
@@ -15,7 +12,7 @@ import type {ITimeout, SchedulePolicy, ClearPolicy} from "./types";
|
|
|
15
12
|
* @implements {ITimeout}
|
|
16
13
|
*/
|
|
17
14
|
export default class Timeout implements ITimeout {
|
|
18
|
-
_timeoutId:
|
|
15
|
+
_timeoutId: ReturnType<typeof setTimeout> | null | undefined;
|
|
19
16
|
_action: () => unknown;
|
|
20
17
|
_timeoutMs: number;
|
|
21
18
|
|
|
@@ -23,10 +20,10 @@ export default class Timeout implements ITimeout {
|
|
|
23
20
|
* Creates a timeout that will invoke the given action after
|
|
24
21
|
* the given period. The timeout does not start until set is called.
|
|
25
22
|
*
|
|
26
|
-
* @param
|
|
23
|
+
* @param action The action to be invoked when the timeout
|
|
27
24
|
* period has passed.
|
|
28
|
-
* @param
|
|
29
|
-
* @param
|
|
25
|
+
* @param timeoutMs The timeout period.
|
|
26
|
+
* @param [schedulePolicy] When SchedulePolicy.Immediately,
|
|
30
27
|
* the timer is set immediately on instantiation; otherwise, `set` must be
|
|
31
28
|
* called to set the timeout.
|
|
32
29
|
* Defaults to `SchedulePolicy.Immediately`.
|
|
@@ -35,7 +32,7 @@ export default class Timeout implements ITimeout {
|
|
|
35
32
|
constructor(
|
|
36
33
|
action: () => unknown,
|
|
37
34
|
timeoutMs: number,
|
|
38
|
-
schedulePolicy: SchedulePolicy =
|
|
35
|
+
schedulePolicy: SchedulePolicy = SchedulePolicy.Immediately,
|
|
39
36
|
) {
|
|
40
37
|
if (typeof action !== "function") {
|
|
41
38
|
throw new Error("Action must be a function");
|
|
@@ -48,7 +45,7 @@ export default class Timeout implements ITimeout {
|
|
|
48
45
|
this._action = action;
|
|
49
46
|
this._timeoutMs = timeoutMs;
|
|
50
47
|
|
|
51
|
-
if (schedulePolicy ===
|
|
48
|
+
if (schedulePolicy === SchedulePolicy.Immediately) {
|
|
52
49
|
this.set();
|
|
53
50
|
}
|
|
54
51
|
}
|
|
@@ -56,7 +53,7 @@ export default class Timeout implements ITimeout {
|
|
|
56
53
|
/**
|
|
57
54
|
* Determine if the timeout is set or not.
|
|
58
55
|
*
|
|
59
|
-
* @returns
|
|
56
|
+
* @returns true if the timeout is set (aka pending), otherwise
|
|
60
57
|
* false.
|
|
61
58
|
* @memberof Timeout
|
|
62
59
|
*/
|
|
@@ -75,11 +72,10 @@ export default class Timeout implements ITimeout {
|
|
|
75
72
|
*/
|
|
76
73
|
set(): void {
|
|
77
74
|
if (this.isSet) {
|
|
78
|
-
this.clear(
|
|
75
|
+
this.clear(ClearPolicy.Cancel);
|
|
79
76
|
}
|
|
80
|
-
// @ts-expect-error [FEI-5019] - TS2322 - Type 'Timeout' is not assignable to type 'number'.
|
|
81
77
|
this._timeoutId = setTimeout(
|
|
82
|
-
() => this.clear(
|
|
78
|
+
() => this.clear(ClearPolicy.Resolve),
|
|
83
79
|
this._timeoutMs,
|
|
84
80
|
);
|
|
85
81
|
}
|
|
@@ -90,7 +86,7 @@ export default class Timeout implements ITimeout {
|
|
|
90
86
|
* If the timeout is pending, this cancels that pending timeout without
|
|
91
87
|
* invoking the action. If no timeout is pending, this does nothing.
|
|
92
88
|
*
|
|
93
|
-
* @param
|
|
89
|
+
* @param [policy] When ClearPolicy.Resolve, if the request
|
|
94
90
|
* was set when called, the request action is invoked after cancelling
|
|
95
91
|
* the request; otherwise, the pending action is cancelled.
|
|
96
92
|
* Defaults to `ClearPolicy.Cancel`.
|
|
@@ -98,14 +94,14 @@ export default class Timeout implements ITimeout {
|
|
|
98
94
|
* @returns {void}
|
|
99
95
|
* @memberof Timeout
|
|
100
96
|
*/
|
|
101
|
-
clear(policy: ClearPolicy =
|
|
97
|
+
clear(policy: ClearPolicy = ClearPolicy.Cancel): void {
|
|
102
98
|
const timeoutId = this._timeoutId;
|
|
103
99
|
this._timeoutId = null;
|
|
104
100
|
if (timeoutId == null) {
|
|
105
101
|
return;
|
|
106
102
|
}
|
|
107
103
|
clearTimeout(timeoutId);
|
|
108
|
-
if (policy ===
|
|
104
|
+
if (policy === ClearPolicy.Resolve) {
|
|
109
105
|
this._action();
|
|
110
106
|
}
|
|
111
107
|
}
|
package/src/util/types.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export type ClearPolicy = "resolve-on-clear" | "cancel-on-clear";
|
|
1
|
+
import * as Policies from "./policies";
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* Encapsulates everything associated with calling setTimeout/clearTimeout, and
|
|
@@ -35,14 +33,14 @@ export interface ITimeout {
|
|
|
35
33
|
* If the timeout is pending, this cancels that pending timeout. If no
|
|
36
34
|
* timeout is pending, this does nothing.
|
|
37
35
|
*
|
|
38
|
-
* @param
|
|
36
|
+
* @param [policy] When ClearPolicy.Resolve, if the request
|
|
39
37
|
* was set when called, the request action is invoked after cancelling
|
|
40
38
|
* the request; otherwise, the pending action is cancelled.
|
|
41
39
|
* Defaults to `ClearPolicy.Cancel`.
|
|
42
40
|
*
|
|
43
41
|
* @memberof ITimeout
|
|
44
42
|
*/
|
|
45
|
-
clear(policy?: ClearPolicy): void;
|
|
43
|
+
clear(policy?: Policies.ClearPolicy): void;
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
/**
|
|
@@ -76,14 +74,14 @@ export interface IInterval {
|
|
|
76
74
|
* If the interval is active, this cancels that interval. If the interval
|
|
77
75
|
* is not active, this does nothing.
|
|
78
76
|
*
|
|
79
|
-
* @param
|
|
77
|
+
* @param [policy] When ClearPolicy.Resolve, if the request
|
|
80
78
|
* was set when called, the request action is invoked after cancelling
|
|
81
79
|
* the request; otherwise, the pending action is cancelled.
|
|
82
80
|
* Defaults to `ClearPolicy.Cancel`.
|
|
83
81
|
*
|
|
84
82
|
* @memberof IInterval
|
|
85
83
|
*/
|
|
86
|
-
clear(policy?: ClearPolicy): void;
|
|
84
|
+
clear(policy?: Policies.ClearPolicy): void;
|
|
87
85
|
}
|
|
88
86
|
|
|
89
87
|
/**
|
|
@@ -119,19 +117,29 @@ export interface IAnimationFrame {
|
|
|
119
117
|
* If the request is pending, this cancels that pending request. If no
|
|
120
118
|
* request is pending, this does nothing.
|
|
121
119
|
*
|
|
122
|
-
* @param
|
|
120
|
+
* @param [policy] When ClearPolicy.Resolve, if the request
|
|
123
121
|
* was set when called, the request action is invoked after cancelling
|
|
124
122
|
* the request; otherwise, the pending action is cancelled.
|
|
125
123
|
* Defaults to `ClearPolicy.Cancel`.
|
|
126
124
|
*
|
|
127
125
|
* @memberof IAnimationFrame
|
|
128
126
|
*/
|
|
129
|
-
clear(policy?: ClearPolicy): void;
|
|
127
|
+
clear(policy?: Policies.ClearPolicy): void;
|
|
130
128
|
}
|
|
131
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Options for the scheduling APIs.
|
|
132
|
+
*/
|
|
132
133
|
export type Options = {
|
|
133
|
-
schedulePolicy?: SchedulePolicy;
|
|
134
|
-
clearPolicy?: ClearPolicy;
|
|
134
|
+
schedulePolicy?: Policies.SchedulePolicy;
|
|
135
|
+
clearPolicy?: Policies.ClearPolicy;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Options for the hook variants of our scheduling APIs.
|
|
140
|
+
*/
|
|
141
|
+
export type HookOptions = Options & {
|
|
142
|
+
actionPolicy?: Policies.ActionPolicy;
|
|
135
143
|
};
|
|
136
144
|
|
|
137
145
|
/**
|
package/tsconfig-build.json
CHANGED