169 lines
5.3 KiB
TypeScript
169 lines
5.3 KiB
TypeScript
import React from 'react';
|
|
|
|
interface ToolbarProps {
|
|
zoom: number;
|
|
onZoomChange: (zoom: number) => void;
|
|
rotation: number;
|
|
onRotationChange: (rot: number) => void;
|
|
activeTool: string;
|
|
onActiveToolChange: (tool: string) => void;
|
|
currentPage: number;
|
|
totalPages: number;
|
|
onUploadStart?: (file: File) => void;
|
|
backendHealthy: boolean | null;
|
|
wasmEngineInfo?: string;
|
|
wasmHasSkia?: boolean;
|
|
}
|
|
|
|
export const Toolbar: React.FC<ToolbarProps> = ({
|
|
zoom,
|
|
onZoomChange,
|
|
rotation,
|
|
onRotationChange,
|
|
activeTool,
|
|
onActiveToolChange,
|
|
currentPage,
|
|
totalPages,
|
|
onUploadStart,
|
|
backendHealthy,
|
|
wasmEngineInfo,
|
|
wasmHasSkia,
|
|
}) => {
|
|
|
|
const handleZoomPercentSelect = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
|
const val = parseFloat(e.target.value);
|
|
onZoomChange(val);
|
|
};
|
|
|
|
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const file = e.target.files?.[0];
|
|
if (file && onUploadStart) {
|
|
onUploadStart(file);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="toolbar">
|
|
{/* Logo & Health */}
|
|
<div className="toolbar-section">
|
|
<div className="logo-container">
|
|
<div className="logo-badge">DQ</div>
|
|
<span className="logo-text">
|
|
DocQube<span className="logo-sub">PDF</span>
|
|
</span>
|
|
</div>
|
|
|
|
<div className={`health-badge status-${backendHealthy === null ? 'checking' : backendHealthy ? 'healthy' : 'unhealthy'}`}>
|
|
<span className="health-dot" />
|
|
{backendHealthy === null
|
|
? 'Checking Gateway...'
|
|
: backendHealthy
|
|
? 'Gateway Connected'
|
|
: 'Gateway Offline'}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Center Tools (Zoom, Rotate, Navigate) */}
|
|
<div className="toolbar-center">
|
|
<div className="page-indicator">
|
|
<span>Page</span>
|
|
<span className="page-current">{currentPage + 1}</span>
|
|
<span className="page-separator">/</span>
|
|
<span>{totalPages}</span>
|
|
</div>
|
|
|
|
<div className="divider-vertical" />
|
|
|
|
<div className="zoom-controls">
|
|
<button
|
|
onClick={() => onZoomChange(Math.max(0.5, zoom - 0.25))}
|
|
className="btn-icon"
|
|
title="Zoom Out"
|
|
>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M20 12H4" />
|
|
</svg>
|
|
</button>
|
|
|
|
<select
|
|
value={zoom}
|
|
onChange={handleZoomPercentSelect}
|
|
className="zoom-select"
|
|
>
|
|
<option value="0.5">50%</option>
|
|
<option value="0.75">75%</option>
|
|
<option value="1.0">100%</option>
|
|
<option value="1.25">125%</option>
|
|
<option value="1.5">150%</option>
|
|
<option value="2.0">200%</option>
|
|
</select>
|
|
|
|
<button
|
|
onClick={() => onZoomChange(Math.min(3.0, zoom + 0.25))}
|
|
className="btn-icon"
|
|
title="Zoom In"
|
|
>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M12 4v16m8-8H4" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<div className="divider-vertical" />
|
|
|
|
<button
|
|
onClick={() => onRotationChange((rotation + 90) % 360)}
|
|
className="btn-icon"
|
|
title="Rotate Clockwise"
|
|
>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M4 4v5h.582m15.356 2A8.001 8.001 0 1121.21 7.89M9 11l3-3 3 3m-3-3v12" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
{/* Right Tools (Active tool highlights, Upload) */}
|
|
<div className="toolbar-section gap-3">
|
|
<div className="tool-selector">
|
|
<button
|
|
onClick={() => onActiveToolChange('select')}
|
|
className={`tool-btn ${activeTool === 'select' ? 'active' : ''}`}
|
|
title="Select Text"
|
|
>
|
|
Select
|
|
</button>
|
|
|
|
<button
|
|
onClick={() => onActiveToolChange('highlight')}
|
|
className={`tool-btn highlight ${activeTool === 'highlight' ? 'active' : ''}`}
|
|
title="Highlight Ink"
|
|
>
|
|
Highlight
|
|
</button>
|
|
|
|
<button
|
|
onClick={() => onActiveToolChange('signature')}
|
|
className={`tool-btn signature ${activeTool === 'signature' ? 'active' : ''}`}
|
|
title="Place Digital Signature"
|
|
>
|
|
Signature
|
|
</button>
|
|
</div>
|
|
|
|
<label className="upload-btn">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
|
|
</svg>
|
|
Upload PDF
|
|
<input
|
|
type="file"
|
|
accept=".pdf"
|
|
onChange={handleFileUpload}
|
|
className="hidden-file-input"
|
|
/>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|