From 6761716dca320f519d56ff0e168f2ea25331f621 Mon Sep 17 00:00:00 2001 From: pheralb Date: Mon, 25 Aug 2025 14:24:44 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20Create=20clipboard,=20down?= =?UTF-8?q?load,=20downloadSvg=20&=20parse=20svg=20utilities?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/clipboard.ts | 3 ++ src/utils/download.ts | 23 ++++++++ src/utils/downloadSvg.ts | 102 +++++++++++++++++++++++++++++++++++ src/utils/parseSvgContent.ts | 27 ++++++++++ src/utils/prefixSvgIds.ts | 20 +++++++ 5 files changed, 175 insertions(+) create mode 100644 src/utils/clipboard.ts create mode 100644 src/utils/download.ts create mode 100644 src/utils/downloadSvg.ts create mode 100644 src/utils/parseSvgContent.ts create mode 100644 src/utils/prefixSvgIds.ts diff --git a/src/utils/clipboard.ts b/src/utils/clipboard.ts new file mode 100644 index 0000000..eb9ec4b --- /dev/null +++ b/src/utils/clipboard.ts @@ -0,0 +1,3 @@ +export const clipboard = (content: string) => { + navigator.clipboard.writeText(content); +}; diff --git a/src/utils/download.ts b/src/utils/download.ts new file mode 100644 index 0000000..2271f13 --- /dev/null +++ b/src/utils/download.ts @@ -0,0 +1,23 @@ +type MimeType = "image/svg+xml" | "application/zip"; + +interface Download { + content: string | Blob; + filename: string; + mimeType: MimeType; +} + +export const download = ({ content, filename, mimeType }: Download) => { + const blob = + typeof content === "string" + ? new Blob([content], { type: mimeType }) + : content; + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = filename; + a.style.display = "none"; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); +}; diff --git a/src/utils/downloadSvg.ts b/src/utils/downloadSvg.ts new file mode 100644 index 0000000..d326f34 --- /dev/null +++ b/src/utils/downloadSvg.ts @@ -0,0 +1,102 @@ +import type { iSVG } from "@/types/svg"; + +import JSZip from "jszip"; +import { download } from "@/utils/download"; +import { getSource } from "@/templates/getSource"; +import { getPrefixFromSvgUrl, prefixSvgIds } from "@/utils/prefixSvgIds"; + +interface DownloadResult { + success: boolean; + message: string; +} + +interface DownloadSvg { + url: string; +} + +interface DownloadAllVariants { + svgInfo: iSVG; + lightRoute: string; + darkRoute: string; + isWordmark?: boolean; +} + +export const downloadSvg = async ({ + url, +}: DownloadSvg): Promise => { + try { + let content = await getSource({ + url: url, + }); + if (url) { + content = prefixSvgIds(content, getPrefixFromSvgUrl(url)); + } + download({ + content: content || "", + filename: url?.split("/").pop() || "", + mimeType: "image/svg+xml", + }); + return { + success: true, + message: "SVG downloaded successfully", + }; + } catch (error) { + console.error("❌ utils/downloadSvg - Error downloading SVG:", error); + return { + success: false, + message: "Error downloading SVG", + }; + } +}; + +export const downloadAllVariants = async ({ + svgInfo, + lightRoute, + darkRoute, + isWordmark, +}: DownloadAllVariants): Promise => { + try { + const zip = new JSZip(); + + let lightSvg = await getSource({ + url: lightRoute, + }); + let darkSvg = await getSource({ + url: darkRoute, + }); + + lightSvg = prefixSvgIds( + lightSvg, + svgInfo.title.toLowerCase() + (isWordmark ? "_wordmark_light" : "_light"), + ); + darkSvg = prefixSvgIds( + darkSvg, + svgInfo.title.toLowerCase() + (isWordmark ? "_wordmark_dark" : "_dark"), + ); + + if (isWordmark) { + zip.file(`${svgInfo.title}_wordmark_light.svg`, lightSvg); + zip.file(`${svgInfo.title}_wordmark_dark.svg`, darkSvg); + } else { + zip.file(`${svgInfo.title}_light.svg`, lightSvg); + zip.file(`${svgInfo.title}_dark.svg`, darkSvg); + } + + zip.generateAsync({ type: "blob" }).then((content) => { + download({ + content, + filename: isWordmark + ? `${svgInfo.title}_wordmark_light_dark.zip` + : `${svgInfo.title}_light_dark.zip`, + mimeType: "application/zip", + }); + }); + return true; + } catch (error) { + console.error( + "❌ utils/downloadSvg - Error downloading all variants:", + error, + ); + return false; + } +}; diff --git a/src/utils/parseSvgContent.ts b/src/utils/parseSvgContent.ts new file mode 100644 index 0000000..3cf57a2 --- /dev/null +++ b/src/utils/parseSvgContent.ts @@ -0,0 +1,27 @@ +export const parseSvgContent = ( + content: string, + framework: "Vue" | "Svelte", +) => { + if (content.includes("]*\?>/i, ""); + } + + const styleTagRegex = /]*>([\s\S]*?)<\/style>/gi; + + const styles = []; + let matched; + while ((matched = styleTagRegex.exec(content)) !== null) { + styles.push(matched[1]); + } + + const templateContent = content.replace(styleTagRegex, ""); + + const componentStyle = styles.length + ? `\n${styles.join("\n")}\n` + : ""; + + return { + componentStyle, + templateContent, + }; +}; diff --git a/src/utils/prefixSvgIds.ts b/src/utils/prefixSvgIds.ts new file mode 100644 index 0000000..7693c50 --- /dev/null +++ b/src/utils/prefixSvgIds.ts @@ -0,0 +1,20 @@ +import { optimize } from "svgo"; + +export const getPrefixFromSvgUrl = (svgUrl: string) => { + return svgUrl.split("/").pop()!.replace(".svg", "").split("-").join("_"); +}; + +export const prefixSvgIds = (content: string, prefix: string): string => { + const result = optimize(content, { + plugins: [ + { + name: "prefixIds", + params: { + prefix, + }, + }, + ], + multipass: false, + }); + return (result as { data: string }).data; +};