w-aura/src/components/Onboarding/index.tsx

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;