@ilokesto/utilinent 0.0.24 → 0.0.25
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/{experimental → components}/Slot.js +10 -4
- package/dist/experimental/Slacker.d.ts +1 -72
- package/dist/experimental/Slacker.js +54 -88
- package/dist/experimental.d.ts +0 -1
- package/dist/experimental.js +0 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types/index.d.ts +13 -4
- package/package.json +1 -1
- /package/dist/{experimental → components}/Slot.d.ts +0 -0
|
@@ -40,9 +40,15 @@ export const Slot = forwardRef((props, ref) => {
|
|
|
40
40
|
const childrenArray = Children.toArray(children);
|
|
41
41
|
const slottable = childrenArray.find(isSlottable);
|
|
42
42
|
if (slottable) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
// Slottable의 children을 가져와서 병합
|
|
44
|
+
const slottableChild = slottable.props.children;
|
|
45
|
+
if (!isValidElement(slottableChild)) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const newElement = cloneElement(slottableChild, {
|
|
49
|
+
...mergeProps(slotProps, slottableChild.props),
|
|
50
|
+
ref: ref ? composeRefs(ref, slottableChild.ref) : slottableChild.ref,
|
|
51
|
+
key: slottable.key
|
|
46
52
|
});
|
|
47
53
|
const newChildren = childrenArray.map((child) => {
|
|
48
54
|
if (child === slottable) {
|
|
@@ -58,7 +64,7 @@ export const Slot = forwardRef((props, ref) => {
|
|
|
58
64
|
}
|
|
59
65
|
return cloneElement(child, {
|
|
60
66
|
...mergeProps(slotProps, child.props),
|
|
61
|
-
ref: composeRefs(ref, child.ref)
|
|
67
|
+
ref: ref ? composeRefs(ref, child.ref) : child.ref
|
|
62
68
|
});
|
|
63
69
|
});
|
|
64
70
|
function isSlottable(child) {
|
|
@@ -1,73 +1,2 @@
|
|
|
1
1
|
import { SlackerProps } from "../types";
|
|
2
|
-
|
|
3
|
-
* Slacker 컴포넌트 - Lazy Loading 전용
|
|
4
|
-
*
|
|
5
|
-
* 뷰포트에 보이지 않을 때는 fallback을 표시하고,
|
|
6
|
-
* 뷰포트에 들어오면 loader를 실행하여 데이터를 로드한 후
|
|
7
|
-
* children 함수에 로드된 데이터를 전달하여 렌더링합니다.
|
|
8
|
-
* 한 번 로드되면 다시 되돌리지 않습니다 (triggerOnce=true).
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```tsx
|
|
12
|
-
* // 컴포넌트 lazy loading
|
|
13
|
-
* <Slacker
|
|
14
|
-
* fallback={<ChartSkeleton />}
|
|
15
|
-
* loader={async () => {
|
|
16
|
-
* const { HeavyChart } = await import('./HeavyChart');
|
|
17
|
-
* return HeavyChart;
|
|
18
|
-
* }}
|
|
19
|
-
* >
|
|
20
|
-
* {(Component) => <Component data={data} />}
|
|
21
|
-
* </Slacker>
|
|
22
|
-
*
|
|
23
|
-
* // 데이터 lazy loading
|
|
24
|
-
* <Slacker
|
|
25
|
-
* fallback={<div>Loading data...</div>}
|
|
26
|
-
* loader={async () => {
|
|
27
|
-
* const response = await fetch('/api/data');
|
|
28
|
-
* return response.json();
|
|
29
|
-
* }}
|
|
30
|
-
* >
|
|
31
|
-
* {(data) => (
|
|
32
|
-
* <div>
|
|
33
|
-
* <h2>{data.title}</h2>
|
|
34
|
-
* <p>{data.description}</p>
|
|
35
|
-
* </div>
|
|
36
|
-
* )}
|
|
37
|
-
* </Slacker>
|
|
38
|
-
*
|
|
39
|
-
* // 라이브러리와 데이터 함께 로딩
|
|
40
|
-
* <Slacker
|
|
41
|
-
* fallback={<div>Loading chart library...</div>}
|
|
42
|
-
* loader={async () => {
|
|
43
|
-
* const [{ Chart }, chartData] = await Promise.all([
|
|
44
|
-
* import('chart.js'),
|
|
45
|
-
* fetch('/api/chart-data').then(r => r.json())
|
|
46
|
-
* ]);
|
|
47
|
-
* return { Chart, data: chartData };
|
|
48
|
-
* }}
|
|
49
|
-
* >
|
|
50
|
-
* {({ Chart, data }) => <Chart data={data} />}
|
|
51
|
-
* </Slacker>
|
|
52
|
-
*
|
|
53
|
-
* // 이미지와 메타데이터 함께 로딩
|
|
54
|
-
* <Slacker
|
|
55
|
-
* fallback={<ImageSkeleton />}
|
|
56
|
-
* loader={async () => {
|
|
57
|
-
* const [imageUrl, metadata] = await Promise.all([
|
|
58
|
-
* loadHighResImage(id),
|
|
59
|
-
* fetch(`/api/images/${id}/metadata`).then(r => r.json())
|
|
60
|
-
* ]);
|
|
61
|
-
* return { imageUrl, metadata };
|
|
62
|
-
* }}
|
|
63
|
-
* >
|
|
64
|
-
* {({ imageUrl, metadata }) => (
|
|
65
|
-
* <div>
|
|
66
|
-
* <img src={imageUrl} alt={metadata.title} />
|
|
67
|
-
* <p>{metadata.description}</p>
|
|
68
|
-
* </div>
|
|
69
|
-
* )}
|
|
70
|
-
* </Slacker>
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
export declare function Slacker({ children, fallback, loader, threshold, rootMargin, }: SlackerProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function Slacker<T = any>({ children, errorFallback, loadingFallback, loader, threshold, rootMargin, onError, maxRetries, retryDelay, }: SlackerProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,96 +1,62 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import { useCallback, useState } from "react";
|
|
3
3
|
import { Observer } from "../components/Observer";
|
|
4
|
-
|
|
5
|
-
* Slacker 컴포넌트 - Lazy Loading 전용
|
|
6
|
-
*
|
|
7
|
-
* 뷰포트에 보이지 않을 때는 fallback을 표시하고,
|
|
8
|
-
* 뷰포트에 들어오면 loader를 실행하여 데이터를 로드한 후
|
|
9
|
-
* children 함수에 로드된 데이터를 전달하여 렌더링합니다.
|
|
10
|
-
* 한 번 로드되면 다시 되돌리지 않습니다 (triggerOnce=true).
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```tsx
|
|
14
|
-
* // 컴포넌트 lazy loading
|
|
15
|
-
* <Slacker
|
|
16
|
-
* fallback={<ChartSkeleton />}
|
|
17
|
-
* loader={async () => {
|
|
18
|
-
* const { HeavyChart } = await import('./HeavyChart');
|
|
19
|
-
* return HeavyChart;
|
|
20
|
-
* }}
|
|
21
|
-
* >
|
|
22
|
-
* {(Component) => <Component data={data} />}
|
|
23
|
-
* </Slacker>
|
|
24
|
-
*
|
|
25
|
-
* // 데이터 lazy loading
|
|
26
|
-
* <Slacker
|
|
27
|
-
* fallback={<div>Loading data...</div>}
|
|
28
|
-
* loader={async () => {
|
|
29
|
-
* const response = await fetch('/api/data');
|
|
30
|
-
* return response.json();
|
|
31
|
-
* }}
|
|
32
|
-
* >
|
|
33
|
-
* {(data) => (
|
|
34
|
-
* <div>
|
|
35
|
-
* <h2>{data.title}</h2>
|
|
36
|
-
* <p>{data.description}</p>
|
|
37
|
-
* </div>
|
|
38
|
-
* )}
|
|
39
|
-
* </Slacker>
|
|
40
|
-
*
|
|
41
|
-
* // 라이브러리와 데이터 함께 로딩
|
|
42
|
-
* <Slacker
|
|
43
|
-
* fallback={<div>Loading chart library...</div>}
|
|
44
|
-
* loader={async () => {
|
|
45
|
-
* const [{ Chart }, chartData] = await Promise.all([
|
|
46
|
-
* import('chart.js'),
|
|
47
|
-
* fetch('/api/chart-data').then(r => r.json())
|
|
48
|
-
* ]);
|
|
49
|
-
* return { Chart, data: chartData };
|
|
50
|
-
* }}
|
|
51
|
-
* >
|
|
52
|
-
* {({ Chart, data }) => <Chart data={data} />}
|
|
53
|
-
* </Slacker>
|
|
54
|
-
*
|
|
55
|
-
* // 이미지와 메타데이터 함께 로딩
|
|
56
|
-
* <Slacker
|
|
57
|
-
* fallback={<ImageSkeleton />}
|
|
58
|
-
* loader={async () => {
|
|
59
|
-
* const [imageUrl, metadata] = await Promise.all([
|
|
60
|
-
* loadHighResImage(id),
|
|
61
|
-
* fetch(`/api/images/${id}/metadata`).then(r => r.json())
|
|
62
|
-
* ]);
|
|
63
|
-
* return { imageUrl, metadata };
|
|
64
|
-
* }}
|
|
65
|
-
* >
|
|
66
|
-
* {({ imageUrl, metadata }) => (
|
|
67
|
-
* <div>
|
|
68
|
-
* <img src={imageUrl} alt={metadata.title} />
|
|
69
|
-
* <p>{metadata.description}</p>
|
|
70
|
-
* </div>
|
|
71
|
-
* )}
|
|
72
|
-
* </Slacker>
|
|
73
|
-
* ```
|
|
74
|
-
*/
|
|
75
|
-
export function Slacker({ children, fallback, loader, threshold = 0.1, rootMargin = "50px", }) {
|
|
4
|
+
export function Slacker({ children, errorFallback, loadingFallback, loader, threshold = 0.1, rootMargin = "50px", onError, maxRetries = 0, retryDelay = 1000, }) {
|
|
76
5
|
const [loadedData, setLoadedData] = useState(null);
|
|
77
6
|
const [isLoading, setIsLoading] = useState(false);
|
|
78
|
-
const [
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
7
|
+
const [error, setError] = useState(null);
|
|
8
|
+
const [retryCount, setRetryCount] = useState(0);
|
|
9
|
+
const load = useCallback(async () => {
|
|
10
|
+
setIsLoading(true);
|
|
11
|
+
setError(null);
|
|
12
|
+
try {
|
|
13
|
+
const data = await loader();
|
|
14
|
+
setLoadedData(data);
|
|
15
|
+
setRetryCount(0); // 성공 시 재시도 카운트 리셋
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
19
|
+
setError(error);
|
|
20
|
+
console.error('Slacker loader failed:', error);
|
|
21
|
+
onError?.(error);
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
setIsLoading(false);
|
|
25
|
+
}
|
|
26
|
+
}, [loader, onError]);
|
|
27
|
+
const retry = useCallback(async () => {
|
|
28
|
+
if (retryCount < maxRetries) {
|
|
29
|
+
setRetryCount(prev => prev + 1);
|
|
30
|
+
if (retryDelay > 0) {
|
|
31
|
+
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
|
92
32
|
}
|
|
33
|
+
await load();
|
|
34
|
+
}
|
|
35
|
+
}, [retryCount, maxRetries, retryDelay, load]);
|
|
36
|
+
const handleIntersect = useCallback(async (isIntersecting) => {
|
|
37
|
+
if (isIntersecting && !isLoading && loadedData === null) {
|
|
38
|
+
await load();
|
|
39
|
+
}
|
|
40
|
+
}, [isLoading, loadedData, load]);
|
|
41
|
+
// 자동 재시도
|
|
42
|
+
const shouldAutoRetry = error && maxRetries > 0 && retryCount < maxRetries;
|
|
43
|
+
if (shouldAutoRetry && !isLoading) {
|
|
44
|
+
retry();
|
|
45
|
+
}
|
|
46
|
+
// 렌더링할 내용 결정
|
|
47
|
+
const renderContent = () => {
|
|
48
|
+
if (loadedData !== null) {
|
|
49
|
+
return children(loadedData);
|
|
50
|
+
}
|
|
51
|
+
if (error) {
|
|
52
|
+
return typeof errorFallback === 'function'
|
|
53
|
+
? errorFallback({ isLoading, error, retry })
|
|
54
|
+
: errorFallback;
|
|
55
|
+
}
|
|
56
|
+
if (isLoading) {
|
|
57
|
+
return loadingFallback;
|
|
93
58
|
}
|
|
59
|
+
return null;
|
|
94
60
|
};
|
|
95
|
-
return (_jsx(Observer, { threshold: threshold, rootMargin: rootMargin,
|
|
61
|
+
return (_jsx(Observer, { threshold: threshold, rootMargin: rootMargin, triggerOnce: true, onIntersect: handleIntersect, children: renderContent() }));
|
|
96
62
|
}
|
package/dist/experimental.d.ts
CHANGED
package/dist/experimental.js
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/types/index.d.ts
CHANGED
|
@@ -32,11 +32,20 @@ export type GetLiteralKeys<T> = {
|
|
|
32
32
|
[K in keyof T]: T[K] extends string ? string extends T[K] ? never : K : T[K] extends number ? number extends T[K] ? never : K : T[K] extends boolean ? boolean extends T[K] ? never : K : T[K] extends bigint ? bigint extends T[K] ? never : K : T[K] extends symbol ? symbol extends T[K] ? never : K : never;
|
|
33
33
|
}[keyof T];
|
|
34
34
|
export type LiteralKeys<T> = [GetLiteralKeys<T>] extends [never] ? keyof T : GetLiteralKeys<T>;
|
|
35
|
-
export type
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
export type SlackerFallbackProps = {
|
|
36
|
+
isLoading: boolean;
|
|
37
|
+
error: Error | null;
|
|
38
|
+
retry: () => void;
|
|
39
|
+
};
|
|
40
|
+
export type SlackerProps<T = any> = {
|
|
41
|
+
children: (loaded: T) => React.ReactNode;
|
|
42
|
+
errorFallback?: React.ReactNode | ((props: SlackerFallbackProps) => React.ReactNode);
|
|
43
|
+
loadingFallback?: React.ReactNode;
|
|
38
44
|
threshold?: number | number[];
|
|
39
45
|
rootMargin?: string;
|
|
40
|
-
loader: () => Promise<
|
|
46
|
+
loader: () => Promise<T> | T;
|
|
47
|
+
onError?: (error: Error) => void;
|
|
48
|
+
maxRetries?: number;
|
|
49
|
+
retryDelay?: number;
|
|
41
50
|
};
|
|
42
51
|
export {};
|
package/package.json
CHANGED
|
File without changes
|