@ilokesto/utilinent 0.0.12 → 0.0.14
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/README.md +20 -20
- package/dist/components/For.d.ts +1 -2
- package/dist/components/Observer.d.ts +2 -2
- package/dist/components/OptionalWrapper.d.ts +2 -7
- package/dist/components/Repeat.d.ts +1 -2
- package/dist/components/Show.d.ts +3 -3
- package/dist/{components → experimental}/Mount.d.ts +1 -2
- package/dist/{components → experimental}/Slacker.d.ts +1 -1
- package/dist/{components → experimental}/Switch.d.ts +1 -2
- package/dist/hooks/useIntersectionObserver.d.ts +15 -0
- package/dist/index.d.ts +6 -5
- package/dist/types/index.d.ts +58 -0
- package/package.json +5 -5
- package/dist/components/For.js +0 -3
- package/dist/components/Mount.js +0 -8
- package/dist/components/Observer.js +0 -61
- package/dist/components/OptionalWrapper.js +0 -3
- package/dist/components/Repeat.js +0 -8
- package/dist/components/Show.js +0 -4
- package/dist/components/Slacker.js +0 -96
- package/dist/components/Switch.js +0 -21
- package/dist/components/types.d.ts +0 -47
- package/dist/components/types.js +0 -1
- package/dist/index.js +0 -8
package/README.md
CHANGED
|
@@ -62,8 +62,8 @@ React에서 조건부 렌더링을 할 때 삼항 연산자(`? :`), AND 연산
|
|
|
62
62
|
```tsx
|
|
63
63
|
interface ShowProps<T> {
|
|
64
64
|
when: T; // 조건값 (truthy/falsy 체크)
|
|
65
|
-
fallback?: ReactNode; // 조건이 false일 때 렌더링할 내용
|
|
66
|
-
children: ReactNode | ((item: NonNullable<T>) => ReactNode); // 조건이 true일 때의 내용
|
|
65
|
+
fallback?: React.ReactNode; // 조건이 false일 때 렌더링할 내용
|
|
66
|
+
children: React.ReactNode | ((item: NonNullable<T>) => React.ReactNode); // 조건이 true일 때의 내용
|
|
67
67
|
}
|
|
68
68
|
```
|
|
69
69
|
|
|
@@ -131,8 +131,8 @@ React에서 배열을 렌더링할 때 `Array.map()`을 사용하는 것은 일
|
|
|
131
131
|
```tsx
|
|
132
132
|
interface ForProps<T extends Array<unknown>> {
|
|
133
133
|
each: T | null | undefined; // 렌더링할 배열
|
|
134
|
-
fallback?: ReactNode; // 배열이 비어있거나 null일 때의 대체 내용
|
|
135
|
-
children: (item: T[number], index: number) => ReactNode; // 각 아이템을 렌더링하는 함수
|
|
134
|
+
fallback?: React.ReactNode; // 배열이 비어있거나 null일 때의 대체 내용
|
|
135
|
+
children: (item: T[number], index: number) => React.ReactNode; // 각 아이템을 렌더링하는 함수
|
|
136
136
|
}
|
|
137
137
|
```
|
|
138
138
|
|
|
@@ -230,13 +230,13 @@ function createSwitcher<T, K extends LiteralKeys<T>>(data: T): {
|
|
|
230
230
|
Switch: ({
|
|
231
231
|
when: K, // 분기할 필드명
|
|
232
232
|
children: Array<ReactElement>, // Match 컴포넌트들
|
|
233
|
-
fallback?: ReactNode // 매칭되는 case가 없을 때의 대체 내용
|
|
234
|
-
}) => ReactNode;
|
|
233
|
+
fallback?: React.ReactNode // 매칭되는 case가 없을 때의 대체 내용
|
|
234
|
+
}) => React.ReactNode;
|
|
235
235
|
|
|
236
236
|
Match: <V extends ExtractValues<T, K>>({
|
|
237
237
|
case: V, // 매칭할 값
|
|
238
|
-
children: (props: ExtractByKeyValue<T, K, V>) => ReactNode // 해당 case의 정확한 타입 제공
|
|
239
|
-
}) => ReactNode;
|
|
238
|
+
children: (props: ExtractByKeyValue<T, K, V>) => React.ReactNode // 해당 case의 정확한 타입 제공
|
|
239
|
+
}) => React.ReactNode;
|
|
240
240
|
}
|
|
241
241
|
```
|
|
242
242
|
|
|
@@ -392,8 +392,8 @@ function ComplexStatus({ state }: { state: ComplexState }) {
|
|
|
392
392
|
```tsx
|
|
393
393
|
interface OptionalWrapperProps {
|
|
394
394
|
when: boolean; // 래퍼를 적용할 조건
|
|
395
|
-
children: ReactNode; // 감싸질 내용
|
|
396
|
-
wrapper: (children: ReactNode) => ReactNode; // 조건이 true일 때 적용할 래퍼 함수
|
|
395
|
+
children: React.ReactNode; // 감싸질 내용
|
|
396
|
+
wrapper: (children: React.ReactNode) => React.ReactNode; // 조건이 true일 때 적용할 래퍼 함수
|
|
397
397
|
}
|
|
398
398
|
```
|
|
399
399
|
|
|
@@ -493,8 +493,8 @@ function ClientOnlyComponent() {
|
|
|
493
493
|
|
|
494
494
|
```tsx
|
|
495
495
|
interface MountProps {
|
|
496
|
-
fallback?: ReactNode; // 마운트 전 또는 로딩 중 표시할 내용
|
|
497
|
-
children: ReactNode | (() => ReactNode | Promise<ReactNode>); // 마운트 후 렌더링할 내용
|
|
496
|
+
fallback?: React.ReactNode; // 마운트 전 또는 로딩 중 표시할 내용
|
|
497
|
+
children: React.ReactNode | (() => React.ReactNode | Promise<ReactNode>); // 마운트 후 렌더링할 내용
|
|
498
498
|
}
|
|
499
499
|
```
|
|
500
500
|
|
|
@@ -598,8 +598,8 @@ function NewWay() {
|
|
|
598
598
|
```tsx
|
|
599
599
|
interface RepeatProps {
|
|
600
600
|
times: number; // 반복 횟수
|
|
601
|
-
fallback?: ReactNode; // times가 0 이하일 때의 대체 내용
|
|
602
|
-
children: (index: number) => ReactNode; // 각 반복에서 렌더링할 함수
|
|
601
|
+
fallback?: React.ReactNode; // times가 0 이하일 때의 대체 내용
|
|
602
|
+
children: (index: number) => React.ReactNode; // 각 반복에서 렌더링할 함수
|
|
603
603
|
}
|
|
604
604
|
```
|
|
605
605
|
|
|
@@ -784,8 +784,8 @@ function LazyImage({ src, alt }: { src: string, alt: string }) {
|
|
|
784
784
|
|
|
785
785
|
```tsx
|
|
786
786
|
interface ObserverProps {
|
|
787
|
-
children: ReactNode | ((isIntersecting: boolean) => ReactNode);
|
|
788
|
-
fallback?: ReactNode; // 뷰포트에 보이지 않을 때 표시할 내용
|
|
787
|
+
children: React.ReactNode | ((isIntersecting: boolean) => React.ReactNode);
|
|
788
|
+
fallback?: React.ReactNode; // 뷰포트에 보이지 않을 때 표시할 내용
|
|
789
789
|
threshold?: number | number[]; // 교차 임계값 (0.0 ~ 1.0)
|
|
790
790
|
rootMargin?: string; // 루트 마진
|
|
791
791
|
triggerOnce?: boolean; // 한 번만 트리거할지 여부
|
|
@@ -957,7 +957,7 @@ function ImageGallery({ images }: { images: ImageData[] }) {
|
|
|
957
957
|
// 뷰포트 진입 분석
|
|
958
958
|
function AnalyticsSection({ sectionId, children }: {
|
|
959
959
|
sectionId: string,
|
|
960
|
-
children: ReactNode
|
|
960
|
+
children: React.ReactNode
|
|
961
961
|
}) {
|
|
962
962
|
return (
|
|
963
963
|
<Observer
|
|
@@ -1003,7 +1003,7 @@ function ScrollProgressIndicator() {
|
|
|
1003
1003
|
// 조건부 로딩 - Show 컴포넌트와 함께 사용
|
|
1004
1004
|
function ConditionalContent({ shouldLoad, children }: {
|
|
1005
1005
|
shouldLoad: boolean,
|
|
1006
|
-
children: ReactNode
|
|
1006
|
+
children: React.ReactNode
|
|
1007
1007
|
}) {
|
|
1008
1008
|
return (
|
|
1009
1009
|
<Show when={shouldLoad} fallback={<div>로딩이 비활성화되었습니다</div>}>
|
|
@@ -1166,8 +1166,8 @@ function LazyChart() {
|
|
|
1166
1166
|
|
|
1167
1167
|
```tsx
|
|
1168
1168
|
interface SlackerProps {
|
|
1169
|
-
children: (loaded: any) => ReactNode; // loader의 결과를 받는 함수
|
|
1170
|
-
fallback?: ReactNode; // 로딩 중 표시할 내용
|
|
1169
|
+
children: (loaded: any) => React.ReactNode; // loader의 결과를 받는 함수
|
|
1170
|
+
fallback?: React.ReactNode; // 로딩 중 표시할 내용
|
|
1171
1171
|
threshold?: number | number[]; // 교차 임계값 (기본: 0.1)
|
|
1172
1172
|
rootMargin?: string; // 루트 마진 (기본: "50px")
|
|
1173
1173
|
loader: () => Promise<any> | any; // 동적 로딩 함수 (필수)
|
package/dist/components/For.d.ts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import type { ForProps } from "./types";
|
|
1
|
+
import type { ForProps } from "../types";
|
|
3
2
|
export declare function For<T extends Array<unknown>>({ each, children, fallback, }: ForProps<T>): string | number | boolean | import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | null;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ObserverProps } from "
|
|
2
|
-
export declare function Observer({ children, fallback, threshold, rootMargin, triggerOnce, onIntersect, }: ObserverProps): import("react/jsx-runtime").JSX.Element;
|
|
1
|
+
import type { ObserverProps } from "../types";
|
|
2
|
+
export declare function Observer({ children, fallback, threshold, rootMargin, triggerOnce: freezeOnceVisible, onIntersect: onChange, }: ObserverProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export declare function OptionalWrapper({ when, children, wrapper, }: {
|
|
4
|
-
when: boolean;
|
|
5
|
-
children: ReactNode;
|
|
6
|
-
wrapper: (children: ReactNode) => ReactNode;
|
|
7
|
-
}): ReactNode;
|
|
1
|
+
import { OptionalWrapperProps } from "../types";
|
|
2
|
+
export declare function OptionalWrapper({ when, children, wrapper, fallback }: OptionalWrapperProps): React.ReactNode;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import type { RepeatProps } from "./types";
|
|
1
|
+
import type { RepeatProps } from "../types";
|
|
3
2
|
export declare function Repeat({ times, children, fallback }: RepeatProps): string | number | true | Iterable<import("react").ReactNode> | import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export declare function Show<T>({ when, children, fallback }: ShowProps<T>):
|
|
1
|
+
import type { ShowProps, ShowPropsArray } from "../types";
|
|
2
|
+
export declare function Show<T extends unknown[]>({ when, children, fallback }: ShowPropsArray<T>): React.ReactNode;
|
|
3
|
+
export declare function Show<T extends unknown>({ when, children, fallback }: ShowProps<T>): React.ReactNode;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import type { ExtractByKeyValue, ExtractValues, LiteralKeys, SwitchProps } from "./types";
|
|
1
|
+
import type { ExtractByKeyValue, ExtractValues, LiteralKeys, SwitchProps } from "../types";
|
|
3
2
|
export declare function createSwitcher<T, K extends LiteralKeys<T>>(data: T): {
|
|
4
3
|
Switch: ({ children, when, fallback }: SwitchProps<T, K>) => string | number | boolean | import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | null;
|
|
5
4
|
Match: <V extends ExtractValues<T, K>>({ children }: {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface UseIntersectionObserverOptions {
|
|
2
|
+
threshold?: number | number[];
|
|
3
|
+
root?: Element | null;
|
|
4
|
+
rootMargin?: string;
|
|
5
|
+
freezeOnceVisible?: boolean;
|
|
6
|
+
initialIsIntersecting?: boolean;
|
|
7
|
+
onChange?: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void;
|
|
8
|
+
}
|
|
9
|
+
interface UseIntersectionObserverResult {
|
|
10
|
+
ref: (node: HTMLElement | null) => void;
|
|
11
|
+
isIntersecting: boolean;
|
|
12
|
+
entry: IntersectionObserverEntry | undefined;
|
|
13
|
+
}
|
|
14
|
+
export declare function useIntersectionObserver({ threshold, root, rootMargin, freezeOnceVisible, initialIsIntersecting, onChange, }?: UseIntersectionObserverOptions): UseIntersectionObserverResult;
|
|
15
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export * from './components/For';
|
|
2
|
-
export * from './components/Show';
|
|
3
|
-
export * from './components/Switch';
|
|
4
|
-
export * from './components/Mount';
|
|
5
|
-
export * from './components/Repeat';
|
|
6
2
|
export * from './components/Observer';
|
|
7
|
-
export * from './components/Slacker';
|
|
8
3
|
export * from './components/OptionalWrapper';
|
|
4
|
+
export * from './components/Repeat';
|
|
5
|
+
export * from './components/Show';
|
|
6
|
+
export * from './experimental/Mount';
|
|
7
|
+
export * from './experimental/Slacker';
|
|
8
|
+
export * from './experimental/Switch';
|
|
9
|
+
export * from './hooks/useIntersectionObserver';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
interface Fallback {
|
|
3
|
+
fallback?: React.ReactNode;
|
|
4
|
+
}
|
|
5
|
+
export type NonNullableElements<T extends readonly any[]> = {
|
|
6
|
+
-readonly [P in keyof T]: NonNullable<T[P]>;
|
|
7
|
+
};
|
|
8
|
+
export interface ShowPropsArray<T extends unknown[]> extends Fallback {
|
|
9
|
+
when: T;
|
|
10
|
+
children: React.ReactNode | ((item: NonNullableElements<T>) => React.ReactNode);
|
|
11
|
+
}
|
|
12
|
+
export interface ShowProps<T = unknown> extends Fallback {
|
|
13
|
+
when: T;
|
|
14
|
+
children: React.ReactNode | ((item: NonNullable<T>) => React.ReactNode);
|
|
15
|
+
}
|
|
16
|
+
export interface ForProps<T extends Array<unknown>> extends Fallback {
|
|
17
|
+
each: T | null | undefined;
|
|
18
|
+
children: (item: T[number], index: number) => React.ReactNode;
|
|
19
|
+
}
|
|
20
|
+
export interface OptionalWrapperProps {
|
|
21
|
+
when: boolean;
|
|
22
|
+
children: React.ReactNode;
|
|
23
|
+
wrapper: (children: React.ReactNode) => React.ReactNode;
|
|
24
|
+
fallback?: (children: React.ReactNode) => React.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
export interface RepeatProps extends Fallback {
|
|
27
|
+
times: number;
|
|
28
|
+
children: (index: number) => React.ReactNode;
|
|
29
|
+
}
|
|
30
|
+
export interface ObserverProps extends Fallback {
|
|
31
|
+
children?: React.ReactNode | ((isIntersecting: boolean) => React.ReactNode);
|
|
32
|
+
threshold?: number | number[];
|
|
33
|
+
rootMargin?: string;
|
|
34
|
+
triggerOnce?: boolean;
|
|
35
|
+
onIntersect?: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void;
|
|
36
|
+
}
|
|
37
|
+
export interface SwitchProps<T, K extends LiteralKeys<T>> extends Fallback {
|
|
38
|
+
children: Array<ReactElement>;
|
|
39
|
+
when: K;
|
|
40
|
+
}
|
|
41
|
+
export interface MountProps extends Fallback {
|
|
42
|
+
children: React.ReactNode | (() => React.ReactNode | Promise<React.ReactNode>);
|
|
43
|
+
}
|
|
44
|
+
export type ExtractValues<T, K extends keyof T> = T extends any ? T[K] : never;
|
|
45
|
+
type IsUnion<T, U = T> = T extends any ? [U] extends [T] ? false : true : false;
|
|
46
|
+
export type ExtractByKeyValue<T, K extends keyof T, V> = T extends any ? IsUnion<T> extends true ? T[K] extends V ? T : never : V extends T[K] ? T : never : never;
|
|
47
|
+
export type GetLiteralKeys<T> = {
|
|
48
|
+
[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;
|
|
49
|
+
}[keyof T];
|
|
50
|
+
export type LiteralKeys<T> = [GetLiteralKeys<T>] extends [never] ? keyof T : GetLiteralKeys<T>;
|
|
51
|
+
export type SlackerProps = {
|
|
52
|
+
children: (loaded: any) => React.ReactNode;
|
|
53
|
+
fallback?: React.ReactNode;
|
|
54
|
+
threshold?: number | number[];
|
|
55
|
+
rootMargin?: string;
|
|
56
|
+
loader: () => Promise<any> | any;
|
|
57
|
+
};
|
|
58
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ilokesto/utilinent",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"scripts": {
|
|
5
|
-
"build": "vite build && tsc --emitDeclarationOnly"
|
|
6
|
-
},
|
|
3
|
+
"version": "0.0.14",
|
|
7
4
|
"repository": {
|
|
8
5
|
"type": "git",
|
|
9
6
|
"url": "git+https://github.com/ilokesto/utilinent.git"
|
|
@@ -38,5 +35,8 @@
|
|
|
38
35
|
"eslint": "^8.57.0",
|
|
39
36
|
"eslint-plugin-react": "^7.34.1",
|
|
40
37
|
"typescript": "^5.4.5"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "rm -rf dist && tsc --emitDeclarationOnly"
|
|
41
41
|
}
|
|
42
|
-
}
|
|
42
|
+
}
|
package/dist/components/For.js
DELETED
package/dist/components/Mount.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from "react";
|
|
2
|
-
export function Mount({ children, fallback = null }) {
|
|
3
|
-
const [resolvedChildren, setResolvedChildren] = useState(fallback);
|
|
4
|
-
useEffect(() => {
|
|
5
|
-
(async () => setResolvedChildren(typeof children === "function" ? await children() : children))();
|
|
6
|
-
}, [children]);
|
|
7
|
-
return resolvedChildren;
|
|
8
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef, useState } from "react";
|
|
3
|
-
import { Show } from "./Show";
|
|
4
|
-
export function Observer({ children, fallback, threshold = 0, rootMargin = "0px", triggerOnce = false, onIntersect, }) {
|
|
5
|
-
const [isIntersecting, setIsIntersecting] = useState(false);
|
|
6
|
-
const [hasTriggered, setHasTriggered] = useState(false);
|
|
7
|
-
const [entry, setEntry] = useState();
|
|
8
|
-
const elementRef = useRef(null);
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
const element = elementRef.current;
|
|
11
|
-
// 요소가 없으면 관찰하지 않음
|
|
12
|
-
if (!element) {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
// triggerOnce가 true이고 이미 트리거되었으면 관찰하지 않음
|
|
16
|
-
if (triggerOnce && hasTriggered) {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
// IntersectionObserver가 지원되지 않는 브라우저에서는 항상 intersecting으로 처리
|
|
20
|
-
if (!window.IntersectionObserver) {
|
|
21
|
-
setIsIntersecting(true);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
const observer = new window.IntersectionObserver((entries) => {
|
|
25
|
-
const [entry] = entries;
|
|
26
|
-
const isCurrentlyIntersecting = entry.isIntersecting;
|
|
27
|
-
setIsIntersecting(isCurrentlyIntersecting);
|
|
28
|
-
setEntry(entry);
|
|
29
|
-
// onIntersect 콜백 호출
|
|
30
|
-
if (onIntersect) {
|
|
31
|
-
onIntersect(isCurrentlyIntersecting, entry);
|
|
32
|
-
}
|
|
33
|
-
// triggerOnce가 true이고 교차가 시작되면 더 이상 관찰하지 않음
|
|
34
|
-
if (triggerOnce && isCurrentlyIntersecting) {
|
|
35
|
-
setHasTriggered(true);
|
|
36
|
-
observer.unobserve(element);
|
|
37
|
-
}
|
|
38
|
-
}, {
|
|
39
|
-
threshold,
|
|
40
|
-
rootMargin,
|
|
41
|
-
});
|
|
42
|
-
observer.observe(element);
|
|
43
|
-
// cleanup
|
|
44
|
-
return () => {
|
|
45
|
-
observer.unobserve(element);
|
|
46
|
-
};
|
|
47
|
-
}, [threshold, rootMargin, triggerOnce, hasTriggered, onIntersect]);
|
|
48
|
-
const content = typeof children === 'function'
|
|
49
|
-
? children(isIntersecting)
|
|
50
|
-
: children;
|
|
51
|
-
return (_jsx("div", { ref: elementRef, style:
|
|
52
|
-
// fallback이 없고 isIntersecting이 false인 경우
|
|
53
|
-
!fallback && !isIntersecting
|
|
54
|
-
? {
|
|
55
|
-
minHeight: '1px',
|
|
56
|
-
minWidth: '1px',
|
|
57
|
-
flexShrink: 0, // flex 컨테이너에서 축소되지 않도록
|
|
58
|
-
display: 'block' // inline 요소가 되지 않도록
|
|
59
|
-
}
|
|
60
|
-
: undefined, children: _jsx(Show, { when: isIntersecting, fallback: fallback, children: content }) }));
|
|
61
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
export function Repeat({ times, children, fallback }) {
|
|
3
|
-
// times가 0 이하이거나 유효하지 않은 경우 fallback 렌더링
|
|
4
|
-
if (!times || times <= 0 || !Number.isInteger(times)) {
|
|
5
|
-
return fallback || null;
|
|
6
|
-
}
|
|
7
|
-
return (_jsx(_Fragment, { children: Array.from({ length: times }, (_, index) => children(index)) }));
|
|
8
|
-
}
|
package/dist/components/Show.js
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
3
|
-
import { Observer } from "./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", }) {
|
|
76
|
-
const [loadedData, setLoadedData] = useState(null);
|
|
77
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
78
|
-
const [hasLoaded, setHasLoaded] = useState(false);
|
|
79
|
-
const handleIntersect = async (isIntersecting) => {
|
|
80
|
-
if (isIntersecting && !hasLoaded) {
|
|
81
|
-
setIsLoading(true);
|
|
82
|
-
try {
|
|
83
|
-
const data = await loader();
|
|
84
|
-
setLoadedData(data);
|
|
85
|
-
setHasLoaded(true);
|
|
86
|
-
}
|
|
87
|
-
catch (error) {
|
|
88
|
-
console.error('Slacker loader failed:', error);
|
|
89
|
-
}
|
|
90
|
-
finally {
|
|
91
|
-
setIsLoading(false);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
return (_jsx(Observer, { threshold: threshold, rootMargin: rootMargin, fallback: fallback, triggerOnce: true, onIntersect: handleIntersect, children: hasLoaded ? children(loadedData) : null }));
|
|
96
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export function createSwitcher(data) {
|
|
2
|
-
function Switch({ children, when, fallback = null }) {
|
|
3
|
-
children.reduce((acc, { type, props }) => {
|
|
4
|
-
if (type !== Match)
|
|
5
|
-
throw new Error("Match 컴포넌트만 사용할 수 있습니다.");
|
|
6
|
-
if (props.case) {
|
|
7
|
-
if (acc.includes(props.case))
|
|
8
|
-
throw new Error(`Duplicate Match key: ${props.case}`);
|
|
9
|
-
else
|
|
10
|
-
acc.push(props.case);
|
|
11
|
-
}
|
|
12
|
-
return acc;
|
|
13
|
-
}, []);
|
|
14
|
-
return children.find(({ props }) => props.case === data[when]) ?? fallback;
|
|
15
|
-
}
|
|
16
|
-
function Match({ children }) {
|
|
17
|
-
return children(data);
|
|
18
|
-
}
|
|
19
|
-
;
|
|
20
|
-
return { Switch, Match };
|
|
21
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { ReactElement, ReactNode } from "react";
|
|
2
|
-
export { ReactNode };
|
|
3
|
-
type Fallback = {
|
|
4
|
-
fallback?: ReactNode;
|
|
5
|
-
};
|
|
6
|
-
export type ShowProps<T> = {
|
|
7
|
-
when: T;
|
|
8
|
-
children: ReactNode | ((item: NonNullable<T>) => ReactNode);
|
|
9
|
-
} & Fallback;
|
|
10
|
-
export type ForProps<T extends Array<unknown>> = {
|
|
11
|
-
each: T | null | undefined;
|
|
12
|
-
fallback?: ReactNode;
|
|
13
|
-
children: (item: T[number], index: number) => ReactNode;
|
|
14
|
-
};
|
|
15
|
-
export type ExtractValues<T, K extends keyof T> = T extends any ? T[K] : never;
|
|
16
|
-
type IsUnion<T, U = T> = T extends any ? [U] extends [T] ? false : true : false;
|
|
17
|
-
export type ExtractByKeyValue<T, K extends keyof T, V> = T extends any ? IsUnion<T> extends true ? T[K] extends V ? T : never : V extends T[K] ? T : never : never;
|
|
18
|
-
export type GetLiteralKeys<T> = {
|
|
19
|
-
[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;
|
|
20
|
-
}[keyof T];
|
|
21
|
-
export type LiteralKeys<T> = [GetLiteralKeys<T>] extends [never] ? keyof T : GetLiteralKeys<T>;
|
|
22
|
-
export type SwitchProps<T, K extends LiteralKeys<T>> = {
|
|
23
|
-
children: Array<ReactElement>;
|
|
24
|
-
when: K;
|
|
25
|
-
} & Fallback;
|
|
26
|
-
export type MountProps = {
|
|
27
|
-
children: ReactNode | (() => ReactNode | Promise<ReactNode>);
|
|
28
|
-
} & Fallback;
|
|
29
|
-
export type RepeatProps = {
|
|
30
|
-
times: number;
|
|
31
|
-
children: (index: number) => ReactNode;
|
|
32
|
-
} & Fallback;
|
|
33
|
-
export type ObserverProps = {
|
|
34
|
-
children?: ReactNode | ((isIntersecting: boolean) => ReactNode);
|
|
35
|
-
fallback?: ReactNode;
|
|
36
|
-
threshold?: number | number[];
|
|
37
|
-
rootMargin?: string;
|
|
38
|
-
triggerOnce?: boolean;
|
|
39
|
-
onIntersect?: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void;
|
|
40
|
-
};
|
|
41
|
-
export type SlackerProps = {
|
|
42
|
-
children: (loaded: any) => ReactNode;
|
|
43
|
-
fallback?: ReactNode;
|
|
44
|
-
threshold?: number | number[];
|
|
45
|
-
rootMargin?: string;
|
|
46
|
-
loader: () => Promise<any> | any;
|
|
47
|
-
};
|
package/dist/components/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/index.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export * from './components/For';
|
|
2
|
-
export * from './components/Show';
|
|
3
|
-
export * from './components/Switch';
|
|
4
|
-
export * from './components/Mount';
|
|
5
|
-
export * from './components/Repeat';
|
|
6
|
-
export * from './components/Observer';
|
|
7
|
-
export * from './components/Slacker';
|
|
8
|
-
export * from './components/OptionalWrapper';
|