import React, { useRef, useLayoutEffect, useEffect, MutableRefObject, } from "react"; import { useRecoilState } from "recoil"; import { motion, AnimatePresence } from "framer-motion"; import { iconPreviewOpenAtom } from "../../state/atoms"; import { IconProps, Icon } from "phosphor-react"; interface IconGridItemProps extends IconProps { index: number; name: string; Icon: Icon; originOffset: MutableRefObject<{ top: number; left: number }>; spans: number; } const itemVariants = { hidden: { opacity: 0 }, visible: (delayRef: any) => ({ opacity: 1, transition: { delay: delayRef.current }, }), }; const whileHover = { boxShadow: "0 0 0 2px rgb(0, 0, 0)" }; const whileTap = { boxShadow: "0 0 0 4px rgb(139, 0, 139)" }; const transition = { duration: 0.2 }; const originIndex = 0; const delayPerPixel = 0.0004; const infoVariants = { open: { opacity: 1, height: 176, marginTop: 4, marginBottom: 4, padding: 16 }, collapsed: { opacity: 0, height: 0, marginTop: 0, marginBottom: 0, padding: 0, }, }; const IconGridItem: React.FC = (props) => { const { index, spans, originOffset, name, Icon, ...iconProps } = props; const [open, setOpen] = useRecoilState(iconPreviewOpenAtom); const delayRef = useRef(0); const offset = useRef({ top: 0, left: 0 }); const ref = useRef(); // 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; } }, []); 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; }, []); return ( <> setOpen((openName) => (name === openName ? false : name)) } >

{name}

{open === name && } ); }; const InfoPanel: React.FC = (props) => { const { index, spans, name, Icon, color, weight } = props; return (

{name}

HTML
{``}
React
{`<${Icon.displayName} ${
          weight === "regular" ? "" : `weight="${weight}"`
        }/>`}
); }; export default IconGridItem;