@oscarpalmer/atoms 0.47.1 → 0.49.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/dist/js/index.js CHANGED
@@ -859,9 +859,9 @@ function isWhen(value) {
859
859
  return is3(value, /^when$/) && typeof value.then === "function";
860
860
  }
861
861
  function repeat(callback, options) {
862
- return timer("repeat", callback, options ?? {}).start();
862
+ return timer("repeat", callback, options ?? {}, true);
863
863
  }
864
- var timer = function(type, callback, partial) {
864
+ var timer = function(type, callback, partial, start) {
865
865
  const isRepeated2 = type === "repeat";
866
866
  const options = {
867
867
  afterCallback: partial.afterCallback,
@@ -872,7 +872,9 @@ var timer = function(type, callback, partial) {
872
872
  };
873
873
  const state = {
874
874
  callback,
875
- active: false
875
+ active: false,
876
+ minimum: options.interval - options.interval % milliseconds / 2,
877
+ paused: false
876
878
  };
877
879
  const instance = Object.create({
878
880
  continue() {
@@ -901,31 +903,48 @@ var timer = function(type, callback, partial) {
901
903
  get() {
902
904
  return state.active;
903
905
  }
906
+ },
907
+ paused: {
908
+ get() {
909
+ return state.paused;
910
+ }
904
911
  }
905
912
  });
913
+ if (start) {
914
+ instance.start();
915
+ }
906
916
  return instance;
907
917
  };
908
918
  function wait(callback, options) {
909
919
  return timer("wait", callback, options == null || typeof options === "number" ? {
910
920
  interval: options
911
- } : options).start();
921
+ } : options, true);
912
922
  }
913
923
  function when(condition, options) {
914
924
  let rejecter;
915
925
  let resolver;
916
- const repeated = repeat(() => {
926
+ const repeated = timer("repeat", () => {
917
927
  if (condition()) {
918
928
  repeated.stop();
919
929
  resolver?.();
920
930
  }
921
931
  }, {
932
+ afterCallback() {
933
+ if (!repeated.paused) {
934
+ if (condition()) {
935
+ resolver?.();
936
+ } else {
937
+ rejecter?.();
938
+ }
939
+ }
940
+ },
922
941
  errorCallback() {
923
942
  rejecter?.();
924
943
  },
925
944
  count: options?.count,
926
945
  interval: options?.interval,
927
946
  timeout: options?.timeout
928
- });
947
+ }, false);
929
948
  const promise = new Promise((resolve, reject) => {
930
949
  resolver = resolve;
931
950
  rejecter = reject;
@@ -967,25 +986,31 @@ var work2 = function(type, timer2, state, options, isRepeated2) {
967
986
  return timer2;
968
987
  }
969
988
  const { count, interval, timeout } = options;
989
+ const { minimum } = state;
970
990
  if (["pause", "stop"].includes(type)) {
991
+ const isStop = type === "stop";
971
992
  activeTimers.delete(timer2);
972
993
  cancelAnimationFrame(state.frame);
973
- options.afterCallback?.(false);
994
+ if (isStop) {
995
+ options.afterCallback?.(false);
996
+ }
974
997
  state.active = false;
975
998
  state.frame = undefined;
976
- if (type === "stop") {
999
+ state.paused = !isStop;
1000
+ if (isStop) {
977
1001
  state.elapsed = undefined;
978
1002
  state.index = undefined;
979
1003
  }
980
1004
  return timer2;
981
1005
  }
982
1006
  state.active = true;
1007
+ state.paused = false;
983
1008
  const canTimeout = timeout > 0 && timeout < Number.POSITIVE_INFINITY;
984
1009
  const elapsed = type === "continue" ? +(state.elapsed ?? 0) : 0;
985
1010
  let index = type === "continue" ? +(state.index ?? 0) : 0;
986
1011
  state.elapsed = elapsed;
987
1012
  state.index = index;
988
- const total = (count === Number.POSITIVE_INFINITY ? Number.POSITIVE_INFINITY : (count - index) * (interval > 0 ? interval : 62.5)) - elapsed;
1013
+ const total = (count === Number.POSITIVE_INFINITY ? Number.POSITIVE_INFINITY : (count - index) * (interval > 0 ? interval : milliseconds)) - elapsed;
989
1014
  let current;
990
1015
  let start;
991
1016
  function finish(finished, error) {
@@ -1008,7 +1033,7 @@ var work2 = function(type, timer2, state, options, isRepeated2) {
1008
1033
  const time2 = timestamp - current;
1009
1034
  state.elapsed = elapsed + (current - start);
1010
1035
  const finished = time2 - elapsed >= total;
1011
- if (finished || time2 - 2 < interval && interval < time2 + 2) {
1036
+ if (finished || time2 >= minimum) {
1012
1037
  if (state.active) {
1013
1038
  state.callback(isRepeated2 ? index : undefined);
1014
1039
  }
@@ -1034,6 +1059,7 @@ var work2 = function(type, timer2, state, options, isRepeated2) {
1034
1059
  };
1035
1060
  var activeTimers = new Set;
1036
1061
  var hiddenTimers = new Set;
1062
+ var milliseconds = 16.666666666666668;
1037
1063
  document.addEventListener("visibilitychange", () => {
1038
1064
  if (document.hidden) {
1039
1065
  for (const timer2 of activeTimers) {
package/dist/js/timer.js CHANGED
@@ -18,9 +18,9 @@ function isWhen(value) {
18
18
  return is(value, /^when$/) && typeof value.then === "function";
19
19
  }
20
20
  function repeat(callback, options) {
21
- return timer("repeat", callback, options ?? {}).start();
21
+ return timer("repeat", callback, options ?? {}, true);
22
22
  }
23
- var timer = function(type, callback, partial) {
23
+ var timer = function(type, callback, partial, start) {
24
24
  const isRepeated2 = type === "repeat";
25
25
  const options = {
26
26
  afterCallback: partial.afterCallback,
@@ -31,7 +31,9 @@ var timer = function(type, callback, partial) {
31
31
  };
32
32
  const state = {
33
33
  callback,
34
- active: false
34
+ active: false,
35
+ minimum: options.interval - options.interval % milliseconds / 2,
36
+ paused: false
35
37
  };
36
38
  const instance = Object.create({
37
39
  continue() {
@@ -60,31 +62,48 @@ var timer = function(type, callback, partial) {
60
62
  get() {
61
63
  return state.active;
62
64
  }
65
+ },
66
+ paused: {
67
+ get() {
68
+ return state.paused;
69
+ }
63
70
  }
64
71
  });
72
+ if (start) {
73
+ instance.start();
74
+ }
65
75
  return instance;
66
76
  };
67
77
  function wait(callback, options) {
68
78
  return timer("wait", callback, options == null || typeof options === "number" ? {
69
79
  interval: options
70
- } : options).start();
80
+ } : options, true);
71
81
  }
72
82
  function when(condition, options) {
73
83
  let rejecter;
74
84
  let resolver;
75
- const repeated = repeat(() => {
85
+ const repeated = timer("repeat", () => {
76
86
  if (condition()) {
77
87
  repeated.stop();
78
88
  resolver?.();
79
89
  }
80
90
  }, {
91
+ afterCallback() {
92
+ if (!repeated.paused) {
93
+ if (condition()) {
94
+ resolver?.();
95
+ } else {
96
+ rejecter?.();
97
+ }
98
+ }
99
+ },
81
100
  errorCallback() {
82
101
  rejecter?.();
83
102
  },
84
103
  count: options?.count,
85
104
  interval: options?.interval,
86
105
  timeout: options?.timeout
87
- });
106
+ }, false);
88
107
  const promise = new Promise((resolve, reject) => {
89
108
  resolver = resolve;
90
109
  rejecter = reject;
@@ -126,25 +145,31 @@ var work = function(type, timer2, state, options, isRepeated2) {
126
145
  return timer2;
127
146
  }
128
147
  const { count, interval, timeout } = options;
148
+ const { minimum } = state;
129
149
  if (["pause", "stop"].includes(type)) {
150
+ const isStop = type === "stop";
130
151
  activeTimers.delete(timer2);
131
152
  cancelAnimationFrame(state.frame);
132
- options.afterCallback?.(false);
153
+ if (isStop) {
154
+ options.afterCallback?.(false);
155
+ }
133
156
  state.active = false;
134
157
  state.frame = undefined;
135
- if (type === "stop") {
158
+ state.paused = !isStop;
159
+ if (isStop) {
136
160
  state.elapsed = undefined;
137
161
  state.index = undefined;
138
162
  }
139
163
  return timer2;
140
164
  }
141
165
  state.active = true;
166
+ state.paused = false;
142
167
  const canTimeout = timeout > 0 && timeout < Number.POSITIVE_INFINITY;
143
168
  const elapsed = type === "continue" ? +(state.elapsed ?? 0) : 0;
144
169
  let index = type === "continue" ? +(state.index ?? 0) : 0;
145
170
  state.elapsed = elapsed;
146
171
  state.index = index;
147
- const total = (count === Number.POSITIVE_INFINITY ? Number.POSITIVE_INFINITY : (count - index) * (interval > 0 ? interval : 62.5)) - elapsed;
172
+ const total = (count === Number.POSITIVE_INFINITY ? Number.POSITIVE_INFINITY : (count - index) * (interval > 0 ? interval : milliseconds)) - elapsed;
148
173
  let current;
149
174
  let start;
150
175
  function finish(finished, error) {
@@ -167,7 +192,7 @@ var work = function(type, timer2, state, options, isRepeated2) {
167
192
  const time = timestamp - current;
168
193
  state.elapsed = elapsed + (current - start);
169
194
  const finished = time - elapsed >= total;
170
- if (finished || time - 2 < interval && interval < time + 2) {
195
+ if (finished || time >= minimum) {
171
196
  if (state.active) {
172
197
  state.callback(isRepeated2 ? index : undefined);
173
198
  }
@@ -193,6 +218,7 @@ var work = function(type, timer2, state, options, isRepeated2) {
193
218
  };
194
219
  var activeTimers = new Set;
195
220
  var hiddenTimers = new Set;
221
+ var milliseconds = 16.666666666666668;
196
222
  document.addEventListener("visibilitychange", () => {
197
223
  if (document.hidden) {
198
224
  for (const timer2 of activeTimers) {
package/dist/js/timer.mjs CHANGED
@@ -18,9 +18,9 @@ function isWhen(value) {
18
18
  return is(value, /^when$/) && typeof value.then === "function";
19
19
  }
20
20
  function repeat(callback, options) {
21
- return timer("repeat", callback, options ?? {}).start();
21
+ return timer("repeat", callback, options ?? {}, true);
22
22
  }
23
- var timer = function(type, callback, partial) {
23
+ var timer = function(type, callback, partial, start) {
24
24
  const isRepeated2 = type === "repeat";
25
25
  const options = {
26
26
  afterCallback: partial.afterCallback,
@@ -31,7 +31,9 @@ var timer = function(type, callback, partial) {
31
31
  };
32
32
  const state = {
33
33
  callback,
34
- active: false
34
+ active: false,
35
+ minimum: options.interval - options.interval % milliseconds / 2,
36
+ paused: false
35
37
  };
36
38
  const instance = Object.create({
37
39
  continue() {
@@ -60,31 +62,48 @@ var timer = function(type, callback, partial) {
60
62
  get() {
61
63
  return state.active;
62
64
  }
65
+ },
66
+ paused: {
67
+ get() {
68
+ return state.paused;
69
+ }
63
70
  }
64
71
  });
72
+ if (start) {
73
+ instance.start();
74
+ }
65
75
  return instance;
66
76
  };
67
77
  function wait(callback, options) {
68
78
  return timer("wait", callback, options == null || typeof options === "number" ? {
69
79
  interval: options
70
- } : options).start();
80
+ } : options, true);
71
81
  }
72
82
  function when(condition, options) {
73
83
  let rejecter;
74
84
  let resolver;
75
- const repeated = repeat(() => {
85
+ const repeated = timer("repeat", () => {
76
86
  if (condition()) {
77
87
  repeated.stop();
78
88
  resolver?.();
79
89
  }
80
90
  }, {
91
+ afterCallback() {
92
+ if (!repeated.paused) {
93
+ if (condition()) {
94
+ resolver?.();
95
+ } else {
96
+ rejecter?.();
97
+ }
98
+ }
99
+ },
81
100
  errorCallback() {
82
101
  rejecter?.();
83
102
  },
84
103
  count: options?.count,
85
104
  interval: options?.interval,
86
105
  timeout: options?.timeout
87
- });
106
+ }, false);
88
107
  const promise = new Promise((resolve, reject) => {
89
108
  resolver = resolve;
90
109
  rejecter = reject;
@@ -126,25 +145,31 @@ var work = function(type, timer2, state, options, isRepeated2) {
126
145
  return timer2;
127
146
  }
128
147
  const { count, interval, timeout } = options;
148
+ const { minimum } = state;
129
149
  if (["pause", "stop"].includes(type)) {
150
+ const isStop = type === "stop";
130
151
  activeTimers.delete(timer2);
131
152
  cancelAnimationFrame(state.frame);
132
- options.afterCallback?.(false);
153
+ if (isStop) {
154
+ options.afterCallback?.(false);
155
+ }
133
156
  state.active = false;
134
157
  state.frame = undefined;
135
- if (type === "stop") {
158
+ state.paused = !isStop;
159
+ if (isStop) {
136
160
  state.elapsed = undefined;
137
161
  state.index = undefined;
138
162
  }
139
163
  return timer2;
140
164
  }
141
165
  state.active = true;
166
+ state.paused = false;
142
167
  const canTimeout = timeout > 0 && timeout < Number.POSITIVE_INFINITY;
143
168
  const elapsed = type === "continue" ? +(state.elapsed ?? 0) : 0;
144
169
  let index = type === "continue" ? +(state.index ?? 0) : 0;
145
170
  state.elapsed = elapsed;
146
171
  state.index = index;
147
- const total = (count === Number.POSITIVE_INFINITY ? Number.POSITIVE_INFINITY : (count - index) * (interval > 0 ? interval : 62.5)) - elapsed;
172
+ const total = (count === Number.POSITIVE_INFINITY ? Number.POSITIVE_INFINITY : (count - index) * (interval > 0 ? interval : milliseconds)) - elapsed;
148
173
  let current;
149
174
  let start;
150
175
  function finish(finished, error) {
@@ -167,7 +192,7 @@ var work = function(type, timer2, state, options, isRepeated2) {
167
192
  const time = timestamp - current;
168
193
  state.elapsed = elapsed + (current - start);
169
194
  const finished = time - elapsed >= total;
170
- if (finished || time - 2 < interval && interval < time + 2) {
195
+ if (finished || time >= minimum) {
171
196
  if (state.active) {
172
197
  state.callback(isRepeated2 ? index : undefined);
173
198
  }
@@ -193,6 +218,7 @@ var work = function(type, timer2, state, options, isRepeated2) {
193
218
  };
194
219
  var activeTimers = new Set;
195
220
  var hiddenTimers = new Set;
221
+ var milliseconds = 16.666666666666668;
196
222
  document.addEventListener("visibilitychange", () => {
197
223
  if (document.hidden) {
198
224
  for (const timer2 of activeTimers) {
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "description": "Sweet little atomic goodies…",
10
10
  "devDependencies": {
11
- "@biomejs/biome": "^1.7",
11
+ "@biomejs/biome": "^1.8",
12
12
  "@happy-dom/global-registrator": "^14.12",
13
13
  "bun": "^1.1",
14
14
  "sass": "^1.77",
@@ -127,5 +127,5 @@
127
127
  },
128
128
  "type": "module",
129
129
  "types": "./types/index.d.ts",
130
- "version": "0.47.1"
130
+ "version": "0.49.0"
131
131
  }
package/src/js/timer.ts CHANGED
@@ -29,6 +29,10 @@ type BaseTimer = {
29
29
  * Is the timer running?
30
30
  */
31
31
  get active(): boolean;
32
+ /**
33
+ * Is the timer paused?
34
+ */
35
+ get paused(): boolean;
32
36
  };
33
37
 
34
38
  type OptionsWithCount = {
@@ -61,6 +65,8 @@ type State = {
61
65
  elapsed?: number;
62
66
  frame?: number;
63
67
  index?: number;
68
+ minimum: number;
69
+ paused: boolean;
64
70
  };
65
71
 
66
72
  /**
@@ -119,9 +125,21 @@ type WhenOptions = {} & OptionsWithCount;
119
125
 
120
126
  type WorkType = 'continue' | 'pause' | 'restart' | 'start' | 'stop';
121
127
 
128
+ /**
129
+ * A set of all active timers
130
+ */
122
131
  const activeTimers = new Set<Timer>();
132
+
133
+ /**
134
+ * A set of timers that were paused due to the document being hidden
135
+ */
123
136
  const hiddenTimers = new Set<Timer>();
124
137
 
138
+ /**
139
+ * Milliseconds in a frame, probably ;-)
140
+ */
141
+ const milliseconds = 1_000 / 60;
142
+
125
143
  function getValueOrDefault(value: unknown, defaultValue: number): number {
126
144
  return typeof value === 'number' && value > 0 ? value : defaultValue;
127
145
  }
@@ -171,13 +189,14 @@ export function repeat(
171
189
  callback: IndexedCallback,
172
190
  options?: Partial<RepeatOptions>,
173
191
  ): Timer {
174
- return timer('repeat', callback, options ?? {}).start();
192
+ return timer('repeat', callback, options ?? {}, true);
175
193
  }
176
194
 
177
195
  function timer(
178
196
  type: 'repeat' | 'wait',
179
197
  callback: AnyCallback,
180
198
  partial: Partial<TimerOptions>,
199
+ start: boolean,
181
200
  ): Timer {
182
201
  const isRepeated = type === 'repeat';
183
202
 
@@ -198,6 +217,8 @@ function timer(
198
217
  const state: State = {
199
218
  callback,
200
219
  active: false,
220
+ minimum: options.interval - (options.interval % milliseconds) / 2,
221
+ paused: false,
201
222
  };
202
223
 
203
224
  const instance = Object.create({
@@ -229,8 +250,17 @@ function timer(
229
250
  return state.active;
230
251
  },
231
252
  },
253
+ paused: {
254
+ get() {
255
+ return state.paused;
256
+ },
257
+ },
232
258
  });
233
259
 
260
+ if (start) {
261
+ instance.start();
262
+ }
263
+
234
264
  return instance;
235
265
  }
236
266
 
@@ -266,7 +296,8 @@ export function wait(
266
296
  interval: options,
267
297
  }
268
298
  : options,
269
- ).start();
299
+ true,
300
+ );
270
301
  }
271
302
 
272
303
  /**
@@ -280,7 +311,8 @@ export function when(
280
311
  let rejecter: () => void;
281
312
  let resolver: () => void;
282
313
 
283
- const repeated = repeat(
314
+ const repeated = timer(
315
+ 'repeat',
284
316
  () => {
285
317
  if (condition()) {
286
318
  repeated.stop();
@@ -289,6 +321,15 @@ export function when(
289
321
  }
290
322
  },
291
323
  {
324
+ afterCallback() {
325
+ if (!repeated.paused) {
326
+ if (condition()) {
327
+ resolver?.();
328
+ } else {
329
+ rejecter?.();
330
+ }
331
+ }
332
+ },
292
333
  errorCallback() {
293
334
  rejecter?.();
294
335
  },
@@ -296,6 +337,7 @@ export function when(
296
337
  interval: options?.interval,
297
338
  timeout: options?.timeout,
298
339
  },
340
+ false,
299
341
  );
300
342
 
301
343
  const promise = new Promise<void>((resolve, reject) => {
@@ -356,18 +398,24 @@ function work(
356
398
  }
357
399
 
358
400
  const {count, interval, timeout} = options;
401
+ const {minimum} = state;
359
402
 
360
403
  if (['pause', 'stop'].includes(type)) {
404
+ const isStop = type === 'stop';
405
+
361
406
  activeTimers.delete(timer);
362
407
 
363
408
  cancelAnimationFrame(state.frame as never);
364
409
 
365
- options.afterCallback?.(false);
410
+ if (isStop) {
411
+ options.afterCallback?.(false);
412
+ }
366
413
 
367
414
  state.active = false;
368
415
  state.frame = undefined;
416
+ state.paused = !isStop;
369
417
 
370
- if (type === 'stop') {
418
+ if (isStop) {
371
419
  state.elapsed = undefined;
372
420
  state.index = undefined;
373
421
  }
@@ -376,6 +424,7 @@ function work(
376
424
  }
377
425
 
378
426
  state.active = true;
427
+ state.paused = false;
379
428
 
380
429
  const canTimeout = timeout > 0 && timeout < Number.POSITIVE_INFINITY;
381
430
  const elapsed = type === 'continue' ? +(state.elapsed ?? 0) : 0;
@@ -388,7 +437,7 @@ function work(
388
437
  const total =
389
438
  (count === Number.POSITIVE_INFINITY
390
439
  ? Number.POSITIVE_INFINITY
391
- : (count - index) * (interval > 0 ? interval : 1000 / 16)) - elapsed;
440
+ : (count - index) * (interval > 0 ? interval : milliseconds)) - elapsed;
392
441
 
393
442
  let current: DOMHighResTimeStamp | null;
394
443
  let start: DOMHighResTimeStamp | null;
@@ -422,7 +471,7 @@ function work(
422
471
 
423
472
  const finished = time - elapsed >= total;
424
473
 
425
- if (finished || (time - 2 < interval && interval < time + 2)) {
474
+ if (finished || time >= minimum) {
426
475
  if (state.active) {
427
476
  state.callback((isRepeated ? index : undefined) as never);
428
477
  }
package/types/timer.d.ts CHANGED
@@ -22,6 +22,10 @@ type BaseTimer = {
22
22
  * Is the timer running?
23
23
  */
24
24
  get active(): boolean;
25
+ /**
26
+ * Is the timer paused?
27
+ */
28
+ get paused(): boolean;
25
29
  };
26
30
  type OptionsWithCount = {
27
31
  /**