@ilokesto/utilinent 0.0.13 → 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.
@@ -1,3 +1,2 @@
1
- /// <reference types="react" />
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 "./types";
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
- /// <reference types="react" />
2
- import { React.ReactNode } from "./types";
3
- export declare function OptionalWrapper({ when, children, wrapper, }: {
4
- when: boolean;
5
- children: React.ReactNode;
6
- wrapper: (children: React.ReactNode) => React.ReactNode;
7
- }): React.ReactNode;
1
+ import { OptionalWrapperProps } from "../types";
2
+ export declare function OptionalWrapper({ when, children, wrapper, fallback }: OptionalWrapperProps): React.ReactNode;
@@ -1,3 +1,2 @@
1
- /// <reference types="react" />
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
- /// <reference types="react" />
2
- import type { ShowProps } from "./types";
3
- export declare function Show<T>({ when, children, fallback }: ShowProps<T>): import("react").ReactNode;
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,3 +1,2 @@
1
- /// <reference types="react" />
2
- import type { MountProps } from "./types";
1
+ import type { MountProps } from "../types";
3
2
  export declare function Mount({ children, fallback }: MountProps): import("react").ReactNode;
@@ -1,4 +1,4 @@
1
- import { SlackerProps } from "./types";
1
+ import { SlackerProps } from "../types";
2
2
  /**
3
3
  * Slacker 컴포넌트 - Lazy Loading 전용
4
4
  *
@@ -1,5 +1,4 @@
1
- /// <reference types="react" />
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';
@@ -1,43 +1,53 @@
1
- import type { ReactElement, React.ReactNode } from "react";
2
- export { React.ReactNode };
3
- type Fallback = {
1
+ import type { ReactElement } from "react";
2
+ interface Fallback {
4
3
  fallback?: React.ReactNode;
4
+ }
5
+ export type NonNullableElements<T extends readonly any[]> = {
6
+ -readonly [P in keyof T]: NonNullable<T[P]>;
5
7
  };
6
- export type ShowProps<T> = {
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 {
7
13
  when: T;
8
14
  children: React.ReactNode | ((item: NonNullable<T>) => React.ReactNode);
9
- } & Fallback;
10
- export type ForProps<T extends Array<unknown>> = {
15
+ }
16
+ export interface ForProps<T extends Array<unknown>> extends Fallback {
11
17
  each: T | null | undefined;
12
- fallback?: React.ReactNode;
13
18
  children: (item: T[number], index: number) => React.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: React.ReactNode | (() => React.ReactNode | Promise<ReactNode>);
28
- } & Fallback;
29
- export type RepeatProps = {
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 {
30
27
  times: number;
31
28
  children: (index: number) => React.ReactNode;
32
- } & Fallback;
33
- export type ObserverProps = {
29
+ }
30
+ export interface ObserverProps extends Fallback {
34
31
  children?: React.ReactNode | ((isIntersecting: boolean) => React.ReactNode);
35
- fallback?: React.ReactNode;
36
32
  threshold?: number | number[];
37
33
  rootMargin?: string;
38
34
  triggerOnce?: boolean;
39
35
  onIntersect?: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void;
40
- };
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>;
41
51
  export type SlackerProps = {
42
52
  children: (loaded: any) => React.ReactNode;
43
53
  fallback?: React.ReactNode;
@@ -45,3 +55,4 @@ export type SlackerProps = {
45
55
  rootMargin?: string;
46
56
  loader: () => Promise<any> | any;
47
57
  };
58
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ilokesto/utilinent",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/ilokesto/utilinent.git"
@@ -37,6 +37,6 @@
37
37
  "typescript": "^5.4.5"
38
38
  },
39
39
  "scripts": {
40
- "build": "vite build && tsc --emitDeclarationOnly"
40
+ "build": "rm -rf dist && tsc --emitDeclarationOnly"
41
41
  }
42
42
  }
@@ -1,3 +0,0 @@
1
- export function For({ each, children, fallback = null, }) {
2
- return (each && each.length > 0) ? each?.map(children) : fallback;
3
- }
@@ -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,3 +0,0 @@
1
- export function OptionalWrapper({ when, children, wrapper, }) {
2
- return when ? wrapper(children) : children;
3
- }
@@ -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
- }
@@ -1,4 +0,0 @@
1
- export function Show({ when, children, fallback = null }) {
2
- return when ? typeof children === "function" ? children(when) : children : fallback;
3
- }
4
- ;
@@ -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 +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';