Skip to content

Commit

Permalink
rewrite svg-tool to use new upload pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
t3dotgg committed Nov 11, 2024
1 parent 677a9cd commit ae364e4
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 84 deletions.
13 changes: 1 addition & 12 deletions src/app/(tools)/svg-to-png/page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
import { SVGTool } from "./svg-tool";
import { FileDropzone } from "@/components/shared/file-dropzone";
import { FileProvider } from "@/components/providers/file-provider";

export const metadata = {
title: "SVG to PNG converter - QuickPic",
description: "Convert SVGs to PNGs. Also makes them bigger.",
};

export default function SVGToolPage() {
return (
<FileProvider>
<FileDropzone
acceptedFileTypes={["image/svg+xml", ".svg"]}
dropText="Drop SVG file"
>
<SVGTool />
</FileDropzone>
</FileProvider>
);
return <SVGTool />;
}
96 changes: 29 additions & 67 deletions src/app/(tools)/svg-to-png/svg-tool.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
"use client";
import { usePlausible } from "next-plausible";
import { useMemo, useState, useEffect } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import { useLocalStorage } from "@/hooks/use-local-storage";

import { type ChangeEvent } from "react";

import { UploadBox } from "@/components/shared/upload-box";
import { SVGScaleSelector } from "@/components/svg-scale-selector";

import { useFileState } from "@/lib/file-context";
import { createFileChangeEvent } from "@/lib/file-utils";

export type Scale = "custom" | number;

function scaleSvg(svgContent: string, scale: number) {
Expand Down Expand Up @@ -80,61 +75,20 @@ function useSvgConverter(props: {
};
}

// export const useFileUploader = () => {
// const [svgContent, setSvgContent] = useState<string>("");

// const [imageMetadata, setImageMetadata] = useState<{
// width: number;
// height: number;
// name: string;
// } | null>(null);

// const handleFileUpload = (event: ChangeEvent<HTMLInputElement>) => {
// const file = event.target.files?.[0];
// if (file) {
// const reader = new FileReader();
// reader.onload = (e) => {
// const content = e.target?.result as string;

// // Extract width and height from SVG content
// const parser = new DOMParser();
// const svgDoc = parser.parseFromString(content, "image/svg+xml");
// const svgElement = svgDoc.documentElement;
// const width = parseInt(svgElement.getAttribute("width") ?? "300");
// const height = parseInt(svgElement.getAttribute("height") ?? "150");

// setSvgContent(content);
// setImageMetadata({ width, height, name: file.name });
// };
// reader.readAsText(file);
// }
// };

// const cancel = () => {
// setSvgContent("");
// setImageMetadata(null);
// };

// return { svgContent, imageMetadata, handleFileUpload, cancel };
// };

import React from "react";
import { useFileUploader } from "@/hooks/use-file-uploader";

interface SVGRendererProps {
svgContent: string;
}

const SVGRenderer: React.FC<SVGRendererProps> = ({ svgContent }) => {
const containerRef = React.useRef<HTMLDivElement>(null);
const containerRef = useRef<HTMLDivElement>(null);

React.useEffect(() => {
useEffect(() => {
if (containerRef.current) {
containerRef.current.innerHTML = svgContent;
const svgElement = containerRef.current.querySelector("svg");
if (svgElement) {
svgElement.setAttribute("width", "100%");
svgElement.setAttribute("height", "auto");
svgElement.setAttribute("height", "100%");
}
}
}, [svgContent]);
Expand All @@ -151,9 +105,7 @@ function SaveAsPngButton({
scale: number;
imageMetadata: { width: number; height: number; name: string };
}) {
const [canvasRef, setCanvasRef] = React.useState<HTMLCanvasElement | null>(
null,
);
const [canvasRef, setCanvasRef] = useState<HTMLCanvasElement | null>(null);
const { convertToPng, canvasProps } = useSvgConverter({
canvas: canvasRef,
svgContent,
Expand All @@ -179,18 +131,15 @@ function SaveAsPngButton({
);
}

export function SVGTool() {
const { currentFile } = useFileState();
const { imageContent, imageMetadata, handleFileUpload, cancel } =
useFileUploader();
import {
type FileUploaderResult,
useFileUploader,
} from "@/hooks/use-file-uploader";
import { FileDropzone } from "@/components/shared/file-dropzone";

// React to file changes from drag and drop
useEffect(() => {
if (currentFile) {
const event = createFileChangeEvent(currentFile);
handleFileUpload(event);
}
}, [currentFile, handleFileUpload]);
function SVGToolCore(props: { fileUploaderProps: FileUploaderResult }) {
const { rawContent, imageMetadata, handleFileUploadEvent, cancel } =
props.fileUploaderProps;

const [scale, setScale] = useLocalStorage<Scale>("svgTool_scale", 1);
const [customScale, setCustomScale] = useLocalStorage<number>(
Expand All @@ -207,15 +156,15 @@ export function SVGTool() {
title="Make SVGs into PNGs. Also makes them bigger. (100% free btw.)"
description="Upload SVG"
accept=".svg"
onChange={handleFileUpload}
onChange={handleFileUploadEvent}
/>
);

return (
<div className="mx-auto flex max-w-2xl flex-col items-center justify-center gap-6 p-6">
{/* Preview Section */}
<div className="flex w-full flex-col items-center gap-4 rounded-xl p-6">
<SVGRenderer svgContent={imageContent} />
<SVGRenderer svgContent={rawContent} />
<p className="text-lg font-medium text-white/80">
{imageMetadata.name}
</p>
Expand Down Expand Up @@ -258,11 +207,24 @@ export function SVGTool() {
Cancel
</button>
<SaveAsPngButton
svgContent={imageContent}
svgContent={rawContent}
scale={effectiveScale}
imageMetadata={imageMetadata}
/>
</div>
</div>
);
}

export function SVGTool() {
const fileUploaderProps = useFileUploader();
return (
<FileDropzone
setCurrentFile={fileUploaderProps.handleFileUpload}
acceptedFileTypes={["image/svg+xml", ".svg"]}
dropText="Drop SVG file"
>
<SVGToolCore fileUploaderProps={fileUploaderProps} />
</FileDropzone>
);
}
4 changes: 2 additions & 2 deletions src/components/shared/file-dropzone.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
"use client";

import React, { useCallback, useState, useRef } from "react";
import { useFileState } from "@/lib/file-context";

interface FileDropzoneProps {
children: React.ReactNode;
acceptedFileTypes: string[];
dropText: string;
setCurrentFile: (file: File) => void;
}

export const FileDropzone: React.FC<FileDropzoneProps> = ({
children,
acceptedFileTypes,
dropText,
setCurrentFile,
}) => {
const [isDragging, setIsDragging] = useState(false);
const dragCounter = useRef(0);
const { setCurrentFile } = useFileState();

const handleDrag = useCallback((e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
Expand Down
14 changes: 11 additions & 3 deletions src/hooks/use-file-uploader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ export type FileUploaderResult = {
name: string;
} | null;
/** Handler for file input change events */
handleFileUpload: (event: ChangeEvent<HTMLInputElement>) => void;
handleFileUpload: (file: File) => void;
handleFileUploadEvent: (event: ChangeEvent<HTMLInputElement>) => void;
/** Resets the upload state */
cancel: () => void;
};
Expand Down Expand Up @@ -111,7 +112,7 @@ export const useFileUploader = (): FileUploaderResult => {
}
};

const handleFileUpload = (event: ChangeEvent<HTMLInputElement>) => {
const handleFileUploadEvent = (event: ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
processFile(file);
Expand All @@ -132,5 +133,12 @@ export const useFileUploader = (): FileUploaderResult => {
setImageMetadata(null);
};

return { imageContent, rawContent, imageMetadata, handleFileUpload, cancel };
return {
imageContent,
rawContent,
imageMetadata,
handleFileUpload: processFile,
handleFileUploadEvent,
cancel,
};
};

0 comments on commit ae364e4

Please sign in to comment.