import React, { useRef, useEffect, CSSProperties } from "react"; import { useRecoilValue, useSetRecoilState } from "recoil"; import { useHotkeys } from "react-hotkeys-hook"; import { motion } from "framer-motion"; import { Svg2Png } from "svg2png-converter"; import { saveAs } from "file-saver"; import { Copy, X, CheckCircle, Download } from "phosphor-react"; import ReactGA from "react-ga"; import { iconWeightAtom, iconSizeAtom, iconColorAtom, iconPreviewOpenAtom, } from "@/state/atoms"; import useTransientState from "@/hooks/useTransientState"; import { IconEntry, SnippetType } from "@/lib"; import { getCodeSnippets, supportsWeight } from "@/utils"; import TagCloud from "./TagCloud"; const panelVariants = { open: { opacity: 1, height: "100%", marginTop: "4px", marginBottom: "4px", transition: { type: "tween", duration: 0.1 }, }, collapsed: { opacity: 0, height: "0px", marginTop: "0px", marginBottom: "0px", transition: { type: "tween", duration: 0.1 }, }, }; const contentVariants = { open: { opacity: 1, transition: { duration: 0.2, delay: 0.1 } }, collapsed: { opacity: 0, transition: { duration: 0 } }, }; const buttonColor = "#35313D"; const successColor = "#1FA647"; const disabledColor = "#B7B7B7"; interface InfoPanelProps { index: number; spans: number; isDark: boolean; entry: IconEntry; } const renderedSnippets = [ SnippetType.REACT, SnippetType.VUE, SnippetType.HTML, SnippetType.FLUTTER, ]; const DetailsPanel = (props: InfoPanelProps) => { const { index, spans, isDark, entry } = props; const { name, Icon, categories, tags } = entry; const weight = useRecoilValue(iconWeightAtom); const size = useRecoilValue(iconSizeAtom); const color = useRecoilValue(iconColorAtom); const setOpen = useSetRecoilState(iconPreviewOpenAtom); const [copied, setCopied] = useTransientState( false, 2000 ); const ref = useRef(null); useHotkeys("esc", () => setOpen(false)); useEffect( () => ReactGA.event({ category: "Grid", action: "Details", label: name }), [name] ); const buttonBarStyle: CSSProperties = { color: isDark ? "white" : buttonColor, }; const snippetButtonStyle: CSSProperties = weight === "duotone" ? { color: disabledColor, userSelect: "none" } : { color: buttonColor }; const snippets = getCodeSnippets({ displayName: Icon.displayName!, name, weight, size, color, }); const handleCopySnippet = ( event: React.MouseEvent, type: SnippetType ) => { event.currentTarget.blur(); setCopied(type); const data = snippets[type]; data && void navigator.clipboard?.writeText(data); }; const handleCopySVG = ( event: React.MouseEvent ) => { event.currentTarget.blur(); setCopied("SVG"); ref.current && void navigator.clipboard?.writeText(ref.current.outerHTML); }; const handleDownloadSVG = ( event: React.MouseEvent ) => { event.currentTarget.blur(); if (!ref.current?.outerHTML) return; const blob = new Blob([ref.current.outerHTML]); saveAs(blob, `${name}${weight === "regular" ? "" : `-${weight}`}.svg`); }; const handleDownloadPNG = async ( event: React.MouseEvent ) => { event.currentTarget.blur(); if (!ref.current?.outerHTML) return; Svg2Png.save( ref.current, `${name}${weight === "regular" ? "" : `-${weight}`}.png`, { scaleX: 2.667, scaleY: 2.667 } ); }; return (

{name}

([...categories, ...name.split("-"), ...tags]) )} isDark={isDark} />
{renderedSnippets.map((type) => { const isWeightSupported = supportsWeight({ type, weight }); return (
{type}
                
                  {isWeightSupported
                    ? snippets[type]
                    : "This weight is not yet supported"}
                
                
              
); })}
setOpen(false)} onKeyDown={(e) => { e.key === "Enter" && setOpen(false); }} />
); }; export default DetailsPanel;