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>
103 lines
3.3 KiB
TypeScript
103 lines
3.3 KiB
TypeScript
import React from 'react';
|
|
import { Stack, Text, Button, Group } from '@mantine/core';
|
|
import HistoryIcon from '@mui/icons-material/History';
|
|
import FolderIcon from '@mui/icons-material/Folder';
|
|
import CloudIcon from '@mui/icons-material/Cloud';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { useFileManagerContext } from '../../contexts/FileManagerContext';
|
|
|
|
interface FileSourceButtonsProps {
|
|
horizontal?: boolean;
|
|
}
|
|
|
|
const FileSourceButtons: React.FC<FileSourceButtonsProps> = ({
|
|
horizontal = false
|
|
}) => {
|
|
const { activeSource, onSourceChange, onLocalFileClick } = useFileManagerContext();
|
|
const { t } = useTranslation();
|
|
|
|
const buttonProps = {
|
|
variant: (source: string) => activeSource === source ? 'filled' : 'subtle',
|
|
getColor: (source: string) => activeSource === source ? 'var(--mantine-color-gray-2)' : undefined,
|
|
getStyles: (source: string) => ({
|
|
root: {
|
|
backgroundColor: activeSource === source ? undefined : 'transparent',
|
|
color: activeSource === source ? 'var(--mantine-color-gray-9)' : 'var(--mantine-color-gray-6)',
|
|
border: 'none',
|
|
'&:hover': {
|
|
backgroundColor: activeSource === source ? undefined : 'var(--mantine-color-gray-0)'
|
|
}
|
|
}
|
|
})
|
|
};
|
|
|
|
const buttons = (
|
|
<>
|
|
<Button
|
|
leftSection={<HistoryIcon />}
|
|
justify={horizontal ? "center" : "flex-start"}
|
|
onClick={() => onSourceChange('recent')}
|
|
fullWidth={!horizontal}
|
|
size={horizontal ? "xs" : "sm"}
|
|
color={buttonProps.getColor('recent')}
|
|
styles={buttonProps.getStyles('recent')}
|
|
>
|
|
{horizontal ? t('fileManager.recent', 'Recent') : t('fileManager.recent', 'Recent')}
|
|
</Button>
|
|
|
|
<Button
|
|
variant="subtle"
|
|
color='var(--mantine-color-gray-6)'
|
|
leftSection={<FolderIcon />}
|
|
justify={horizontal ? "center" : "flex-start"}
|
|
onClick={onLocalFileClick}
|
|
fullWidth={!horizontal}
|
|
size={horizontal ? "xs" : "sm"}
|
|
styles={{
|
|
root: {
|
|
backgroundColor: 'transparent',
|
|
border: 'none',
|
|
'&:hover': {
|
|
backgroundColor: 'var(--mantine-color-gray-0)'
|
|
}
|
|
}
|
|
}}
|
|
>
|
|
{horizontal ? t('fileManager.localFiles', 'Local') : t('fileManager.localFiles', 'Local Files')}
|
|
</Button>
|
|
|
|
<Button
|
|
variant={buttonProps.variant('drive')}
|
|
leftSection={<CloudIcon />}
|
|
justify={horizontal ? "center" : "flex-start"}
|
|
onClick={() => onSourceChange('drive')}
|
|
fullWidth={!horizontal}
|
|
size={horizontal ? "xs" : "sm"}
|
|
disabled
|
|
color={activeSource === 'drive' ? 'gray' : undefined}
|
|
styles={buttonProps.getStyles('drive')}
|
|
>
|
|
{horizontal ? t('fileManager.googleDriveShort', 'Drive') : t('fileManager.googleDrive', 'Google Drive')}
|
|
</Button>
|
|
</>
|
|
);
|
|
|
|
if (horizontal) {
|
|
return (
|
|
<Group gap="xs" justify="center" style={{ width: '100%' }}>
|
|
{buttons}
|
|
</Group>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Stack gap="xs" style={{ height: '100%' }}>
|
|
<Text size="sm" pt="sm" fw={500} c="dimmed" mb="xs" style={{ paddingLeft: '1rem' }}>
|
|
{t('fileManager.myFiles', 'My Files')}
|
|
</Text>
|
|
{buttons}
|
|
</Stack>
|
|
);
|
|
};
|
|
|
|
export default FileSourceButtons; |