@oscarpalmer/atoms 0.9.0 → 0.10.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/element/index.js +60 -0
- package/dist/js/event.js +16 -0
- package/dist/js/{atoms.js → index.js} +199 -82
- package/dist/js/number.js +35 -0
- package/dist/js/string.js +15 -0
- package/dist/js/timer.js +96 -0
- package/dist/js/value.js +84 -0
- package/package.json +37 -11
- package/src/js/element/index.ts +42 -0
- package/src/js/event.ts +1 -1
- package/src/js/index.ts +1 -0
- package/src/js/timer.ts +207 -0
- package/types/element/index.d.ts +7 -0
- package/types/event.d.ts +1 -1
- package/types/index.d.ts +1 -0
- package/types/timer.d.ts +51 -0
- /package/dist/js/{focusable.js → element/focusable.js} +0 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// src/js/element/index.ts
|
|
2
|
+
function findElements(selector, context) {
|
|
3
|
+
const contexts = context === undefined ? [document] : findElements(context);
|
|
4
|
+
const elements = [];
|
|
5
|
+
if (typeof selector === "string") {
|
|
6
|
+
for (const context2 of contexts) {
|
|
7
|
+
elements.push(...Array.from(context2.querySelectorAll(selector) ?? []));
|
|
8
|
+
}
|
|
9
|
+
return elements;
|
|
10
|
+
}
|
|
11
|
+
const nodes = Array.isArray(selector) || selector instanceof NodeList ? selector : [selector];
|
|
12
|
+
for (const node of nodes) {
|
|
13
|
+
if (node instanceof Element && contexts.some((context2) => context2.contains(node))) {
|
|
14
|
+
elements.push(node);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return elements;
|
|
18
|
+
}
|
|
19
|
+
function findParentElement(origin, selector) {
|
|
20
|
+
if (origin == null || selector == null) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
function matches(element) {
|
|
24
|
+
return typeof selector === "string" ? element.matches?.(selector) ?? false : typeof selector === "function" ? selector(element) : false;
|
|
25
|
+
}
|
|
26
|
+
if (matches(origin)) {
|
|
27
|
+
return origin;
|
|
28
|
+
}
|
|
29
|
+
let parent = origin.parentElement;
|
|
30
|
+
while (parent != null && !matches(parent)) {
|
|
31
|
+
if (parent === document.body) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
parent = parent.parentElement;
|
|
35
|
+
}
|
|
36
|
+
return parent ?? undefined;
|
|
37
|
+
}
|
|
38
|
+
function getElementUnderPointer(skipIgnore) {
|
|
39
|
+
const elements = Array.from(document.querySelectorAll(":hover")).filter((element) => {
|
|
40
|
+
if (/^head$/i.test(element.tagName)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const style = getComputedStyle(element);
|
|
44
|
+
return typeof skipIgnore === "boolean" && skipIgnore || style.pointerEvents !== "none" && style.visibility !== "hidden";
|
|
45
|
+
});
|
|
46
|
+
return elements[elements.length - 1];
|
|
47
|
+
}
|
|
48
|
+
function getTextDirection(element) {
|
|
49
|
+
const direction = element.getAttribute("dir");
|
|
50
|
+
if (direction !== null && /^(ltr|rtl)$/i.test(direction)) {
|
|
51
|
+
return direction.toLowerCase();
|
|
52
|
+
}
|
|
53
|
+
return getComputedStyle?.(element)?.direction === "rtl" ? "rtl" : "ltr";
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
getTextDirection,
|
|
57
|
+
getElementUnderPointer,
|
|
58
|
+
findParentElement,
|
|
59
|
+
findElements
|
|
60
|
+
};
|
package/dist/js/event.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/js/event.ts
|
|
2
|
+
function getEventPosition(event) {
|
|
3
|
+
let x;
|
|
4
|
+
let y;
|
|
5
|
+
if (event instanceof MouseEvent) {
|
|
6
|
+
x = event.clientX;
|
|
7
|
+
y = event.clientY;
|
|
8
|
+
} else if (event instanceof TouchEvent) {
|
|
9
|
+
x = event.touches[0]?.clientX;
|
|
10
|
+
y = event.touches[0]?.clientY;
|
|
11
|
+
}
|
|
12
|
+
return typeof x === "number" && typeof y === "number" ? { x, y } : undefined;
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
getEventPosition
|
|
16
|
+
};
|
|
@@ -1,84 +1,3 @@
|
|
|
1
|
-
// src/js/element/index.ts
|
|
2
|
-
function findParentElement(origin, selector) {
|
|
3
|
-
if (origin == null || selector == null) {
|
|
4
|
-
return;
|
|
5
|
-
}
|
|
6
|
-
function matches(element) {
|
|
7
|
-
return typeof selector === "string" ? element.matches?.(selector) ?? false : typeof selector === "function" ? selector(element) : false;
|
|
8
|
-
}
|
|
9
|
-
if (matches(origin)) {
|
|
10
|
-
return origin;
|
|
11
|
-
}
|
|
12
|
-
let parent = origin.parentElement;
|
|
13
|
-
while (parent != null && !matches(parent)) {
|
|
14
|
-
if (parent === document.body) {
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
parent = parent.parentElement;
|
|
18
|
-
}
|
|
19
|
-
return parent ?? undefined;
|
|
20
|
-
}
|
|
21
|
-
function getElementUnderPointer(skipIgnore) {
|
|
22
|
-
const elements = Array.from(document.querySelectorAll(":hover")).filter((element) => {
|
|
23
|
-
if (/^head$/i.test(element.tagName)) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
const style = getComputedStyle(element);
|
|
27
|
-
return typeof skipIgnore === "boolean" && skipIgnore || style.pointerEvents !== "none" && style.visibility !== "hidden";
|
|
28
|
-
});
|
|
29
|
-
return elements[elements.length - 1];
|
|
30
|
-
}
|
|
31
|
-
function getTextDirection(element) {
|
|
32
|
-
const direction = element.getAttribute("dir");
|
|
33
|
-
if (direction !== null && /^(ltr|rtl)$/i.test(direction)) {
|
|
34
|
-
return direction.toLowerCase();
|
|
35
|
-
}
|
|
36
|
-
return getComputedStyle?.(element)?.direction === "rtl" ? "rtl" : "ltr";
|
|
37
|
-
}
|
|
38
|
-
// src/js/event.ts
|
|
39
|
-
function getPosition(event) {
|
|
40
|
-
let x;
|
|
41
|
-
let y;
|
|
42
|
-
if (event instanceof MouseEvent) {
|
|
43
|
-
x = event.clientX;
|
|
44
|
-
y = event.clientY;
|
|
45
|
-
} else if (event instanceof TouchEvent) {
|
|
46
|
-
x = event.touches[0]?.clientX;
|
|
47
|
-
y = event.touches[0]?.clientY;
|
|
48
|
-
}
|
|
49
|
-
return typeof x === "number" && typeof y === "number" ? { x, y } : undefined;
|
|
50
|
-
}
|
|
51
|
-
// src/js/number.ts
|
|
52
|
-
function clampNumber(value, min, max) {
|
|
53
|
-
return Math.min(Math.max(getNumber(value), getNumber(min)), getNumber(max));
|
|
54
|
-
}
|
|
55
|
-
function getNumber(value) {
|
|
56
|
-
if (typeof value === "number") {
|
|
57
|
-
return value;
|
|
58
|
-
}
|
|
59
|
-
if (typeof value === "symbol") {
|
|
60
|
-
return NaN;
|
|
61
|
-
}
|
|
62
|
-
let parsed = value?.valueOf?.() ?? value;
|
|
63
|
-
if (typeof parsed === "object") {
|
|
64
|
-
parsed = parsed?.toString() ?? parsed;
|
|
65
|
-
}
|
|
66
|
-
if (typeof parsed !== "string") {
|
|
67
|
-
return parsed == null ? NaN : typeof parsed === "number" ? parsed : +parsed;
|
|
68
|
-
}
|
|
69
|
-
if (/^\s*0+\s*$/.test(parsed)) {
|
|
70
|
-
return 0;
|
|
71
|
-
}
|
|
72
|
-
const trimmed = parsed.trim();
|
|
73
|
-
if (trimmed.length === 0) {
|
|
74
|
-
return NaN;
|
|
75
|
-
}
|
|
76
|
-
const isBinary = /^0b[01]+$/i.test(trimmed);
|
|
77
|
-
if (isBinary || /^0o[0-7]+$/i.test(trimmed)) {
|
|
78
|
-
return parseInt(trimmed.slice(2), isBinary ? 2 : 8);
|
|
79
|
-
}
|
|
80
|
-
return +(/^0x[0-9a-f]+$/i.test(trimmed) ? trimmed : trimmed.replace(/_/g, ""));
|
|
81
|
-
}
|
|
82
1
|
// src/js/string.ts
|
|
83
2
|
function createUuid() {
|
|
84
3
|
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (substring) => (substring ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> substring / 4).toString(16));
|
|
@@ -89,6 +8,7 @@ function getString(value) {
|
|
|
89
8
|
function isNullableOrWhitespace(value) {
|
|
90
9
|
return value == null || getString(value).trim().length === 0;
|
|
91
10
|
}
|
|
11
|
+
|
|
92
12
|
// src/js/value.ts
|
|
93
13
|
var _getValue = function(data, key) {
|
|
94
14
|
if (typeof data !== "object" || data === null || /^(__proto__|constructor|prototype)$/i.test(key)) {
|
|
@@ -155,8 +75,204 @@ function setValue(data, key, value) {
|
|
|
155
75
|
}
|
|
156
76
|
return data;
|
|
157
77
|
}
|
|
78
|
+
|
|
79
|
+
// src/js/number.ts
|
|
80
|
+
function clampNumber(value, min, max) {
|
|
81
|
+
return Math.min(Math.max(getNumber(value), getNumber(min)), getNumber(max));
|
|
82
|
+
}
|
|
83
|
+
function getNumber(value) {
|
|
84
|
+
if (typeof value === "number") {
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
if (typeof value === "symbol") {
|
|
88
|
+
return NaN;
|
|
89
|
+
}
|
|
90
|
+
let parsed = value?.valueOf?.() ?? value;
|
|
91
|
+
if (typeof parsed === "object") {
|
|
92
|
+
parsed = parsed?.toString() ?? parsed;
|
|
93
|
+
}
|
|
94
|
+
if (typeof parsed !== "string") {
|
|
95
|
+
return parsed == null ? NaN : typeof parsed === "number" ? parsed : +parsed;
|
|
96
|
+
}
|
|
97
|
+
if (/^\s*0+\s*$/.test(parsed)) {
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
const trimmed = parsed.trim();
|
|
101
|
+
if (trimmed.length === 0) {
|
|
102
|
+
return NaN;
|
|
103
|
+
}
|
|
104
|
+
const isBinary = /^0b[01]+$/i.test(trimmed);
|
|
105
|
+
if (isBinary || /^0o[0-7]+$/i.test(trimmed)) {
|
|
106
|
+
return parseInt(trimmed.slice(2), isBinary ? 2 : 8);
|
|
107
|
+
}
|
|
108
|
+
return +(/^0x[0-9a-f]+$/i.test(trimmed) ? trimmed : trimmed.replace(/_/g, ""));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/js/timer.ts
|
|
112
|
+
function repeat(callback, options) {
|
|
113
|
+
const count = typeof options?.count === "number" ? options.count : Infinity;
|
|
114
|
+
return timer(callback, { ...{ count }, ...options ?? {} }).start();
|
|
115
|
+
}
|
|
116
|
+
var timer = function(callback, config) {
|
|
117
|
+
const options = {
|
|
118
|
+
afterCallback: typeof config.afterCallback === "function" ? config.afterCallback : undefined,
|
|
119
|
+
callback,
|
|
120
|
+
count: typeof config.count === "number" && config.count >= 1 ? config.count : 1,
|
|
121
|
+
interval: typeof config.interval === "number" && config.interval >= 0 ? config.interval : 0
|
|
122
|
+
};
|
|
123
|
+
const state = {
|
|
124
|
+
active: false
|
|
125
|
+
};
|
|
126
|
+
const timer2 = Object.create(null);
|
|
127
|
+
Object.defineProperties(timer2, {
|
|
128
|
+
active: {
|
|
129
|
+
get() {
|
|
130
|
+
return state.active;
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
restart: {
|
|
134
|
+
value() {
|
|
135
|
+
return work("restart", timer2, state, options);
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
start: {
|
|
139
|
+
value() {
|
|
140
|
+
return work("start", timer2, state, options);
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
stop: {
|
|
144
|
+
value() {
|
|
145
|
+
return work("stop", timer2, state, options);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return timer2;
|
|
150
|
+
};
|
|
151
|
+
function wait(callback, time) {
|
|
152
|
+
return timer(callback, {
|
|
153
|
+
count: 1,
|
|
154
|
+
interval: time
|
|
155
|
+
}).start();
|
|
156
|
+
}
|
|
157
|
+
var work = function(type, timer2, state, options) {
|
|
158
|
+
if (type === "start" && timer2.active || type === "stop" && !timer2.active) {
|
|
159
|
+
return timer2;
|
|
160
|
+
}
|
|
161
|
+
const { afterCallback, callback, count, interval } = options;
|
|
162
|
+
if (typeof state.frame === "number") {
|
|
163
|
+
cancelAnimationFrame(state.frame);
|
|
164
|
+
afterCallback?.(false);
|
|
165
|
+
}
|
|
166
|
+
if (type === "stop") {
|
|
167
|
+
state.active = false;
|
|
168
|
+
state.frame = undefined;
|
|
169
|
+
return timer2;
|
|
170
|
+
}
|
|
171
|
+
state.active = true;
|
|
172
|
+
const isRepeated = count > 0;
|
|
173
|
+
const milliseconds = 16.666666666666668;
|
|
174
|
+
let index = 0;
|
|
175
|
+
let start;
|
|
176
|
+
function step(timestamp) {
|
|
177
|
+
if (!state.active) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
start ??= timestamp;
|
|
181
|
+
const elapsed = timestamp - start;
|
|
182
|
+
const maximum = elapsed + milliseconds;
|
|
183
|
+
const minimum = elapsed - milliseconds;
|
|
184
|
+
if (minimum < interval && interval < maximum) {
|
|
185
|
+
if (state.active) {
|
|
186
|
+
callback(isRepeated ? index : undefined);
|
|
187
|
+
}
|
|
188
|
+
index += 1;
|
|
189
|
+
if (index < count) {
|
|
190
|
+
start = undefined;
|
|
191
|
+
} else {
|
|
192
|
+
state.active = false;
|
|
193
|
+
state.frame = undefined;
|
|
194
|
+
afterCallback?.(true);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
state.frame = requestAnimationFrame(step);
|
|
199
|
+
}
|
|
200
|
+
state.frame = requestAnimationFrame(step);
|
|
201
|
+
return timer2;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// src/js/element/index.ts
|
|
205
|
+
function findElements(selector, context) {
|
|
206
|
+
const contexts = context === undefined ? [document] : findElements(context);
|
|
207
|
+
const elements = [];
|
|
208
|
+
if (typeof selector === "string") {
|
|
209
|
+
for (const context2 of contexts) {
|
|
210
|
+
elements.push(...Array.from(context2.querySelectorAll(selector) ?? []));
|
|
211
|
+
}
|
|
212
|
+
return elements;
|
|
213
|
+
}
|
|
214
|
+
const nodes = Array.isArray(selector) || selector instanceof NodeList ? selector : [selector];
|
|
215
|
+
for (const node of nodes) {
|
|
216
|
+
if (node instanceof Element && contexts.some((context2) => context2.contains(node))) {
|
|
217
|
+
elements.push(node);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return elements;
|
|
221
|
+
}
|
|
222
|
+
function findParentElement(origin, selector) {
|
|
223
|
+
if (origin == null || selector == null) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
function matches(element) {
|
|
227
|
+
return typeof selector === "string" ? element.matches?.(selector) ?? false : typeof selector === "function" ? selector(element) : false;
|
|
228
|
+
}
|
|
229
|
+
if (matches(origin)) {
|
|
230
|
+
return origin;
|
|
231
|
+
}
|
|
232
|
+
let parent = origin.parentElement;
|
|
233
|
+
while (parent != null && !matches(parent)) {
|
|
234
|
+
if (parent === document.body) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
parent = parent.parentElement;
|
|
238
|
+
}
|
|
239
|
+
return parent ?? undefined;
|
|
240
|
+
}
|
|
241
|
+
function getElementUnderPointer(skipIgnore) {
|
|
242
|
+
const elements = Array.from(document.querySelectorAll(":hover")).filter((element) => {
|
|
243
|
+
if (/^head$/i.test(element.tagName)) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
const style = getComputedStyle(element);
|
|
247
|
+
return typeof skipIgnore === "boolean" && skipIgnore || style.pointerEvents !== "none" && style.visibility !== "hidden";
|
|
248
|
+
});
|
|
249
|
+
return elements[elements.length - 1];
|
|
250
|
+
}
|
|
251
|
+
function getTextDirection(element) {
|
|
252
|
+
const direction = element.getAttribute("dir");
|
|
253
|
+
if (direction !== null && /^(ltr|rtl)$/i.test(direction)) {
|
|
254
|
+
return direction.toLowerCase();
|
|
255
|
+
}
|
|
256
|
+
return getComputedStyle?.(element)?.direction === "rtl" ? "rtl" : "ltr";
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// src/js/event.ts
|
|
260
|
+
function getEventPosition(event) {
|
|
261
|
+
let x;
|
|
262
|
+
let y;
|
|
263
|
+
if (event instanceof MouseEvent) {
|
|
264
|
+
x = event.clientX;
|
|
265
|
+
y = event.clientY;
|
|
266
|
+
} else if (event instanceof TouchEvent) {
|
|
267
|
+
x = event.touches[0]?.clientX;
|
|
268
|
+
y = event.touches[0]?.clientY;
|
|
269
|
+
}
|
|
270
|
+
return typeof x === "number" && typeof y === "number" ? { x, y } : undefined;
|
|
271
|
+
}
|
|
158
272
|
export {
|
|
273
|
+
wait,
|
|
159
274
|
setValue,
|
|
275
|
+
repeat,
|
|
160
276
|
isObject,
|
|
161
277
|
isNullableOrWhitespace,
|
|
162
278
|
isNullable,
|
|
@@ -164,10 +280,11 @@ export {
|
|
|
164
280
|
getValue,
|
|
165
281
|
getTextDirection,
|
|
166
282
|
getString,
|
|
167
|
-
getPosition,
|
|
168
283
|
getNumber,
|
|
284
|
+
getEventPosition,
|
|
169
285
|
getElementUnderPointer,
|
|
170
286
|
findParentElement,
|
|
287
|
+
findElements,
|
|
171
288
|
createUuid,
|
|
172
289
|
clampNumber
|
|
173
290
|
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/js/number.ts
|
|
2
|
+
function clampNumber(value, min, max) {
|
|
3
|
+
return Math.min(Math.max(getNumber(value), getNumber(min)), getNumber(max));
|
|
4
|
+
}
|
|
5
|
+
function getNumber(value) {
|
|
6
|
+
if (typeof value === "number") {
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
if (typeof value === "symbol") {
|
|
10
|
+
return NaN;
|
|
11
|
+
}
|
|
12
|
+
let parsed = value?.valueOf?.() ?? value;
|
|
13
|
+
if (typeof parsed === "object") {
|
|
14
|
+
parsed = parsed?.toString() ?? parsed;
|
|
15
|
+
}
|
|
16
|
+
if (typeof parsed !== "string") {
|
|
17
|
+
return parsed == null ? NaN : typeof parsed === "number" ? parsed : +parsed;
|
|
18
|
+
}
|
|
19
|
+
if (/^\s*0+\s*$/.test(parsed)) {
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
const trimmed = parsed.trim();
|
|
23
|
+
if (trimmed.length === 0) {
|
|
24
|
+
return NaN;
|
|
25
|
+
}
|
|
26
|
+
const isBinary = /^0b[01]+$/i.test(trimmed);
|
|
27
|
+
if (isBinary || /^0o[0-7]+$/i.test(trimmed)) {
|
|
28
|
+
return parseInt(trimmed.slice(2), isBinary ? 2 : 8);
|
|
29
|
+
}
|
|
30
|
+
return +(/^0x[0-9a-f]+$/i.test(trimmed) ? trimmed : trimmed.replace(/_/g, ""));
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
getNumber,
|
|
34
|
+
clampNumber
|
|
35
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/js/string.ts
|
|
2
|
+
function createUuid() {
|
|
3
|
+
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (substring) => (substring ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> substring / 4).toString(16));
|
|
4
|
+
}
|
|
5
|
+
function getString(value) {
|
|
6
|
+
return typeof value === "string" ? value : typeof value?.toString === "function" ? value.toString() : String(value);
|
|
7
|
+
}
|
|
8
|
+
function isNullableOrWhitespace(value) {
|
|
9
|
+
return value == null || getString(value).trim().length === 0;
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
isNullableOrWhitespace,
|
|
13
|
+
getString,
|
|
14
|
+
createUuid
|
|
15
|
+
};
|
package/dist/js/timer.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// src/js/timer.ts
|
|
2
|
+
function repeat(callback, options) {
|
|
3
|
+
const count = typeof options?.count === "number" ? options.count : Infinity;
|
|
4
|
+
return timer(callback, { ...{ count }, ...options ?? {} }).start();
|
|
5
|
+
}
|
|
6
|
+
var timer = function(callback, config) {
|
|
7
|
+
const options = {
|
|
8
|
+
afterCallback: typeof config.afterCallback === "function" ? config.afterCallback : undefined,
|
|
9
|
+
callback,
|
|
10
|
+
count: typeof config.count === "number" && config.count >= 1 ? config.count : 1,
|
|
11
|
+
interval: typeof config.interval === "number" && config.interval >= 0 ? config.interval : 0
|
|
12
|
+
};
|
|
13
|
+
const state = {
|
|
14
|
+
active: false
|
|
15
|
+
};
|
|
16
|
+
const timer2 = Object.create(null);
|
|
17
|
+
Object.defineProperties(timer2, {
|
|
18
|
+
active: {
|
|
19
|
+
get() {
|
|
20
|
+
return state.active;
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
restart: {
|
|
24
|
+
value() {
|
|
25
|
+
return work("restart", timer2, state, options);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
start: {
|
|
29
|
+
value() {
|
|
30
|
+
return work("start", timer2, state, options);
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
stop: {
|
|
34
|
+
value() {
|
|
35
|
+
return work("stop", timer2, state, options);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return timer2;
|
|
40
|
+
};
|
|
41
|
+
function wait(callback, time) {
|
|
42
|
+
return timer(callback, {
|
|
43
|
+
count: 1,
|
|
44
|
+
interval: time
|
|
45
|
+
}).start();
|
|
46
|
+
}
|
|
47
|
+
var work = function(type, timer2, state, options) {
|
|
48
|
+
if (type === "start" && timer2.active || type === "stop" && !timer2.active) {
|
|
49
|
+
return timer2;
|
|
50
|
+
}
|
|
51
|
+
const { afterCallback, callback, count, interval } = options;
|
|
52
|
+
if (typeof state.frame === "number") {
|
|
53
|
+
cancelAnimationFrame(state.frame);
|
|
54
|
+
afterCallback?.(false);
|
|
55
|
+
}
|
|
56
|
+
if (type === "stop") {
|
|
57
|
+
state.active = false;
|
|
58
|
+
state.frame = undefined;
|
|
59
|
+
return timer2;
|
|
60
|
+
}
|
|
61
|
+
state.active = true;
|
|
62
|
+
const isRepeated = count > 0;
|
|
63
|
+
const milliseconds = 16.666666666666668;
|
|
64
|
+
let index = 0;
|
|
65
|
+
let start;
|
|
66
|
+
function step(timestamp) {
|
|
67
|
+
if (!state.active) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
start ??= timestamp;
|
|
71
|
+
const elapsed = timestamp - start;
|
|
72
|
+
const maximum = elapsed + milliseconds;
|
|
73
|
+
const minimum = elapsed - milliseconds;
|
|
74
|
+
if (minimum < interval && interval < maximum) {
|
|
75
|
+
if (state.active) {
|
|
76
|
+
callback(isRepeated ? index : undefined);
|
|
77
|
+
}
|
|
78
|
+
index += 1;
|
|
79
|
+
if (index < count) {
|
|
80
|
+
start = undefined;
|
|
81
|
+
} else {
|
|
82
|
+
state.active = false;
|
|
83
|
+
state.frame = undefined;
|
|
84
|
+
afterCallback?.(true);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
state.frame = requestAnimationFrame(step);
|
|
89
|
+
}
|
|
90
|
+
state.frame = requestAnimationFrame(step);
|
|
91
|
+
return timer2;
|
|
92
|
+
};
|
|
93
|
+
export {
|
|
94
|
+
wait,
|
|
95
|
+
repeat
|
|
96
|
+
};
|
package/dist/js/value.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// src/js/string.ts
|
|
2
|
+
function createUuid() {
|
|
3
|
+
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (substring) => (substring ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> substring / 4).toString(16));
|
|
4
|
+
}
|
|
5
|
+
function getString(value) {
|
|
6
|
+
return typeof value === "string" ? value : typeof value?.toString === "function" ? value.toString() : String(value);
|
|
7
|
+
}
|
|
8
|
+
function isNullableOrWhitespace(value) {
|
|
9
|
+
return value == null || getString(value).trim().length === 0;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// src/js/value.ts
|
|
13
|
+
var _getValue = function(data, key) {
|
|
14
|
+
if (typeof data !== "object" || data === null || /^(__proto__|constructor|prototype)$/i.test(key)) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (data instanceof Map) {
|
|
18
|
+
return data.get(key);
|
|
19
|
+
}
|
|
20
|
+
return data[key];
|
|
21
|
+
};
|
|
22
|
+
var _setValue = function(data, key, value) {
|
|
23
|
+
if (typeof data !== "object" || data === null || /^(__proto__|constructor|prototype)$/i.test(key)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (data instanceof Map) {
|
|
27
|
+
data.set(key, value);
|
|
28
|
+
} else {
|
|
29
|
+
data[key] = value;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
function getValue(data, key) {
|
|
33
|
+
if (typeof data !== "object" || data === null || isNullableOrWhitespace(key)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const parts = getString(key).split(".").reverse();
|
|
37
|
+
let position = parts.length;
|
|
38
|
+
let value = data;
|
|
39
|
+
while (position--) {
|
|
40
|
+
value = _getValue(value, parts[position]);
|
|
41
|
+
if (value == null) {
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
function isArrayOrObject(value) {
|
|
48
|
+
return /^(array|object)$/i.test(value?.constructor?.name);
|
|
49
|
+
}
|
|
50
|
+
function isNullable(value) {
|
|
51
|
+
return value == null;
|
|
52
|
+
}
|
|
53
|
+
function isObject(value) {
|
|
54
|
+
return value?.constructor?.name === "Object";
|
|
55
|
+
}
|
|
56
|
+
function setValue(data, key, value) {
|
|
57
|
+
if (typeof data !== "object" || data === null || isNullableOrWhitespace(key)) {
|
|
58
|
+
return data;
|
|
59
|
+
}
|
|
60
|
+
const parts = getString(key).split(".").reverse();
|
|
61
|
+
let position = parts.length;
|
|
62
|
+
let target = data;
|
|
63
|
+
while (position--) {
|
|
64
|
+
const key2 = parts[position];
|
|
65
|
+
if (position === 0) {
|
|
66
|
+
_setValue(target, key2, value);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
let next = _getValue(target, key2);
|
|
70
|
+
if (typeof next !== "object" || next === null) {
|
|
71
|
+
next = /^\d+$/.test(parts[position - 1]) ? [] : {};
|
|
72
|
+
target[key2] = next;
|
|
73
|
+
}
|
|
74
|
+
target = next;
|
|
75
|
+
}
|
|
76
|
+
return data;
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
setValue,
|
|
80
|
+
isObject,
|
|
81
|
+
isNullable,
|
|
82
|
+
isArrayOrObject,
|
|
83
|
+
getValue
|
|
84
|
+
};
|
package/package.json
CHANGED
|
@@ -14,40 +14,66 @@
|
|
|
14
14
|
"exports": {
|
|
15
15
|
".": {
|
|
16
16
|
"types": "./types/index.d.ts",
|
|
17
|
-
"import": "./dist/js/
|
|
17
|
+
"import": "./dist/js/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./element": {
|
|
20
|
+
"types": "./types/element/index.d.ts",
|
|
21
|
+
"import": "./dist/js/element.index.js"
|
|
22
|
+
},
|
|
23
|
+
"./event": {
|
|
24
|
+
"types": "./types/event.d.ts",
|
|
25
|
+
"import": "./dist/js/event.js"
|
|
18
26
|
},
|
|
19
27
|
"./focusable": {
|
|
20
|
-
"types": "./types/focusable.d.ts",
|
|
28
|
+
"types": "./types/element/focusable.d.ts",
|
|
21
29
|
"import": "./dist/js/focusable.js"
|
|
22
30
|
},
|
|
31
|
+
"./number": {
|
|
32
|
+
"types": "./types/number.d.ts",
|
|
33
|
+
"import": "./dist/js/number.js"
|
|
34
|
+
},
|
|
35
|
+
"./string": {
|
|
36
|
+
"types": "./types/string.d.ts",
|
|
37
|
+
"import": "./dist/js/string.js"
|
|
38
|
+
},
|
|
23
39
|
"./supports-touch": {
|
|
24
40
|
"types": "./types/touch.d.ts",
|
|
25
41
|
"import": "./dist/js/touch.js"
|
|
26
42
|
},
|
|
43
|
+
"./timer": {
|
|
44
|
+
"types": "./types/timer.d.ts",
|
|
45
|
+
"import": "./dist/js/timer.js"
|
|
46
|
+
},
|
|
47
|
+
"./value": {
|
|
48
|
+
"types": "./types/value.d.ts",
|
|
49
|
+
"import": "./dist/js/value.js"
|
|
50
|
+
},
|
|
27
51
|
"./package.json": "./package.json"
|
|
28
52
|
},
|
|
29
|
-
"files": [
|
|
53
|
+
"files": [
|
|
54
|
+
"dist",
|
|
55
|
+
"src",
|
|
56
|
+
"types"
|
|
57
|
+
],
|
|
30
58
|
"keywords": [],
|
|
31
59
|
"license": "MIT",
|
|
32
|
-
"main": "./dist/js/
|
|
33
|
-
"module": "./dist/js/
|
|
60
|
+
"main": "./dist/js/index.js",
|
|
61
|
+
"module": "./dist/js/index.js",
|
|
34
62
|
"name": "@oscarpalmer/atoms",
|
|
35
63
|
"repository": {
|
|
36
64
|
"type": "git",
|
|
37
65
|
"url": "git+https://github.com/oscarpalmer/atoms.git"
|
|
38
66
|
},
|
|
39
67
|
"scripts": {
|
|
40
|
-
"build": "bun run build:css && bun run build:js && bun run
|
|
68
|
+
"build": "bun run build:css && bun run build:js && bun run types",
|
|
41
69
|
"build:css": "bunx sass ./src/css:./dist/css --no-source-map",
|
|
42
|
-
"build:js": "bunx bun
|
|
43
|
-
"build:js-focusable": "bunx bun build ./src/js/element/focusable.ts --outfile ./dist/js/focusable.js",
|
|
44
|
-
"build:js-touch": "bunx bun build ./src/js/touch.ts --outfile ./dist/js/touch.js",
|
|
70
|
+
"build:js": "bunx bun ./.bun.ts",
|
|
45
71
|
"test": "bun test --preload ./test/_preload.ts --coverage",
|
|
46
72
|
"types": "bunx tsc -p ./tsconfig.json",
|
|
47
73
|
"watch:css": "bunx sass ./src/css:./dist/css --no-source-map --watch",
|
|
48
|
-
"watch:js": "bun build ./src/js/index.ts --outfile ./dist/js/
|
|
74
|
+
"watch:js": "bun build ./src/js/index.ts --outfile ./dist/js/index.js --watch"
|
|
49
75
|
},
|
|
50
76
|
"type": "module",
|
|
51
77
|
"types": "./types/index.d.ts",
|
|
52
|
-
"version": "0.
|
|
78
|
+
"version": "0.10.0"
|
|
53
79
|
}
|
package/src/js/element/index.ts
CHANGED
|
@@ -1,8 +1,50 @@
|
|
|
1
|
+
type Selector = string | Element | Element[] | NodeList;
|
|
2
|
+
|
|
1
3
|
type TextDirection = 'ltr' | 'rtl';
|
|
2
4
|
|
|
5
|
+
/**
|
|
6
|
+
* - Find elements that match the selector
|
|
7
|
+
* - `context` is optional and defaults to `document`
|
|
8
|
+
*/
|
|
9
|
+
export function findElements(
|
|
10
|
+
selector: Selector,
|
|
11
|
+
context?: Selector,
|
|
12
|
+
): Element[] {
|
|
13
|
+
const contexts = context === undefined ? [document] : findElements(context);
|
|
14
|
+
|
|
15
|
+
const elements: Element[] = [];
|
|
16
|
+
|
|
17
|
+
if (typeof selector === 'string') {
|
|
18
|
+
for (const context of contexts) {
|
|
19
|
+
elements.push(
|
|
20
|
+
...Array.from((context as Element).querySelectorAll(selector) ?? []),
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return elements;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const nodes =
|
|
28
|
+
Array.isArray(selector) || selector instanceof NodeList
|
|
29
|
+
? selector
|
|
30
|
+
: [selector];
|
|
31
|
+
|
|
32
|
+
for (const node of nodes) {
|
|
33
|
+
if (
|
|
34
|
+
node instanceof Element &&
|
|
35
|
+
contexts.some(context => context.contains(node))
|
|
36
|
+
) {
|
|
37
|
+
elements.push(node);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return elements;
|
|
42
|
+
}
|
|
43
|
+
|
|
3
44
|
/**
|
|
4
45
|
* - Find the parent element that matches the selector
|
|
5
46
|
* - Matches may be found by a query string or a callback
|
|
47
|
+
* - If no match is found, `undefined` is returned
|
|
6
48
|
*/
|
|
7
49
|
export function findParentElement(
|
|
8
50
|
origin: Element,
|
package/src/js/event.ts
CHANGED
package/src/js/index.ts
CHANGED
package/src/js/timer.ts
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callback that runs after the timer has finished (or is stopped)
|
|
3
|
+
* - `finished` is `true` if the timer was allowed to finish, and `false` if it was stopped
|
|
4
|
+
*/
|
|
5
|
+
type AfterCallback = (finished: boolean) => void;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Callback that runs for each iteration of the timer
|
|
9
|
+
*/
|
|
10
|
+
type IndexedCallback = (index: number) => void;
|
|
11
|
+
|
|
12
|
+
type Options = {
|
|
13
|
+
/**
|
|
14
|
+
* Callback to run after the timer has finished (or is stopped)
|
|
15
|
+
* - `finished` is `true` if the timer was allowed to finish, and `false` if it was stopped
|
|
16
|
+
*/
|
|
17
|
+
afterCallback?: AfterCallback;
|
|
18
|
+
/**
|
|
19
|
+
* How many times the timer should repeat
|
|
20
|
+
*/
|
|
21
|
+
count?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Interval between each callback
|
|
24
|
+
*/
|
|
25
|
+
interval?: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type Timer = {
|
|
29
|
+
/**
|
|
30
|
+
* Is the timer running?
|
|
31
|
+
*/
|
|
32
|
+
get active(): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Restarts the timer
|
|
35
|
+
*/
|
|
36
|
+
restart: () => Timer;
|
|
37
|
+
/**
|
|
38
|
+
* Starts the timer
|
|
39
|
+
*/
|
|
40
|
+
start: () => Timer;
|
|
41
|
+
/**
|
|
42
|
+
* Stops the timer
|
|
43
|
+
*/
|
|
44
|
+
stop: () => Timer;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
type TimerOptions = {
|
|
48
|
+
afterCallback?: AfterCallback;
|
|
49
|
+
callback: IndexedCallback;
|
|
50
|
+
count: number;
|
|
51
|
+
interval: number;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
type TimerState = {
|
|
55
|
+
active: boolean;
|
|
56
|
+
frame?: number;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
type WorkType = 'restart' | 'start' | 'stop';
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* - Creates a timer which calls a callback after a certain amount of time, and repeats it a certain amount of times, with an optional callback after it's finished (or stopped)
|
|
63
|
+
* - `options.count` defaults to `Infinity`
|
|
64
|
+
* - `options.time` defaults to `0`
|
|
65
|
+
* - `options.afterCallback` defaults to `undefined`
|
|
66
|
+
*/
|
|
67
|
+
export function repeat(
|
|
68
|
+
callback: (index: number) => void,
|
|
69
|
+
options?: Options,
|
|
70
|
+
): Timer {
|
|
71
|
+
const count = typeof options?.count === 'number' ? options.count : Infinity;
|
|
72
|
+
|
|
73
|
+
return timer(callback, {...{count}, ...(options ?? {})}).start();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function timer(callback: IndexedCallback, config: Options): Timer {
|
|
77
|
+
const options: TimerOptions = {
|
|
78
|
+
afterCallback:
|
|
79
|
+
typeof config.afterCallback === 'function'
|
|
80
|
+
? config.afterCallback
|
|
81
|
+
: undefined,
|
|
82
|
+
callback,
|
|
83
|
+
count:
|
|
84
|
+
typeof config.count === 'number' && config.count >= 1 ? config.count : 1,
|
|
85
|
+
interval:
|
|
86
|
+
typeof config.interval === 'number' && config.interval >= 0
|
|
87
|
+
? config.interval
|
|
88
|
+
: 0,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const state: TimerState = {
|
|
92
|
+
active: false,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const timer = Object.create(null);
|
|
96
|
+
|
|
97
|
+
Object.defineProperties(timer, {
|
|
98
|
+
active: {
|
|
99
|
+
get() {
|
|
100
|
+
return state.active;
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
restart: {
|
|
104
|
+
value() {
|
|
105
|
+
return work('restart', timer, state, options);
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
start: {
|
|
109
|
+
value() {
|
|
110
|
+
return work('start', timer, state, options);
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
stop: {
|
|
114
|
+
value() {
|
|
115
|
+
return work('stop', timer, state, options);
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return timer;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* - Creates a timer which calls a callback after a certain amount of time
|
|
125
|
+
* - `time` defaults to `0`
|
|
126
|
+
*/
|
|
127
|
+
export function wait(callback: () => void, time?: number): Timer {
|
|
128
|
+
return timer(callback, {
|
|
129
|
+
count: 1,
|
|
130
|
+
interval: time,
|
|
131
|
+
}).start();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function work(
|
|
135
|
+
type: WorkType,
|
|
136
|
+
timer: Timer,
|
|
137
|
+
state: TimerState,
|
|
138
|
+
options: TimerOptions,
|
|
139
|
+
): Timer {
|
|
140
|
+
if (
|
|
141
|
+
(type === 'start' && timer.active) ||
|
|
142
|
+
(type === 'stop' && !timer.active)
|
|
143
|
+
) {
|
|
144
|
+
return timer;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const {afterCallback, callback, count, interval} = options;
|
|
148
|
+
|
|
149
|
+
if (typeof state.frame === 'number') {
|
|
150
|
+
cancelAnimationFrame(state.frame);
|
|
151
|
+
|
|
152
|
+
afterCallback?.(false);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (type === 'stop') {
|
|
156
|
+
state.active = false;
|
|
157
|
+
state.frame = undefined;
|
|
158
|
+
|
|
159
|
+
return timer;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
state.active = true;
|
|
163
|
+
|
|
164
|
+
const isRepeated = count > 0;
|
|
165
|
+
const milliseconds = 1000 / 60;
|
|
166
|
+
|
|
167
|
+
let index = 0;
|
|
168
|
+
|
|
169
|
+
let start: DOMHighResTimeStamp | undefined;
|
|
170
|
+
|
|
171
|
+
function step(timestamp: DOMHighResTimeStamp): void {
|
|
172
|
+
if (!state.active) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
start ??= timestamp;
|
|
177
|
+
|
|
178
|
+
const elapsed = timestamp - start;
|
|
179
|
+
const maximum = elapsed + milliseconds;
|
|
180
|
+
const minimum = elapsed - milliseconds;
|
|
181
|
+
|
|
182
|
+
if (minimum < interval && interval < maximum) {
|
|
183
|
+
if (state.active) {
|
|
184
|
+
callback((isRepeated ? index : undefined) as never);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
index += 1;
|
|
188
|
+
|
|
189
|
+
if (index < count) {
|
|
190
|
+
start = undefined;
|
|
191
|
+
} else {
|
|
192
|
+
state.active = false;
|
|
193
|
+
state.frame = undefined;
|
|
194
|
+
|
|
195
|
+
afterCallback?.(true);
|
|
196
|
+
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
state.frame = requestAnimationFrame(step);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
state.frame = requestAnimationFrame(step);
|
|
205
|
+
|
|
206
|
+
return timer;
|
|
207
|
+
}
|
package/types/element/index.d.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
type Selector = string | Element | Element[] | NodeList;
|
|
1
2
|
type TextDirection = 'ltr' | 'rtl';
|
|
3
|
+
/**
|
|
4
|
+
* - Find elements that match the selector
|
|
5
|
+
* - `context` is optional and defaults to `document`
|
|
6
|
+
*/
|
|
7
|
+
export declare function findElements(selector: Selector, context?: Selector): Element[];
|
|
2
8
|
/**
|
|
3
9
|
* - Find the parent element that matches the selector
|
|
4
10
|
* - Matches may be found by a query string or a callback
|
|
11
|
+
* - If no match is found, `undefined` is returned
|
|
5
12
|
*/
|
|
6
13
|
export declare function findParentElement(origin: Element, selector: string | ((element: Element) => boolean)): Element | undefined;
|
|
7
14
|
/**
|
package/types/event.d.ts
CHANGED
|
@@ -5,5 +5,5 @@ type Position = {
|
|
|
5
5
|
/**
|
|
6
6
|
* Get the X- and Y-coordinates from a pointer event
|
|
7
7
|
*/
|
|
8
|
-
export declare function
|
|
8
|
+
export declare function getEventPosition(event: MouseEvent | TouchEvent): Position | undefined;
|
|
9
9
|
export {};
|
package/types/index.d.ts
CHANGED
package/types/timer.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callback that runs after the timer has finished (or is stopped)
|
|
3
|
+
* - `finished` is `true` if the timer was allowed to finish, and `false` if it was stopped
|
|
4
|
+
*/
|
|
5
|
+
type AfterCallback = (finished: boolean) => void;
|
|
6
|
+
type Options = {
|
|
7
|
+
/**
|
|
8
|
+
* Callback to run after the timer has finished (or is stopped)
|
|
9
|
+
* - `finished` is `true` if the timer was allowed to finish, and `false` if it was stopped
|
|
10
|
+
*/
|
|
11
|
+
afterCallback?: AfterCallback;
|
|
12
|
+
/**
|
|
13
|
+
* How many times the timer should repeat
|
|
14
|
+
*/
|
|
15
|
+
count?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Interval between each callback
|
|
18
|
+
*/
|
|
19
|
+
interval?: number;
|
|
20
|
+
};
|
|
21
|
+
type Timer = {
|
|
22
|
+
/**
|
|
23
|
+
* Is the timer running?
|
|
24
|
+
*/
|
|
25
|
+
get active(): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Restarts the timer
|
|
28
|
+
*/
|
|
29
|
+
restart: () => Timer;
|
|
30
|
+
/**
|
|
31
|
+
* Starts the timer
|
|
32
|
+
*/
|
|
33
|
+
start: () => Timer;
|
|
34
|
+
/**
|
|
35
|
+
* Stops the timer
|
|
36
|
+
*/
|
|
37
|
+
stop: () => Timer;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* - Creates a timer which calls a callback after a certain amount of time, and repeats it a certain amount of times, with an optional callback after it's finished (or stopped)
|
|
41
|
+
* - `options.count` defaults to `Infinity`
|
|
42
|
+
* - `options.time` defaults to `0`
|
|
43
|
+
* - `options.afterCallback` defaults to `undefined`
|
|
44
|
+
*/
|
|
45
|
+
export declare function repeat(callback: (index: number) => void, options?: Options): Timer;
|
|
46
|
+
/**
|
|
47
|
+
* - Creates a timer which calls a callback after a certain amount of time
|
|
48
|
+
* - `time` defaults to `0`
|
|
49
|
+
*/
|
|
50
|
+
export declare function wait(callback: () => void, time?: number): Timer;
|
|
51
|
+
export {};
|
|
File without changes
|