refactor(ui): clean up snippets code

This commit is contained in:
rektdeckard
2021-12-29 00:54:56 -05:00
parent 4137a8b5c1
commit c3cd3c1d80
3 changed files with 122 additions and 106 deletions

View File

@@ -15,7 +15,8 @@ import {
} from "../../state/atoms"; } from "../../state/atoms";
import useTransientState from "../../hooks/useTransientState"; import useTransientState from "../../hooks/useTransientState";
import TagCloud from "./TagCloud"; import TagCloud from "./TagCloud";
import { IconEntry } from "../../lib"; import { IconEntry, SnippetType } from "../../lib";
import { getCodeSnippets, supportsWeight } from "../../utils";
const panelVariants = { const panelVariants = {
open: { open: {
@@ -50,6 +51,13 @@ interface InfoPanelProps {
entry: IconEntry; entry: IconEntry;
} }
const renderedSnippets = [
SnippetType.REACT,
SnippetType.VUE,
SnippetType.HTML,
SnippetType.FLUTTER,
];
const DetailsPanel: React.FC<InfoPanelProps> = (props) => { const DetailsPanel: React.FC<InfoPanelProps> = (props) => {
const { index, spans, isDark, entry } = props; const { index, spans, isDark, entry } = props;
const { name, Icon, categories, tags } = entry; const { name, Icon, categories, tags } = entry;
@@ -57,7 +65,10 @@ const DetailsPanel: React.FC<InfoPanelProps> = (props) => {
const size = useRecoilValue(iconSizeAtom); const size = useRecoilValue(iconSizeAtom);
const color = useRecoilValue(iconColorAtom); const color = useRecoilValue(iconColorAtom);
const setOpen = useSetRecoilState(iconPreviewOpenAtom); const setOpen = useSetRecoilState(iconPreviewOpenAtom);
const [copied, setCopied] = useTransientState<string | false>(false, 2000); const [copied, setCopied] = useTransientState<SnippetType | "SVG" | false>(
false,
2000
);
const ref = useRef<SVGSVGElement>(null); const ref = useRef<SVGSVGElement>(null);
useHotkeys("esc", () => setOpen(false)); useHotkeys("esc", () => setOpen(false));
@@ -75,38 +86,17 @@ const DetailsPanel: React.FC<InfoPanelProps> = (props) => {
? { color: disabledColor, userSelect: "none" } ? { color: disabledColor, userSelect: "none" }
: { color: buttonColor }; : { color: buttonColor };
const snippets = { const snippets = getCodeSnippets({
html: displayName: Icon.displayName!,
weight === "duotone" name,
? "This weight is not yet supported" weight,
: `<i class="ph-${name}${ size,
weight === "regular" ? "" : `-${weight}` color,
}"></i>`, });
react: `<${Icon.displayName} size={${size}} ${
color !== "#000000" ? `color="${color}" ` : ""
}${weight === "regular" ? "" : `weight="${weight}" `}/>`,
vue: `<ph${Icon.displayName!.replace(
/([a-z0-9]|(?=[A-Z]))([A-Z])/g,
"$1-$2"
).toLowerCase()} :size="${size}" ${
color !== "#000000" ? `color="${color}" ` : ""
}${weight === "regular" ? "" : `weight="${weight}" `}/>`,
flutter:
weight === "duotone"
? "This weight is not yet supported"
: `Icon(\n PhosphorIcons.${
Icon.displayName!.replace(/^\w/, (c) => c.toLowerCase())
}${weight === "regular"
? ""
: weight.replace(/^\w/, (c) => c.toUpperCase())
},\n size: ${size.toFixed(1)},\n color: Color(0xff${
color.replace("#", "")
}),\n)`,
};
const handleCopySnippet = ( const handleCopySnippet = (
event: React.MouseEvent<HTMLButtonElement, MouseEvent>, event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
type: "html" | "react" | "vue" | "flutter" type: SnippetType
) => { ) => {
event.currentTarget.blur(); event.currentTarget.blur();
setCopied(type); setCopied(type);
@@ -118,7 +108,7 @@ const DetailsPanel: React.FC<InfoPanelProps> = (props) => {
event: React.MouseEvent<HTMLButtonElement, MouseEvent> event: React.MouseEvent<HTMLButtonElement, MouseEvent>
) => { ) => {
event.currentTarget.blur(); event.currentTarget.blur();
setCopied("svg"); setCopied("SVG");
ref.current && void navigator.clipboard?.writeText(ref.current.outerHTML); ref.current && void navigator.clipboard?.writeText(ref.current.outerHTML);
}; };
@@ -171,6 +161,7 @@ const DetailsPanel: React.FC<InfoPanelProps> = (props) => {
isDark={isDark} isDark={isDark}
/> />
</motion.div> </motion.div>
<motion.div <motion.div
initial="collapsed" initial="collapsed"
animate="open" animate="open"
@@ -178,78 +169,45 @@ const DetailsPanel: React.FC<InfoPanelProps> = (props) => {
variants={contentVariants} variants={contentVariants}
className="icon-usage" className="icon-usage"
> >
<div className="snippet"> {renderedSnippets.map((type) => {
React const isWeightSupported = supportsWeight({ type, weight });
<pre tabIndex={0}>
<span>{snippets.react}</span> return (
<button <div className="snippet" key={type}>
title="Copy snippet" {type}
onClick={(e) => handleCopySnippet(e, "react")} <pre
> tabIndex={0}
{copied === "react" ? ( style={isWeightSupported ? undefined : snippetButtonStyle}
<CheckCircle size={24} color={successColor} weight="fill" /> >
) : ( <span>
<Copy size={24} color={buttonColor} weight="fill" /> {isWeightSupported
)} ? snippets[type]
</button> : "This weight is not yet supported"}
</pre> </span>
</div> <button
<div className="snippet"> title="Copy snippet"
Vue onClick={(e) => handleCopySnippet(e, type)}
<pre tabIndex={0}> disabled={!isWeightSupported}
<span>{snippets.vue}</span> style={isWeightSupported ? undefined : snippetButtonStyle}
<button >
title="Copy snippet" {copied === type ? (
onClick={(e) => handleCopySnippet(e, "vue")} <CheckCircle size={24} color={successColor} weight="fill" />
> ) : (
{copied === "vue" ? ( <Copy
<CheckCircle size={24} color={successColor} weight="fill" /> size={24}
) : ( color={
<Copy size={24} color={buttonColor} weight="fill" /> isWeightSupported
)} ? buttonColor
</button> : snippetButtonStyle.color
</pre> }
</div> weight="fill"
<div className="snippet"> />
HTML/CSS )}
<pre tabIndex={0} style={snippetButtonStyle}> </button>
<span>{snippets.html}</span> </pre>
<button </div>
title="Copy snippet" );
onClick={(e) => handleCopySnippet(e, "html")} })}
disabled={weight === "duotone"}
style={snippetButtonStyle}
>
{copied === "html" ? (
<CheckCircle size={24} color={successColor} weight="fill" />
) : (
<Copy
size={24}
color={snippetButtonStyle.color}
weight="fill"
/>
)}
</button>
</pre>
</div>
<div className="snippet">
Flutter
<pre tabIndex={0} style={snippetButtonStyle}>
<span>{snippets.flutter}</span>
<button
title="Copy snippet"
onClick={(e) => handleCopySnippet(e, "flutter")}
disabled={weight === "duotone"}
style={snippetButtonStyle}
>
{copied === "flutter" ? (
<CheckCircle size={24} color={successColor} weight="fill" />
) : (
<Copy size={24} color={snippetButtonStyle.color} weight="fill" />
)}
</button>
</pre>
</div>
<div className="button-row"> <div className="button-row">
<button style={buttonBarStyle} onClick={handleDownloadPNG}> <button style={buttonBarStyle} onClick={handleDownloadPNG}>
@@ -261,12 +219,12 @@ const DetailsPanel: React.FC<InfoPanelProps> = (props) => {
SVG SVG
</button> </button>
<button style={buttonBarStyle} onClick={handleCopySVG}> <button style={buttonBarStyle} onClick={handleCopySVG}>
{copied === "svg" ? ( {copied === "SVG" ? (
<CheckCircle size={32} color={successColor} weight="fill" /> <CheckCircle size={32} color={successColor} weight="fill" />
) : ( ) : (
<Copy size={32} color="currentColor" weight="fill" /> <Copy size={32} color="currentColor" weight="fill" />
)} )}
{copied === "svg" ? "Copied!" : "Copy SVG"} {copied === "SVG" ? "Copied!" : "Copy SVG"}
</button> </button>
</div> </div>
</motion.div> </motion.div>

View File

@@ -36,3 +36,10 @@ export interface IconEntry {
tags: string[]; tags: string[];
Icon: Icon; Icon: Icon;
} }
export enum SnippetType {
REACT = "React",
VUE = "Vue",
HTML = "HTML/CSS",
FLUTTER = "Flutter",
}

51
src/utils/index.ts Normal file
View File

@@ -0,0 +1,51 @@
import { SnippetType, IconStyle } from "../lib";
export function getCodeSnippets({
name,
displayName,
weight,
size,
color,
}: {
name: string;
displayName: string;
weight: IconStyle;
size: number;
color: string;
}): Record<SnippetType, string> {
const isDefaultWeight = weight === "regular";
const isDefaultColor = color === "#000000";
return {
[SnippetType.HTML]: `<i class="ph-${name}${
isDefaultWeight ? "" : `-${weight}`
}"></i>`,
[SnippetType.REACT]: `<${displayName} size={${size}} ${
!isDefaultColor ? `color="${color}" ` : ""
}${isDefaultWeight ? "" : `weight="${weight}" `}/>`,
[SnippetType.VUE]: `<ph${displayName
.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, "$1-$2")
.toLowerCase()} :size="${size}" ${
!isDefaultColor ? `color="${color}" ` : ""
}${isDefaultWeight ? "" : `weight="${weight}" `}/>`,
[SnippetType.FLUTTER]: `Icon(\n PhosphorIcons.${displayName.replace(
/^\w/,
(c) => c.toLowerCase()
)}${
isDefaultWeight ? "" : weight.replace(/^\w/, (c) => c.toUpperCase())
},\n size: ${size.toFixed(1)},\n${
!isDefaultColor ? ` color: Color(0xff${color.replace("#", "")}),\n` : ""
})`,
};
}
export function supportsWeight({
type,
weight,
}: {
type: SnippetType;
weight: IconStyle;
}): boolean {
if (type !== SnippetType.HTML && type !== SnippetType.FLUTTER) return true;
return weight !== IconStyle.DUOTONE;
}