Skip to content

Commit

Permalink
fix: Re-Implementation for t3dotgg#23
Browse files Browse the repository at this point in the history
  • Loading branch information
philipbrembeck committed Nov 10, 2024
1 parent 45fb14c commit 9699f54
Show file tree
Hide file tree
Showing 4 changed files with 378 additions and 452 deletions.
276 changes: 111 additions & 165 deletions src/app/(tools)/rounded-border/rounded-tool.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
"use client";
import { useFileUpload } from "@/app/hooks/useFileUpload";
import { usePlausible } from "next-plausible";
import { useMemo, useState, DragEvent } from "react";
import { ChangeEvent } from "react";
import { useMemo, useState } from "react";
import React from "react";

type Radius = 2 | 4 | 8 | 16 | 32 | 64;

type BackgroundOption = "white" | "black" | "transparent";

function useImageConverter(props: {
Expand Down Expand Up @@ -68,77 +67,6 @@ function useImageConverter(props: {
};
}

export const useFileUploader = () => {
const [imageContent, setImageContent] = useState<string>("");
const [isDragging, setIsDragging] = useState(false);
const [imageMetadata, setImageMetadata] = useState<{
width: number;
height: number;
name: string;
} | null>(null);

const processImageFile = (file: File) => {
const reader = new FileReader();
reader.onload = (e) => {
const content = e.target?.result as string;
const img = new Image();
img.onload = () => {
setImageMetadata({
width: img.width,
height: img.height,
name: file.name,
});
setImageContent(content);
};
img.src = content;
};
reader.readAsDataURL(file);
};

const handleFileUpload = (event: ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
processImageFile(file);
}
};

const handleDragOver = (event: DragEvent<HTMLDivElement>) => {
event.preventDefault();
setIsDragging(true);
};

const handleDragLeave = (event: DragEvent<HTMLDivElement>) => {
event.preventDefault();
setIsDragging(false);
};

const handleDrop = (event: DragEvent<HTMLDivElement>) => {
event.preventDefault();
setIsDragging(false);

const file = event.dataTransfer.files[0];
if (file) {
processImageFile(file);
}
};

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

return {
imageContent,
imageMetadata,
handleFileUpload,
cancel,
isDragging,
handleDragOver,
handleDragLeave,
handleDrop,
};
};

interface ImageRendererProps {
imageContent: string;
radius: Radius;
Expand Down Expand Up @@ -218,103 +146,121 @@ function SaveAsPngButton({
}

export function RoundedTool() {
const {
imageContent,
imageMetadata,
handleFileUpload,
cancel,
isDragging,
handleDragOver,
handleDragLeave,
handleDrop,
} = useFileUploader();

const [imageContent, setImageContent] = useState<string>("");
const [imageMetadata, setImageMetadata] = useState<{
width: number;
height: number;
name: string;
} | null>(null);
const [radius, setRadius] = useState<Radius>(2);
const [background, setBackground] = useState<BackgroundOption>("transparent");

if (!imageMetadata)
return (
<div
className={`flex flex-col p-8 gap-4 rounded-lg transition-colors ${
isDragging && "border-2 border-dashed"
}`}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
>
<p className="text-center">Round the corners of any image</p>
<p className="text-center">Drag and drop your image here, or</p>
<div className="flex justify-center">
<label className="cursor-pointer inline-flex items-center px-4 py-2 bg-blue-600 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-75 transition-colors duration-200 gap-2">
<span>Upload Image</span>
<input
type="file"
onChange={handleFileUpload}
accept="image/*"
className="hidden"
/>
</label>
</div>
</div>
);
const processImage = (file: File) => {
const reader = new FileReader();
reader.onload = (e) => {
const content = e.target?.result as string;
const img = new Image();
img.onload = () => {
setImageMetadata({
width: img.width,
height: img.height,
name: file.name,
});
setImageContent(content);
};
img.src = content;
};
reader.readAsDataURL(file);
};

const { handleFileUpload } = useFileUpload({
onFileProcess: processImage,
acceptedTypes: "image/*",
});

const handleCancel = () => {
setImageContent("");
setImageMetadata(null);
};

return (
<div className="flex flex-col p-4 gap-4 justify-center items-center text-2xl">
<ImageRenderer
imageContent={imageContent}
radius={radius}
background={background}
/>
<p>{imageMetadata.name}</p>
<p>
Original size: {imageMetadata.width}px x {imageMetadata.height}px
</p>
<div className="flex gap-2">
{([2, 4, 8, 16, 32, 64] as Radius[]).map((value) => (
<button
key={value}
onClick={() => setRadius(value)}
className={`px-3 py-1 rounded-md text-sm font-medium transition-colors ${
radius === value
? "bg-green-600 text-white"
: "bg-gray-200 text-gray-800 hover:bg-gray-300"
}`}
>
{value}px
</button>
))}
</div>
<div className="flex gap-2">
{(["white", "black", "transparent"] as BackgroundOption[]).map(
(option) => (
<button
key={option}
onClick={() => setBackground(option)}
className={`px-3 py-1 rounded-md text-sm font-medium transition-colors ${
background === option
? "bg-purple-600 text-white"
: "bg-gray-200 text-gray-800 hover:bg-gray-300"
}`}
>
{option.charAt(0).toUpperCase() + option.slice(1)}
</button>
)
<>
<div className="flex flex-col p-8 gap-4">
<p className="text-center">Round the corners of any image</p>
{!imageMetadata ? (
<>
<div className="flex justify-center">
<label className="cursor-pointer inline-flex items-center px-4 py-2 bg-blue-600 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-75 transition-colors duration-200 gap-2">
<span>Upload Image</span>
<input
type="file"
onChange={handleFileUpload}
accept="image/*"
className="hidden"
/>
</label>
</div>
</>
) : (
<div className="flex flex-col gap-4 justify-center items-center text-2xl">
<ImageRenderer
imageContent={imageContent}
radius={radius}
background={background}
/>
<p>{imageMetadata.name}</p>
<p>
Original size: {imageMetadata.width}px x {imageMetadata.height}px
</p>
<div className="flex gap-2">
{([2, 4, 8, 16, 32, 64] as Radius[]).map((value) => (
<button
key={value}
onClick={() => setRadius(value)}
className={`px-3 py-1 rounded-md text-sm font-medium transition-colors ${
radius === value
? "bg-green-600 text-white"
: "bg-gray-200 text-gray-800 hover:bg-gray-300"
}`}
>
{value}px
</button>
))}
</div>
<div className="flex gap-2">
{(["white", "black", "transparent"] as BackgroundOption[]).map(
(option) => (
<button
key={option}
onClick={() => setBackground(option)}
className={`px-3 py-1 rounded-md text-sm font-medium transition-colors ${
background === option
? "bg-purple-600 text-white"
: "bg-gray-200 text-gray-800 hover:bg-gray-300"
}`}
>
{option.charAt(0).toUpperCase() + option.slice(1)}
</button>
)
)}
</div>
<div className="flex gap-2">
<SaveAsPngButton
imageContent={imageContent}
radius={radius}
background={background}
imageMetadata={imageMetadata}
/>
<button
onClick={handleCancel}
className="px-3 py-1 rounded-md text-sm font-medium bg-red-700 text-white hover:bg-red-800 transition-colors"
>
Cancel
</button>
</div>
</div>
)}
</div>
<div className="flex gap-2">
<SaveAsPngButton
imageContent={imageContent}
radius={radius}
background={background}
imageMetadata={imageMetadata}
/>
<button
onClick={cancel}
className="px-3 py-1 rounded-md text-sm font-medium bg-red-700 text-white hover:bg-red-800 transition-colors"
>
Cancel
</button>
</div>
</div>
</>
);
}
Loading

0 comments on commit 9699f54

Please sign in to comment.