@oscarpalmer/timer 0.48.0 → 0.50.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/src/timer.ts CHANGED
@@ -1,16 +1,26 @@
1
- import {noop} from '@oscarpalmer/atoms/function';
2
- import {WORK_CONTINUE, WORK_PAUSE, WORK_RESTART, WORK_START, WORK_STOP} from './constants';
1
+ import {SYMBOL, WORK_CONTINUE, WORK_PAUSE, WORK_RESTART, WORK_START, WORK_STOP} from './constants';
3
2
  import type {Timer, TimerName, TimerOptions, TimerState} from './models';
4
3
  import {work} from './work';
5
4
 
6
- export function createTimer(
5
+ // #region Types
6
+
7
+ type InternalTimer = {
8
+ [SYMBOL]: TimerState;
9
+ } & Timer;
10
+
11
+ // #endregion
12
+
13
+ // #region Instances
14
+
15
+ function Timer(
16
+ this: any,
7
17
  name: TimerName,
8
- pick: Pick<TimerState, 'callback' | 'trace'>,
18
+ state: Pick<TimerState, 'callback' | 'trace'>,
9
19
  options: TimerOptions,
10
20
  start: boolean,
11
- ): Timer {
12
- const state: TimerState = {
13
- ...pick,
21
+ ) {
22
+ this[SYMBOL] = {
23
+ ...state,
14
24
  name,
15
25
  options,
16
26
  active: false,
@@ -23,43 +33,76 @@ export function createTimer(
23
33
  total: 0,
24
34
  };
25
35
 
26
- const instance = {
27
- continue: () => work(WORK_CONTINUE, state),
28
- destroy: noop,
29
- pause: () => work(WORK_PAUSE, state),
30
- restart: () => work(WORK_RESTART, state),
31
- start: () => work(WORK_START, state),
32
- stop: () => work(WORK_STOP, state),
33
- };
34
-
35
- Object.defineProperties(instance, {
36
- $timer: {
37
- enumerable: false,
38
- value: name,
39
- },
40
- active: {
41
- enumerable: true,
42
- get: () => state.active && !state.paused,
43
- },
44
- destroyed: {
45
- enumerable: true,
46
- value: false,
47
- },
48
- paused: {
49
- enumerable: true,
50
- get: () => state.paused,
51
- },
52
- trace: {
53
- enumerable: true,
54
- get: () => ((globalThis._oscarpalmer_timer_debug ?? false) ? state.trace : undefined),
55
- },
56
- });
57
-
58
- state.timer = Object.freeze(instance) as Timer;
59
-
60
36
  if (start) {
61
- state.timer.start();
37
+ startTimer.call(this);
62
38
  }
39
+ }
40
+
41
+ Timer.prototype.continue = continueTimer;
42
+ Timer.prototype.pause = pauseTimer;
43
+ Timer.prototype.restart = restartTimer;
44
+ Timer.prototype.start = startTimer;
45
+ Timer.prototype.stop = stopTimer;
46
+
47
+ Object.defineProperties(Timer.prototype, {
48
+ active: {
49
+ enumerable: true,
50
+ get: getTimerActive,
51
+ },
52
+ paused: {
53
+ enumerable: true,
54
+ get: getTimerPaused,
55
+ },
56
+ trace: {
57
+ enumerable: true,
58
+ get: getTimerTrace,
59
+ },
60
+ });
61
+
62
+ // #endregion
63
+
64
+ // #region Functions
65
+
66
+ function continueTimer(this: InternalTimer): Timer {
67
+ return work(WORK_CONTINUE, this[SYMBOL]);
68
+ }
69
+
70
+ export function createTimer(
71
+ name: TimerName,
72
+ state: Pick<TimerState, 'callback' | 'trace'>,
73
+ options: TimerOptions,
74
+ start: boolean,
75
+ ): Timer {
76
+ // @ts-expect-error All good, no worries :-)
77
+ return new Timer(name, state, options, start);
78
+ }
79
+
80
+ function getTimerActive(this: InternalTimer): boolean {
81
+ return this[SYMBOL].active && !this[SYMBOL].paused;
82
+ }
83
+
84
+ function getTimerPaused(this: InternalTimer): boolean {
85
+ return this[SYMBOL].paused;
86
+ }
63
87
 
64
- return state.timer;
88
+ function getTimerTrace(this: InternalTimer): string | undefined {
89
+ return (globalThis._oscarpalmer_timer_debug ?? false) ? this[SYMBOL].trace : undefined;
65
90
  }
91
+
92
+ function pauseTimer(this: InternalTimer): Timer {
93
+ return work(WORK_PAUSE, this[SYMBOL]);
94
+ }
95
+
96
+ function restartTimer(this: InternalTimer): Timer {
97
+ return work(WORK_RESTART, this[SYMBOL]);
98
+ }
99
+
100
+ function startTimer(this: InternalTimer): Timer {
101
+ return work(WORK_START, this[SYMBOL]);
102
+ }
103
+
104
+ function stopTimer(this: InternalTimer): Timer {
105
+ return work(WORK_STOP, this[SYMBOL]);
106
+ }
107
+
108
+ // #endregion
package/src/wait.ts CHANGED
@@ -4,6 +4,8 @@ import './global';
4
4
  import {type Timer, TimerTrace} from './models';
5
5
  import {createTimer} from './timer';
6
6
 
7
+ // #region Functions
8
+
7
9
  /**
8
10
  * Create a waiting timer
9
11
  *
@@ -28,3 +30,5 @@ export function wait(callback: () => void, time?: number): Timer {
28
30
  true,
29
31
  );
30
32
  }
33
+
34
+ // #endregion
package/src/when.ts CHANGED
@@ -1,7 +1,13 @@
1
- import {noop} from '@oscarpalmer/atoms/function';
2
- import {MESSAGE_STARTED, TYPE_WHEN, WORK_CONTINUE, WORK_PAUSE, WORK_STOP} from './constants';
3
- import {getValidNumber, getValidTimeout} from './misc';
1
+ import {
2
+ MESSAGE_STARTED,
3
+ SYMBOL,
4
+ TYPE_WHEN,
5
+ WORK_CONTINUE,
6
+ WORK_PAUSE,
7
+ WORK_STOP,
8
+ } from './constants';
4
9
  import './global';
10
+ import {getValidNumber, getValidTimeout} from './misc';
5
11
  import {
6
12
  TimerTrace,
7
13
  type When,
@@ -11,6 +17,95 @@ import {
11
17
  } from './models';
12
18
  import {createTimer} from './timer';
13
19
 
20
+ // #region Types
21
+
22
+ type InternalWhen = {
23
+ [SYMBOL]: WhenState;
24
+ } & When;
25
+
26
+ // #endregion
27
+
28
+ // #region Instances
29
+
30
+ function When(this: any, condition: () => boolean, options: WhenOptions) {
31
+ const state: WhenState = {
32
+ name: TYPE_WHEN,
33
+ promise: undefined as never,
34
+ result: false,
35
+ started: false,
36
+ timer: undefined as never,
37
+ };
38
+
39
+ this[SYMBOL] = state;
40
+
41
+ state.promise = new Promise<void>((resolve, reject) => {
42
+ state.resolver = resolve;
43
+ state.rejecter = reject;
44
+ });
45
+
46
+ state.timer = createTimer(
47
+ TYPE_WHEN,
48
+ {
49
+ callback: () => onCallback(condition, state),
50
+ trace: new TimerTrace().stack,
51
+ },
52
+ {
53
+ ...options,
54
+ onAfter: () => onAfter(state),
55
+ onError: () => state.rejecter?.(),
56
+ },
57
+ false,
58
+ );
59
+ }
60
+
61
+ When.prototype.continue = continueWhen;
62
+ When.prototype.pause = pauseWhen;
63
+ When.prototype.start = startWhen;
64
+ When.prototype.stop = stopWhen;
65
+
66
+ Object.defineProperties(When.prototype, {
67
+ active: {
68
+ get: getWhenActive,
69
+ },
70
+ paused: {
71
+ get: getWhenPaused,
72
+ },
73
+ trace: {
74
+ get: getWhenTrace,
75
+ },
76
+ });
77
+
78
+ // #endregion
79
+
80
+ // #region Functions
81
+
82
+ function continueWhen(this: InternalWhen): When {
83
+ return onWhen(WORK_CONTINUE, this, this[SYMBOL]);
84
+ }
85
+
86
+ function getWhenOptions(input: unknown): WhenOptions {
87
+ const options =
88
+ typeof input === 'object' && input !== null ? (input as Partial<WhenOptions>) : {};
89
+
90
+ return {
91
+ count: getValidNumber(options?.count),
92
+ interval: getValidNumber(options?.interval),
93
+ timeout: getValidTimeout(options?.timeout),
94
+ };
95
+ }
96
+
97
+ function getWhenActive(this: InternalWhen): boolean {
98
+ return this[SYMBOL].timer.active;
99
+ }
100
+
101
+ function getWhenPaused(this: InternalWhen): boolean {
102
+ return this[SYMBOL].timer.paused;
103
+ }
104
+
105
+ function getWhenTrace(this: InternalWhen): string | undefined {
106
+ return (globalThis._oscarpalmer_timer_debug ?? false) ? this[SYMBOL].timer?.trace : undefined;
107
+ }
108
+
14
109
  function onAfter(state: WhenState): void {
15
110
  if (state.result) {
16
111
  state.resolver?.();
@@ -37,7 +132,13 @@ function onWhen(type: WorkHandlerType, instance: When, state: WhenState): When {
37
132
  return instance;
38
133
  }
39
134
 
40
- function startWhen(state: WhenState, resolve?: (() => void) | null): Promise<void> {
135
+ function pauseWhen(this: InternalWhen): When {
136
+ return onWhen(WORK_PAUSE, this, this[SYMBOL]);
137
+ }
138
+
139
+ function startWhen(this: InternalWhen, resolve?: (() => void) | null): Promise<void> {
140
+ const state = this[SYMBOL];
141
+
41
142
  if (state.started) {
42
143
  throw new Error(MESSAGE_STARTED);
43
144
  }
@@ -49,6 +150,10 @@ function startWhen(state: WhenState, resolve?: (() => void) | null): Promise<voi
49
150
  return state.promise.then(resolve);
50
151
  }
51
152
 
153
+ function stopWhen(this: InternalWhen): When {
154
+ return onWhen(WORK_STOP, this, this[SYMBOL]);
155
+ }
156
+
52
157
  /**
53
158
  * Create a conditional timer
54
159
  * @param condition Condition to check
@@ -56,66 +161,8 @@ function startWhen(state: WhenState, resolve?: (() => void) | null): Promise<voi
56
161
  * @returns Timer instance
57
162
  */
58
163
  export function when(condition: () => boolean, options?: Partial<WhenOptions>): When {
59
- const state: WhenState = {
60
- promise: undefined as never,
61
- result: false,
62
- started: false,
63
- timer: undefined as never,
64
- };
65
-
66
- let instance: When;
67
-
68
- state.promise = new Promise<void>((resolve, reject) => {
69
- state.resolver = resolve;
70
- state.rejecter = reject;
71
- });
72
-
73
- state.timer = createTimer(
74
- TYPE_WHEN,
75
- {
76
- callback: () => onCallback(condition, state),
77
- trace: new TimerTrace().stack,
78
- },
79
- {
80
- onAfter: () => onAfter(state),
81
- onError: () => state.rejecter?.(),
82
- count: getValidNumber(options?.count),
83
- interval: getValidNumber(options?.interval),
84
- timeout: getValidTimeout(options?.timeout),
85
- },
86
- false,
87
- );
88
-
89
- instance = {
90
- continue: () => onWhen(WORK_CONTINUE, instance, state),
91
- destroy: noop,
92
- pause: () => onWhen(WORK_PAUSE, instance, state),
93
- start: (resolve: never) => startWhen(state, resolve),
94
- stop: () => onWhen(WORK_STOP, instance, state),
95
- } as When;
96
-
97
- Object.defineProperties(instance, {
98
- $timer: {
99
- enumerable: false,
100
- value: TYPE_WHEN,
101
- },
102
- active: {
103
- enumerable: true,
104
- get: () => state.timer.active,
105
- },
106
- destroyed: {
107
- enumerable: true,
108
- value: false,
109
- },
110
- paused: {
111
- enumerable: true,
112
- get: () => state.timer.paused,
113
- },
114
- trace: {
115
- enumerable: true,
116
- get: () => ((globalThis._oscarpalmer_timer_debug ?? false) ? state.timer?.trace : undefined),
117
- },
118
- });
119
-
120
- return Object.freeze(instance) as When;
164
+ // @ts-expect-error All good, no worries :-)
165
+ return new When(condition, getWhenOptions(options));
121
166
  }
167
+
168
+ // #endregion
package/src/work.ts CHANGED
@@ -7,13 +7,15 @@ import {
7
7
  WORK_START,
8
8
  WORK_STOP,
9
9
  } from './constants';
10
- import {updateStates} from './misc';
10
+ import {startTimer, stopTimer, updateStates} from './misc';
11
11
  import type {Timer, TimerState, WorkHandlerType} from './models';
12
12
 
13
+ // #region Functions
14
+
13
15
  function finish(state: TimerState, success: boolean): void {
14
16
  updateStates(state);
15
17
 
16
- cancelAnimationFrame(state.frame as never);
18
+ stopTimer(state.frame as never);
17
19
 
18
20
  state.active = false;
19
21
  state.elapsed = 0;
@@ -34,14 +36,16 @@ function ignore(type: WorkHandlerType, state: TimerState): boolean {
34
36
  return state.active && (type === WORK_CONTINUE || type === WORK_START);
35
37
  }
36
38
 
37
- function run(state: TimerState): (now: DOMHighResTimeStamp) => void {
38
- let last: DOMHighResTimeStamp | undefined;
39
+ function run(state: TimerState): () => void {
40
+ let last: number | undefined;
39
41
 
40
- return function step(now: DOMHighResTimeStamp): void {
42
+ return function step(): void {
41
43
  if (!state.active) {
42
44
  return;
43
45
  }
44
46
 
47
+ const now = performance.now();
48
+
45
49
  last ??= now;
46
50
 
47
51
  const difference = now - last;
@@ -77,7 +81,7 @@ function run(state: TimerState): (now: DOMHighResTimeStamp) => void {
77
81
  }
78
82
  }
79
83
 
80
- state.frame = requestAnimationFrame(step);
84
+ state.frame = startTimer(step);
81
85
  };
82
86
  }
83
87
 
@@ -92,7 +96,7 @@ function setState(type: WorkHandlerType, state: TimerState): void {
92
96
  export function stop(state: TimerState): Timer {
93
97
  updateStates(state);
94
98
 
95
- cancelAnimationFrame(state.frame as never);
99
+ stopTimer(state.frame as never);
96
100
 
97
101
  state.options.onAfter?.(false);
98
102
 
@@ -115,7 +119,7 @@ export function work(type: WorkHandlerType, state: TimerState, hide?: boolean):
115
119
  }
116
120
 
117
121
  if (type === WORK_PAUSE || type === WORK_RESTART) {
118
- cancelAnimationFrame(state.frame as never);
122
+ stopTimer(state.frame as never);
119
123
 
120
124
  state.frame = undefined;
121
125
  }
@@ -131,7 +135,9 @@ export function work(type: WorkHandlerType, state: TimerState, hide?: boolean):
131
135
 
132
136
  const runner = run(state);
133
137
 
134
- state.frame = requestAnimationFrame(runner);
138
+ state.frame = startTimer(runner);
135
139
 
136
140
  return state.timer;
137
141
  }
142
+
143
+ // #endregion