121 lines
3.1 KiB
TypeScript
121 lines
3.1 KiB
TypeScript
import { useEffect, useRef, useState } from "react";
|
|
import styles from "./styles.module.css";
|
|
import { EDirectionOnboarding } from "@/types";
|
|
|
|
interface OnboardingProps {
|
|
targetRef: HTMLElement;
|
|
isShow: boolean;
|
|
direction?: EDirectionOnboarding;
|
|
showBackground?: boolean;
|
|
children: JSX.Element;
|
|
classNameContainer?: string;
|
|
}
|
|
interface IBoardingCoordinates {
|
|
top: number;
|
|
left: number;
|
|
}
|
|
|
|
const getCoordinates = (
|
|
targetRef: HTMLElement,
|
|
direction: EDirectionOnboarding,
|
|
onboardingRef: React.RefObject<HTMLDivElement>
|
|
): IBoardingCoordinates => {
|
|
if (targetRef && onboardingRef.current) {
|
|
switch (direction) {
|
|
case EDirectionOnboarding.LEFT:
|
|
return {
|
|
top:
|
|
targetRef.offsetTop +
|
|
targetRef.offsetHeight / 2 -
|
|
onboardingRef.current.offsetHeight / 2,
|
|
left: targetRef.offsetLeft - onboardingRef.current.offsetWidth,
|
|
};
|
|
case EDirectionOnboarding.RIGHT:
|
|
return {
|
|
top:
|
|
targetRef.offsetTop +
|
|
targetRef.offsetHeight / 2 -
|
|
onboardingRef.current.offsetHeight / 2,
|
|
left: targetRef.offsetLeft + targetRef.offsetWidth,
|
|
};
|
|
case EDirectionOnboarding.TOP:
|
|
return {
|
|
top: targetRef.offsetTop - onboardingRef.current.offsetHeight,
|
|
left:
|
|
targetRef.offsetLeft +
|
|
targetRef.offsetWidth / 2 -
|
|
onboardingRef.current.offsetWidth / 2,
|
|
};
|
|
case EDirectionOnboarding.BOTTOM:
|
|
return {
|
|
top: targetRef.offsetTop + targetRef.offsetHeight,
|
|
left:
|
|
targetRef.offsetLeft +
|
|
targetRef.offsetWidth / 2 -
|
|
onboardingRef.current.offsetWidth / 2,
|
|
};
|
|
}
|
|
}
|
|
return {
|
|
top: 0,
|
|
left: 0,
|
|
};
|
|
};
|
|
|
|
const getClassNameContainer = (
|
|
direction: EDirectionOnboarding,
|
|
showBackground: boolean,
|
|
classNameContainer?: string
|
|
) => {
|
|
return `${styles["onboarding-container"]} ${
|
|
styles[`direction-${direction}`]
|
|
} ${showBackground ? styles["background"] : ""} ${classNameContainer}`;
|
|
};
|
|
|
|
function Onboarding({
|
|
targetRef,
|
|
isShow,
|
|
direction = EDirectionOnboarding.TOP,
|
|
showBackground = false,
|
|
classNameContainer = "",
|
|
children,
|
|
}: OnboardingProps): JSX.Element {
|
|
const onboardingRef = useRef<HTMLDivElement>(null);
|
|
const [coordinates, setCoordinates] = useState<IBoardingCoordinates>({
|
|
top: 0,
|
|
left: 0,
|
|
});
|
|
useEffect(() => {
|
|
if (!onboardingRef.current || !targetRef) {
|
|
return;
|
|
}
|
|
setCoordinates(getCoordinates(targetRef, direction, onboardingRef));
|
|
}, [direction, targetRef, children]);
|
|
|
|
if (!isShow) {
|
|
return <></>;
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={getClassNameContainer(
|
|
direction,
|
|
showBackground,
|
|
classNameContainer
|
|
)}
|
|
>
|
|
<div
|
|
className={`${styles["onboarding"]} ${
|
|
!targetRef || !onboardingRef.current ? styles["hide"] : ""
|
|
}`}
|
|
style={{ top: `${coordinates.top}px`, left: `${coordinates.left}px` }}
|
|
ref={onboardingRef}
|
|
>
|
|
{children}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default Onboarding;
|