hts/apps/staffai/components/shiny-card.tsx

113 lines
3.3 KiB
TypeScript

"use client";
import { useMousePosition } from "@/lib/mouse";
import React, { PropsWithChildren, useEffect, useRef, useState } from "react";
type ShinyCardGroupProps = {
children: React.ReactNode;
className?: string;
refresh?: boolean;
};
export const ShinyCardGroup: React.FC<ShinyCardGroupProps> = ({
children,
className = "",
refresh = false,
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const mousePosition = useMousePosition();
const mouse = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
const containerSize = useRef<{ w: number; h: number }>({ w: 0, h: 0 });
const [boxes, setBoxes] = useState<Array<HTMLElement>>([]);
useEffect(() => {
containerRef.current &&
setBoxes(Array.from(containerRef.current.children).map((el) => el as HTMLElement));
}, []);
useEffect(() => {
initContainer();
window.addEventListener("resize", initContainer);
return () => {
window.removeEventListener("resize", initContainer);
};
}, [setBoxes]);
useEffect(() => {
onMouseMove();
}, [mousePosition]);
useEffect(() => {
initContainer();
}, [refresh]);
const initContainer = () => {
if (containerRef.current) {
containerSize.current.w = containerRef.current.offsetWidth;
containerSize.current.h = containerRef.current.offsetHeight;
}
};
const onMouseMove = () => {
if (containerRef.current) {
const rect = containerRef.current.getBoundingClientRect();
const { w, h } = containerSize.current;
const x = mousePosition.x - rect.left;
const y = mousePosition.y - rect.top;
const inside = x < w && x > 0 && y < h && y > 0;
if (inside) {
mouse.current.x = x;
mouse.current.y = y;
boxes.forEach((box) => {
const boxX = -(box.getBoundingClientRect().left - rect.left) + mouse.current.x;
const boxY = -(box.getBoundingClientRect().top - rect.top) + mouse.current.y;
box.style.setProperty("--mouse-x", `${boxX}px`);
box.style.setProperty("--mouse-y", `${boxY}px`);
});
}
}
};
return (
<div className={className} ref={containerRef}>
{children}
</div>
);
};
type ShinyCardProps = {
children: React.ReactNode;
className?: string;
shine?: string;
};
export const ShinyCard: React.FC<PropsWithChildren<ShinyCardProps>> = ({
children,
className = "",
shine = "#ffffff",
}) => {
return (
<div
className={`relative bg-neutral-800 rounded-4xl p-px
after:absolute after:inset-0 after:rounded-[inherit] after:opacity-0 after:transition-opacity after:duration-500 after:[background:_radial-gradient(250px_circle_at_var(--mouse-x)_var(--mouse-y),${shine},transparent)] after:group-hover:opacity-100 after:z-10 overflow-hidden ${className}`}
>
{children}
</div>
);
};
export const WhiteShinyCard: React.FC<PropsWithChildren<ShinyCardProps>> = ({
children,
className = "",
}) => {
return (
<div
className={`relative bg-neutral-800 rounded-4xl p-px
after:absolute after:inset-0 after:rounded-[inherit] after:opacity-0 after:transition-opacity after:duration-500 after:[background:_radial-gradient(250px_circle_at_var(--mouse-x)_var(--mouse-y),theme(colors.gray.500),transparent)] after:group-hover:opacity-100 after:z-10 overflow-hidden ${className}`}
>
{children}
</div>
);
};