From 50b603b525a501995fb622b2ad5e0aa96652c94f Mon Sep 17 00:00:00 2001 From: rektdeckard Date: Sun, 4 Oct 2020 23:15:51 -0400 Subject: [PATCH] InfoPanel: massive overhaul to support mobile size and PNG download This patch reworks the mobile breakpoint to allow whitespace wrapping of code snippets and does away with horizontal scroll. Overall, the usability and intuitiveness is much better, though readability of the code itself takes a hit. In addition, we added the ability to download an icon as a PNG thanks to the svg2png-converter library. PNGs adopt the current preview weight and color, and are sized at 256x256. --- package.json | 1 + src/components/App/App.css | 1 + src/components/IconGrid/IconGrid.css | 14 +-- src/components/IconGrid/InfoPanel.tsx | 145 +++++++++++++++++--------- 4 files changed, 106 insertions(+), 55 deletions(-) diff --git a/package.json b/package.json index 04bac2a..d074738 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "react-scripts": "3.4.1", "react-use": "^15.3.2", "recoil": "^0.0.10", + "svg2png-converter": "^1.0.0", "tinycolor2": "^1.4.1" }, "scripts": { diff --git a/src/components/App/App.css b/src/components/App/App.css index b80c244..2bb4400 100644 --- a/src/components/App/App.css +++ b/src/components/App/App.css @@ -28,6 +28,7 @@ pre { background-color: white; border-radius: 6px; border: 1px solid #e1d4d7; + white-space: pre-wrap; } input { diff --git a/src/components/IconGrid/IconGrid.css b/src/components/IconGrid/IconGrid.css index 4a85880..26c51aa 100644 --- a/src/components/IconGrid/IconGrid.css +++ b/src/components/IconGrid/IconGrid.css @@ -48,11 +48,9 @@ display: flex; width: 100%; height: 0px; - margin: 0px; + margin: 0 4px; border-radius: 16px; background-color: rgba(163, 159, 171, 0.1); - overflow-y: hidden; - overflow-x: auto; } @media screen and (max-width: 1023px) { @@ -90,8 +88,8 @@ } .snippet pre { - /* white-space: nowrap; */ - /* overflow: hidden; */ + display: flex; + align-items: center; text-overflow: ellipsis; color: black; user-select: all; @@ -111,12 +109,15 @@ } } +.snippet span { + flex: 1; +} + .snippet button { background-color: transparent; margin: 0; padding: 0; height: 24px; - float: right; cursor: pointer; } @@ -126,6 +127,7 @@ .button-row { display: flex; + flex-wrap: wrap; } .button-row button { diff --git a/src/components/IconGrid/InfoPanel.tsx b/src/components/IconGrid/InfoPanel.tsx index c56de80..7fb4e4a 100644 --- a/src/components/IconGrid/InfoPanel.tsx +++ b/src/components/IconGrid/InfoPanel.tsx @@ -1,6 +1,7 @@ import React, { useRef } from "react"; import { useRecoilValue, useSetRecoilState } from "recoil"; import { motion } from "framer-motion"; +import { Svg2Png } from "svg2png-converter"; import { saveAs } from "file-saver"; import { Icon, Copy, X, CheckCircle, Download } from "phosphor-react"; @@ -12,21 +13,32 @@ import { } from "../../state/atoms"; import useTransientState from "../../hooks/useTransientState"; -const infoVariants = { +const panelVariants = { open: { opacity: 1, - height: 496, - margin: 4, + height: "100%", + marginTop: 4, + marginBottom: 4, // transition: { stiffness: 600, damping: 32, duration: 0.2 }, }, collapsed: { opacity: 0, height: 0, - margin: 0, + marginTop: 0, + marginBottom: 0, // transition: { stiffness: 600, damping: 32, duration: 0.2 }, }, }; +const contentVariants = { + open: { opacity: 1, transition: { duration: 0.2 } }, + collapsed: { opacity: 0, transition: { duration: 0.1 } }, +}; + +const buttonColor = "#35313D"; +const successColor = "#1FA647"; +const disabledColor = "#B7B7B7"; + interface InfoPanelProps { index: number; spans: number; @@ -44,6 +56,12 @@ const InfoPanel: React.FC = (props) => { const [copied, setCopied] = useTransientState(false, 2000); const ref = useRef(null); + const buttonBarStyle = { color: isDark ? "white" : buttonColor }; + const snippetButtonStyle = + weight === "duotone" + ? { color: disabledColor, "user-select": "none" } + : { color: buttonColor }; + const snippets = { html: weight === "duotone" @@ -69,6 +87,14 @@ const InfoPanel: React.FC = (props) => { 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 ) => { @@ -78,12 +104,16 @@ const InfoPanel: React.FC = (props) => { saveAs(blob, `${name}${weight === "regular" ? "" : `-${weight}`}.svg`); }; - const handleCopySVG = ( + const handleDownloadPNG = async ( event: React.MouseEvent ) => { event.currentTarget.blur(); - setCopied("svg"); - ref.current && void navigator.clipboard?.writeText(ref.current.outerHTML); + if (!ref.current?.outerHTML) return; + Svg2Png.save( + ref.current, + `${name}${weight === "regular" ? "" : `-${weight}`}.png`, + { scaleX: 1.334, scaleY: 1.334 } + ); }; return ( @@ -91,31 +121,41 @@ const InfoPanel: React.FC = (props) => { className="info-box" animate="open" exit="collapsed" - variants={infoVariants} + variants={panelVariants} style={{ order: index + (spans - (index % spans)), color: isDark ? "white" : "black", }} > -
-
- -

{name}

-
-
-
+ + +

{name}

+
+
React
-            {snippets.react}
+            {snippets.react}
             
           
@@ -123,71 +163,78 @@ const InfoPanel: React.FC = (props) => {
Vue
-            {snippets.vue}
+            {snippets.vue}
             
           
HTML/CSS -
-            {snippets.html}
+          
+            {snippets.html}
             
           
- + -
-
- setOpen(false)} - onKeyDown={(e) => { - e.key === "Enter" && setOpen(false); - }} - /> +
+ + setOpen(false)} + onKeyDown={(e) => { + e.key === "Enter" && setOpen(false); + }} + /> + ); };