[dyad] Reordered panels for mobile view - wrote 1 file(s)
This commit is contained in:
@@ -402,7 +402,123 @@ export function ImageConverter() {
|
|||||||
return (
|
return (
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 w-full">
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 w-full">
|
||||||
<div className="lg:col-span-1 flex flex-col gap-4 lg:sticky lg:top-8 self-start">
|
<div className="lg:col-span-2 flex flex-col gap-4 lg:order-2">
|
||||||
|
<Card>
|
||||||
|
<CardContent className="pt-6">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h3 className="text-lg font-medium">Upload Images</h3>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"w-full h-48 rounded-lg border-2 border-dashed flex items-center justify-center relative transition-colors cursor-pointer hover:border-primary/60",
|
||||||
|
isDraggingOver ? "border-primary bg-accent" : "border-input"
|
||||||
|
)}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDragLeave={handleDragLeave}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
onClick={() => fileInputRef.current?.click()}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-center justify-center text-center text-muted-foreground">
|
||||||
|
<Upload className="w-8 h-8 mb-2" />
|
||||||
|
<p className="font-semibold">Click or drag and drop to upload</p>
|
||||||
|
<p className="text-xs text-muted-foreground mt-1">PNG, JPG, WEBP supported</p>
|
||||||
|
</div>
|
||||||
|
<Input type="file" ref={fileInputRef} onChange={handleImageChange} className="hidden" accept="image/*" multiple />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{hasImages && (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<CardTitle>Uploaded Images</CardTitle>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button variant="ghost" size="sm" onClick={handleClearAll} disabled={isConverting || convertingIndex !== null}><Trash2 className="mr-2 h-4 w-4" />Clear All</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Remove all uploaded images.</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button onClick={handleConvertAndDownloadAll} disabled={!hasImages || isConverting || convertingIndex !== null}>
|
||||||
|
<Download className="mr-2 h-4 w-4" />
|
||||||
|
{isConverting ? "Converting..." : `Download All (${images.length})`}
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Convert and download all images with the current settings.</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{previewUrls.map((url, index) => {
|
||||||
|
const finalFilename = generateFinalFilename(index);
|
||||||
|
return (
|
||||||
|
<div key={url} className="p-4 border rounded-lg flex items-center gap-4">
|
||||||
|
<img src={url} alt={`Preview ${index + 1}`} className="w-20 h-20 object-cover rounded-md shrink-0" />
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<Label htmlFor={`filename-${index}`} className="text-xs text-muted-foreground">Base Name</Label>
|
||||||
|
<Input
|
||||||
|
id={`filename-${index}`}
|
||||||
|
value={filenames[index]}
|
||||||
|
onChange={(e) => handleFilenameChange(index, e.target.value)}
|
||||||
|
className="text-sm font-medium h-8 mt-1"
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-muted-foreground truncate mt-1" title={`${finalFilename}.${format}`}>
|
||||||
|
Final name: {finalFilename}.{format}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center shrink-0">
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="text-gray-500 hover:text-primary"
|
||||||
|
onClick={() => handleConvertAndDownloadSingle(index)}
|
||||||
|
disabled={isConverting || convertingIndex !== null}
|
||||||
|
>
|
||||||
|
<Download className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Download this image</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="shrink-0 text-gray-500 hover:text-destructive"
|
||||||
|
onClick={() => handleRemoveImage(index)}
|
||||||
|
disabled={isConverting || convertingIndex !== null}
|
||||||
|
>
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Remove this image</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="lg:col-span-1 flex flex-col gap-4 lg:sticky lg:top-8 self-start lg:order-1">
|
||||||
<Accordion type="single" collapsible defaultValue="image-settings" className="w-full space-y-4">
|
<Accordion type="single" collapsible defaultValue="image-settings" className="w-full space-y-4">
|
||||||
<AccordionItem value="image-settings" className="border rounded-lg bg-card">
|
<AccordionItem value="image-settings" className="border rounded-lg bg-card">
|
||||||
<AccordionTrigger className="p-6 hover:no-underline">
|
<AccordionTrigger className="p-6 hover:no-underline">
|
||||||
@@ -767,122 +883,6 @@ export function ImageConverter() {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="lg:col-span-2 flex flex-col gap-4">
|
|
||||||
<Card>
|
|
||||||
<CardContent className="pt-6">
|
|
||||||
<div className="space-y-4">
|
|
||||||
<h3 className="text-lg font-medium">Upload Images</h3>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"w-full h-48 rounded-lg border-2 border-dashed flex items-center justify-center relative transition-colors cursor-pointer hover:border-primary/60",
|
|
||||||
isDraggingOver ? "border-primary bg-accent" : "border-input"
|
|
||||||
)}
|
|
||||||
onDragOver={handleDragOver}
|
|
||||||
onDragLeave={handleDragLeave}
|
|
||||||
onDrop={handleDrop}
|
|
||||||
onClick={() => fileInputRef.current?.click()}
|
|
||||||
>
|
|
||||||
<div className="flex flex-col items-center justify-center text-center text-muted-foreground">
|
|
||||||
<Upload className="w-8 h-8 mb-2" />
|
|
||||||
<p className="font-semibold">Click or drag and drop to upload</p>
|
|
||||||
<p className="text-xs text-muted-foreground mt-1">PNG, JPG, WEBP supported</p>
|
|
||||||
</div>
|
|
||||||
<Input type="file" ref={fileInputRef} onChange={handleImageChange} className="hidden" accept="image/*" multiple />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{hasImages && (
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<CardTitle>Uploaded Images</CardTitle>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button variant="ghost" size="sm" onClick={handleClearAll} disabled={isConverting || convertingIndex !== null}><Trash2 className="mr-2 h-4 w-4" />Clear All</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>Remove all uploaded images.</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button onClick={handleConvertAndDownloadAll} disabled={!hasImages || isConverting || convertingIndex !== null}>
|
|
||||||
<Download className="mr-2 h-4 w-4" />
|
|
||||||
{isConverting ? "Converting..." : `Download All (${images.length})`}
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>Convert and download all images with the current settings.</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="space-y-4">
|
|
||||||
{previewUrls.map((url, index) => {
|
|
||||||
const finalFilename = generateFinalFilename(index);
|
|
||||||
return (
|
|
||||||
<div key={url} className="p-4 border rounded-lg flex items-center gap-4">
|
|
||||||
<img src={url} alt={`Preview ${index + 1}`} className="w-20 h-20 object-cover rounded-md shrink-0" />
|
|
||||||
<div className="flex-1 min-w-0">
|
|
||||||
<Label htmlFor={`filename-${index}`} className="text-xs text-muted-foreground">Base Name</Label>
|
|
||||||
<Input
|
|
||||||
id={`filename-${index}`}
|
|
||||||
value={filenames[index]}
|
|
||||||
onChange={(e) => handleFilenameChange(index, e.target.value)}
|
|
||||||
className="text-sm font-medium h-8 mt-1"
|
|
||||||
/>
|
|
||||||
<p className="text-xs text-muted-foreground truncate mt-1" title={`${finalFilename}.${format}`}>
|
|
||||||
Final name: {finalFilename}.{format}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center shrink-0">
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className="text-gray-500 hover:text-primary"
|
|
||||||
onClick={() => handleConvertAndDownloadSingle(index)}
|
|
||||||
disabled={isConverting || convertingIndex !== null}
|
|
||||||
>
|
|
||||||
<Download className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>Download this image</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className="shrink-0 text-gray-500 hover:text-destructive"
|
|
||||||
onClick={() => handleRemoveImage(index)}
|
|
||||||
disabled={isConverting || convertingIndex !== null}
|
|
||||||
>
|
|
||||||
<X className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>Remove this image</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user