Created
February 17, 2024 05:44
-
-
Save composite/cc45e88eba3c47363109dd3ae68c45af to your computer and use it in GitHub Desktop.
React Polymorphic Component for Dummies & IntersectionObserver based Component Example
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
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-assignment */ | |
'use client'; | |
import type { ElementType, RefCallback } from 'react'; | |
import { createElement, useCallback, useEffect, useMemo, useRef, useState } from 'react'; | |
import { twMerge } from 'tailwind-merge'; | |
import type { PolymorphicComponentProps } from './polymorphic'; | |
export type InViewProps = { | |
/** | |
* The CSS Class that active when inside browser's sight | |
*/ | |
inClass?: string; | |
/** | |
* The CSS Class that active when outside browser's sight | |
*/ | |
outClass?: string; | |
/** | |
* Activate once when your component in your sight | |
*/ | |
once?: boolean; | |
/** | |
* Intersection Observer rootMargin (you must obtain number with 'px' unit. example, 0px) | |
*/ | |
rootMargin?: string; | |
/** | |
* The event fired when your component in your sight | |
*/ | |
onView?: (el: HTMLElement) => void; | |
}; | |
/** | |
* The polymorphic compoenent that Change CSS class even your component is on your sight. | |
*/ | |
export default function InView<T extends ElementType = 'div'>({ | |
as, | |
className, | |
children, | |
inClass, | |
once, | |
outClass, | |
rootMargin, | |
onView, | |
...props | |
}: PolymorphicComponentProps<T, InViewProps>) { | |
const ref = useRef<any>(); | |
const exRef = useCallback<RefCallback<any>>((r) => { | |
ref.current = r; | |
}, []); | |
const [isView, setIsView] = useState(false); | |
const calcClass = useMemo(() => twMerge(className, isView ? inClass : outClass), [isView]); | |
useEffect(() => { | |
const observer = new IntersectionObserver( | |
([entry]) => { | |
setIsView(entry.isIntersecting); | |
if (once && entry.isIntersecting && ref.current) observer.unobserve(ref.current); | |
onView && onView(ref.current as HTMLElement); | |
}, | |
{ rootMargin: rootMargin || '-100px' } | |
); | |
ref.current && observer.observe(ref.current); | |
return () => { | |
observer.disconnect(); | |
}; | |
}, [ref]); | |
return createElement(as || 'div', { ...props, ref: exRef, className: calcClass }, children); | |
} |
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
/* eslint-disable */ | |
import type { ComponentPropsWithoutRef, ComponentPropsWithRef, ElementType } from 'react'; | |
export type AsProp<T extends ElementType> = { | |
as?: T; | |
}; | |
export type PolymorphicRef<T extends ElementType> = ComponentPropsWithRef<T>['ref']; | |
export type PolymorphicRefProps<T extends ElementType> = { | |
ref?: PolymorphicRef<T>; | |
}; | |
export type PolymorphicComponentProps<T extends ElementType, Props = {}> = AsProp<T> & | |
ComponentPropsWithoutRef<T> & | |
Props; | |
export type PolymorphicRefComponentProps<T extends ElementType, Props = {}> = AsProp<T> & | |
ComponentPropsWithoutRef<T> & | |
Props & | |
PolymorphicRefProps<T>; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment