-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
154 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,78 @@ | ||
import React, { Children } from 'react'; | ||
import { Slot } from '../Slot'; | ||
import { | ||
useIntersectionObserver, | ||
UseIntersectionObserverProps, | ||
} from '../../hooks/useIntersectionObserver'; | ||
|
||
interface InViewProps extends UseIntersectionObserverProps { | ||
children: JSX.Element; | ||
children: React.ReactNode; | ||
asChild?: boolean; | ||
} | ||
|
||
/** | ||
* @description `InView`는 `IntersectionObserver`를 선언적으로 활용 할 수 있는 컴포넌트입니다. | ||
* | ||
* 관찰 대상이 `viewport`에 노출될 때(`onIntersectStart`) 혹은 나갈 때(`onIntersectEnd`) 특정 action 함수를 호출 할 수 있는 컴포넌트입니다. | ||
* | ||
* `@modern-kit/react`의 `useIntersectionObserver` 훅을 사용하여 구현되었습니다. | ||
* | ||
* @see https://modern-agile-team.github.io/modern-kit/docs/react/hooks/useIntersectionObserver | ||
* | ||
* @param {InViewProps} props - 컴포넌트에 전달되는 속성들입니다. | ||
* @param {JSX.Element} props.children - 관찰할 자식 요소입니다. | ||
* @param {React.ReactNode} props.children - 관찰할 자식 요소입니다. | ||
* @param {boolean} props.asChild - 자식 요소를 그대로 렌더링할지 여부를 나타냅니다. `true`일 경우 자식 요소가 그대로 렌더링되며, 자식 요소가 관찰 대상이됩니다. | ||
* @param {(entry: IntersectionObserverEntry) => void} props.onIntersectStart - 타겟 요소가 viewport 내에 들어갈 때 호출되는 콜백 함수입니다. | ||
* @param {(entry: IntersectionObserverEntry) => void} props.onIntersectEnd - 타겟 요소가 viewport에서 나갈 때 호출되는 콜백 함수입니다. | ||
* @param {number | number[]} props.threshold - 관찰을 시작할 viewport의 가시성 비율입니다. | ||
* @param {Element | Document | null} props.root - 교차할 때 기준이 되는 root 요소입니다. 기본값은 `null`이며 이는 viewport를 의미합니다. | ||
* @param {string} props.rootMargin - 루트 요소에 대한 마진을 지정합니다. 이는 뷰포트 또는 루트 요소의 경계를 확장하거나 축소하는데 사용됩니다. | ||
* @param {boolean} props.enabled - Observer를 활성화할지 여부를 나타냅니다. `false`일 경우 Observer가 작동하지 않습니다. | ||
* @param {boolean} props.calledOnce - 요소가 교차할 때 콜백을 `한 번`만 호출할지 여부를 나타냅니다. | ||
* | ||
* @returns {JSX.Element} | ||
* | ||
* @example | ||
* ```tsx | ||
* // 기본적으로 div로 감싸지며, 해당 div를 관찰 대상으로 설정합니다. | ||
* // 해당 div가 viewport에 노출되거나 숨겨질 때 onIntersectStart/onIntersectEnd 콜백 함수를 호출합니다. | ||
* <InView onIntersectStart={onIntersectStart} onIntersectEnd={onIntersectEnd}> | ||
* <div>Content1</div> | ||
* <div>Content2</div> | ||
* </InView> | ||
* ``` | ||
* | ||
* @example | ||
* ```tsx | ||
* // asChild 프로퍼티를 사용하면 자식 요소를 그대로 렌더링하고, 자식 요소를 관찰 대상으로 설정합니다. | ||
* // 자식 요소가 viewport에 노출되거나 숨겨질 때 onIntersectStart/onIntersectEnd 콜백이 호출됩니다. | ||
* // 이때 자식 요소는 단일 요소만 허용됩니다. | ||
* const ref = useRef<HTMLDivElement>(null); | ||
* | ||
* <InView asChild onIntersectStart={onIntersectStart} onIntersectEnd={onIntersectEnd}> | ||
* <div ref={ref} style={style}> | ||
* <span>Content1</span> | ||
* <span>Content2</span> | ||
* </div> | ||
* </InView> | ||
* ``` | ||
*/ | ||
export const InView = ({ children, ...props }: InViewProps) => { | ||
const { ref: intersectionObserverRef } = useIntersectionObserver(props); | ||
export const InView = ({ | ||
children, | ||
asChild = false, | ||
...props | ||
}: InViewProps): JSX.Element => { | ||
const InViewWrapper = asChild ? Slot : 'div'; | ||
const { ref: intersectionObserverRef } = | ||
useIntersectionObserver<HTMLElement>(props); | ||
const childrenCount = Children.count(children); | ||
|
||
return <Slot ref={intersectionObserverRef}>{children}</Slot>; | ||
if (asChild && childrenCount > 1) { | ||
throw new Error( | ||
'InView 컴포넌트는 asChild 프로퍼티가 true일 경우 자식으로 단일 요소만 허용합니다.' | ||
); | ||
} | ||
return ( | ||
<InViewWrapper ref={intersectionObserverRef}>{children}</InViewWrapper> | ||
); | ||
}; |