@ilokesto/utilinent 0.0.13 → 0.0.15
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/components/For.d.ts +1 -2
- package/dist/components/For.js +3 -1
- package/dist/components/Observer.d.ts +2 -2
- package/dist/components/Observer.js +13 -53
- package/dist/components/OptionalWrapper.d.ts +2 -7
- package/dist/components/OptionalWrapper.js +4 -2
- package/dist/components/Repeat.d.ts +1 -2
- package/dist/components/Show.d.ts +3 -3
- package/dist/components/Show.js +6 -1
- package/dist/{components → experimental}/Mount.d.ts +1 -2
- package/dist/{components → experimental}/Slacker.d.ts +1 -1
- package/dist/{components → experimental}/Slacker.js +1 -1
- package/dist/{components → experimental}/Switch.d.ts +1 -2
- package/dist/hooks/useIntersectionObserver.d.ts +15 -0
- package/dist/hooks/useIntersectionObserver.js +61 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.js +6 -5
- package/dist/{components/types.d.ts → types/index.d.ts} +38 -27
- package/package.json +2 -2
- /package/dist/{components → experimental}/Mount.js +0 -0
- /package/dist/{components → experimental}/Switch.js +0 -0
- /package/dist/{components/types.js → types/index.js} +0 -0
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;
|
package/dist/components/For.js
CHANGED
|
@@ -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,61 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useIntersectionObserver } from "../hooks/useIntersectionObserver";
|
|
3
3
|
import { Show } from "./Show";
|
|
4
|
-
export function Observer({ children, fallback, threshold = 0, rootMargin = "0px", triggerOnce = false, onIntersect, }) {
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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:
|
|
4
|
+
export function Observer({ children, fallback = null, threshold = 0, rootMargin = "0px", triggerOnce: freezeOnceVisible = false, onIntersect: onChange, }) {
|
|
5
|
+
const { ref, isIntersecting } = useIntersectionObserver({
|
|
6
|
+
threshold,
|
|
7
|
+
rootMargin,
|
|
8
|
+
freezeOnceVisible,
|
|
9
|
+
onChange,
|
|
10
|
+
});
|
|
11
|
+
return (_jsx("div", { ref: ref, style:
|
|
52
12
|
// fallback이 없고 isIntersecting이 false인 경우
|
|
53
13
|
!fallback && !isIntersecting
|
|
54
14
|
? {
|
|
55
|
-
minHeight:
|
|
56
|
-
minWidth:
|
|
15
|
+
minHeight: "1px",
|
|
16
|
+
minWidth: "1px",
|
|
57
17
|
flexShrink: 0, // flex 컨테이너에서 축소되지 않도록
|
|
58
|
-
display:
|
|
18
|
+
display: "block", // inline 요소가 되지 않도록
|
|
59
19
|
}
|
|
60
|
-
: undefined, children: _jsx(Show, { when: isIntersecting, fallback: fallback, children:
|
|
20
|
+
: undefined, children: _jsx(Show, { when: isIntersecting, fallback: fallback, children: typeof children === "function" ? children(isIntersecting) : children }) }));
|
|
61
21
|
}
|
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Show } from "./Show";
|
|
3
|
+
export function OptionalWrapper({ when, children, wrapper, fallback }) {
|
|
4
|
+
return _jsx(Show, { when: when, fallback: fallback ? fallback(children) : children, children: wrapper(children) });
|
|
3
5
|
}
|
|
@@ -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;
|
package/dist/components/Show.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export function Show({ when, children, fallback = null }) {
|
|
2
|
-
|
|
2
|
+
const shouldRender = Array.isArray(when) ? when.every(Boolean) : !!when;
|
|
3
|
+
return shouldRender
|
|
4
|
+
? typeof children === "function"
|
|
5
|
+
? children(when)
|
|
6
|
+
: children
|
|
7
|
+
: fallback;
|
|
3
8
|
}
|
|
4
9
|
;
|
|
@@ -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 {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
export function useIntersectionObserver({ threshold = 0, root = null, rootMargin = "0%", freezeOnceVisible = false, initialIsIntersecting = false, onChange, } = {}) {
|
|
3
|
+
const [element, setElement] = useState(null);
|
|
4
|
+
const [isIntersecting, setIsIntersecting] = useState(initialIsIntersecting);
|
|
5
|
+
const [entry, setEntry] = useState();
|
|
6
|
+
const observerRef = useRef(null);
|
|
7
|
+
const changeCallbackRef = useRef(onChange);
|
|
8
|
+
// Memoize options to prevent unnecessary observer recreation
|
|
9
|
+
const observerOptions = useMemo(() => ({
|
|
10
|
+
threshold,
|
|
11
|
+
root,
|
|
12
|
+
rootMargin,
|
|
13
|
+
}), [threshold, root, rootMargin]);
|
|
14
|
+
const isFrozen = freezeOnceVisible && isIntersecting;
|
|
15
|
+
// Keep callback ref updated
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
changeCallbackRef.current = onChange;
|
|
18
|
+
}, [onChange]);
|
|
19
|
+
// Ref callback to set the element
|
|
20
|
+
const ref = useCallback((node) => {
|
|
21
|
+
setElement(node);
|
|
22
|
+
}, []);
|
|
23
|
+
// Main intersection observer effect
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!element || !("IntersectionObserver" in window) || isFrozen) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const observer = new IntersectionObserver((entries) => {
|
|
29
|
+
const [intersectionEntry] = entries;
|
|
30
|
+
if (!intersectionEntry)
|
|
31
|
+
return;
|
|
32
|
+
const thresholds = Array.isArray(observer.thresholds)
|
|
33
|
+
? observer.thresholds
|
|
34
|
+
: [observer.thresholds];
|
|
35
|
+
const isCurrentlyIntersecting = intersectionEntry.isIntersecting &&
|
|
36
|
+
thresholds.some((t) => intersectionEntry.intersectionRatio >= t);
|
|
37
|
+
setIsIntersecting(isCurrentlyIntersecting);
|
|
38
|
+
setEntry(intersectionEntry);
|
|
39
|
+
// Call onChange callback if provided
|
|
40
|
+
changeCallbackRef.current?.(isCurrentlyIntersecting, intersectionEntry);
|
|
41
|
+
}, observerOptions);
|
|
42
|
+
observer.observe(element);
|
|
43
|
+
observerRef.current = observer;
|
|
44
|
+
return () => {
|
|
45
|
+
observer.disconnect();
|
|
46
|
+
observerRef.current = null;
|
|
47
|
+
};
|
|
48
|
+
}, [element, observerOptions, isFrozen]);
|
|
49
|
+
// Reset state when element is removed and not frozen
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!element && !freezeOnceVisible && !isFrozen) {
|
|
52
|
+
setIsIntersecting(initialIsIntersecting);
|
|
53
|
+
setEntry(undefined);
|
|
54
|
+
}
|
|
55
|
+
}, [element, freezeOnceVisible, isFrozen, initialIsIntersecting]);
|
|
56
|
+
return useMemo(() => ({
|
|
57
|
+
ref,
|
|
58
|
+
isIntersecting,
|
|
59
|
+
entry,
|
|
60
|
+
}), [ref, isIntersecting, entry]);
|
|
61
|
+
}
|
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';
|
package/dist/index.js
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
|
|
2
|
-
|
|
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
|
|
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
|
-
}
|
|
10
|
-
export
|
|
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
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
export
|
|
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
|
-
}
|
|
33
|
-
export
|
|
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.
|
|
3
|
+
"version": "0.0.15",
|
|
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": "
|
|
40
|
+
"build": "rm -rf dist && tsc"
|
|
41
41
|
}
|
|
42
42
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|