FileManager Component Overview
Purpose: Modal component for selecting and managing PDF files with
preview capabilities
Architecture:
- Responsive Layouts: MobileLayout.tsx (stacked) vs DesktopLayout.tsx
(3-column)
- Central State: FileManagerContext handles file operations, selection,
and modal state
- File Storage: IndexedDB persistence with thumbnail caching
Key Components:
- FileSourceButtons: Switch between Recent/Local/Drive sources
- FileListArea: Scrollable file grid with search functionality
- FilePreview: PDF thumbnails with dynamic shadow stacking (1-2 shadow
pages based on file count)
- FileDetails: File info card with metadata
- CompactFileDetails: Mobile-optimized file info layout
File Flow:
1. Users select source → browse/search files → select multiple files →
preview with navigation → open in
tools
2. Files persist across tool switches via FileContext integration
3. Memory management handles large PDFs (up to 100GB+)
```mermaid
graph TD
FM[FileManager] --> ML[MobileLayout]
FM --> DL[DesktopLayout]
ML --> FSB[FileSourceButtons<br/>Recent/Local/Drive]
ML --> FLA[FileListArea]
ML --> FD[FileDetails]
DL --> FSB
DL --> FLA
DL --> FD
FLA --> FLI[FileListItem]
FD --> FP[FilePreview]
FD --> CFD[CompactFileDetails]
```
---------
Co-authored-by: Connor Yoh <connor@stirlingpdf.com>
89 lines
2.5 KiB
TypeScript
89 lines
2.5 KiB
TypeScript
import React from 'react';
|
|
import { Grid } from '@mantine/core';
|
|
import FileSourceButtons from './FileSourceButtons';
|
|
import FileDetails from './FileDetails';
|
|
import SearchInput from './SearchInput';
|
|
import FileListArea from './FileListArea';
|
|
import HiddenFileInput from './HiddenFileInput';
|
|
import { useFileManagerContext } from '../../contexts/FileManagerContext';
|
|
|
|
const DesktopLayout: React.FC = () => {
|
|
const {
|
|
activeSource,
|
|
recentFiles,
|
|
modalHeight,
|
|
} = useFileManagerContext();
|
|
|
|
return (
|
|
<Grid gutter="xs" h="100%" grow={false} style={{ flexWrap: 'nowrap', minWidth: 0 }}>
|
|
{/* Column 1: File Sources */}
|
|
<Grid.Col span="content" p="lg" style={{
|
|
minWidth: '13.625rem',
|
|
width: '13.625rem',
|
|
flexShrink: 0,
|
|
height: '100%',
|
|
}}>
|
|
<FileSourceButtons />
|
|
</Grid.Col>
|
|
|
|
{/* Column 2: File List */}
|
|
<Grid.Col span="auto" style={{
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
height: '100%',
|
|
minHeight: 0,
|
|
minWidth: 0,
|
|
flex: '1 1 0px'
|
|
}}>
|
|
<div style={{
|
|
flex: 1,
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
backgroundColor: 'var(--bg-file-list)',
|
|
border: '1px solid var(--mantine-color-gray-2)',
|
|
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
|
|
overflow: 'hidden'
|
|
}}>
|
|
{activeSource === 'recent' && (
|
|
<div style={{
|
|
flexShrink: 0,
|
|
borderBottom: '1px solid var(--mantine-color-gray-3)'
|
|
}}>
|
|
<SearchInput />
|
|
</div>
|
|
)}
|
|
|
|
<div style={{ flex: 1, minHeight: 0 }}>
|
|
<FileListArea
|
|
scrollAreaHeight={`calc(${modalHeight} )`}
|
|
scrollAreaStyle={{
|
|
height: activeSource === 'recent' && recentFiles.length > 0 ? modalHeight : '100%',
|
|
backgroundColor: 'transparent',
|
|
border: 'none',
|
|
borderRadius: 0
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Grid.Col>
|
|
|
|
{/* Column 3: File Details */}
|
|
<Grid.Col p="xl" span="content" style={{
|
|
minWidth: '25rem',
|
|
width: '25rem',
|
|
flexShrink: 0,
|
|
height: '100%',
|
|
maxWidth: '18rem'
|
|
}}>
|
|
<div style={{ height: '100%', overflow: 'hidden' }}>
|
|
<FileDetails />
|
|
</div>
|
|
</Grid.Col>
|
|
|
|
{/* Hidden file input for local file selection */}
|
|
<HiddenFileInput />
|
|
</Grid>
|
|
);
|
|
};
|
|
|
|
export default DesktopLayout; |