"use client"; import React, { useState, useCallback } from "react"; import Cropper from "react-easy-crop"; const RATIOS = [ { label: "Hero Banner (Ultra Wide)", value: 21 / 9, text: "21/9" }, { label: "News Feed (Widescreen)", value: 16 / 9, text: "16/9" }, { label: "Profile / Post (Square)", value: 1 / 1, text: "1/1" }, ]; export default function FullPageEditor() { const [image, setImage] = useState(null); const [crops, setCrops] = useState>( RATIOS.reduce((acc, r) => ({ ...acc, [r.text]: { x: 0, y: 0, zoom: 1 } }), {}) ); const [completedCrops, setCompletedCrops] = useState>({}); const [isExporting, setIsExporting] = useState(false); const [results, setResults] = useState>({}); const onSelectFile = (e: React.ChangeEvent) => { if (e.target.files && e.target.files.length > 0) { const reader = new FileReader(); reader.onload = () => setImage(reader.result as string); reader.readAsDataURL(e.target.files[0]); } }; const handleExport = async () => { setIsExporting(true); const bundle: Record = {}; for (const ratio of RATIOS) { const crop = completedCrops[ratio.text]; if (crop) bundle[ratio.text] = await getCroppedImg(image!, crop); } setResults(bundle); setIsExporting(false); }; return (
{/* 1. Global Navigation */} {/* 2. Workspace Area */}
{!image ? (
📁

Editor is Empty

Upload a high-resolution image to begin the multi-aspect framing process.

) : (
{RATIOS.map((ratio) => (
{/* Section Header */}
Aspect Ratio

{ratio.label}

Current Zoom

{Math.round((crops[ratio.text]?.zoom || 1) * 100)}%

{/* BIG Editor Window */}
setCrops(prev => ({ ...prev, [ratio.text]: { ...prev[ratio.text], ...c } }))} onZoomChange={(z) => setCrops(prev => ({ ...prev, [ratio.text]: { ...prev[ratio.text], zoom: z } }))} onCropComplete={(_, px) => setCompletedCrops(prev => ({ ...prev, [ratio.text]: px }))} objectFit="cover" showGrid={true} />
{/* Floating Zoom Control (Large & Accessible) */}
Adjust Zoom setCrops(prev => ({ ...prev, [ratio.text]: { ...prev[ratio.text], zoom: Number(e.target.value) } }))} className="flex-1 accent-indigo-500 h-2 bg-zinc-800 rounded-lg appearance-none cursor-pointer" />
))}
)}
{/* 3. Global Results Modal/Area */} {Object.keys(results).length > 0 && (

EXPORT BUNDLE

{Object.entries(results).map(([ratio, b64]) => (
{ratio} Result
Crop preview