refactor(ui): clean up snippets code
This commit is contained in:
@@ -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 (
|
||||||
|
<div className="snippet" key={type}>
|
||||||
|
{type}
|
||||||
|
<pre
|
||||||
|
tabIndex={0}
|
||||||
|
style={isWeightSupported ? undefined : snippetButtonStyle}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{isWeightSupported
|
||||||
|
? snippets[type]
|
||||||
|
: "This weight is not yet supported"}
|
||||||
|
</span>
|
||||||
<button
|
<button
|
||||||
title="Copy snippet"
|
title="Copy snippet"
|
||||||
onClick={(e) => handleCopySnippet(e, "react")}
|
onClick={(e) => handleCopySnippet(e, type)}
|
||||||
|
disabled={!isWeightSupported}
|
||||||
|
style={isWeightSupported ? undefined : snippetButtonStyle}
|
||||||
>
|
>
|
||||||
{copied === "react" ? (
|
{copied === type ? (
|
||||||
<CheckCircle size={24} color={successColor} weight="fill" />
|
|
||||||
) : (
|
|
||||||
<Copy size={24} color={buttonColor} weight="fill" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
<div className="snippet">
|
|
||||||
Vue
|
|
||||||
<pre tabIndex={0}>
|
|
||||||
<span>{snippets.vue}</span>
|
|
||||||
<button
|
|
||||||
title="Copy snippet"
|
|
||||||
onClick={(e) => handleCopySnippet(e, "vue")}
|
|
||||||
>
|
|
||||||
{copied === "vue" ? (
|
|
||||||
<CheckCircle size={24} color={successColor} weight="fill" />
|
|
||||||
) : (
|
|
||||||
<Copy size={24} color={buttonColor} weight="fill" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
<div className="snippet">
|
|
||||||
HTML/CSS
|
|
||||||
<pre tabIndex={0} style={snippetButtonStyle}>
|
|
||||||
<span>{snippets.html}</span>
|
|
||||||
<button
|
|
||||||
title="Copy snippet"
|
|
||||||
onClick={(e) => handleCopySnippet(e, "html")}
|
|
||||||
disabled={weight === "duotone"}
|
|
||||||
style={snippetButtonStyle}
|
|
||||||
>
|
|
||||||
{copied === "html" ? (
|
|
||||||
<CheckCircle size={24} color={successColor} weight="fill" />
|
<CheckCircle size={24} color={successColor} weight="fill" />
|
||||||
) : (
|
) : (
|
||||||
<Copy
|
<Copy
|
||||||
size={24}
|
size={24}
|
||||||
color={snippetButtonStyle.color}
|
color={
|
||||||
|
isWeightSupported
|
||||||
|
? buttonColor
|
||||||
|
: snippetButtonStyle.color
|
||||||
|
}
|
||||||
weight="fill"
|
weight="fill"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</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>
|
||||||
|
|||||||
@@ -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
51
src/utils/index.ts
Normal 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user