2024-02-09 10:23:15 +00:00
|
|
|
import React, { useState, useRef, useEffect, ReactNode, MutableRefObject } from "react";
|
2024-03-19 14:38:35 +00:00
|
|
|
import { cn } from "@/helpers/common.helper";
|
2024-02-09 10:23:15 +00:00
|
|
|
|
|
|
|
type Props = {
|
|
|
|
defaultHeight?: string;
|
|
|
|
verticalOffset?: number;
|
2024-03-06 13:09:14 +00:00
|
|
|
horizontalOffset?: number;
|
2024-02-09 10:23:15 +00:00
|
|
|
root?: MutableRefObject<HTMLElement | null>;
|
|
|
|
children: ReactNode;
|
|
|
|
as?: keyof JSX.IntrinsicElements;
|
|
|
|
classNames?: string;
|
|
|
|
alwaysRender?: boolean;
|
|
|
|
placeholderChildren?: ReactNode;
|
|
|
|
pauseHeightUpdateWhileRendering?: boolean;
|
|
|
|
changingReference?: any;
|
|
|
|
};
|
|
|
|
|
|
|
|
const RenderIfVisible: React.FC<Props> = (props) => {
|
|
|
|
const {
|
|
|
|
defaultHeight = "300px",
|
|
|
|
root,
|
|
|
|
verticalOffset = 50,
|
2024-03-06 13:09:14 +00:00
|
|
|
horizontalOffset = 0,
|
2024-02-09 10:23:15 +00:00
|
|
|
as = "div",
|
|
|
|
children,
|
|
|
|
classNames = "",
|
2024-04-16 10:18:11 +00:00
|
|
|
alwaysRender = false, //render the children even if it is not visible in root
|
2024-02-09 10:23:15 +00:00
|
|
|
placeholderChildren = null, //placeholder children
|
|
|
|
pauseHeightUpdateWhileRendering = false, //while this is true the height of the blocks are maintained
|
|
|
|
changingReference, //This is to force render when this reference is changed
|
|
|
|
} = props;
|
|
|
|
const [shouldVisible, setShouldVisible] = useState<boolean>(alwaysRender);
|
|
|
|
const placeholderHeight = useRef<string>(defaultHeight);
|
|
|
|
const intersectionRef = useRef<HTMLElement | null>(null);
|
|
|
|
|
|
|
|
const isVisible = alwaysRender || shouldVisible;
|
|
|
|
|
|
|
|
// Set visibility with intersection observer
|
|
|
|
useEffect(() => {
|
|
|
|
if (intersectionRef.current) {
|
|
|
|
const observer = new IntersectionObserver(
|
|
|
|
(entries) => {
|
2024-02-12 14:55:37 +00:00
|
|
|
//DO no remove comments for future
|
|
|
|
// if (typeof window !== undefined && window.requestIdleCallback) {
|
|
|
|
// window.requestIdleCallback(() => setShouldVisible(entries[0].isIntersecting), {
|
|
|
|
// timeout: 300,
|
|
|
|
// });
|
|
|
|
// } else {
|
|
|
|
// setShouldVisible(entries[0].isIntersecting);
|
|
|
|
// }
|
|
|
|
setShouldVisible(entries[0].isIntersecting);
|
2024-02-09 10:23:15 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
root: root?.current,
|
2024-03-06 13:09:14 +00:00
|
|
|
rootMargin: `${verticalOffset}% ${horizontalOffset}% ${verticalOffset}% ${horizontalOffset}%`,
|
2024-02-09 10:23:15 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
observer.observe(intersectionRef.current);
|
|
|
|
return () => {
|
|
|
|
if (intersectionRef.current) {
|
2024-03-06 13:09:14 +00:00
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2024-02-09 10:23:15 +00:00
|
|
|
observer.unobserve(intersectionRef.current);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2024-03-06 13:09:14 +00:00
|
|
|
}, [intersectionRef, children, changingReference, root, verticalOffset, horizontalOffset]);
|
2024-02-09 10:23:15 +00:00
|
|
|
|
|
|
|
//Set height after render
|
|
|
|
useEffect(() => {
|
|
|
|
if (intersectionRef.current && isVisible) {
|
|
|
|
placeholderHeight.current = `${intersectionRef.current.offsetHeight}px`;
|
|
|
|
}
|
|
|
|
}, [isVisible, intersectionRef, alwaysRender, pauseHeightUpdateWhileRendering]);
|
|
|
|
|
|
|
|
const child = isVisible ? <>{children}</> : placeholderChildren;
|
|
|
|
const style =
|
|
|
|
isVisible && !pauseHeightUpdateWhileRendering ? {} : { height: placeholderHeight.current, width: "100%" };
|
|
|
|
const className = isVisible ? classNames : cn(classNames, "bg-custom-background-80");
|
|
|
|
|
|
|
|
return React.createElement(as, { ref: intersectionRef, style, className }, child);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default RenderIfVisible;
|