mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-08 17:51:20 +02:00
Replace filemanager with recent files system
This commit is contained in:
parent
61699a08a5
commit
35820e8a24
168
frontend/src/components/shared/FileGrid.tsx
Normal file
168
frontend/src/components/shared/FileGrid.tsx
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { Box, Flex, Group, Text, Button, TextInput, Select, Badge } from "@mantine/core";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import SearchIcon from "@mui/icons-material/Search";
|
||||||
|
import SortIcon from "@mui/icons-material/Sort";
|
||||||
|
import FileCard from "../fileManagement/FileCard";
|
||||||
|
import { FileWithUrl } from "../../types/file";
|
||||||
|
|
||||||
|
interface FileGridProps {
|
||||||
|
files: FileWithUrl[];
|
||||||
|
onRemove?: (index: number) => void;
|
||||||
|
onDoubleClick?: (file: FileWithUrl) => void;
|
||||||
|
onView?: (file: FileWithUrl) => void;
|
||||||
|
onEdit?: (file: FileWithUrl) => void;
|
||||||
|
onSelect?: (fileId: string) => void;
|
||||||
|
selectedFiles?: string[];
|
||||||
|
showSearch?: boolean;
|
||||||
|
showSort?: boolean;
|
||||||
|
maxDisplay?: number; // If set, shows only this many files with "Show All" option
|
||||||
|
onShowAll?: () => void;
|
||||||
|
showingAll?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type SortOption = 'date' | 'name' | 'size';
|
||||||
|
|
||||||
|
const FileGrid = ({
|
||||||
|
files,
|
||||||
|
onRemove,
|
||||||
|
onDoubleClick,
|
||||||
|
onView,
|
||||||
|
onEdit,
|
||||||
|
onSelect,
|
||||||
|
selectedFiles = [],
|
||||||
|
showSearch = false,
|
||||||
|
showSort = false,
|
||||||
|
maxDisplay,
|
||||||
|
onShowAll,
|
||||||
|
showingAll = false
|
||||||
|
}: FileGridProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
|
const [sortBy, setSortBy] = useState<SortOption>('date');
|
||||||
|
|
||||||
|
// Filter files based on search term
|
||||||
|
const filteredFiles = files.filter(file =>
|
||||||
|
file.name.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sort files
|
||||||
|
const sortedFiles = [...filteredFiles].sort((a, b) => {
|
||||||
|
switch (sortBy) {
|
||||||
|
case 'date':
|
||||||
|
return (b.lastModified || 0) - (a.lastModified || 0);
|
||||||
|
case 'name':
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
case 'size':
|
||||||
|
return (b.size || 0) - (a.size || 0);
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Apply max display limit if specified
|
||||||
|
const displayFiles = maxDisplay && !showingAll
|
||||||
|
? sortedFiles.slice(0, maxDisplay)
|
||||||
|
: sortedFiles;
|
||||||
|
|
||||||
|
const hasMoreFiles = maxDisplay && !showingAll && sortedFiles.length > maxDisplay;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{/* Search and Sort Controls */}
|
||||||
|
{(showSearch || showSort) && (
|
||||||
|
<Group mb="md" justify="space-between" wrap="wrap" gap="sm">
|
||||||
|
{showSearch && (
|
||||||
|
<TextInput
|
||||||
|
placeholder={t("fileManager.searchFiles", "Search files...")}
|
||||||
|
leftSection={<SearchIcon size={16} />}
|
||||||
|
value={searchTerm}
|
||||||
|
onChange={(e) => setSearchTerm(e.currentTarget.value)}
|
||||||
|
style={{ flexGrow: 1, maxWidth: 300, minWidth: 200 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showSort && (
|
||||||
|
<Select
|
||||||
|
data={[
|
||||||
|
{ value: 'date', label: t("fileManager.sortByDate", "Sort by Date") },
|
||||||
|
{ value: 'name', label: t("fileManager.sortByName", "Sort by Name") },
|
||||||
|
{ value: 'size', label: t("fileManager.sortBySize", "Sort by Size") }
|
||||||
|
]}
|
||||||
|
value={sortBy}
|
||||||
|
onChange={(value) => setSortBy(value as SortOption)}
|
||||||
|
leftSection={<SortIcon size={16} />}
|
||||||
|
style={{ minWidth: 150 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* File Count Badge */}
|
||||||
|
{(showSearch || showSort) && (
|
||||||
|
<Group mb="sm">
|
||||||
|
<Badge variant="light" size="sm">
|
||||||
|
{displayFiles.length} {displayFiles.length === 1 ? 'file' : 'files'}
|
||||||
|
{hasMoreFiles && ` (${sortedFiles.length - maxDisplay!} more)`}
|
||||||
|
</Badge>
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Files Grid */}
|
||||||
|
<Flex
|
||||||
|
wrap="wrap"
|
||||||
|
gap="lg"
|
||||||
|
justify="flex-start"
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
// Responsive grid spacing
|
||||||
|
'@media (max-width: 768px)': {
|
||||||
|
gap: 'md'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{displayFiles.map((file, idx) => {
|
||||||
|
const originalIdx = files.findIndex(f => (f.id || f.name) === (file.id || file.name));
|
||||||
|
return (
|
||||||
|
<FileCard
|
||||||
|
key={file.id || file.name + idx}
|
||||||
|
file={file}
|
||||||
|
onRemove={onRemove ? () => onRemove(originalIdx) : undefined}
|
||||||
|
onDoubleClick={onDoubleClick ? () => onDoubleClick(file) : undefined}
|
||||||
|
onView={onView ? () => onView(file) : undefined}
|
||||||
|
onEdit={onEdit ? () => onEdit(file) : undefined}
|
||||||
|
isSelected={selectedFiles.includes(file.id || file.name)}
|
||||||
|
onSelect={onSelect ? () => onSelect(file.id || file.name) : undefined}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
{/* Show All Button */}
|
||||||
|
{hasMoreFiles && onShowAll && (
|
||||||
|
<Group justify="center" mt="md">
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
onClick={onShowAll}
|
||||||
|
>
|
||||||
|
{t("fileManager.showAll", "Show All")} ({sortedFiles.length} files)
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Empty State */}
|
||||||
|
{displayFiles.length === 0 && (
|
||||||
|
<Box style={{ textAlign: 'center', padding: '2rem' }}>
|
||||||
|
<Text c="dimmed">
|
||||||
|
{searchTerm
|
||||||
|
? t("fileManager.noFilesFound", "No files found matching your search")
|
||||||
|
: t("fileManager.noFiles", "No files available")
|
||||||
|
}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FileGrid;
|
@ -1,9 +1,12 @@
|
|||||||
import React, { useState, useCallback, useRef } from 'react';
|
import React, { useState, useCallback, useRef, useEffect } from 'react';
|
||||||
import { Stack, Button, Text, Center } from '@mantine/core';
|
import { Stack, Button, Text, Center, Box, Divider } from '@mantine/core';
|
||||||
import { Dropzone } from '@mantine/dropzone';
|
import { Dropzone } from '@mantine/dropzone';
|
||||||
import UploadFileIcon from '@mui/icons-material/UploadFile';
|
import UploadFileIcon from '@mui/icons-material/UploadFile';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import FilePickerModal from './FilePickerModal';
|
import { fileStorage } from '../../services/fileStorage';
|
||||||
|
import { FileWithUrl } from '../../types/file';
|
||||||
|
import FileGrid from './FileGrid';
|
||||||
|
import MultiSelectControls from './MultiSelectControls';
|
||||||
|
|
||||||
interface FileUploadSelectorProps {
|
interface FileUploadSelectorProps {
|
||||||
// Appearance
|
// Appearance
|
||||||
@ -20,6 +23,10 @@ interface FileUploadSelectorProps {
|
|||||||
// Loading state
|
// Loading state
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
|
||||||
|
// Recent files
|
||||||
|
showRecentFiles?: boolean;
|
||||||
|
maxRecentFiles?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FileUploadSelector = ({
|
const FileUploadSelector = ({
|
||||||
@ -32,20 +39,40 @@ const FileUploadSelector = ({
|
|||||||
accept = ["application/pdf"],
|
accept = ["application/pdf"],
|
||||||
loading = false,
|
loading = false,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
|
showRecentFiles = true,
|
||||||
|
maxRecentFiles = 8,
|
||||||
}: FileUploadSelectorProps) => {
|
}: FileUploadSelectorProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [showFilePickerModal, setShowFilePickerModal] = useState(false);
|
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const handleFileUpload = useCallback((uploadedFiles: File[]) => {
|
// Recent files state
|
||||||
|
const [recentFiles, setRecentFiles] = useState<FileWithUrl[]>([]);
|
||||||
|
const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
|
||||||
|
const [showingAllRecent, setShowingAllRecent] = useState(false);
|
||||||
|
const [recentFilesLoading, setRecentFilesLoading] = useState(false);
|
||||||
|
|
||||||
|
const handleFileUpload = useCallback(async (uploadedFiles: File[]) => {
|
||||||
if (uploadedFiles.length === 0) return;
|
if (uploadedFiles.length === 0) return;
|
||||||
|
|
||||||
|
// Auto-save uploaded files to recent files
|
||||||
|
if (showRecentFiles) {
|
||||||
|
try {
|
||||||
|
for (const file of uploadedFiles) {
|
||||||
|
await fileStorage.storeFile(file);
|
||||||
|
}
|
||||||
|
// Refresh recent files list
|
||||||
|
loadRecentFiles();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save files to recent:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (onFilesSelect) {
|
if (onFilesSelect) {
|
||||||
onFilesSelect(uploadedFiles);
|
onFilesSelect(uploadedFiles);
|
||||||
} else if (onFileSelect) {
|
} else if (onFileSelect) {
|
||||||
onFileSelect(uploadedFiles[0]);
|
onFileSelect(uploadedFiles[0]);
|
||||||
}
|
}
|
||||||
}, [onFileSelect, onFilesSelect]);
|
}, [onFileSelect, onFilesSelect, showRecentFiles]);
|
||||||
|
|
||||||
const handleFileInputChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFileInputChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const files = event.target.files;
|
const files = event.target.files;
|
||||||
@ -64,23 +91,98 @@ const FileUploadSelector = ({
|
|||||||
fileInputRef.current?.click();
|
fileInputRef.current?.click();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleStorageSelection = useCallback((selectedFiles: File[]) => {
|
// Load recent files from storage
|
||||||
if (selectedFiles.length === 0) return;
|
const loadRecentFiles = useCallback(async () => {
|
||||||
|
if (!showRecentFiles) return;
|
||||||
|
|
||||||
if (onFilesSelect) {
|
setRecentFilesLoading(true);
|
||||||
onFilesSelect(selectedFiles);
|
try {
|
||||||
} else if (onFileSelect) {
|
const files = await fileStorage.getAllFiles();
|
||||||
onFileSelect(selectedFiles[0]);
|
// Sort by last modified date (newest first)
|
||||||
|
const sortedFiles = files.sort((a, b) => (b.lastModified || 0) - (a.lastModified || 0));
|
||||||
|
setRecentFiles(sortedFiles);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load recent files:', error);
|
||||||
|
setRecentFiles([]);
|
||||||
|
} finally {
|
||||||
|
setRecentFilesLoading(false);
|
||||||
|
}
|
||||||
|
}, [showRecentFiles]);
|
||||||
|
|
||||||
|
// Convert FileWithUrl to File for upload
|
||||||
|
const convertToFile = async (fileWithUrl: FileWithUrl): Promise<File> => {
|
||||||
|
if (fileWithUrl.url && fileWithUrl.url.startsWith('blob:')) {
|
||||||
|
const response = await fetch(fileWithUrl.url);
|
||||||
|
const data = await response.arrayBuffer();
|
||||||
|
return new File([data], fileWithUrl.name, {
|
||||||
|
type: fileWithUrl.type || 'application/pdf',
|
||||||
|
lastModified: fileWithUrl.lastModified || Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load from IndexedDB
|
||||||
|
const storedFile = await fileStorage.getFile(fileWithUrl.id || fileWithUrl.name);
|
||||||
|
if (storedFile) {
|
||||||
|
return new File([storedFile.data], storedFile.name, {
|
||||||
|
type: storedFile.type,
|
||||||
|
lastModified: storedFile.lastModified
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('File not found in storage');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRecentFileSelection = useCallback(async (file: FileWithUrl) => {
|
||||||
|
try {
|
||||||
|
const fileObj = await convertToFile(file);
|
||||||
|
if (onFilesSelect) {
|
||||||
|
onFilesSelect([fileObj]);
|
||||||
|
} else if (onFileSelect) {
|
||||||
|
onFileSelect(fileObj);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load file from recent:', error);
|
||||||
}
|
}
|
||||||
}, [onFileSelect, onFilesSelect]);
|
}, [onFileSelect, onFilesSelect]);
|
||||||
|
|
||||||
|
const handleSelectedRecentFiles = useCallback(async () => {
|
||||||
|
if (selectedFiles.length === 0) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const selectedFileObjects = recentFiles.filter(f => selectedFiles.includes(f.id || f.name));
|
||||||
|
const filePromises = selectedFileObjects.map(convertToFile);
|
||||||
|
const files = await Promise.all(filePromises);
|
||||||
|
|
||||||
|
if (onFilesSelect) {
|
||||||
|
onFilesSelect(files);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedFiles([]);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load selected files:', error);
|
||||||
|
}
|
||||||
|
}, [selectedFiles, recentFiles, onFilesSelect]);
|
||||||
|
|
||||||
|
const toggleFileSelection = useCallback((fileId: string) => {
|
||||||
|
setSelectedFiles(prev =>
|
||||||
|
prev.includes(fileId)
|
||||||
|
? prev.filter(id => id !== fileId)
|
||||||
|
: [...prev, fileId]
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Load recent files on mount
|
||||||
|
useEffect(() => {
|
||||||
|
loadRecentFiles();
|
||||||
|
}, [loadRecentFiles]);
|
||||||
|
|
||||||
// Get default title and subtitle from translations if not provided
|
// Get default title and subtitle from translations if not provided
|
||||||
const displayTitle = title || t("fileUpload.selectFiles", "Select files");
|
const displayTitle = title || t("fileUpload.selectFiles", "Select files");
|
||||||
const displaySubtitle = subtitle || t("fileUpload.chooseFromStorageMultiple", "Choose files from storage or upload new PDFs");
|
const displaySubtitle = subtitle || t("fileUpload.chooseFromStorageMultiple", "Choose files from storage or upload new PDFs");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack align="center" gap="xl">
|
<Stack align="center" gap="sm">
|
||||||
{/* Title and description */}
|
{/* Title and description */}
|
||||||
<Stack align="center" gap="md">
|
<Stack align="center" gap="md">
|
||||||
<UploadFileIcon style={{ fontSize: 64 }} />
|
<UploadFileIcon style={{ fontSize: 64 }} />
|
||||||
@ -94,19 +196,6 @@ const FileUploadSelector = ({
|
|||||||
|
|
||||||
{/* Action buttons */}
|
{/* Action buttons */}
|
||||||
<Stack align="center" gap="md" w="100%">
|
<Stack align="center" gap="md" w="100%">
|
||||||
<Button
|
|
||||||
variant="filled"
|
|
||||||
size="lg"
|
|
||||||
onClick={() => setShowFilePickerModal(true)}
|
|
||||||
disabled={disabled || sharedFiles.length === 0}
|
|
||||||
loading={loading}
|
|
||||||
>
|
|
||||||
{loading ? "Loading..." : `Load from Storage (${sharedFiles.length} files available)`}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Text size="md" c="dimmed">
|
|
||||||
{t("fileUpload.or", "or")}
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
{showDropzone ? (
|
{showDropzone ? (
|
||||||
<Dropzone
|
<Dropzone
|
||||||
@ -114,7 +203,7 @@ const FileUploadSelector = ({
|
|||||||
accept={accept}
|
accept={accept}
|
||||||
multiple={true}
|
multiple={true}
|
||||||
disabled={disabled || loading}
|
disabled={disabled || loading}
|
||||||
style={{ width: '100%', minHeight: 120 }}
|
style={{ width: '100%', height: "5rem" }}
|
||||||
activateOnClick={true}
|
activateOnClick={true}
|
||||||
>
|
>
|
||||||
<Center>
|
<Center>
|
||||||
@ -155,15 +244,45 @@ const FileUploadSelector = ({
|
|||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
|
||||||
|
|
||||||
{/* File Picker Modal */}
|
{/* Recent Files Section */}
|
||||||
<FilePickerModal
|
{showRecentFiles && recentFiles.length > 0 && (
|
||||||
opened={showFilePickerModal}
|
<Box w="100%" >
|
||||||
onClose={() => setShowFilePickerModal(false)}
|
<Divider my="md" />
|
||||||
storedFiles={sharedFiles}
|
<Text size="lg" fw={500} mb="md">
|
||||||
onSelectFiles={handleStorageSelection}
|
{t("fileUpload.recentFiles", "Recent Files")}
|
||||||
/>
|
</Text>
|
||||||
|
<MultiSelectControls
|
||||||
|
selectedCount={selectedFiles.length}
|
||||||
|
onClearSelection={() => setSelectedFiles([])}
|
||||||
|
onAddToUpload={handleSelectedRecentFiles}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FileGrid
|
||||||
|
files={recentFiles}
|
||||||
|
onDoubleClick={handleRecentFileSelection}
|
||||||
|
onSelect={toggleFileSelection}
|
||||||
|
selectedFiles={selectedFiles}
|
||||||
|
maxDisplay={showingAllRecent ? undefined : maxRecentFiles}
|
||||||
|
onShowAll={() => setShowingAllRecent(true)}
|
||||||
|
showingAll={showingAllRecent}
|
||||||
|
showSearch={showingAllRecent}
|
||||||
|
showSort={showingAllRecent}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{showingAllRecent && (
|
||||||
|
<Center mt="md">
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
onClick={() => setShowingAllRecent(false)}
|
||||||
|
>
|
||||||
|
{t("fileUpload.showLess", "Show Less")}
|
||||||
|
</Button>
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
76
frontend/src/components/shared/MultiSelectControls.tsx
Normal file
76
frontend/src/components/shared/MultiSelectControls.tsx
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Box, Group, Text, Button } from "@mantine/core";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
interface MultiSelectControlsProps {
|
||||||
|
selectedCount: number;
|
||||||
|
onClearSelection: () => void;
|
||||||
|
onOpenInFileEditor?: () => void;
|
||||||
|
onOpenInPageEditor?: () => void;
|
||||||
|
onAddToUpload?: () => void; // New action for recent files
|
||||||
|
}
|
||||||
|
|
||||||
|
const MultiSelectControls = ({
|
||||||
|
selectedCount,
|
||||||
|
onClearSelection,
|
||||||
|
onOpenInFileEditor,
|
||||||
|
onOpenInPageEditor,
|
||||||
|
onAddToUpload
|
||||||
|
}: MultiSelectControlsProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
if (selectedCount === 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box mb="md" p="md" style={{ backgroundColor: 'var(--mantine-color-blue-0)', borderRadius: 8 }}>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text size="sm">
|
||||||
|
{selectedCount} {t("fileManager.filesSelected", "files selected")}
|
||||||
|
</Text>
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
variant="light"
|
||||||
|
onClick={onClearSelection}
|
||||||
|
>
|
||||||
|
{t("fileManager.clearSelection", "Clear Selection")}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{onAddToUpload && (
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
color="green"
|
||||||
|
onClick={onAddToUpload}
|
||||||
|
>
|
||||||
|
{t("fileManager.addToUpload", "Add to Upload")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{onOpenInFileEditor && (
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
color="orange"
|
||||||
|
onClick={onOpenInFileEditor}
|
||||||
|
disabled={selectedCount === 0}
|
||||||
|
>
|
||||||
|
{t("fileManager.openInFileEditor", "Open in File Editor")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{onOpenInPageEditor && (
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
color="blue"
|
||||||
|
onClick={onOpenInPageEditor}
|
||||||
|
disabled={selectedCount === 0}
|
||||||
|
>
|
||||||
|
{t("fileManager.openInPageEditor", "Open in Page Editor")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MultiSelectControls;
|
@ -8,7 +8,6 @@ import LightModeIcon from '@mui/icons-material/LightMode';
|
|||||||
import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
|
import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
|
||||||
import VisibilityIcon from "@mui/icons-material/Visibility";
|
import VisibilityIcon from "@mui/icons-material/Visibility";
|
||||||
import EditNoteIcon from "@mui/icons-material/EditNote";
|
import EditNoteIcon from "@mui/icons-material/EditNote";
|
||||||
import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile";
|
|
||||||
import FolderIcon from "@mui/icons-material/Folder";
|
import FolderIcon from "@mui/icons-material/Folder";
|
||||||
import { Group } from "@mantine/core";
|
import { Group } from "@mantine/core";
|
||||||
|
|
||||||
@ -29,14 +28,6 @@ const VIEW_OPTIONS = [
|
|||||||
),
|
),
|
||||||
value: "pageEditor",
|
value: "pageEditor",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: (
|
|
||||||
<Group gap={4}>
|
|
||||||
<InsertDriveFileIcon fontSize="small" />
|
|
||||||
</Group>
|
|
||||||
),
|
|
||||||
value: "fileManager",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: (
|
label: (
|
||||||
<Group gap={4}>
|
<Group gap={4}>
|
||||||
|
@ -13,7 +13,6 @@ import rainbowStyles from '../styles/rainbow.module.css';
|
|||||||
|
|
||||||
import ToolPicker from "../components/tools/ToolPicker";
|
import ToolPicker from "../components/tools/ToolPicker";
|
||||||
import TopControls from "../components/shared/TopControls";
|
import TopControls from "../components/shared/TopControls";
|
||||||
import FileManager from "../components/fileManagement/FileManager";
|
|
||||||
import FileEditor from "../components/pageEditor/FileEditor";
|
import FileEditor from "../components/pageEditor/FileEditor";
|
||||||
import PageEditor from "../components/pageEditor/PageEditor";
|
import PageEditor from "../components/pageEditor/PageEditor";
|
||||||
import PageEditorControls from "../components/pageEditor/PageEditorControls";
|
import PageEditorControls from "../components/pageEditor/PageEditorControls";
|
||||||
@ -40,7 +39,7 @@ type ToolRegistry = {
|
|||||||
const baseToolRegistry = {
|
const baseToolRegistry = {
|
||||||
split: { icon: <ContentCutIcon />, component: SplitPdfPanel, view: "viewer" },
|
split: { icon: <ContentCutIcon />, component: SplitPdfPanel, view: "viewer" },
|
||||||
compress: { icon: <ZoomInMapIcon />, component: CompressPdfPanel, view: "viewer" },
|
compress: { icon: <ZoomInMapIcon />, component: CompressPdfPanel, view: "viewer" },
|
||||||
merge: { icon: <AddToPhotosIcon />, component: MergePdfPanel, view: "fileManager" },
|
merge: { icon: <AddToPhotosIcon />, component: MergePdfPanel, view: "pageEditor" },
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
@ -376,16 +375,7 @@ export default function HomePage() {
|
|||||||
/>
|
/>
|
||||||
{/* Main content area */}
|
{/* Main content area */}
|
||||||
<Box className="flex-1 min-h-0 margin-top-200 relative z-10">
|
<Box className="flex-1 min-h-0 margin-top-200 relative z-10">
|
||||||
{currentView === "fileManager" ? (
|
{!activeFiles[0] ? (
|
||||||
<FileManager
|
|
||||||
files={storedFiles}
|
|
||||||
setFiles={setStoredFiles}
|
|
||||||
setCurrentView={handleViewChange}
|
|
||||||
onOpenFileEditor={handleOpenFileEditor}
|
|
||||||
onOpenPageEditor={handleOpenPageEditor}
|
|
||||||
onLoadFileToActive={addToActiveFiles}
|
|
||||||
/>
|
|
||||||
) : (currentView != "fileManager") && !activeFiles[0] ? (
|
|
||||||
<Container size="lg" p="xl" h="100%" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
<Container size="lg" p="xl" h="100%" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
<FileUploadSelector
|
<FileUploadSelector
|
||||||
title={currentView === "viewer"
|
title={currentView === "viewer"
|
||||||
@ -397,9 +387,13 @@ export default function HomePage() {
|
|||||||
onFileSelect={(file) => {
|
onFileSelect={(file) => {
|
||||||
addToActiveFiles(file);
|
addToActiveFiles(file);
|
||||||
}}
|
}}
|
||||||
allowMultiple={false}
|
onFilesSelect={(files) => {
|
||||||
|
files.forEach(addToActiveFiles);
|
||||||
|
}}
|
||||||
accept={["application/pdf"]}
|
accept={["application/pdf"]}
|
||||||
loading={false}
|
loading={false}
|
||||||
|
showRecentFiles={true}
|
||||||
|
maxRecentFiles={8}
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
) : currentView === "fileEditor" ? (
|
) : currentView === "fileEditor" ? (
|
||||||
|
Loading…
Reference in New Issue
Block a user