next-enterprise/components/Ui/Card.tsx

126 lines
3.7 KiB
TypeScript
Raw Normal View History

2024-04-09 08:52:48 +05:30
"use client"
import { cn } from "./../../utils/cn"
import Image from "next/image"
2024-04-16 11:09:22 +00:00
import React, { createContext, useContext, useEffect, useRef, useState } from "react"
2024-04-09 08:52:48 +05:30
const MouseEnterContext = createContext<[boolean, React.Dispatch<React.SetStateAction<boolean>>] | undefined>(undefined)
export const CardContainer = ({
children,
className,
containerClassName,
}: {
children?: React.ReactNode
className?: string
containerClassName?: string
}) => {
const containerRef = useRef<HTMLDivElement>(null)
const [isMouseEntered, setIsMouseEntered] = useState(false)
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
if (!containerRef.current) return
const { left, top, width, height } = containerRef.current.getBoundingClientRect()
const x = (e.clientX - left - width / 2) / 25
const y = (e.clientY - top - height / 2) / 25
containerRef.current.style.transform = `rotateY(${x}deg) rotateX(${y}deg)`
}
const handleMouseEnter = (e: React.MouseEvent<HTMLDivElement>) => {
setIsMouseEntered(true)
if (!containerRef.current) return
}
const handleMouseLeave = (e: React.MouseEvent<HTMLDivElement>) => {
if (!containerRef.current) return
setIsMouseEntered(false)
containerRef.current.style.transform = `rotateY(0deg) rotateX(0deg)`
}
return (
<MouseEnterContext.Provider value={[isMouseEntered, setIsMouseEntered]}>
<div
className={cn("flex items-center justify-center", containerClassName)}
style={{
perspective: "1000px",
}}
>
<div
ref={containerRef}
onMouseEnter={handleMouseEnter}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
className={cn("relative flex items-center justify-center transition-all duration-200 ease-linear", className)}
style={{
transformStyle: "preserve-3d",
}}
>
{children}
</div>
</div>
</MouseEnterContext.Provider>
)
}
export const CardBody = ({ children, className }: { children: React.ReactNode; className?: string }) => {
return (
<div className={cn("h-full w-full [transform-style:preserve-3d] [&>*]:[transform-style:preserve-3d]", className)}>
{children}
</div>
)
}
export const CardItem = ({
as: Tag = "div",
children,
className,
translateX = 0,
translateY = 0,
translateZ = 0,
rotateX = 0,
rotateY = 0,
rotateZ = 0,
...rest
}: {
as?: React.ElementType
children: React.ReactNode
className?: string
translateX?: number | string
translateY?: number | string
translateZ?: number | string
rotateX?: number | string
rotateY?: number | string
rotateZ?: number | string
[key: string]: any
}) => {
const ref = useRef<HTMLDivElement>(null)
const [isMouseEntered] = useMouseEnter()
useEffect(() => {
handleAnimations()
}, [isMouseEntered])
const handleAnimations = () => {
if (!ref.current) return
if (isMouseEntered) {
ref.current.style.transform = `translateX(${translateX}px) translateY(${translateY}px) translateZ(${translateZ}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) rotateZ(${rotateZ}deg)`
} else {
ref.current.style.transform = `translateX(0px) translateY(0px) translateZ(0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg)`
}
}
return (
<Tag ref={ref} className={cn("w-full transition duration-200 ease-linear", className)} {...rest}>
{children}
</Tag>
)
}
// Create a hook to use the context
export const useMouseEnter = () => {
const context = useContext(MouseEnterContext)
if (context === undefined) {
throw new Error("useMouseEnter must be used within a MouseEnterProvider")
}
return context
}