Pathora / frontend /src /components /viewer /TopToolbar.tsx
malavikapradeep2001's picture
Deploy Pathora Viewer: tile server, viewer components, and root app.py
fc6a9fa verified
raw
history blame
6.01 kB
import { Undo2, Trash2, ZoomIn, ZoomOut, Eye, EyeOff, Layers } from "lucide-react";
interface TopToolbarProps {
slideName: string;
zoomLevel: number;
zoomPresets: number[];
onZoomPreset: (level: number) => void;
micronsPerPixel?: number | null;
showAnnotations: boolean;
showHeatmap: boolean;
onUndo: () => void;
onDelete: () => void;
onZoomIn: () => void;
onZoomOut: () => void;
onToggleAnnotations: () => void;
onToggleHeatmap: () => void;
canUndo: boolean;
canDelete: boolean;
}
export function TopToolbar({
slideName,
zoomLevel,
zoomPresets,
onZoomPreset,
micronsPerPixel,
showAnnotations,
showHeatmap,
onUndo,
onDelete,
onZoomIn,
onZoomOut,
onToggleAnnotations,
onToggleHeatmap,
canUndo,
canDelete,
}: TopToolbarProps) {
const scaleBarPixels = 90;
const safeZoom = Math.max(zoomLevel, 0.0001);
const microns = micronsPerPixel ? (micronsPerPixel * scaleBarPixels) / safeZoom : null;
const micronsLabel = microns
? `${microns >= 100 ? Math.round(microns) : microns.toFixed(1)} µm`
: "";
const isActivePreset = (preset: number) => {
return Math.abs(zoomLevel - preset) < 0.5;
};
return (
<div className="min-h-14 bg-white border-b border-gray-200 flex flex-wrap items-center justify-between gap-3 px-4 py-2 shadow-sm">
{/* Left Section: Slide Info */}
<div className="flex items-center gap-3">
<div>
<h2 className="text-base font-semibold text-gray-800">{slideName}</h2>
<p className="text-[11px] text-gray-500">
Zoom: {zoomLevel.toFixed(2)}x
</p>
</div>
</div>
{/* Center-Left Section: Annotation Controls */}
<div className="flex items-center gap-2">
<button
onClick={onUndo}
disabled={!canUndo}
className={`px-2 py-1 rounded-md flex items-center gap-1.5 transition-all ${
canUndo
? "bg-blue-600 text-white hover:bg-blue-700"
: "bg-gray-300 text-gray-500 cursor-not-allowed"
}`}
title="Undo last annotation"
>
<Undo2 className="w-3.5 h-3.5" />
<span className="text-[11px] font-semibold">Undo</span>
</button>
<button
onClick={onDelete}
disabled={!canDelete}
className={`px-2 py-1 rounded-md flex items-center gap-1.5 transition-all ${
canDelete
? "bg-red-600 text-white hover:bg-red-700"
: "bg-gray-300 text-gray-500 cursor-not-allowed"
}`}
title="Delete selected annotation"
>
<Trash2 className="w-3.5 h-3.5" />
<span className="text-[11px] font-semibold">Delete</span>
</button>
</div>
{/* Center Section: Zoom Controls */}
<div className="flex items-center gap-2">
<div className="flex items-center gap-1">
{zoomPresets.map((preset) => (
<button
key={preset}
onClick={() => onZoomPreset(preset)}
className={`px-2 py-1 rounded-md text-[11px] font-semibold border transition-colors ${
isActivePreset(preset)
? "bg-teal-600 text-white border-teal-600"
: "bg-white text-gray-700 border-gray-300 hover:bg-gray-100"
}`}
title={`Set zoom to ${preset}x`}
>
{preset}x
</button>
))}
</div>
<button
onClick={onZoomOut}
className="p-1.5 rounded-lg hover:bg-gray-100 transition-colors"
title="Zoom Out"
>
<ZoomOut className="w-4.5 h-4.5 text-gray-700" />
</button>
<button
onClick={onZoomIn}
className="p-1.5 rounded-lg hover:bg-gray-100 transition-colors"
title="Zoom In"
>
<ZoomIn className="w-4.5 h-4.5 text-gray-700" />
</button>
</div>
{/* Right Section: Scale + Toggle Buttons */}
<div className="flex items-center gap-3">
{micronsPerPixel && (
<div className="flex items-center gap-2">
<div className="text-[11px] text-gray-500">Scale</div>
<div className="flex flex-col items-end">
<div
className="h-1 bg-gray-700 rounded"
style={{ width: scaleBarPixels }}
/>
<div className="text-[11px] text-gray-600 mt-1">{micronsLabel}</div>
</div>
</div>
)}
<div className="flex items-center gap-2">
<button
onClick={onToggleAnnotations}
className={`
px-3 py-1.5 rounded-lg flex items-center gap-2 transition-all
${
showAnnotations
? "bg-teal-100 text-teal-700 border border-teal-300"
: "bg-gray-100 text-gray-600 border border-gray-300 hover:bg-gray-200"
}
`}
title="Toggle Annotations"
>
{showAnnotations ? (
<Eye className="w-4 h-4" />
) : (
<EyeOff className="w-4 h-4" />
)}
<span className="text-[11px] font-medium">Annotations</span>
</button>
<button
onClick={onToggleHeatmap}
className={`
px-3 py-1.5 rounded-lg flex items-center gap-2 transition-all
${
showHeatmap
? "bg-orange-100 text-orange-700 border border-orange-300"
: "bg-gray-100 text-gray-600 border border-gray-300 hover:bg-gray-200"
}
`}
title="Toggle Heatmap"
>
<Layers className="w-4 h-4" />
<span className="text-[11px] font-medium">Heatmap</span>
</button>
</div>
</div>
</div>
);
}