152 lines
5.4 KiB
TypeScript
152 lines
5.4 KiB
TypeScript
import React from 'react';
|
|||
|
|
import type { DocumentInfo } from '../lib/gatewayService';
|
||
|
|
import type { Annotation } from '../viewer/AnnotationLayer';
|
||
|
|
|
||
|
|
interface SidebarProps {
|
||
|
|
documents: DocumentInfo[];
|
||
|
|
selectedDocumentId: string;
|
||
|
|
onSelectDocument: (id: string) => void;
|
||
|
|
annotations: Annotation[];
|
||
|
|
totalPages: number;
|
||
|
|
activeTab: 'documents' | 'annotations' | 'outline';
|
||
|
|
setActiveTab: (tab: 'documents' | 'annotations' | 'outline') => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
export const Sidebar: React.FC<SidebarProps> = ({
|
||
|
|
documents,
|
||
|
|
selectedDocumentId,
|
||
|
|
onSelectDocument,
|
||
|
|
annotations,
|
||
|
|
totalPages,
|
||
|
|
activeTab,
|
||
|
|
setActiveTab,
|
||
|
|
}) => {
|
||
|
|
const formatBytes = (bytes: number) => {
|
||
|
|
if (bytes === 0) return '0 Bytes';
|
||
|
|
const k = 1024;
|
||
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="sidebar">
|
||
|
|
{/* Sidebar Tabs */}
|
||
|
|
<div className="sidebar-tabs">
|
||
|
|
<button
|
||
|
|
onClick={() => setActiveTab('documents')}
|
||
|
|
className={`tab-btn ${activeTab === 'documents' ? 'active' : ''}`}
|
||
|
|
>
|
||
|
|
Files
|
||
|
|
</button>
|
||
|
|
|
||
|
|
<button
|
||
|
|
onClick={() => setActiveTab('outline')}
|
||
|
|
className={`tab-btn ${activeTab === 'outline' ? 'active' : ''}`}
|
||
|
|
>
|
||
|
|
Thumbnails
|
||
|
|
</button>
|
||
|
|
|
||
|
|
<button
|
||
|
|
onClick={() => setActiveTab('annotations')}
|
||
|
|
className={`tab-btn ${activeTab === 'annotations' ? 'active' : ''} relative-tab`}
|
||
|
|
>
|
||
|
|
Notes
|
||
|
|
{annotations.length > 0 && (
|
||
|
|
<span className="badge-notification">
|
||
|
|
{annotations.length}
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Tab Contents */}
|
||
|
|
<div className="sidebar-content">
|
||
|
|
{/* DOCUMENTS TAB */}
|
||
|
|
{activeTab === 'documents' && (
|
||
|
|
<div className="doc-list-container">
|
||
|
|
<span className="tab-section-header">
|
||
|
|
Active Documents
|
||
|
|
</span>
|
||
|
|
{documents.map((doc) => {
|
||
|
|
const isSelected = doc.id === selectedDocumentId;
|
||
|
|
return (
|
||
|
|
<div
|
||
|
|
key={doc.id}
|
||
|
|
onClick={() => onSelectDocument(doc.id)}
|
||
|
|
className={`doc-card ${isSelected ? 'active' : ''}`}
|
||
|
|
>
|
||
|
|
<div className="doc-card-body">
|
||
|
|
<div className={`doc-icon-container ${isSelected ? 'active' : ''}`}>
|
||
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="doc-details">
|
||
|
|
<p className="doc-filename">
|
||
|
|
{doc.filename}
|
||
|
|
</p>
|
||
|
|
<div className="doc-metadata">
|
||
|
|
<span>{formatBytes(doc.sizeBytes)}</span>
|
||
|
|
<span>•</span>
|
||
|
|
<span>{doc.totalPages} Pages</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
})}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{/* THUMBNAILS TAB */}
|
||
|
|
{activeTab === 'outline' && (
|
||
|
|
<div className="thumbnails-grid">
|
||
|
|
{Array.from({ length: totalPages }).map((_, idx) => (
|
||
|
|
<div key={idx} className="thumbnail-card">
|
||
|
|
<div className="thumbnail-preview">
|
||
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" className="thumbnail-svg-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||
|
|
</svg>
|
||
|
|
<span className="thumbnail-label">Page {idx + 1}</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{/* ANNOTATIONS TAB */}
|
||
|
|
{activeTab === 'annotations' && (
|
||
|
|
<div className="annotations-list">
|
||
|
|
<span className="tab-section-header">
|
||
|
|
Active Highlights
|
||
|
|
</span>
|
||
|
|
{annotations.length === 0 ? (
|
||
|
|
<div className="empty-state">
|
||
|
|
No active highlights yet. Select 'Highlight' tool and drag/select text on pages.
|
||
|
|
</div>
|
||
|
|
) : (
|
||
|
|
annotations.map((anno) => (
|
||
|
|
<div key={anno.id} className="annotation-card">
|
||
|
|
<div className="annotation-header">
|
||
|
|
<span className="annotation-type-badge">
|
||
|
|
{anno.type}
|
||
|
|
</span>
|
||
|
|
<span className="annotation-author">
|
||
|
|
{anno.author}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
<p className="annotation-content">
|
||
|
|
"{anno.content}"
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
))
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|