@sohanemon/utils 5.2.3 → 5.2.4
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/hooks/async.d.ts
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
type AsyncStatus = 'idle' | 'pending' | 'success' | 'error';
|
|
3
|
+
type OnSuccess<TData> = (data: TData) => void | Promise<void>;
|
|
4
|
+
type OnError<TError extends Error = Error> = (error: TError) => void | Promise<void>;
|
|
5
|
+
type OnSettled<TData, TError extends Error = Error> = (data: TData | undefined, error: TError | undefined) => void | Promise<void>;
|
|
6
|
+
interface UseAsyncOptions<TData = unknown, TError extends Error = Error> {
|
|
7
|
+
mode?: 'auto' | 'manual';
|
|
8
|
+
deps?: React.DependencyList;
|
|
9
|
+
onSuccess?: OnSuccess<TData>;
|
|
10
|
+
onError?: OnError<TError>;
|
|
11
|
+
onSettled?: OnSettled<TData, TError>;
|
|
10
12
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
interface UseAsyncReturn<TData, TError extends Error = Error> {
|
|
14
|
+
data: TData | undefined;
|
|
15
|
+
error: TError | undefined;
|
|
16
|
+
status: AsyncStatus;
|
|
17
|
+
isIdle: boolean;
|
|
18
|
+
isPending: boolean;
|
|
19
|
+
isSuccess: boolean;
|
|
20
|
+
isError: boolean;
|
|
21
|
+
execute: () => Promise<TData>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A fully typesafe async hook inspired by TanStack Query
|
|
25
|
+
* @param asyncFn - Async function that returns data
|
|
26
|
+
* @param options - Configuration options including callbacks
|
|
27
|
+
* @returns Object with data, error, status and helper methods
|
|
28
|
+
*/
|
|
29
|
+
export declare function useAsync<TData, TError extends Error = Error>(asyncFn: () => Promise<TData>, options?: UseAsyncOptions<TData, TError>): UseAsyncReturn<TData, TError>;
|
|
17
30
|
export {};
|
package/dist/hooks/async.js
CHANGED
|
@@ -1,44 +1,72 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* A fully typesafe async hook inspired by TanStack Query
|
|
5
|
+
* @param asyncFn - Async function that returns data
|
|
6
|
+
* @param options - Configuration options including callbacks
|
|
7
|
+
* @returns Object with data, error, status and helper methods
|
|
8
|
+
*/
|
|
9
|
+
export function useAsync(asyncFn, options = {}) {
|
|
10
|
+
const { mode = 'manual', deps, onSuccess, onError, onSettled } = options;
|
|
11
|
+
const [data, setData] = React.useState(undefined);
|
|
12
|
+
const [error, setError] = React.useState(undefined);
|
|
13
|
+
const [status, setStatus] = React.useState('idle');
|
|
14
|
+
const isMountedRef = React.useRef(true);
|
|
15
|
+
const memoizedOnSuccess = React.useCallback(onSuccess || (() => { }), [
|
|
16
|
+
onSuccess,
|
|
17
|
+
]);
|
|
18
|
+
const memoizedOnError = React.useCallback(onError || (() => { }), [onError]);
|
|
19
|
+
const memoizedOnSettled = React.useCallback(onSettled || (() => { }), [
|
|
20
|
+
onSettled,
|
|
21
|
+
]);
|
|
22
|
+
const execute = React.useCallback(async () => {
|
|
23
|
+
setStatus('pending');
|
|
24
|
+
setError(undefined);
|
|
15
25
|
try {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
26
|
+
const result = await asyncFn();
|
|
27
|
+
if (isMountedRef.current) {
|
|
28
|
+
setData(result);
|
|
29
|
+
setStatus('success');
|
|
30
|
+
await memoizedOnSuccess(result);
|
|
31
|
+
await memoizedOnSettled(result, undefined);
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
23
34
|
}
|
|
24
|
-
catch (
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
catch (err) {
|
|
36
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
37
|
+
const typedError = error;
|
|
38
|
+
if (isMountedRef.current) {
|
|
39
|
+
setError(typedError);
|
|
40
|
+
setStatus('error');
|
|
41
|
+
await memoizedOnError(typedError);
|
|
42
|
+
await memoizedOnSettled(undefined, typedError);
|
|
43
|
+
}
|
|
44
|
+
throw error;
|
|
27
45
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}, [fn, onExecute, onSuccess, onError, onSettle]);
|
|
33
|
-
useIsomorphicEffect(() => {
|
|
34
|
-
if (mode === 'onLoad') {
|
|
35
|
-
execute(initialArgs);
|
|
46
|
+
}, [asyncFn, memoizedOnSuccess, memoizedOnError, memoizedOnSettled]);
|
|
47
|
+
React.useEffect(() => {
|
|
48
|
+
if (mode === 'auto') {
|
|
49
|
+
execute();
|
|
36
50
|
}
|
|
51
|
+
}, [asyncFn, mode, execute]);
|
|
52
|
+
React.useEffect(() => {
|
|
53
|
+
return () => {
|
|
54
|
+
isMountedRef.current = false;
|
|
55
|
+
};
|
|
37
56
|
}, []);
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
if (deps && mode === 'auto') {
|
|
59
|
+
execute();
|
|
60
|
+
}
|
|
61
|
+
}, deps ? deps : []);
|
|
38
62
|
return {
|
|
39
|
-
|
|
40
|
-
isLoading: isLoading || isPending,
|
|
41
|
-
result,
|
|
63
|
+
data,
|
|
42
64
|
error,
|
|
65
|
+
status,
|
|
66
|
+
isIdle: status === 'idle',
|
|
67
|
+
isPending: status === 'pending',
|
|
68
|
+
isSuccess: status === 'success',
|
|
69
|
+
isError: status === 'error',
|
|
70
|
+
execute,
|
|
43
71
|
};
|
|
44
|
-
}
|
|
72
|
+
}
|
package/dist/hooks/index.d.ts
CHANGED
package/dist/hooks/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function ScrollableMarker(): any;
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { useScheduledEffect } from '../hooks/schedule';
|
|
3
|
-
export function ScrollableMarker() {
|
|
4
|
-
useScheduledEffect(() => {
|
|
5
|
-
const root = document.body;
|
|
6
|
-
if (!root)
|
|
7
|
-
return;
|
|
8
|
-
const isScrollable = (el) => {
|
|
9
|
-
const style = getComputedStyle(el);
|
|
10
|
-
if (style.overflow === 'hidden' &&
|
|
11
|
-
style.overflowY === 'hidden' &&
|
|
12
|
-
style.overflowX === 'hidden')
|
|
13
|
-
return false;
|
|
14
|
-
const canScrollY = (style.overflowY === 'auto' || style.overflowY === 'scroll') &&
|
|
15
|
-
el.scrollHeight > el.clientHeight;
|
|
16
|
-
const canScrollX = (style.overflowX === 'auto' || style.overflowX === 'scroll') &&
|
|
17
|
-
el.scrollWidth > el.clientWidth;
|
|
18
|
-
return canScrollY || canScrollX;
|
|
19
|
-
};
|
|
20
|
-
const markScrollable = (el) => {
|
|
21
|
-
if (isScrollable(el))
|
|
22
|
-
el.dataset.scrollable = 'true';
|
|
23
|
-
else
|
|
24
|
-
delete el.dataset.scrollable;
|
|
25
|
-
};
|
|
26
|
-
const scanTree = (node) => {
|
|
27
|
-
markScrollable(node);
|
|
28
|
-
for (let i = 0; i < node.children.length; i++) {
|
|
29
|
-
const child = node.children[i];
|
|
30
|
-
scanTree(child);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
requestIdleCallback(() => scanTree(root));
|
|
34
|
-
const observer = new MutationObserver((mutations) => {
|
|
35
|
-
for (const m of mutations) {
|
|
36
|
-
if (m.type === 'childList') {
|
|
37
|
-
m.addedNodes.forEach((n) => {
|
|
38
|
-
if (n instanceof HTMLElement)
|
|
39
|
-
scanTree(n);
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
else if (m.type === 'attributes' && m.target instanceof HTMLElement) {
|
|
43
|
-
markScrollable(m.target);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
observer.observe(root, {
|
|
48
|
-
subtree: true,
|
|
49
|
-
childList: true,
|
|
50
|
-
attributes: true,
|
|
51
|
-
attributeFilter: ['style', 'class'],
|
|
52
|
-
});
|
|
53
|
-
return () => observer.disconnect();
|
|
54
|
-
}, []);
|
|
55
|
-
return null;
|
|
56
|
-
}
|