@thepassle/app-tools 0.0.10 → 0.7.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/utils/async.js ADDED
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @param {Function} f
3
+ * @returns {<Args>(...args: Args[]) => void}
4
+ */
5
+ export function debounce(f) {
6
+ let timeoutId;
7
+
8
+ return (...args) => {
9
+ clearTimeout(timeoutId);
10
+ timeoutId = setTimeout(() => {
11
+ timeoutId = null;
12
+ f(...args);
13
+ });
14
+ };
15
+ }
16
+
17
+ /**
18
+ * @returns {Promise<number>}
19
+ */
20
+ export const onePaint = () => new Promise(r => requestAnimationFrame(r));
21
+ /**
22
+ * @param {HTMLElement} element
23
+ * @returns {Promise<PromiseSettledResult<Animation>[]>}
24
+ */
25
+ export const animationsComplete = element => Promise.allSettled(element.getAnimations().map(animation => animation.finished));
26
+
27
+ /**
28
+ * @param {() => void} f
29
+ * @param {number} ms
30
+ * @param {{
31
+ * signal?: AbortSignal
32
+ * }} options
33
+ */
34
+ export function setAbortableTimeout(f, ms, {signal}) {
35
+ let t;
36
+ if(!signal?.aborted) {
37
+ t = setTimeout(f, ms);
38
+ }
39
+ signal?.addEventListener('abort', () => clearTimeout(t), {once: true});
40
+ };
41
+
42
+ /**
43
+ * @param {() => boolean} predicate
44
+ * @param {{
45
+ * timeout?: number,
46
+ * message?: string,
47
+ * interval?: number
48
+ * }} options
49
+ * @returns {Promise<void>}
50
+ */
51
+ export function waitUntil(predicate, options = {}) {
52
+ const {
53
+ timeout = 1000,
54
+ message = `waitUntil timed out after ${timeout}ms`,
55
+ interval = 50,
56
+ } = options;
57
+
58
+ return new Promise((resolve, reject) => {
59
+ let timeoutId;
60
+
61
+ setTimeout(() => {
62
+ clearTimeout(timeoutId);
63
+ reject(new Error(message));
64
+ }, timeout);
65
+
66
+ async function nextInterval() {
67
+ try {
68
+ if (await predicate()) {
69
+ resolve();
70
+ } else {
71
+ timeoutId = setTimeout(() => {
72
+ nextInterval();
73
+ }, interval);
74
+ }
75
+ } catch (error) {
76
+ reject(error);
77
+ }
78
+ }
79
+ nextInterval();
80
+ });
81
+ }
package/utils/log.js ADDED
@@ -0,0 +1,44 @@
1
+ const KEY = Symbol.for('app-tools::log::1.x');
2
+
3
+ globalThis[KEY] = {
4
+ setDebug,
5
+ debug: new URL(window.location.href).searchParams.has('app-tools-debug')
6
+ };
7
+
8
+ /**
9
+ * @param {boolean} value
10
+ */
11
+ export function setDebug(value) {
12
+ globalThis[KEY].debug = !!value;
13
+ }
14
+
15
+ /**
16
+ * @returns {boolean}
17
+ */
18
+ export function getDebug() {
19
+ return globalThis[KEY].debug;
20
+ }
21
+
22
+ /**
23
+ * @param {string} action - describing the action
24
+ * @param {*} [data] - any js value
25
+ */
26
+ export function log(action, data) {
27
+ if(globalThis[KEY].debug) {
28
+ console.groupCollapsed(`[app-tools] ${action}`);
29
+ if(data) {
30
+ console.log(data);
31
+ }
32
+ console.groupEnd();
33
+ }
34
+ }
35
+
36
+ /**
37
+ * @param {string} title
38
+ * @returns {(action: string, data?: any) => void}
39
+ */
40
+ export function createLogger(title) {
41
+ return (action, data) => {
42
+ log(`${title}: ${action}`, data);
43
+ }
44
+ }
package/utils/media.js ADDED
@@ -0,0 +1,47 @@
1
+ export const media = {
2
+ MIN: {
3
+ XXXL: createMatch('(min-width: 1440px)'),
4
+ XXL: createMatch('(min-width: 1280px)'),
5
+ XL: createMatch('(min-width: 960px)'),
6
+ LG: createMatch('(min-width: 840px)'),
7
+ MD: createMatch('(min-width: 600px)'),
8
+ SM: createMatch('(min-width: 480px)'),
9
+ XS: createMatch('(min-width: 320px)'),
10
+ XXS: createMatch('(min-width: 0px)'),
11
+ XXXS: createMatch('(min-width: 0px)'),
12
+ },
13
+ MAX: {
14
+ XXXL: createMatch('(max-width: 1600px)'),
15
+ XXL: createMatch('(max-width: 1440px)'),
16
+ XL: createMatch('(max-width: 1280px)'),
17
+ LG: createMatch('(max-width: 960px)'),
18
+ MD: createMatch('(max-width: 840px)'),
19
+ SM: createMatch('(max-width: 600px)'),
20
+ XS: createMatch('(max-width: 480px)'),
21
+ XXS: createMatch('(max-width: 320px)'),
22
+ XXXS: createMatch('(max-width: 0px)'),
23
+ },
24
+ STANDALONE: createMatch('(display-mode: standalone)'),
25
+ REDUCED_MOTION: createMatch('(prefers-reduced-motion: reduce)'),
26
+ DARK_MODE: createMatch('(prefers-color-scheme: dark)'),
27
+ LIGHT_MODE: createMatch('(prefers-color-scheme: light)'),
28
+ };
29
+
30
+ function createMatch(query) {
31
+ return function match(callback) {
32
+ const mediaQuery = window.matchMedia(query);
33
+
34
+ if(callback) {
35
+ function executeCb({matches}) {
36
+ callback?.(matches);
37
+ }
38
+
39
+ mediaQuery.addListener(executeCb);
40
+ callback(mediaQuery.matches);
41
+ return () => {
42
+ mediaQuery.removeListener(executeCb);
43
+ }
44
+ }
45
+ return mediaQuery.matches;
46
+ }
47
+ }
package/utils.js CHANGED
@@ -1,2 +1,5 @@
1
+ export { waitUntil, debounce, setAbortableTimeout } from './utils/async.js';
2
+ export { media } from './utils/media.js';
1
3
  export { when } from './utils/index.js';
4
+ export { log, setDebug, getDebug } from './utils/log.js';
2
5
  export { createService } from './utils/Service.js';