@oscarpalmer/atoms 0.41.4 → 0.43.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
@@ -597,6 +597,14 @@ function getString(value) {
597
597
  function titleCase(value) {
598
598
  return value.split(/\s+/).map((word) => capitalise(word)).join(" ");
599
599
  }
600
+ function truncate(value, length, suffix) {
601
+ const suffixLength = suffix?.length ?? 0;
602
+ const truncatedLength = length - suffixLength;
603
+ return value.length <= length ? value : `${value.slice(0, truncatedLength)}${suffix ?? ""}`;
604
+ }
605
+ function words(value) {
606
+ return [];
607
+ }
600
608
 
601
609
  // src/js/is.ts
602
610
  function isArrayOrPlainObject(value) {
@@ -637,6 +645,79 @@ function isPlainObject(value) {
637
645
  function isPrimitive(value) {
638
646
  return value == null || /^(bigint|boolean|number|string|symbol)$/.test(typeof value);
639
647
  }
648
+ // src/js/log.ts
649
+ var time = function(label) {
650
+ const started = log.enabled;
651
+ let stopped = false;
652
+ if (started) {
653
+ console.time(label);
654
+ }
655
+ return Object.create({
656
+ log() {
657
+ if (started && log.enabled) {
658
+ console.timeLog(label);
659
+ }
660
+ },
661
+ stop() {
662
+ if (started && !stopped) {
663
+ stopped = true;
664
+ console.timeEnd(label);
665
+ }
666
+ }
667
+ });
668
+ };
669
+ var work = function(type, data) {
670
+ if (log.enabled) {
671
+ console[type](...data);
672
+ }
673
+ };
674
+ var types = new Set([
675
+ "dir",
676
+ "debug",
677
+ "error",
678
+ "info",
679
+ "table",
680
+ "trace",
681
+ "warn"
682
+ ]);
683
+ var log = (() => {
684
+ let enabled = true;
685
+ function instance(...data) {
686
+ work("log", data);
687
+ }
688
+ Object.defineProperties(instance, {
689
+ enabled: {
690
+ get() {
691
+ return enabled;
692
+ },
693
+ set(value) {
694
+ enabled = value;
695
+ }
696
+ },
697
+ time: {
698
+ value: time
699
+ }
700
+ });
701
+ for (const type of types) {
702
+ Object.defineProperty(instance, type, {
703
+ value: (...data) => work(type, data)
704
+ });
705
+ }
706
+ return instance;
707
+ })();
708
+ // src/js/math.ts
709
+ function average(values) {
710
+ return values.length > 0 ? sum(values) / values.length : Number.NaN;
711
+ }
712
+ function max(values) {
713
+ return values.length > 0 ? Math.max(...values) : Number.NaN;
714
+ }
715
+ function min(values) {
716
+ return values.length > 0 ? Math.min(...values) : Number.NaN;
717
+ }
718
+ function sum(values) {
719
+ return values.reduce((a, b) => a + b, 0);
720
+ }
640
721
  // src/js/queue.ts
641
722
  function queue(callback) {
642
723
  _atomic_queued.add(callback);
@@ -670,31 +751,37 @@ function getRandomDate(earliest, latest) {
670
751
  const latestTime = latest?.getTime() ?? 8640000000000000;
671
752
  return new Date(getRandomInteger(earliestTime, latestTime));
672
753
  }
673
- function getRandomFloat(min, max) {
674
- const minimum = min ?? Number.MIN_SAFE_INTEGER;
675
- return Math.random() * ((max ?? Number.MAX_SAFE_INTEGER) - minimum) + minimum;
754
+ function getRandomFloat(min2, max2) {
755
+ const minimum = min2 ?? Number.MIN_SAFE_INTEGER;
756
+ return Math.random() * ((max2 ?? Number.MAX_SAFE_INTEGER) - minimum) + minimum;
676
757
  }
677
- function getRandomInteger(min, max) {
678
- return Math.floor(getRandomFloat(min, max));
758
+ function getRandomInteger(min2, max2) {
759
+ return Math.floor(getRandomFloat(min2, max2));
679
760
  }
680
761
  function getRandomHex() {
681
762
  return "0123456789ABCDEF"[getRandomInteger(0, 16)];
682
763
  }
683
764
  // src/js/timer.ts
765
+ var is = function(value, pattern) {
766
+ return pattern.test(value?.$timer);
767
+ };
684
768
  function isRepeated(value) {
685
- return /^repeat$/.test(value?.$timer ?? "");
769
+ return is(value, /^repeat$/);
686
770
  }
687
771
  function isTimer(value) {
688
- return /^repeat|wait$/.test(value?.$timer ?? "");
772
+ return is(value, /^repeat|wait$/);
689
773
  }
690
774
  function isWaited(value) {
691
- return /^wait$/.test(value?.$timer ?? "");
775
+ return is(value, /^wait$/);
776
+ }
777
+ function isWhen(value) {
778
+ return is(value, /^when$/) && typeof value.then === "function";
692
779
  }
693
780
  function repeat(callback, options) {
694
781
  return timer("repeat", callback, {
695
782
  ...options ?? {},
696
783
  ...{
697
- count: typeof options?.count === "number" ? options.count : Number.POSITIVE_INFINITY
784
+ count: typeof options?.count === "number" ? options.count > 0 ? options.count : 1 : Number.POSITIVE_INFINITY
698
785
  }
699
786
  }).start();
700
787
  }
@@ -712,13 +799,13 @@ var timer = function(type, callback, options) {
712
799
  };
713
800
  const instance = Object.create({
714
801
  restart() {
715
- return work("restart", this, state, extended);
802
+ return work2("restart", this, state, extended);
716
803
  },
717
804
  start() {
718
- return work("start", this, state, extended);
805
+ return work2("start", this, state, extended);
719
806
  },
720
807
  stop() {
721
- return work("stop", this, state, extended);
808
+ return work2("stop", this, state, extended);
722
809
  }
723
810
  });
724
811
  Object.defineProperties(instance, {
@@ -775,16 +862,28 @@ function when(condition, options) {
775
862
  return promise.then(resolve, reject);
776
863
  }
777
864
  });
865
+ Object.defineProperties(instance, {
866
+ $timer: {
867
+ get() {
868
+ return "when";
869
+ }
870
+ },
871
+ active: {
872
+ get() {
873
+ return repeated.active;
874
+ }
875
+ }
876
+ });
778
877
  return instance;
779
878
  }
780
- var work = function(type, timer2, state, options) {
879
+ var work2 = function(type, timer2, state, options) {
781
880
  if (type === "start" && state.active || type === "stop" && !state.active) {
782
881
  return timer2;
783
882
  }
784
- const { afterCallback, count, errorCallback, interval, timeout } = options;
883
+ const { count, interval, timeout } = options;
785
884
  if (typeof state.frame === "number") {
786
885
  cancelAnimationFrame(state.frame);
787
- afterCallback?.(false);
886
+ options.afterCallback?.(false);
788
887
  }
789
888
  if (type === "stop") {
790
889
  state.active = false;
@@ -801,9 +900,9 @@ var work = function(type, timer2, state, options) {
801
900
  state.active = false;
802
901
  state.frame = undefined;
803
902
  if (error) {
804
- errorCallback?.();
903
+ options.errorCallback?.();
805
904
  }
806
- afterCallback?.(finished);
905
+ options.afterCallback?.(finished);
807
906
  }
808
907
  function step(timestamp) {
809
908
  if (!state.active) {
@@ -1045,10 +1144,13 @@ function setValue(data, path, value, ignoreCase) {
1045
1144
  return data;
1046
1145
  }
1047
1146
  export {
1147
+ words,
1048
1148
  when,
1049
1149
  wait,
1050
1150
  unique,
1151
+ truncate,
1051
1152
  titleCase,
1153
+ sum,
1052
1154
  splice,
1053
1155
  setValue,
1054
1156
  rgbToHsl,
@@ -1056,7 +1158,11 @@ export {
1056
1158
  repeat,
1057
1159
  queue,
1058
1160
  push,
1161
+ min,
1059
1162
  merge,
1163
+ max,
1164
+ log,
1165
+ isWhen,
1060
1166
  isWaited,
1061
1167
  isTimer,
1062
1168
  isTabbableElement,
@@ -1107,6 +1213,7 @@ export {
1107
1213
  capitalise as capitalize,
1108
1214
  capitalise,
1109
1215
  between,
1216
+ average,
1110
1217
  findElements as $$,
1111
1218
  findElement as $
1112
1219
  };
package/dist/js/index.mjs CHANGED
@@ -5,6 +5,8 @@ export * from "./element/index";
5
5
  export * from "./element/focusable";
6
6
  export * from "./event";
7
7
  export * from "./is";
8
+ export * from "./log";
9
+ export * from "./math";
8
10
  export * from "./models";
9
11
  export * from "./number";
10
12
  export * from "./queue";
package/dist/js/log.js ADDED
@@ -0,0 +1,63 @@
1
+ // src/js/log.ts
2
+ var time = function(label) {
3
+ const started = log.enabled;
4
+ let stopped = false;
5
+ if (started) {
6
+ console.time(label);
7
+ }
8
+ return Object.create({
9
+ log() {
10
+ if (started && log.enabled) {
11
+ console.timeLog(label);
12
+ }
13
+ },
14
+ stop() {
15
+ if (started && !stopped) {
16
+ stopped = true;
17
+ console.timeEnd(label);
18
+ }
19
+ }
20
+ });
21
+ };
22
+ var work = function(type, data) {
23
+ if (log.enabled) {
24
+ console[type](...data);
25
+ }
26
+ };
27
+ var types = new Set([
28
+ "dir",
29
+ "debug",
30
+ "error",
31
+ "info",
32
+ "table",
33
+ "trace",
34
+ "warn"
35
+ ]);
36
+ var log = (() => {
37
+ let enabled = true;
38
+ function instance(...data) {
39
+ work("log", data);
40
+ }
41
+ Object.defineProperties(instance, {
42
+ enabled: {
43
+ get() {
44
+ return enabled;
45
+ },
46
+ set(value) {
47
+ enabled = value;
48
+ }
49
+ },
50
+ time: {
51
+ value: time
52
+ }
53
+ });
54
+ for (const type of types) {
55
+ Object.defineProperty(instance, type, {
56
+ value: (...data) => work(type, data)
57
+ });
58
+ }
59
+ return instance;
60
+ })();
61
+ export {
62
+ log
63
+ };
@@ -0,0 +1,63 @@
1
+ // src/js/log.ts
2
+ var time = function(label) {
3
+ const started = log.enabled;
4
+ let stopped = false;
5
+ if (started) {
6
+ console.time(label);
7
+ }
8
+ return Object.create({
9
+ log() {
10
+ if (started && log.enabled) {
11
+ console.timeLog(label);
12
+ }
13
+ },
14
+ stop() {
15
+ if (started && !stopped) {
16
+ stopped = true;
17
+ console.timeEnd(label);
18
+ }
19
+ }
20
+ });
21
+ };
22
+ var work = function(type, data) {
23
+ if (log.enabled) {
24
+ console[type](...data);
25
+ }
26
+ };
27
+ var types = new Set([
28
+ "dir",
29
+ "debug",
30
+ "error",
31
+ "info",
32
+ "table",
33
+ "trace",
34
+ "warn"
35
+ ]);
36
+ var log = (() => {
37
+ let enabled = true;
38
+ function instance(...data) {
39
+ work("log", data);
40
+ }
41
+ Object.defineProperties(instance, {
42
+ enabled: {
43
+ get() {
44
+ return enabled;
45
+ },
46
+ set(value) {
47
+ enabled = value;
48
+ }
49
+ },
50
+ time: {
51
+ value: time
52
+ }
53
+ });
54
+ for (const type of types) {
55
+ Object.defineProperty(instance, type, {
56
+ value: (...data) => work(type, data)
57
+ });
58
+ }
59
+ return instance;
60
+ })();
61
+ export {
62
+ log
63
+ };
@@ -0,0 +1,19 @@
1
+ // src/js/math.ts
2
+ function average(values) {
3
+ return values.length > 0 ? sum(values) / values.length : Number.NaN;
4
+ }
5
+ function max(values) {
6
+ return values.length > 0 ? Math.max(...values) : Number.NaN;
7
+ }
8
+ function min(values) {
9
+ return values.length > 0 ? Math.min(...values) : Number.NaN;
10
+ }
11
+ function sum(values) {
12
+ return values.reduce((a, b) => a + b, 0);
13
+ }
14
+ export {
15
+ sum,
16
+ min,
17
+ max,
18
+ average
19
+ };
@@ -0,0 +1,19 @@
1
+ // src/js/math.ts
2
+ function average(values) {
3
+ return values.length > 0 ? sum(values) / values.length : Number.NaN;
4
+ }
5
+ function max(values) {
6
+ return values.length > 0 ? Math.max(...values) : Number.NaN;
7
+ }
8
+ function min(values) {
9
+ return values.length > 0 ? Math.min(...values) : Number.NaN;
10
+ }
11
+ function sum(values) {
12
+ return values.reduce((a, b) => a + b, 0);
13
+ }
14
+ export {
15
+ sum,
16
+ min,
17
+ max,
18
+ average
19
+ };
package/dist/js/string.js CHANGED
@@ -22,7 +22,17 @@ function getString(value) {
22
22
  function titleCase(value) {
23
23
  return value.split(/\s+/).map((word) => capitalise(word)).join(" ");
24
24
  }
25
+ function truncate(value, length, suffix) {
26
+ const suffixLength = suffix?.length ?? 0;
27
+ const truncatedLength = length - suffixLength;
28
+ return value.length <= length ? value : `${value.slice(0, truncatedLength)}${suffix ?? ""}`;
29
+ }
30
+ function words(value) {
31
+ return [];
32
+ }
25
33
  export {
34
+ words,
35
+ truncate,
26
36
  titleCase,
27
37
  getString,
28
38
  createUuid,
@@ -22,7 +22,17 @@ function getString(value) {
22
22
  function titleCase(value) {
23
23
  return value.split(/\s+/).map((word) => capitalise(word)).join(" ");
24
24
  }
25
+ function truncate(value, length, suffix) {
26
+ const suffixLength = suffix?.length ?? 0;
27
+ const truncatedLength = length - suffixLength;
28
+ return value.length <= length ? value : `${value.slice(0, truncatedLength)}${suffix ?? ""}`;
29
+ }
30
+ function words(value) {
31
+ return [];
32
+ }
25
33
  export {
34
+ words,
35
+ truncate,
26
36
  titleCase,
27
37
  getString,
28
38
  createUuid,
package/dist/js/timer.js CHANGED
@@ -1,18 +1,24 @@
1
1
  // src/js/timer.ts
2
+ var is = function(value, pattern) {
3
+ return pattern.test(value?.$timer);
4
+ };
2
5
  function isRepeated(value) {
3
- return /^repeat$/.test(value?.$timer ?? "");
6
+ return is(value, /^repeat$/);
4
7
  }
5
8
  function isTimer(value) {
6
- return /^repeat|wait$/.test(value?.$timer ?? "");
9
+ return is(value, /^repeat|wait$/);
7
10
  }
8
11
  function isWaited(value) {
9
- return /^wait$/.test(value?.$timer ?? "");
12
+ return is(value, /^wait$/);
13
+ }
14
+ function isWhen(value) {
15
+ return is(value, /^when$/) && typeof value.then === "function";
10
16
  }
11
17
  function repeat(callback, options) {
12
18
  return timer("repeat", callback, {
13
19
  ...options ?? {},
14
20
  ...{
15
- count: typeof options?.count === "number" ? options.count : Number.POSITIVE_INFINITY
21
+ count: typeof options?.count === "number" ? options.count > 0 ? options.count : 1 : Number.POSITIVE_INFINITY
16
22
  }
17
23
  }).start();
18
24
  }
@@ -93,16 +99,28 @@ function when(condition, options) {
93
99
  return promise.then(resolve, reject);
94
100
  }
95
101
  });
102
+ Object.defineProperties(instance, {
103
+ $timer: {
104
+ get() {
105
+ return "when";
106
+ }
107
+ },
108
+ active: {
109
+ get() {
110
+ return repeated.active;
111
+ }
112
+ }
113
+ });
96
114
  return instance;
97
115
  }
98
116
  var work = function(type, timer2, state, options) {
99
117
  if (type === "start" && state.active || type === "stop" && !state.active) {
100
118
  return timer2;
101
119
  }
102
- const { afterCallback, count, errorCallback, interval, timeout } = options;
120
+ const { count, interval, timeout } = options;
103
121
  if (typeof state.frame === "number") {
104
122
  cancelAnimationFrame(state.frame);
105
- afterCallback?.(false);
123
+ options.afterCallback?.(false);
106
124
  }
107
125
  if (type === "stop") {
108
126
  state.active = false;
@@ -119,9 +137,9 @@ var work = function(type, timer2, state, options) {
119
137
  state.active = false;
120
138
  state.frame = undefined;
121
139
  if (error) {
122
- errorCallback?.();
140
+ options.errorCallback?.();
123
141
  }
124
- afterCallback?.(finished);
142
+ options.afterCallback?.(finished);
125
143
  }
126
144
  function step(timestamp) {
127
145
  if (!state.active) {
@@ -157,6 +175,7 @@ export {
157
175
  when,
158
176
  wait,
159
177
  repeat,
178
+ isWhen,
160
179
  isWaited,
161
180
  isTimer,
162
181
  isRepeated
package/dist/js/timer.mjs CHANGED
@@ -1,18 +1,24 @@
1
1
  // src/js/timer.ts
2
+ var is = function(value, pattern) {
3
+ return pattern.test(value?.$timer);
4
+ };
2
5
  function isRepeated(value) {
3
- return /^repeat$/.test(value?.$timer ?? "");
6
+ return is(value, /^repeat$/);
4
7
  }
5
8
  function isTimer(value) {
6
- return /^repeat|wait$/.test(value?.$timer ?? "");
9
+ return is(value, /^repeat|wait$/);
7
10
  }
8
11
  function isWaited(value) {
9
- return /^wait$/.test(value?.$timer ?? "");
12
+ return is(value, /^wait$/);
13
+ }
14
+ function isWhen(value) {
15
+ return is(value, /^when$/) && typeof value.then === "function";
10
16
  }
11
17
  function repeat(callback, options) {
12
18
  return timer("repeat", callback, {
13
19
  ...options ?? {},
14
20
  ...{
15
- count: typeof options?.count === "number" ? options.count : Number.POSITIVE_INFINITY
21
+ count: typeof options?.count === "number" ? options.count > 0 ? options.count : 1 : Number.POSITIVE_INFINITY
16
22
  }
17
23
  }).start();
18
24
  }
@@ -93,16 +99,28 @@ function when(condition, options) {
93
99
  return promise.then(resolve, reject);
94
100
  }
95
101
  });
102
+ Object.defineProperties(instance, {
103
+ $timer: {
104
+ get() {
105
+ return "when";
106
+ }
107
+ },
108
+ active: {
109
+ get() {
110
+ return repeated.active;
111
+ }
112
+ }
113
+ });
96
114
  return instance;
97
115
  }
98
116
  var work = function(type, timer2, state, options) {
99
117
  if (type === "start" && state.active || type === "stop" && !state.active) {
100
118
  return timer2;
101
119
  }
102
- const { afterCallback, count, errorCallback, interval, timeout } = options;
120
+ const { count, interval, timeout } = options;
103
121
  if (typeof state.frame === "number") {
104
122
  cancelAnimationFrame(state.frame);
105
- afterCallback?.(false);
123
+ options.afterCallback?.(false);
106
124
  }
107
125
  if (type === "stop") {
108
126
  state.active = false;
@@ -119,9 +137,9 @@ var work = function(type, timer2, state, options) {
119
137
  state.active = false;
120
138
  state.frame = undefined;
121
139
  if (error) {
122
- errorCallback?.();
140
+ options.errorCallback?.();
123
141
  }
124
- afterCallback?.(finished);
142
+ options.afterCallback?.(finished);
125
143
  }
126
144
  function step(timestamp) {
127
145
  if (!state.active) {
@@ -157,6 +175,7 @@ export {
157
175
  when,
158
176
  wait,
159
177
  repeat,
178
+ isWhen,
160
179
  isWaited,
161
180
  isTimer,
162
181
  isRepeated
package/package.json CHANGED
@@ -130,5 +130,5 @@
130
130
  },
131
131
  "type": "module",
132
132
  "types": "./types/index.d.ts",
133
- "version": "0.41.4"
133
+ "version": "0.43.0"
134
134
  }
package/src/js/index.ts CHANGED
@@ -4,6 +4,8 @@ export * from './element/index';
4
4
  export * from './element/focusable';
5
5
  export * from './event';
6
6
  export * from './is';
7
+ export * from './log';
8
+ export * from './math';
7
9
  export * from './models';
8
10
  export * from './number';
9
11
  export * from './queue';
package/src/js/is.ts CHANGED
@@ -10,6 +10,9 @@ export function isArrayOrPlainObject(
10
10
  return Array.isArray(value) || isPlainObject(value);
11
11
  }
12
12
 
13
+ /**
14
+ * Is the array or object completely empty or only containing `null` or `undefined` values?
15
+ */
13
16
  export function isEmpty(value: ArrayOrPlainObject): boolean {
14
17
  if (Array.isArray(value)) {
15
18
  return (
package/src/js/log.ts ADDED
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Logs any number of values at the "log" log level
3
+ */
4
+ type LogCallback = (...data: unknown[]) => void;
5
+
6
+ type LogPrototype = {
7
+ /**
8
+ * Is logging to the console enabled? _(defaults to `true`)_
9
+ */
10
+ enabled: boolean;
11
+ /**
12
+ * Logs the value and shows all its properties
13
+ */
14
+ dir(value: unknown): void;
15
+ /**
16
+ * Logs any number of values at the "debug" log level
17
+ */
18
+ debug(...data: unknown[]): void;
19
+ /**
20
+ * Logs any number of values at the "error" log level
21
+ */
22
+ error(...data: unknown[]): void;
23
+ /**
24
+ * Logs any number of values at the "info" log level
25
+ */
26
+ info(...data: unknown[]): void;
27
+ /**
28
+ * Logs data as a table, with optional properties to use as columns
29
+ */
30
+ table(data: unknown, properties?: string[]): void;
31
+ /**
32
+ * - Starts a logged timer with a label
33
+ * - Returns a `Time`-object for logging the current duration of the timer and stopping the timer _(and logging the total duration)_
34
+ */
35
+ time(label: string): Time;
36
+ /**
37
+ * Logs any number of values together with a trace from where it was called
38
+ */
39
+ trace(...data: unknown[]): void;
40
+ /**
41
+ * Logs any number of values at the "warn" log level
42
+ */
43
+ warn(...data: unknown[]): void;
44
+ };
45
+
46
+ type Time = {
47
+ /**
48
+ * - Logs the current duration of the timer
49
+ * - Ignored if logging is disabled
50
+ */
51
+ log(): void;
52
+ /**
53
+ * - Stops the timer and logs the total duration
54
+ * - Will always log the total duration, even if logging is disabled
55
+ */
56
+ stop(): void;
57
+ };
58
+
59
+ type Type =
60
+ | 'dir'
61
+ | 'debug'
62
+ | 'error'
63
+ | 'info'
64
+ | 'log'
65
+ | 'table'
66
+ | 'trace'
67
+ | 'warn';
68
+
69
+ const types = new Set<Type>([
70
+ 'dir',
71
+ 'debug',
72
+ 'error',
73
+ 'info',
74
+ 'table',
75
+ 'trace',
76
+ 'warn',
77
+ ]);
78
+
79
+ const log = (() => {
80
+ let enabled = true;
81
+
82
+ function instance(...data: unknown[]): void {
83
+ work('log', data);
84
+ }
85
+
86
+ Object.defineProperties(instance, {
87
+ enabled: {
88
+ get() {
89
+ return enabled;
90
+ },
91
+ set(value: boolean) {
92
+ enabled = value;
93
+ },
94
+ },
95
+ time: {
96
+ value: time,
97
+ },
98
+ });
99
+
100
+ for (const type of types) {
101
+ Object.defineProperty(instance, type, {
102
+ value: (...data: unknown[]) => work(type, data),
103
+ });
104
+ }
105
+
106
+ return instance;
107
+ })() as LogCallback & LogPrototype;
108
+
109
+ function time(label: string): Time {
110
+ const started = log.enabled;
111
+
112
+ let stopped = false;
113
+
114
+ if (started) {
115
+ console.time(label);
116
+ }
117
+
118
+ return Object.create({
119
+ log() {
120
+ if (started && log.enabled) {
121
+ console.timeLog(label);
122
+ }
123
+ },
124
+ stop() {
125
+ if (started && !stopped) {
126
+ stopped = true;
127
+
128
+ console.timeEnd(label);
129
+ }
130
+ },
131
+ });
132
+ }
133
+
134
+ function work(type: Type, data: unknown[]): void {
135
+ if (log.enabled) {
136
+ console[type](...data);
137
+ }
138
+ }
139
+
140
+ export {log};
package/src/js/math.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Get the average value from a list of numbers
3
+ */
4
+ export function average(values: number[]): number {
5
+ return values.length > 0 ? sum(values) / values.length : Number.NaN;
6
+ }
7
+
8
+ /**
9
+ * Get the maximum value from a list of numbers
10
+ */
11
+ export function max(values: number[]): number {
12
+ return values.length > 0 ? Math.max(...values) : Number.NaN;
13
+ }
14
+
15
+ /**
16
+ * Get the minimum value from a list of numbers
17
+ */
18
+ export function min(values: number[]): number {
19
+ return values.length > 0 ? Math.min(...values) : Number.NaN;
20
+ }
21
+
22
+ /**
23
+ * Get the sum of a list of numbers
24
+ */
25
+ export function sum(values: number[]): number {
26
+ return values.reduce((a, b) => a + b, 0);
27
+ }
package/src/js/string.ts CHANGED
@@ -53,4 +53,26 @@ export function titleCase(value: string): string {
53
53
  .join(' ');
54
54
  }
55
55
 
56
+ /**
57
+ * Truncates a string to a specified length, when possible
58
+ * - Returned as-is if the string is already short enough
59
+ * - A suffix may be appended to the truncated string, e.g., an ellipsis
60
+ */
61
+ export function truncate(
62
+ value: string,
63
+ length: number,
64
+ suffix?: string,
65
+ ): string {
66
+ const suffixLength = suffix?.length ?? 0;
67
+ const truncatedLength = length - suffixLength;
68
+
69
+ return value.length <= length
70
+ ? value
71
+ : `${value.slice(0, truncatedLength)}${suffix ?? ''}`;
72
+ }
73
+
74
+ export function words(value: string): string[] {
75
+ return [];
76
+ }
77
+
56
78
  export {capitalise as capitalize};
package/src/js/timer.ts CHANGED
@@ -80,39 +80,57 @@ type TimerOptions = {} & RepeatOptions;
80
80
  type WaitOptions = {} & BaseOptions & OptionsWithError;
81
81
 
82
82
  export type When = {
83
- /**
84
- * Stops the timer
85
- */
86
- stop(): void;
87
- then(
88
- resolve?: (() => void) | null,
89
- reject?: (() => void) | null,
90
- ): Promise<void>;
91
- };
83
+ /**
84
+ * Is the timer running?
85
+ */
86
+ get active(): boolean;
87
+ /**
88
+ * Stops the timer
89
+ */
90
+ stop(): void;
91
+ /**
92
+ * Starts the timer and returns a promise that resolves when the condition is met
93
+ */
94
+ then(
95
+ resolve?: (() => void) | null,
96
+ reject?: (() => void) | null,
97
+ ): Promise<void>;
98
+ };
92
99
 
93
100
  type WhenOptions = {} & OptionsWithCount;
94
101
 
95
102
  type WorkType = 'restart' | 'start' | 'stop';
96
103
 
104
+ function is(value: unknown, pattern: RegExp) {
105
+ return pattern.test((value as PlainObject)?.$timer as string);
106
+ }
107
+
97
108
  /**
98
109
  * Is the value a repeating timer?
99
110
  */
100
111
  export function isRepeated(value: unknown): value is Timer {
101
- return /^repeat$/.test(((value as PlainObject)?.$timer as string) ?? '');
112
+ return is(value, /^repeat$/);
102
113
  }
103
114
 
104
115
  /**
105
116
  * Is the value a timer?
106
117
  */
107
118
  export function isTimer(value: unknown): value is Timer {
108
- return /^repeat|wait$/.test(((value as PlainObject)?.$timer as string) ?? '');
119
+ return is(value, /^repeat|wait$/);
109
120
  }
110
121
 
111
122
  /**
112
123
  * Is the value a waiting timer?
113
124
  */
114
125
  export function isWaited(value: unknown): value is Timer {
115
- return /^wait$/.test(((value as PlainObject)?.$timer as string) ?? '');
126
+ return is(value, /^wait$/);
127
+ }
128
+
129
+ /**
130
+ * Is the value a conditional timer?
131
+ */
132
+ export function isWhen(value: unknown): value is When {
133
+ return is(value, /^when$/) && typeof (value as When).then === 'function';
116
134
  }
117
135
 
118
136
  /**
@@ -120,8 +138,9 @@ export function isWaited(value: unknown): value is Timer {
120
138
  * - calls a callback after a certain amount of time...
121
139
  * - ... and repeats it a certain amount of times
122
140
  * ---
123
- * - `options.count` defaults to `Infinity`
124
- * - `options.time` defaults to `0`
141
+ * - `options.count` defaults to `Infinity` _(minimum `1`)_
142
+ * - `options.interval` defaults to `0`
143
+ * - `options.timeout` defaults to `30_000` _(30 seconds)_
125
144
  */
126
145
  export function repeat(
127
146
  callback: IndexedCallback,
@@ -132,7 +151,9 @@ export function repeat(
132
151
  ...{
133
152
  count:
134
153
  typeof options?.count === 'number'
135
- ? options.count
154
+ ? options.count > 0
155
+ ? options.count
156
+ : 1
136
157
  : Number.POSITIVE_INFINITY,
137
158
  },
138
159
  }).start();
@@ -268,7 +289,7 @@ export function when(
268
289
  rejecter?.();
269
290
  }
270
291
  },
271
- // biome-ignore lint/suspicious/noThenProperty: <explanation>
292
+ // biome-ignore lint/suspicious/noThenProperty: returning a promise-like object, so it's ok ;)
272
293
  then(resolve?: () => void, reject?: () => void) {
273
294
  repeated.start();
274
295
 
@@ -276,6 +297,19 @@ export function when(
276
297
  },
277
298
  });
278
299
 
300
+ Object.defineProperties(instance, {
301
+ $timer: {
302
+ get() {
303
+ return 'when';
304
+ },
305
+ },
306
+ active: {
307
+ get() {
308
+ return repeated.active;
309
+ },
310
+ },
311
+ });
312
+
279
313
  return instance;
280
314
  }
281
315
 
@@ -292,12 +326,12 @@ function work(
292
326
  return timer;
293
327
  }
294
328
 
295
- const {afterCallback, count, errorCallback, interval, timeout} = options;
329
+ const {count, interval, timeout} = options;
296
330
 
297
331
  if (typeof state.frame === 'number') {
298
332
  cancelAnimationFrame(state.frame);
299
333
 
300
- afterCallback?.(false);
334
+ options.afterCallback?.(false);
301
335
  }
302
336
 
303
337
  if (type === 'stop') {
@@ -326,10 +360,10 @@ function work(
326
360
  state.frame = undefined;
327
361
 
328
362
  if (error) {
329
- errorCallback?.();
363
+ options.errorCallback?.();
330
364
  }
331
365
 
332
- afterCallback?.(finished);
366
+ options.afterCallback?.(finished);
333
367
  }
334
368
 
335
369
  function step(timestamp: DOMHighResTimeStamp): void {
package/types/index.d.ts CHANGED
@@ -4,6 +4,8 @@ export * from './element/index';
4
4
  export * from './element/focusable';
5
5
  export * from './event';
6
6
  export * from './is';
7
+ export * from './log';
8
+ export * from './math';
7
9
  export * from './models';
8
10
  export * from './number';
9
11
  export * from './queue';
package/types/is.d.ts CHANGED
@@ -3,6 +3,9 @@ import type { ArrayOrPlainObject, PlainObject, Primitive } from './models';
3
3
  * Is the value an array or a record?
4
4
  */
5
5
  export declare function isArrayOrPlainObject(value: unknown): value is ArrayOrPlainObject;
6
+ /**
7
+ * Is the array or object completely empty or only containing `null` or `undefined` values?
8
+ */
6
9
  export declare function isEmpty(value: ArrayOrPlainObject): boolean;
7
10
  /**
8
11
  * Is the value undefined or null?
package/types/log.d.ts ADDED
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Logs any number of values at the "log" log level
3
+ */
4
+ type LogCallback = (...data: unknown[]) => void;
5
+ type LogPrototype = {
6
+ /**
7
+ * Is logging to the console enabled? _(defaults to `true`)_
8
+ */
9
+ enabled: boolean;
10
+ /**
11
+ * Logs the value and shows all its properties
12
+ */
13
+ dir(value: unknown): void;
14
+ /**
15
+ * Logs any number of values at the "debug" log level
16
+ */
17
+ debug(...data: unknown[]): void;
18
+ /**
19
+ * Logs any number of values at the "error" log level
20
+ */
21
+ error(...data: unknown[]): void;
22
+ /**
23
+ * Logs any number of values at the "info" log level
24
+ */
25
+ info(...data: unknown[]): void;
26
+ /**
27
+ * Logs data as a table, with optional properties to use as columns
28
+ */
29
+ table(data: unknown, properties?: string[]): void;
30
+ /**
31
+ * - Starts a logged timer with a label
32
+ * - Returns a `Time`-object for logging the current duration of the timer and stopping the timer _(and logging the total duration)_
33
+ */
34
+ time(label: string): Time;
35
+ /**
36
+ * Logs any number of values together with a trace from where it was called
37
+ */
38
+ trace(...data: unknown[]): void;
39
+ /**
40
+ * Logs any number of values at the "warn" log level
41
+ */
42
+ warn(...data: unknown[]): void;
43
+ };
44
+ type Time = {
45
+ /**
46
+ * - Logs the current duration of the timer
47
+ * - Ignored if logging is disabled
48
+ */
49
+ log(): void;
50
+ /**
51
+ * - Stops the timer and logs the total duration
52
+ * - Will always log the total duration, even if logging is disabled
53
+ */
54
+ stop(): void;
55
+ };
56
+ declare const log: LogCallback & LogPrototype;
57
+ export { log };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Get the average value from a list of numbers
3
+ */
4
+ export declare function average(values: number[]): number;
5
+ /**
6
+ * Get the maximum value from a list of numbers
7
+ */
8
+ export declare function max(values: number[]): number;
9
+ /**
10
+ * Get the minimum value from a list of numbers
11
+ */
12
+ export declare function min(values: number[]): number;
13
+ /**
14
+ * Get the sum of a list of numbers
15
+ */
16
+ export declare function sum(values: number[]): number;
package/types/string.d.ts CHANGED
@@ -14,4 +14,11 @@ export declare function getString(value: unknown): string;
14
14
  * Convert a string to title case _(capitalising every word)_
15
15
  */
16
16
  export declare function titleCase(value: string): string;
17
+ /**
18
+ * Truncates a string to a specified length, when possible
19
+ * - Returned as-is if the string is already short enough
20
+ * - A suffix may be appended to the truncated string, e.g., an ellipsis
21
+ */
22
+ export declare function truncate(value: string, length: number, suffix?: string): string;
23
+ export declare function words(value: string): string[];
17
24
  export { capitalise as capitalize };
package/types/timer.d.ts CHANGED
@@ -59,10 +59,17 @@ export type Timer = {
59
59
  };
60
60
  type WaitOptions = {} & BaseOptions & OptionsWithError;
61
61
  export type When = {
62
+ /**
63
+ * Is the timer running?
64
+ */
65
+ get active(): boolean;
62
66
  /**
63
67
  * Stops the timer
64
68
  */
65
69
  stop(): void;
70
+ /**
71
+ * Starts the timer and returns a promise that resolves when the condition is met
72
+ */
66
73
  then(resolve?: (() => void) | null, reject?: (() => void) | null): Promise<void>;
67
74
  };
68
75
  type WhenOptions = {} & OptionsWithCount;
@@ -78,13 +85,18 @@ export declare function isTimer(value: unknown): value is Timer;
78
85
  * Is the value a waiting timer?
79
86
  */
80
87
  export declare function isWaited(value: unknown): value is Timer;
88
+ /**
89
+ * Is the value a conditional timer?
90
+ */
91
+ export declare function isWhen(value: unknown): value is When;
81
92
  /**
82
93
  * Creates a timer which:
83
94
  * - calls a callback after a certain amount of time...
84
95
  * - ... and repeats it a certain amount of times
85
96
  * ---
86
- * - `options.count` defaults to `Infinity`
87
- * - `options.time` defaults to `0`
97
+ * - `options.count` defaults to `Infinity` _(minimum `1`)_
98
+ * - `options.interval` defaults to `0`
99
+ * - `options.timeout` defaults to `30_000` _(30 seconds)_
88
100
  */
89
101
  export declare function repeat(callback: IndexedCallback, options?: Partial<RepeatOptions>): Timer;
90
102
  /**