import React, { useRef, useLayoutEffect, useEffect, MutableRefObject, } from "react"; import { useRecoilState } from "recoil"; import { motion, AnimatePresence } from "framer-motion"; import { iconPreviewOpenAtom } from "../../state/atoms"; import DetailsPanel from "./DetailsPanel"; import { IconEntry } from "../../lib"; interface IconGridItemProps { index: number; spans: number; isDark: boolean; entry: IconEntry; originOffset: MutableRefObject<{ top: number; left: number }>; } const transition = { duration: 0.2 }; const originIndex = 0; const delayPerPixel = 0.0004; const itemVariants = { hidden: { opacity: 0 }, visible: (delayRef: any) => ({ opacity: 1, transition: { delay: delayRef.current }, }), }; const IconGridItem: React.FC = (props) => { const { index, originOffset, entry } = props; const { name, Icon } = entry; const [open, setOpen] = useRecoilState(iconPreviewOpenAtom); const isOpen = open === name; const delayRef = useRef(0); const offset = useRef({ top: 0, left: 0 }); const ref = useRef(); const handleOpen = () => setOpen(isOpen ? false : name); // The measurement for all elements happens in the layoutEffect cycle // This ensures that when we calculate distance in the effect cycle // all elements have already been measured useLayoutEffect(() => { const element = ref.current; if (!element) return; offset.current = { top: element.offsetTop, left: element.offsetLeft, }; if (index === originIndex) { originOffset.current = offset.current; } }, [index, originOffset]); useEffect(() => { const dx = Math.abs(offset.current.left - originOffset.current.left); const dy = Math.abs(offset.current.top - originOffset.current.top); const d = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); delayRef.current = d * delayPerPixel; }, [originOffset]); return ( <> e.key === "Enter" && handleOpen()} onClick={handleOpen} >

{name}

{isOpen && } ); }; export default IconGridItem;