feat(app): add copy unicode button

This commit is contained in:
rektdeckard
2024-01-10 01:06:34 -07:00
committed by Tobias Fried
parent 7502b8b3ce
commit 697c6c836c
8 changed files with 73 additions and 41 deletions

View File

@@ -8,7 +8,7 @@ import IconGrid from "@/components/IconGrid";
import Footer from "@/components/Footer";
import ErrorBoundary from "@/components/ErrorBoundary";
import Notice from "@/components/Notice";
import Recipes from "@/components/Recipes";
// import Recipes from "@/components/Recipes";
import { useCSSVariables } from "@/hooks";
import { isDarkThemeSelector } from "@/state";

View File

@@ -10,7 +10,7 @@
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
max-width: 1120px;
max-width: 1152px;
margin: auto;
}

View File

@@ -64,10 +64,15 @@ const IconGrid = (_: IconGridProps) => {
<IconContext.Provider value={{ weight, size, color, mirrored: false }}>
<div className="grid-container">
<i id="beacon" className="beacon" />
<motion.div className="grid" initial="hidden" animate={controls}>
<motion.div
key={query}
className="grid"
initial="hidden"
animate={controls}
>
{filteredQueryResults.map((iconEntry, index) => (
<IconGridItem
key={index}
key={iconEntry.name}
index={index}
isDark={isDark}
entry={iconEntry}

View File

@@ -20,7 +20,7 @@ interface IconGridItemProps extends HTMLAttributes<HTMLDivElement> {
const transition = { duration: 0.2 };
const originIndex = 0;
const delayPerPixel = 0.0004;
const delayPerPixel = 0.0003;
const itemVariants = {
hidden: { opacity: 0 },
@@ -68,30 +68,28 @@ const IconGridItem = (props: IconGridItemProps) => {
}, [originOffset]);
return (
<>
<motion.div
className="grid-item"
key={name}
ref={ref}
tabIndex={0}
style={{
...style,
backgroundColor: isOpen ? "var(--background-layer)" : undefined,
}}
custom={delayRef}
transition={transition}
variants={itemVariants}
onKeyPress={(e) => e.key === "Enter" && handleOpen()}
onClick={handleOpen}
>
<Icon />
<p>
<span className="name">{name}</span>
{isNew && <span className="badge new"></span>}
{isUpdated && <span className="badge updated"></span>}
</p>
</motion.div>
</>
<motion.div
className="grid-item"
key={name}
ref={ref}
tabIndex={0}
style={{
...style,
backgroundColor: isOpen ? "var(--background-layer)" : undefined,
}}
custom={delayRef}
transition={transition}
variants={itemVariants}
onKeyPress={(e) => e.key === "Enter" && handleOpen()}
onClick={handleOpen}
>
<Icon />
<p>
<span className="name">{name}</span>
{isNew && <span className="badge new"></span>}
{isUpdated && <span className="badge updated"></span>}
</p>
</motion.div>
);
};

View File

@@ -18,6 +18,7 @@ import {
CaretDoubleLeft,
CaretDoubleRight,
} from "@phosphor-icons/react";
import { IconStyle } from "@phosphor-icons/core";
import ReactGA from "react-ga4";
import Tabs, { Tab } from "@/components/Tabs";
@@ -61,6 +62,7 @@ enum CopyType {
SVG_DATA,
PNG,
PNG_DATA,
UNICODE,
}
function cloneWithSize(svg: SVGSVGElement, size: number): SVGSVGElement {
@@ -75,12 +77,18 @@ const ActionButton = (
active?: boolean;
label: string;
download?: boolean;
disabled?: boolean;
} & HTMLAttributes<HTMLButtonElement>
) => {
const { active, download, label, ...rest } = props;
const Icon = download ? ArrowFatLinesDown : Copy;
return (
<button {...rest} className="action-button text" tabIndex={0}>
<button
{...rest}
className={`action-button text ${props.disabled ? "disabled" : ""}`}
aria-disabled={props.disabled}
tabIndex={0}
>
{active ? (
<CheckCircle size={20} color="var(--olive)" weight="fill" />
) : (
@@ -246,6 +254,14 @@ const Panel = () => {
setCopied(CopyType.SVG_RAW);
};
const handleCopyUnicode = async () => {
if (!entry) return;
const content = String.fromCharCode(entry.codepoint);
navigator.clipboard?.writeText(content);
setCopied(CopyType.UNICODE);
};
const handleDownloadSVG = (
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
) => {
@@ -335,6 +351,9 @@ const Panel = () => {
<entry.Icon ref={ref} size={64}></entry.Icon>
<figcaption>
<p>{entry.name}</p>
<small className="versioning">
U+{entry.codepoint.toString(16).toUpperCase()}
</small>
<small className="versioning">
available in v{entry.published_in.toFixed(1)}.0+
</small>
@@ -395,6 +414,14 @@ const Panel = () => {
active={copied === CopyType.SVG_DATA}
onClick={handleCopyDataSVG}
/>
<ActionButton
label="Unicode"
title="Copy Unicode character (v2.1.0 or newer)"
active={copied === CopyType.UNICODE}
disabled={weight === IconStyle.DUOTONE}
onClick={handleCopyUnicode}
/>
</>
)}
</div>

View File

@@ -42,6 +42,7 @@ export default ({ children }: { children: ReactNode }) => {
const listen: ListenToItems = useCallback(
({ updateItem, updateAllKnownItems }) => {
void updateAllKnownItems;
const onStorage = (event: StorageEvent) => {
// ignore clear() calls
if (event.storageArea === localStorage && event.key !== null) {