@khanacademy/wonder-blocks-timing 2.0.3 → 2.1.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 ADDED
@@ -0,0 +1,11 @@
1
+ # @khanacademy/wonder-blocks-timing
2
+
3
+ ## 2.1.0
4
+ ### Minor Changes
5
+
6
+ - 029b4810: Adds `useInterval()` hook that mimics the behavior of `ActionScheduler`'s
7
+ `interval()` method.
8
+ - c57cd770: Rename `useInterval` and `useTimeout` to `useScheduledInterval`
9
+ and `useScheduledTimeout` respectively.
10
+ - 29766c8e: Add `useInterval` and `useTimeout` hooks to provide a simple API for
11
+ using intervals and timeouts.
package/dist/es/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import _extends from '@babel/runtime/helpers/extends';
2
- import { Component, forwardRef, createElement, useState, useRef, useEffect, useCallback } from 'react';
2
+ import * as React from 'react';
3
+ import { useRef, useEffect, useState, useCallback } from 'react';
3
4
 
4
5
  const SchedulePolicy = {
5
6
  Immediately: "schedule-immediately",
@@ -395,7 +396,7 @@ ActionScheduler.NoopAction = {
395
396
  * </ActionSchedulerProvider>
396
397
  * ```
397
398
  */
398
- class ActionSchedulerProvider extends Component {
399
+ class ActionSchedulerProvider extends React.Component {
399
400
  constructor(...args) {
400
401
  super(...args);
401
402
  this._actionScheduler = new ActionScheduler();
@@ -424,59 +425,188 @@ class ActionSchedulerProvider extends Component {
424
425
  * these props use the `WithActionScheduler` type.
425
426
  */
426
427
  function withActionScheduler(WrappedComponent) {
427
- return /*#__PURE__*/forwardRef((props, ref) => /*#__PURE__*/createElement(ActionSchedulerProvider, null, schedule => /*#__PURE__*/createElement(WrappedComponent, _extends({}, props, {
428
+ return /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.createElement(ActionSchedulerProvider, null, schedule => /*#__PURE__*/React.createElement(WrappedComponent, _extends({}, props, {
428
429
  ref: ref,
429
430
  schedule: schedule
430
431
  }))));
431
432
  }
432
433
 
433
- function useTimeout(action, timeoutMs, options) {
434
+ /**
435
+ * Returns a ref whose .current value is updated whenever
436
+ * the `value` passed to this hook changes.
437
+ *
438
+ * this is great for values that you want to reference from
439
+ * within a useCallback or useEffect event listener, without
440
+ * re-triggering the effect when the value changes
441
+ *
442
+ * @returns {{current: T}}
443
+ */
444
+
445
+ const useUpdatingRef = value => {
446
+ const ref = useRef(value);
447
+ useEffect(() => {
448
+ ref.current = value;
449
+ }, [value]);
450
+ return ref;
451
+ };
452
+
453
+ /**
454
+ * A simple hook for using `setInterval`.
455
+ *
456
+ * @param action called every `intervalMs` when `active` is true
457
+ * @param intervalMs the duration between calls to `action`
458
+ * @param active whether or not the interval is active
459
+ */
460
+
461
+ function useInterval(action, intervalMs, active) {
462
+ // We using a ref instead of a callback for `action` to avoid resetting
463
+ // the interval whenever the `action` changes.
464
+ const actionRef = useUpdatingRef(action);
465
+ useEffect(() => {
466
+ if (active) {
467
+ const intervalId = setInterval(() => {
468
+ actionRef.current();
469
+ }, intervalMs);
470
+ return () => {
471
+ clearInterval(intervalId);
472
+ };
473
+ } // actionRef isn't actually required, but react-hooks/exhaustive-deps
474
+ // doesn't recognize it as a ref and thus complains if it isn't in the
475
+ // deps list. It isn't a big deal though since the value ofactionRef
476
+ // never changes (only its contents do).
477
+
478
+ }, [intervalMs, active, actionRef]);
479
+ }
480
+
481
+ /**
482
+ * A simple hook for using `setTimeout`.
483
+ *
484
+ * @param action called after `timeoutMs` when `active` is true
485
+ * @param timeoutMs the duration after which `action` is called
486
+ * @param active whether or not the interval is active
487
+ */
488
+
489
+ function useTimeout(action, timeoutMs, active) {
490
+ // We using a ref instead of a callback for `action` to avoid resetting
491
+ // the interval whenever the `action` changes.
492
+ const actionRef = useUpdatingRef(action);
493
+ useEffect(() => {
494
+ if (active) {
495
+ const timeoutId = setTimeout(() => {
496
+ actionRef.current();
497
+ }, timeoutMs);
498
+ return () => {
499
+ clearTimeout(timeoutId);
500
+ };
501
+ } // actionRef isn't actually required, but react-hooks/exhaustive-deps
502
+ // doesn't recognize it as a ref and thus complains if it isn't in the
503
+ // deps list. It isn't a big deal though since the value ofactionRef
504
+ // never changes (only its contents do).
505
+
506
+ }, [timeoutMs, active, actionRef]);
507
+ }
508
+
509
+ function useScheduledInterval(action, intervalMs, options) {
434
510
  var _options$schedulePoli;
435
511
 
512
+ if (typeof action !== "function") {
513
+ throw new Error("Action must be a function");
514
+ }
515
+
516
+ if (intervalMs < 1) {
517
+ throw new Error("Interval period must be >= 1");
518
+ }
519
+
436
520
  const schedulePolicy = (_options$schedulePoli = options == null ? void 0 : options.schedulePolicy) != null ? _options$schedulePoli : SchedulePolicy.Immediately;
437
521
  const [isSet, setIsSet] = useState(schedulePolicy === SchedulePolicy.Immediately);
438
- const actionRef = useRef(action);
439
- const mountedRef = useRef(false);
522
+ const set = useCallback(() => setIsSet(true), []);
523
+ const actionRef = useUpdatingRef(action);
524
+ const clear = useCallback(policy => {
525
+ var _policy;
526
+
527
+ policy = (_policy = policy) != null ? _policy : options == null ? void 0 : options.clearPolicy;
528
+
529
+ if (isSet && policy === ClearPolicy.Resolve) {
530
+ actionRef.current();
531
+ }
532
+
533
+ setIsSet(false);
534
+ }, // react-hooks/exhaustive-deps doesn't require refs to be
535
+ // listed in the deps array. Unfortunately, in this situation
536
+ // it doesn't recognized actionRef as a ref.
537
+ [actionRef, isSet, options == null ? void 0 : options.clearPolicy]);
538
+ const runOnUnmountRef = useUpdatingRef(isSet && (options == null ? void 0 : options.clearPolicy) === ClearPolicy.Resolve);
440
539
  useEffect(() => {
441
- mountedRef.current = true;
442
540
  return () => {
443
- mountedRef.current = false;
444
- };
541
+ // This code will only run with the component using this
542
+ // hook is unmounted.
543
+ // eslint-disable-next-line react-hooks/exhaustive-deps
544
+ if (runOnUnmountRef.current) {
545
+ // eslint-disable-next-line react-hooks/exhaustive-deps
546
+ actionRef.current();
547
+ }
548
+ }; // This eslint rule doesn't realize actionRef and runOnUnmountRef
549
+ // a both refs and thus do not have to be listed as deps.
550
+ // eslint-disable-next-line react-hooks/exhaustive-deps
445
551
  }, []);
446
- useEffect(() => {
447
- actionRef.current = action;
448
- }, [action]);
449
- const clear = useCallback(policy => {
450
- if ((policy != null ? policy : options == null ? void 0 : options.clearPolicy) === ClearPolicy.Resolve) {
451
- actionRef.current();
452
- } // This will cause the useEffect below to re-run
552
+ useInterval(action, intervalMs, isSet);
553
+ return {
554
+ isSet,
555
+ set,
556
+ clear
557
+ };
558
+ }
559
+
560
+ function useScheduledTimeout(action, timeoutMs, options) {
561
+ var _options$schedulePoli;
453
562
 
563
+ if (typeof action !== "function") {
564
+ throw new Error("Action must be a function");
565
+ }
454
566
 
567
+ if (timeoutMs < 0) {
568
+ throw new Error("Timeout period must be >= 0");
569
+ }
570
+
571
+ const schedulePolicy = (_options$schedulePoli = options == null ? void 0 : options.schedulePolicy) != null ? _options$schedulePoli : SchedulePolicy.Immediately;
572
+ const [isSet, setIsSet] = useState(schedulePolicy === SchedulePolicy.Immediately);
573
+ const set = useCallback(() => setIsSet(true), []); // This wrapper isn't present in useScheduledInterval because we
574
+ // don't need to update `isSet` in that situations.
575
+
576
+ const wrappedAction = useCallback(() => {
455
577
  setIsSet(false);
456
- }, [options == null ? void 0 : options.clearPolicy]);
457
- const set = useCallback(() => {
458
- if (isSet) {
459
- clear();
460
- } // This will cause the useEffect below to re-run
578
+ action();
579
+ }, [action]);
580
+ const actionRef = useUpdatingRef(wrappedAction);
581
+ const clear = useCallback(policy => {
582
+ var _policy;
461
583
 
584
+ policy = (_policy = policy) != null ? _policy : options == null ? void 0 : options.clearPolicy;
585
+
586
+ if (isSet && policy === ClearPolicy.Resolve) {
587
+ actionRef.current();
588
+ }
462
589
 
463
- setIsSet(true);
464
- }, [clear, isSet]);
590
+ setIsSet(false);
591
+ }, // react-hooks/exhaustive-deps doesn't require refs to be
592
+ // listed in the deps array. Unfortunately, in this situation
593
+ // it doesn't recognized actionRef as a ref.
594
+ [actionRef, isSet, options == null ? void 0 : options.clearPolicy]);
595
+ const runOnUnmountRef = useUpdatingRef(isSet && (options == null ? void 0 : options.clearPolicy) === ClearPolicy.Resolve);
465
596
  useEffect(() => {
466
- if (isSet && mountedRef.current) {
467
- const timeout = window.setTimeout(() => {
597
+ return () => {
598
+ // This code will only run with the component using this
599
+ // hook is unmounted.
600
+ // eslint-disable-next-line react-hooks/exhaustive-deps
601
+ if (runOnUnmountRef.current) {
602
+ // eslint-disable-next-line react-hooks/exhaustive-deps
468
603
  actionRef.current();
469
- setIsSet(false);
470
- }, timeoutMs);
471
- return () => {
472
- window.clearTimeout(timeout);
473
-
474
- if (!mountedRef.current) {
475
- clear();
476
- }
477
- };
478
- }
479
- }, [clear, isSet, timeoutMs]);
604
+ }
605
+ }; // This eslint rule doesn't realize actionRef and runOnUnmountRef
606
+ // a both refs and thus do not have to be listed as deps.
607
+ // eslint-disable-next-line react-hooks/exhaustive-deps
608
+ }, []);
609
+ useTimeout(wrappedAction, timeoutMs, isSet);
480
610
  return {
481
611
  isSet,
482
612
  set,
@@ -484,4 +614,4 @@ function useTimeout(action, timeoutMs, options) {
484
614
  };
485
615
  }
486
616
 
487
- export { ClearPolicy, SchedulePolicy, useTimeout, withActionScheduler };
617
+ export { ClearPolicy, SchedulePolicy, useInterval, useScheduledInterval, useScheduledTimeout, useTimeout, withActionScheduler };
package/dist/index.js CHANGED
@@ -82,7 +82,7 @@ module.exports =
82
82
  /******/
83
83
  /******/
84
84
  /******/ // Load entry module and return exports
85
- /******/ return __webpack_require__(__webpack_require__.s = 9);
85
+ /******/ return __webpack_require__(__webpack_require__.s = 13);
86
86
  /******/ })
87
87
  /************************************************************************/
88
88
  /******/ ([
@@ -111,11 +111,117 @@ module.exports = require("react");
111
111
  /* 2 */
112
112
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
113
113
 
114
+ "use strict";
115
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useUpdatingRef; });
116
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
117
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
118
+
119
+ /**
120
+ * Returns a ref whose .current value is updated whenever
121
+ * the `value` passed to this hook changes.
122
+ *
123
+ * this is great for values that you want to reference from
124
+ * within a useCallback or useEffect event listener, without
125
+ * re-triggering the effect when the value changes
126
+ *
127
+ * @returns {{current: T}}
128
+ */
129
+
130
+ const useUpdatingRef = value => {
131
+ const ref = Object(react__WEBPACK_IMPORTED_MODULE_0__["useRef"])(value);
132
+ Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => {
133
+ ref.current = value;
134
+ }, [value]);
135
+ return ref;
136
+ };
137
+
138
+ /***/ }),
139
+ /* 3 */
140
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
141
+
142
+ "use strict";
143
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useInterval; });
144
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
145
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
146
+ /* harmony import */ var _internal_use_updating_ref_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
147
+
148
+
149
+ /**
150
+ * A simple hook for using `setInterval`.
151
+ *
152
+ * @param action called every `intervalMs` when `active` is true
153
+ * @param intervalMs the duration between calls to `action`
154
+ * @param active whether or not the interval is active
155
+ */
156
+
157
+ function useInterval(action, intervalMs, active) {
158
+ // We using a ref instead of a callback for `action` to avoid resetting
159
+ // the interval whenever the `action` changes.
160
+ const actionRef = Object(_internal_use_updating_ref_js__WEBPACK_IMPORTED_MODULE_1__[/* useUpdatingRef */ "a"])(action);
161
+ Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => {
162
+ if (active) {
163
+ const intervalId = setInterval(() => {
164
+ actionRef.current();
165
+ }, intervalMs);
166
+ return () => {
167
+ clearInterval(intervalId);
168
+ };
169
+ } // actionRef isn't actually required, but react-hooks/exhaustive-deps
170
+ // doesn't recognize it as a ref and thus complains if it isn't in the
171
+ // deps list. It isn't a big deal though since the value ofactionRef
172
+ // never changes (only its contents do).
173
+
174
+ }, [intervalMs, active, actionRef]);
175
+ }
176
+
177
+ /***/ }),
178
+ /* 4 */
179
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
180
+
181
+ "use strict";
182
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useTimeout; });
183
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
184
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
185
+ /* harmony import */ var _internal_use_updating_ref_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
186
+
187
+
188
+ /**
189
+ * A simple hook for using `setTimeout`.
190
+ *
191
+ * @param action called after `timeoutMs` when `active` is true
192
+ * @param timeoutMs the duration after which `action` is called
193
+ * @param active whether or not the interval is active
194
+ */
195
+
196
+ function useTimeout(action, timeoutMs, active) {
197
+ // We using a ref instead of a callback for `action` to avoid resetting
198
+ // the interval whenever the `action` changes.
199
+ const actionRef = Object(_internal_use_updating_ref_js__WEBPACK_IMPORTED_MODULE_1__[/* useUpdatingRef */ "a"])(action);
200
+ Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => {
201
+ if (active) {
202
+ const timeoutId = setTimeout(() => {
203
+ actionRef.current();
204
+ }, timeoutMs);
205
+ return () => {
206
+ clearTimeout(timeoutId);
207
+ };
208
+ } // actionRef isn't actually required, but react-hooks/exhaustive-deps
209
+ // doesn't recognize it as a ref and thus complains if it isn't in the
210
+ // deps list. It isn't a big deal though since the value ofactionRef
211
+ // never changes (only its contents do).
212
+
213
+ }, [timeoutMs, active, actionRef]);
214
+ }
215
+
216
+ /***/ }),
217
+ /* 5 */
218
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
219
+
114
220
  "use strict";
115
221
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return withActionScheduler; });
116
222
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
117
223
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
118
- /* harmony import */ var _action_scheduler_provider_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
224
+ /* harmony import */ var _action_scheduler_provider_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8);
119
225
  function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
120
226
 
121
227
 
@@ -138,63 +244,136 @@ function withActionScheduler(WrappedComponent) {
138
244
  }
139
245
 
140
246
  /***/ }),
141
- /* 3 */
247
+ /* 6 */
142
248
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
143
249
 
144
250
  "use strict";
145
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useTimeout; });
251
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useScheduledInterval; });
146
252
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
147
253
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
148
254
  /* harmony import */ var _util_policies_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(0);
255
+ /* harmony import */ var _internal_use_updating_ref_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2);
256
+ /* harmony import */ var _use_interval_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(3);
257
+
149
258
 
150
259
 
151
- function useTimeout(action, timeoutMs, options) {
260
+
261
+ function useScheduledInterval(action, intervalMs, options) {
152
262
  var _options$schedulePoli;
153
263
 
264
+ if (typeof action !== "function") {
265
+ throw new Error("Action must be a function");
266
+ }
267
+
268
+ if (intervalMs < 1) {
269
+ throw new Error("Interval period must be >= 1");
270
+ }
271
+
154
272
  const schedulePolicy = (_options$schedulePoli = options == null ? void 0 : options.schedulePolicy) != null ? _options$schedulePoli : _util_policies_js__WEBPACK_IMPORTED_MODULE_1__[/* SchedulePolicy */ "b"].Immediately;
155
273
  const [isSet, setIsSet] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(schedulePolicy === _util_policies_js__WEBPACK_IMPORTED_MODULE_1__[/* SchedulePolicy */ "b"].Immediately);
156
- const actionRef = Object(react__WEBPACK_IMPORTED_MODULE_0__["useRef"])(action);
157
- const mountedRef = Object(react__WEBPACK_IMPORTED_MODULE_0__["useRef"])(false);
274
+ const set = Object(react__WEBPACK_IMPORTED_MODULE_0__["useCallback"])(() => setIsSet(true), []);
275
+ const actionRef = Object(_internal_use_updating_ref_js__WEBPACK_IMPORTED_MODULE_2__[/* useUpdatingRef */ "a"])(action);
276
+ const clear = Object(react__WEBPACK_IMPORTED_MODULE_0__["useCallback"])(policy => {
277
+ var _policy;
278
+
279
+ policy = (_policy = policy) != null ? _policy : options == null ? void 0 : options.clearPolicy;
280
+
281
+ if (isSet && policy === _util_policies_js__WEBPACK_IMPORTED_MODULE_1__[/* ClearPolicy */ "a"].Resolve) {
282
+ actionRef.current();
283
+ }
284
+
285
+ setIsSet(false);
286
+ }, // react-hooks/exhaustive-deps doesn't require refs to be
287
+ // listed in the deps array. Unfortunately, in this situation
288
+ // it doesn't recognized actionRef as a ref.
289
+ [actionRef, isSet, options == null ? void 0 : options.clearPolicy]);
290
+ const runOnUnmountRef = Object(_internal_use_updating_ref_js__WEBPACK_IMPORTED_MODULE_2__[/* useUpdatingRef */ "a"])(isSet && (options == null ? void 0 : options.clearPolicy) === _util_policies_js__WEBPACK_IMPORTED_MODULE_1__[/* ClearPolicy */ "a"].Resolve);
158
291
  Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => {
159
- mountedRef.current = true;
160
292
  return () => {
161
- mountedRef.current = false;
162
- };
293
+ // This code will only run with the component using this
294
+ // hook is unmounted.
295
+ // eslint-disable-next-line react-hooks/exhaustive-deps
296
+ if (runOnUnmountRef.current) {
297
+ // eslint-disable-next-line react-hooks/exhaustive-deps
298
+ actionRef.current();
299
+ }
300
+ }; // This eslint rule doesn't realize actionRef and runOnUnmountRef
301
+ // a both refs and thus do not have to be listed as deps.
302
+ // eslint-disable-next-line react-hooks/exhaustive-deps
163
303
  }, []);
164
- Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => {
165
- actionRef.current = action;
166
- }, [action]);
167
- const clear = Object(react__WEBPACK_IMPORTED_MODULE_0__["useCallback"])(policy => {
168
- if ((policy != null ? policy : options == null ? void 0 : options.clearPolicy) === _util_policies_js__WEBPACK_IMPORTED_MODULE_1__[/* ClearPolicy */ "a"].Resolve) {
169
- actionRef.current();
170
- } // This will cause the useEffect below to re-run
304
+ Object(_use_interval_js__WEBPACK_IMPORTED_MODULE_3__[/* useInterval */ "a"])(action, intervalMs, isSet);
305
+ return {
306
+ isSet,
307
+ set,
308
+ clear
309
+ };
310
+ }
311
+
312
+ /***/ }),
313
+ /* 7 */
314
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
315
+
316
+ "use strict";
317
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useScheduledTimeout; });
318
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
319
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
320
+ /* harmony import */ var _util_policies_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(0);
321
+ /* harmony import */ var _internal_use_updating_ref_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2);
322
+ /* harmony import */ var _use_timeout_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
323
+
324
+
325
+
326
+
327
+ function useScheduledTimeout(action, timeoutMs, options) {
328
+ var _options$schedulePoli;
171
329
 
330
+ if (typeof action !== "function") {
331
+ throw new Error("Action must be a function");
332
+ }
333
+
334
+ if (timeoutMs < 0) {
335
+ throw new Error("Timeout period must be >= 0");
336
+ }
172
337
 
338
+ const schedulePolicy = (_options$schedulePoli = options == null ? void 0 : options.schedulePolicy) != null ? _options$schedulePoli : _util_policies_js__WEBPACK_IMPORTED_MODULE_1__[/* SchedulePolicy */ "b"].Immediately;
339
+ const [isSet, setIsSet] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(schedulePolicy === _util_policies_js__WEBPACK_IMPORTED_MODULE_1__[/* SchedulePolicy */ "b"].Immediately);
340
+ const set = Object(react__WEBPACK_IMPORTED_MODULE_0__["useCallback"])(() => setIsSet(true), []); // This wrapper isn't present in useScheduledInterval because we
341
+ // don't need to update `isSet` in that situations.
342
+
343
+ const wrappedAction = Object(react__WEBPACK_IMPORTED_MODULE_0__["useCallback"])(() => {
173
344
  setIsSet(false);
174
- }, [options == null ? void 0 : options.clearPolicy]);
175
- const set = Object(react__WEBPACK_IMPORTED_MODULE_0__["useCallback"])(() => {
176
- if (isSet) {
177
- clear();
178
- } // This will cause the useEffect below to re-run
345
+ action();
346
+ }, [action]);
347
+ const actionRef = Object(_internal_use_updating_ref_js__WEBPACK_IMPORTED_MODULE_2__[/* useUpdatingRef */ "a"])(wrappedAction);
348
+ const clear = Object(react__WEBPACK_IMPORTED_MODULE_0__["useCallback"])(policy => {
349
+ var _policy;
179
350
 
351
+ policy = (_policy = policy) != null ? _policy : options == null ? void 0 : options.clearPolicy;
180
352
 
181
- setIsSet(true);
182
- }, [clear, isSet]);
353
+ if (isSet && policy === _util_policies_js__WEBPACK_IMPORTED_MODULE_1__[/* ClearPolicy */ "a"].Resolve) {
354
+ actionRef.current();
355
+ }
356
+
357
+ setIsSet(false);
358
+ }, // react-hooks/exhaustive-deps doesn't require refs to be
359
+ // listed in the deps array. Unfortunately, in this situation
360
+ // it doesn't recognized actionRef as a ref.
361
+ [actionRef, isSet, options == null ? void 0 : options.clearPolicy]);
362
+ const runOnUnmountRef = Object(_internal_use_updating_ref_js__WEBPACK_IMPORTED_MODULE_2__[/* useUpdatingRef */ "a"])(isSet && (options == null ? void 0 : options.clearPolicy) === _util_policies_js__WEBPACK_IMPORTED_MODULE_1__[/* ClearPolicy */ "a"].Resolve);
183
363
  Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => {
184
- if (isSet && mountedRef.current) {
185
- const timeout = window.setTimeout(() => {
364
+ return () => {
365
+ // This code will only run with the component using this
366
+ // hook is unmounted.
367
+ // eslint-disable-next-line react-hooks/exhaustive-deps
368
+ if (runOnUnmountRef.current) {
369
+ // eslint-disable-next-line react-hooks/exhaustive-deps
186
370
  actionRef.current();
187
- setIsSet(false);
188
- }, timeoutMs);
189
- return () => {
190
- window.clearTimeout(timeout);
191
-
192
- if (!mountedRef.current) {
193
- clear();
194
- }
195
- };
196
- }
197
- }, [clear, isSet, timeoutMs]);
371
+ }
372
+ }; // This eslint rule doesn't realize actionRef and runOnUnmountRef
373
+ // a both refs and thus do not have to be listed as deps.
374
+ // eslint-disable-next-line react-hooks/exhaustive-deps
375
+ }, []);
376
+ Object(_use_timeout_js__WEBPACK_IMPORTED_MODULE_3__[/* useTimeout */ "a"])(wrappedAction, timeoutMs, isSet);
198
377
  return {
199
378
  isSet,
200
379
  set,
@@ -203,14 +382,14 @@ function useTimeout(action, timeoutMs, options) {
203
382
  }
204
383
 
205
384
  /***/ }),
206
- /* 4 */
385
+ /* 8 */
207
386
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
208
387
 
209
388
  "use strict";
210
389
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return ActionSchedulerProvider; });
211
390
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
212
391
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
213
- /* harmony import */ var _util_action_scheduler_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
392
+ /* harmony import */ var _util_action_scheduler_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
214
393
 
215
394
 
216
395
 
@@ -244,14 +423,14 @@ class ActionSchedulerProvider extends react__WEBPACK_IMPORTED_MODULE_0__["Compon
244
423
  }
245
424
 
246
425
  /***/ }),
247
- /* 5 */
426
+ /* 9 */
248
427
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
249
428
 
250
429
  "use strict";
251
430
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return ActionScheduler; });
252
- /* harmony import */ var _timeout_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
253
- /* harmony import */ var _interval_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
254
- /* harmony import */ var _animation_frame_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(8);
431
+ /* harmony import */ var _timeout_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(10);
432
+ /* harmony import */ var _interval_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11);
433
+ /* harmony import */ var _animation_frame_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(12);
255
434
 
256
435
 
257
436
 
@@ -332,7 +511,7 @@ ActionScheduler.NoopAction = {
332
511
  };
333
512
 
334
513
  /***/ }),
335
- /* 6 */
514
+ /* 10 */
336
515
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
337
516
 
338
517
  "use strict";
@@ -443,7 +622,7 @@ class Timeout {
443
622
  }
444
623
 
445
624
  /***/ }),
446
- /* 7 */
625
+ /* 11 */
447
626
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
448
627
 
449
628
  "use strict";
@@ -552,7 +731,7 @@ class Interval {
552
731
  }
553
732
 
554
733
  /***/ }),
555
- /* 8 */
734
+ /* 12 */
556
735
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
557
736
 
558
737
  "use strict";
@@ -659,7 +838,7 @@ class AnimationFrame {
659
838
  }
660
839
 
661
840
  /***/ }),
662
- /* 9 */
841
+ /* 13 */
663
842
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
664
843
 
665
844
  "use strict";
@@ -669,11 +848,23 @@ __webpack_require__.r(__webpack_exports__);
669
848
 
670
849
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ClearPolicy", function() { return _util_policies_js__WEBPACK_IMPORTED_MODULE_0__["a"]; });
671
850
 
672
- /* harmony import */ var _components_with_action_scheduler_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
851
+ /* harmony import */ var _components_with_action_scheduler_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
673
852
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "withActionScheduler", function() { return _components_with_action_scheduler_js__WEBPACK_IMPORTED_MODULE_1__["a"]; });
674
853
 
675
- /* harmony import */ var _hooks_use_timeout_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
676
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useTimeout", function() { return _hooks_use_timeout_js__WEBPACK_IMPORTED_MODULE_2__["a"]; });
854
+ /* harmony import */ var _hooks_use_interval_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
855
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useInterval", function() { return _hooks_use_interval_js__WEBPACK_IMPORTED_MODULE_2__["a"]; });
856
+
857
+ /* harmony import */ var _hooks_use_timeout_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
858
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useTimeout", function() { return _hooks_use_timeout_js__WEBPACK_IMPORTED_MODULE_3__["a"]; });
859
+
860
+ /* harmony import */ var _hooks_use_scheduled_interval_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6);
861
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useScheduledInterval", function() { return _hooks_use_scheduled_interval_js__WEBPACK_IMPORTED_MODULE_4__["a"]; });
862
+
863
+ /* harmony import */ var _hooks_use_scheduled_timeout_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(7);
864
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useScheduledTimeout", function() { return _hooks_use_scheduled_timeout_js__WEBPACK_IMPORTED_MODULE_5__["a"]; });
865
+
866
+
867
+
677
868
 
678
869
 
679
870
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-timing",
3
3
  "private": false,
4
- "version": "2.0.3",
4
+ "version": "2.1.0",
5
5
  "design": "v1",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -20,6 +20,5 @@
20
20
  "wb-dev-build-settings": "^0.2.0"
21
21
  },
22
22
  "author": "",
23
- "license": "MIT",
24
- "gitHead": "9ebea88533e702011165072f090a377e02fa3f0f"
23
+ "license": "MIT"
25
24
  }