InfoPanel: show confirmation icons on successful SVG or snippet copy
This patch makes immediate use of useTransientState() to fire a temporary state transition to show success/failure of copy action. Note: we should handle error states using other icon.
This commit is contained in:
@@ -1,10 +1,22 @@
|
|||||||
import React, { useRef } from "react";
|
import React, { useRef } from "react";
|
||||||
import { useRecoilValue } from "recoil";
|
import { useRecoilValue, useSetRecoilState } from "recoil";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { saveAs } from "file-saver";
|
import { saveAs } from "file-saver";
|
||||||
|
|
||||||
import { styleQueryAtom, iconSizeAtom, iconColorAtom } from "../../state/atoms";
|
import {
|
||||||
import { Icon, ArrowUpRightCircle, Copy } from "phosphor-react";
|
styleQueryAtom,
|
||||||
|
iconSizeAtom,
|
||||||
|
iconColorAtom,
|
||||||
|
iconPreviewOpenAtom,
|
||||||
|
} from "../../state/atoms";
|
||||||
|
import useTransientState from "../../hooks/useTransientState";
|
||||||
|
import {
|
||||||
|
Icon,
|
||||||
|
ArrowUpRightCircle,
|
||||||
|
Copy,
|
||||||
|
Prohibit,
|
||||||
|
CheckCircle,
|
||||||
|
} from "phosphor-react";
|
||||||
|
|
||||||
const infoVariants = {
|
const infoVariants = {
|
||||||
open: {
|
open: {
|
||||||
@@ -34,26 +46,43 @@ const InfoPanel: React.FC<InfoPanelProps> = (props) => {
|
|||||||
const weight = useRecoilValue(styleQueryAtom);
|
const weight = useRecoilValue(styleQueryAtom);
|
||||||
const size = useRecoilValue(iconSizeAtom);
|
const size = useRecoilValue(iconSizeAtom);
|
||||||
const color = useRecoilValue(iconColorAtom);
|
const color = useRecoilValue(iconColorAtom);
|
||||||
|
const setOpen = useSetRecoilState(iconPreviewOpenAtom);
|
||||||
|
const [copied, setCopied] = useTransientState<string | false>(false, 2000);
|
||||||
const ref = useRef<SVGSVGElement>(null);
|
const ref = useRef<SVGSVGElement>(null);
|
||||||
|
|
||||||
const htmlString = `<i class="ph-${name}${
|
const snippets = {
|
||||||
|
html: `<i class="ph-${name}${
|
||||||
weight === "regular" ? "" : `-${weight}`
|
weight === "regular" ? "" : `-${weight}`
|
||||||
}"></i>`;
|
}"></i>`,
|
||||||
const reactString = `<${Icon.displayName} size={${size}} color="${color}" ${
|
react: `<${Icon.displayName} size={${size}} color="${color}" ${
|
||||||
weight === "regular" ? "" : `weight="${weight}" `
|
weight === "regular" ? "" : `weight="${weight}" `
|
||||||
}/>`;
|
}/>`,
|
||||||
|
};
|
||||||
|
|
||||||
const handleCopySnippet = (data: string) => {
|
const handleCopySnippet = (
|
||||||
|
event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
|
||||||
|
type: "html" | "react"
|
||||||
|
) => {
|
||||||
|
event.currentTarget.blur();
|
||||||
|
setCopied(type);
|
||||||
|
const data = snippets[type];
|
||||||
data && navigator.clipboard.writeText(data);
|
data && navigator.clipboard.writeText(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDownloadSVG = () => {
|
const handleDownloadSVG = (
|
||||||
|
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||||
|
) => {
|
||||||
|
event.currentTarget.blur();
|
||||||
if (!ref.current?.outerHTML) return;
|
if (!ref.current?.outerHTML) return;
|
||||||
const blob = new Blob([ref.current.outerHTML]);
|
const blob = new Blob([ref.current.outerHTML]);
|
||||||
saveAs(blob, `${name}.svg`);
|
saveAs(blob, `${name}.svg`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCopySVG = () => {
|
const handleCopySVG = (
|
||||||
|
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||||
|
) => {
|
||||||
|
event.currentTarget.blur();
|
||||||
|
setCopied("svg");
|
||||||
ref.current && navigator.clipboard.writeText(ref.current.outerHTML);
|
ref.current && navigator.clipboard.writeText(ref.current.outerHTML);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,24 +108,32 @@ const InfoPanel: React.FC<InfoPanelProps> = (props) => {
|
|||||||
<div className="snippet">
|
<div className="snippet">
|
||||||
HTML/CSS
|
HTML/CSS
|
||||||
<pre style={{ color: "black" }}>
|
<pre style={{ color: "black" }}>
|
||||||
{htmlString}
|
{snippets.html}
|
||||||
<button
|
<button
|
||||||
title="Copy snippet"
|
title="Copy snippet"
|
||||||
onClick={() => handleCopySnippet(htmlString)}
|
onClick={(e) => handleCopySnippet(e, "html")}
|
||||||
>
|
>
|
||||||
|
{copied === "html" ? (
|
||||||
|
<CheckCircle size={24} color="#1FA647" weight="fill" />
|
||||||
|
) : (
|
||||||
<Copy size={24} color="currentColor" weight="regular" />
|
<Copy size={24} color="currentColor" weight="regular" />
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
<div className="snippet">
|
<div className="snippet">
|
||||||
React
|
React
|
||||||
<pre style={{ color: "black" }}>
|
<pre style={{ color: "black" }}>
|
||||||
{reactString}
|
{snippets.react}
|
||||||
<button
|
<button
|
||||||
title="Copy snippet"
|
title="Copy snippet"
|
||||||
onClick={() => handleCopySnippet(reactString)}
|
onClick={(e) => handleCopySnippet(e, "react")}
|
||||||
>
|
>
|
||||||
|
{copied === "react" ? (
|
||||||
|
<CheckCircle size={24} color="#1FA647" weight="fill" />
|
||||||
|
) : (
|
||||||
<Copy size={24} color="currentColor" weight="regular" />
|
<Copy size={24} color="currentColor" weight="regular" />
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
@@ -117,16 +154,34 @@ const InfoPanel: React.FC<InfoPanelProps> = (props) => {
|
|||||||
style={{ color: isDark ? "white" : "black" }}
|
style={{ color: isDark ? "white" : "black" }}
|
||||||
onClick={handleCopySVG}
|
onClick={handleCopySVG}
|
||||||
>
|
>
|
||||||
|
{copied === "svg" ? (
|
||||||
|
<CheckCircle
|
||||||
|
size={32}
|
||||||
|
style={{ marginRight: 8 }}
|
||||||
|
color="#1FA647"
|
||||||
|
weight="fill"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<Copy
|
<Copy
|
||||||
size={32}
|
size={32}
|
||||||
style={{ marginRight: 8 }}
|
style={{ marginRight: 8 }}
|
||||||
color="currentColor"
|
color="currentColor"
|
||||||
weight="regular"
|
weight="regular"
|
||||||
/>{" "}
|
/>
|
||||||
Copy SVG
|
)}
|
||||||
|
{copied === "svg" ? "Copied!" : "Copy SVG"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="close">
|
||||||
|
<Prohibit
|
||||||
|
className="close-icon"
|
||||||
|
color="currentColor"
|
||||||
|
size={32}
|
||||||
|
weight="regular"
|
||||||
|
onClick={() => setOpen(false)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</motion.section>
|
</motion.section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user