xpaintdev / components /Toolbar.tsx
suisuyy
Initialize xpaintai project with core files and basic structure
763be49
import React from 'react';
import { UndoIcon, RedoIcon, UploadIcon, DownloadIcon, ClearIcon, BrushIcon, EraserIcon, MagicSparkleIcon, SettingsIcon } from './icons';
interface ToolbarProps {
penColor: string;
onColorChange: (color: string) => void;
penSize: number;
onSizeChange: (size: number) => void;
onUndo: () => void;
canUndo: boolean;
onRedo: () => void;
canRedo: boolean;
onLoadImage: (event: React.ChangeEvent<HTMLInputElement>) => void;
onExportImage: () => void;
onClearCanvas: () => void;
isEraserMode: boolean;
onToggleEraser: () => void;
onMagicUpload: () => void;
isMagicUploading: boolean;
canShare: boolean;
onToggleSettings: () => void; // New prop for settings
}
const Toolbar: React.FC<ToolbarProps> = ({
penColor,
onColorChange,
penSize,
onSizeChange,
onUndo,
canUndo,
onRedo,
canRedo,
onLoadImage,
onExportImage,
onClearCanvas,
isEraserMode,
onToggleEraser,
onMagicUpload,
isMagicUploading,
canShare,
onToggleSettings, // New prop
}) => {
const fileInputRef = React.useRef<HTMLInputElement>(null);
const handleUploadClick = () => {
fileInputRef.current?.click();
};
return (
<div className="bg-slate-200 p-3 shadow-lg flex flex-col items-start justify-center gap-y-2 sticky top-0 z-50 border-b border-slate-300 w-full">
{/* First Row: Action Buttons */}
<div className="flex flex-wrap items-center justify-start gap-x-3 gap-y-2">
<button
onClick={onUndo}
disabled={!canUndo}
title="Undo"
className="p-2 rounded-md hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed transition-colors border-gray-300"
aria-label="Undo last action"
>
<UndoIcon className="w-5 h-5 text-gray-700" />
</button>
<button
onClick={onRedo}
disabled={!canRedo}
title="Redo"
className="p-2 rounded-md hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed transition-colors border-gray-300"
aria-label="Redo last undone action"
>
<RedoIcon className="w-5 h-5 text-gray-700" />
</button>
<input
type="file"
ref={fileInputRef}
onChange={onLoadImage}
accept="image/png, image/jpeg"
className="hidden"
aria-label="Load image from file"
/>
<button
onClick={handleUploadClick}
title="Load Image"
className="p-2 rounded-md hover:bg-gray-100 transition-colors border-gray-300"
aria-label="Load image"
>
<UploadIcon className="w-5 h-5 text-gray-700" />
</button>
<button
onClick={onExportImage}
title="Export Image"
className="p-2 rounded-md hover:bg-gray-100 transition-colors border-gray-300"
aria-label="Export canvas as image"
>
<DownloadIcon className="w-5 h-5 text-gray-700" />
</button>
<button
onClick={onMagicUpload}
title="Share Canvas & Edit with AI"
disabled={isMagicUploading || !canShare}
className="p-2 rounded-md hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed transition-colors border-gray-300"
aria-label="Share canvas by uploading and open AI edit options"
>
<MagicSparkleIcon className="w-5 h-5 text-purple-600" />
</button>
<button
onClick={onClearCanvas}
title="Clear Canvas"
className="p-2 bg-red-500 text-white rounded-md hover:bg-red-600 transition-colors tool-button"
aria-label="Clear canvas"
>
<ClearIcon className="w-5 h-5" />
</button>
</div>
{/* Second Row: Drawing Tools & Settings */}
<div className="flex flex-wrap items-center justify-start gap-x-3 gap-y-2 mt-2">
<button
onClick={onToggleEraser}
title={isEraserMode ? "Switch to Brush" : "Switch to Eraser"}
className={`p-2 rounded-md transition-colors ${isEraserMode ? 'bg-blue-500 text-white hover:bg-blue-600' : ' hover:bg-gray-100 text-gray-700 border-gray-300'}`}
aria-label={isEraserMode ? "Switch to Brush mode" : "Switch to Eraser mode"}
aria-pressed={isEraserMode}
>
{isEraserMode ? <BrushIcon className="w-5 h-5" /> : <EraserIcon className="w-5 h-5" />}
</button>
<div className="flex items-center gap-2 p-1 rounded-md hover:bg-slate-300 transition-colors">
<label htmlFor="penColor" className="text-sm font-medium text-gray-700 sr-only">Color:</label>
<input
type="color"
id="penColor"
title="Pen Color"
value={penColor}
onChange={(e) => onColorChange(e.target.value)}
className={`w-8 h-8 rounded-full cursor-pointer border-2 ${isEraserMode ? 'border-gray-400 opacity-50' : 'border-white shadow-sm'}`}
disabled={isEraserMode}
aria-label="Select pen color"
/>
</div>
<div className="flex items-center gap-2 p-1 rounded-md hover:bg-slate-300 transition-colors">
<label htmlFor="penSize" className="text-sm font-medium text-gray-700 sr-only">Size:</label>
<input
type="range"
id="penSize"
title={`Pen Size: ${penSize}`}
min="1"
max="50"
value={penSize}
onChange={(e) => onSizeChange(parseInt(e.target.value, 10))}
className="w-24 md:w-32 cursor-pointer accent-blue-600"
aria-label={`Pen size ${penSize} pixels`}
aria-valuemin={1}
aria-valuemax={50}
aria-valuenow={penSize}
/>
<span className="text-xs text-gray-600 w-6 text-right" aria-hidden="true">{penSize}</span>
</div>
<button
onClick={onToggleSettings}
title="Open Settings"
className="p-2 rounded-md hover:bg-gray-100 transition-colors border-gray-300"
aria-label="Open application settings"
>
<SettingsIcon className="w-5 h-5 text-gray-700" />
</button>
</div>
</div>
);
};
export default Toolbar;