suisuyy
Rename project to xpaintai_zh in package.json and metadata.json; integrate translation support in various components and hooks
69ae4e4 | import React from 'react'; | |
| import { UndoIcon, RedoIcon, UploadIcon, DownloadIcon, ClearIcon, BrushIcon, EraserIcon, MagicSparkleIcon, SettingsIcon } from './icons'; | |
| import { TFunction } from '../types'; | |
| 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; | |
| t: TFunction; | |
| } | |
| const Toolbar: React.FC<ToolbarProps> = ({ | |
| penColor, | |
| onColorChange, | |
| penSize, | |
| onSizeChange, | |
| onUndo, | |
| canUndo, | |
| onRedo, | |
| canRedo, | |
| onLoadImage, | |
| onExportImage, | |
| onClearCanvas, | |
| isEraserMode, | |
| onToggleEraser, | |
| onMagicUpload, | |
| isMagicUploading, | |
| canShare, | |
| onToggleSettings, | |
| t, | |
| }) => { | |
| 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={t('undo')} | |
| className="p-2 rounded-md hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed transition-colors border-gray-300" | |
| aria-label={t('undoAria')} | |
| > | |
| <UndoIcon className="w-5 h-5 text-gray-700" /> | |
| </button> | |
| <button | |
| onClick={onRedo} | |
| disabled={!canRedo} | |
| title={t('redo')} | |
| className="p-2 rounded-md hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed transition-colors border-gray-300" | |
| aria-label={t('redoAria')} | |
| > | |
| <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={t('loadImageAria')} | |
| /> | |
| <button | |
| onClick={handleUploadClick} | |
| title={t('loadImage')} | |
| className="p-2 rounded-md hover:bg-gray-100 transition-colors border-gray-300" | |
| aria-label={t('loadImageAria')} | |
| > | |
| <UploadIcon className="w-5 h-5 text-gray-700" /> | |
| </button> | |
| <button | |
| onClick={onExportImage} | |
| title={t('exportImage')} | |
| className="p-2 rounded-md hover:bg-gray-100 transition-colors border-gray-300" | |
| aria-label={t('exportImageAria')} | |
| > | |
| <DownloadIcon className="w-5 h-5 text-gray-700" /> | |
| </button> | |
| <button | |
| onClick={onMagicUpload} | |
| title={t('shareAndEditWithAI')} | |
| 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={t('shareAndEditWithAIAria')} | |
| > | |
| <MagicSparkleIcon className="w-5 h-5 text-purple-600" /> | |
| </button> | |
| <button | |
| onClick={onClearCanvas} | |
| title={t('clearCanvas')} | |
| className="p-2 bg-red-500 text-white rounded-md hover:bg-red-600 transition-colors tool-button" | |
| aria-label={t('clearCanvasAria')} | |
| > | |
| <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 ? t('switchToBrush') : t('switchToEraser')} | |
| 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 ? t('switchToBrushAria') : t('switchToEraserAria')} | |
| 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">{t('penColor')}:</label> | |
| <input | |
| type="color" | |
| id="penColor" | |
| title={t('penColor')} | |
| 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={t('selectPenColorAria')} | |
| /> | |
| </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">{t('penSize', { size: penSize })}:</label> | |
| <input | |
| type="range" | |
| id="penSize" | |
| title={t('penSize', { 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={t('penSizeAria', { size: penSize })} | |
| 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={t('openSettings')} | |
| className="p-2 rounded-md hover:bg-gray-100 transition-colors border-gray-300" | |
| aria-label={t('openSettings')} | |
| > | |
| <SettingsIcon className="w-5 h-5 text-gray-700" /> | |
| </button> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| export default Toolbar; | |