- import React, { useEffect, useRef, useState } from "react";
- import { motion, useAnimation } from "framer-motion";
- import clsx from "clsx";
- import MouseTracker from "../../MouseTracker";
- const HoverableSkillList = ({
- title,
- items = [], // Default to an empty array to avoid undefined errors
- color = "red",
- hoveredItem,
- setHoveredItem,
- section,
- getKey,
- isVisible,
- }) => {
- const borderClass = `text-${color}-400`;
- const controls = useAnimation();
- const skillLevelRefs = useRef([]);
- const [isMobile, setIsMobile] = useState(false);
-
- useEffect(() => {
- const checkIsMobile = () => {
- setIsMobile(window.innerWidth <= 768);
- };
- checkIsMobile();
- window.addEventListener("resize", checkIsMobile);
- return () => {
- window.removeEventListener("resize", checkIsMobile);
- };
- }, []);
- useEffect(() => {
- if (isVisible) {
- controls.start((item) => {
- const circumference = 2 * Math.PI * 40;
- const skillLevel = parseInt(
- item.skill.split(": ")[1].split("/")[0],
- 10
- );
- return {
- strokeDashoffset: circumference - (skillLevel / 5) * circumference,
- transition: { duration: 1, ease: "easeInOut" },
- };
- });
- } else {
- controls.set({ strokeDashoffset: 2 * Math.PI * 40 });
- }
- }, [isVisible, controls]);
-
- const isHovered =
- hoveredItem.section === section && hoveredItem.index !== null;
- return (
- <div className="relative mx-auto text-center">
- <h2
- className={clsx("text-2xl font-sans font-semibold mb-8", borderClass)}
- >
- {title}
- </h2>
- <div className="flex gap-3 flex-wrap justify-center">
- {items.map((item, index) => {
- const circumference = 2 * Math.PI * 40;
- return (
- <div
- key={getKey(item, index)}
- className="relative flex flex-col items-center mb-2 group"
- onMouseEnter={() =>
- !isMobile && setHoveredItem({ section, index })
- }
- onMouseLeave={() =>
- !isMobile && setHoveredItem({ section: null, index: null })
- }
- onClick={() => isMobile && setHoveredItem({ section, index })}
- >
- <div className="relative w-[70px] h-[70px] sm:w-[70px] sm:h-[70px] md:w-[80px] md:h-[80px] lg:w-[90px] lg:h-[90px]">
- {" "}
- {/* Adjusted size for mobile */}
- <svg
- className="absolute top-0 left-0 w-full h-full transform -rotate-90"
- viewBox="0 0 100 100"
- >
- {/* Background Circle */}
- <circle
- cx="50"
- cy="50"
- r="40"
- fill="transparent"
- stroke="gray"
- strokeWidth="6"
- strokeDasharray={circumference}
- />
- {/* Animated Progress Circle */}
- <motion.circle
- cx="50"
- cy="50"
- r="40"
- fill="transparent"
- stroke="currentColor"
- strokeWidth="7"
- strokeDasharray={circumference}
- initial={{ strokeDashoffset: circumference }}
- animate={controls}
- custom={item}
- className={clsx(borderClass, "transition-all duration-300")}
- />
- </svg>
- {/* Icon */}
- <motion.img
- alt={item.name}
- src={item.logo}
- className={clsx(
- "p-2 w-[60px] h-[60px] sm:w-[70px] sm:h-[70px] rounded-full transition-all duration-100 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2",
- isHovered &&
- hoveredItem.index === index &&
- "shadow-[0_0_50px_rgba(255,255,255,0.9)]"
- )}
- />
- </div>
- <p className="mt-2 text-base text-white">{item.name}</p>
- {/* Inline Tooltip for Mobile */}
- {isMobile && isHovered && hoveredItem.index === index && (
- <div className="absolute bottom-full mb-2 bg-black text-white text-sm font-bold rounded-md px-8 py-3 shadow-lg">
- {item.skill}
- </div>
- )}
- </div>
- );
- })}
- </div>
- {/* MouseTracker for Desktop Tooltip */}
- {!isMobile && isHovered && (
- <MouseTracker text={items[hoveredItem.index].skill} />
- )}
- </div>
- );
- };
- export default HoverableSkillList;