mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-04-16 23:08:38 +02:00
File in context indication (#4990)
# Description of Changes <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details.
This commit is contained in:
@@ -4686,7 +4686,9 @@
|
||||
"download": "Download",
|
||||
"delete": "Delete",
|
||||
"unsupported": "Unsupported",
|
||||
"active": "Active",
|
||||
"addToUpload": "Add to Upload",
|
||||
"closeFile": "Close File",
|
||||
"deleteAll": "Delete All",
|
||||
"loadingFiles": "Loading files...",
|
||||
"noFiles": "No files available",
|
||||
|
||||
@@ -12,6 +12,7 @@ import { FileManagerProvider } from '@app/contexts/FileManagerContext';
|
||||
import { Z_INDEX_FILE_MANAGER_MODAL } from '@app/styles/zIndex';
|
||||
import { isGoogleDriveConfigured } from '@app/services/googleDrivePickerService';
|
||||
import { loadScript } from '@app/utils/scriptLoader';
|
||||
import { useAllFiles } from '@app/contexts/FileContext';
|
||||
|
||||
interface FileManagerProps {
|
||||
selectedTool?: Tool | null;
|
||||
@@ -25,6 +26,9 @@ const FileManager: React.FC<FileManagerProps> = ({ selectedTool }) => {
|
||||
|
||||
const { loadRecentFiles, handleRemoveFile, loading } = useFileManager();
|
||||
|
||||
// Get active file IDs from FileContext to show which files are already loaded
|
||||
const { fileIds: activeFileIds } = useAllFiles();
|
||||
|
||||
// File management handlers
|
||||
const isFileSupported = useCallback((fileName: string) => {
|
||||
if (!selectedTool?.supportedFormats) return true;
|
||||
@@ -175,6 +179,7 @@ const FileManager: React.FC<FileManagerProps> = ({ selectedTool }) => {
|
||||
modalHeight={modalHeight}
|
||||
refreshRecentFiles={refreshRecentFiles}
|
||||
isLoading={loading}
|
||||
activeFileIds={activeFileIds}
|
||||
>
|
||||
{isMobile ? <MobileLayout /> : <DesktopLayout />}
|
||||
</FileManagerProvider>
|
||||
|
||||
@@ -30,6 +30,7 @@ const FileListArea: React.FC<FileListAreaProps> = ({
|
||||
onDownloadSingle,
|
||||
isFileSupported,
|
||||
isLoading,
|
||||
activeFileIds,
|
||||
} = useFileManagerContext();
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -55,6 +56,7 @@ const FileListArea: React.FC<FileListAreaProps> = ({
|
||||
// All files in filteredFiles are now leaf files only
|
||||
const historyFiles = loadedHistoryFiles.get(file.id) || [];
|
||||
const isExpanded = expandedFileIds.has(file.id);
|
||||
const isActive = activeFileIds.includes(file.id);
|
||||
|
||||
return (
|
||||
<React.Fragment key={file.id}>
|
||||
@@ -68,6 +70,7 @@ const FileListArea: React.FC<FileListAreaProps> = ({
|
||||
onDoubleClick={() => onFileDoubleClick(file)}
|
||||
isHistoryFile={false} // All files here are leaf files
|
||||
isLatestVersion={true} // All files here are the latest versions
|
||||
isActive={isActive}
|
||||
/>
|
||||
|
||||
<FileHistoryGroup
|
||||
|
||||
@@ -6,6 +6,7 @@ import DownloadIcon from '@mui/icons-material/Download';
|
||||
import HistoryIcon from '@mui/icons-material/History';
|
||||
import RestoreIcon from '@mui/icons-material/Restore';
|
||||
import UnarchiveIcon from '@mui/icons-material/Unarchive';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getFileSize, getFileDate } from '@app/utils/fileUtils';
|
||||
import { FileId, StirlingFileStub } from '@app/types/fileContext';
|
||||
@@ -14,6 +15,7 @@ import { zipFileService } from '@app/services/zipFileService';
|
||||
import ToolChain from '@app/components/shared/ToolChain';
|
||||
import { Z_INDEX_OVER_FILE_MANAGER_MODAL } from '@app/styles/zIndex';
|
||||
import { PrivateContent } from '@app/components/shared/PrivateContent';
|
||||
import { useFileManagement } from '@app/contexts/FileContext';
|
||||
|
||||
interface FileListItemProps {
|
||||
file: StirlingFileStub;
|
||||
@@ -26,6 +28,7 @@ interface FileListItemProps {
|
||||
isLast?: boolean;
|
||||
isHistoryFile?: boolean; // Whether this is a history file (indented)
|
||||
isLatestVersion?: boolean; // Whether this is the latest version (shows chevron)
|
||||
isActive?: boolean; // Whether this file is currently loaded in FileContext
|
||||
}
|
||||
|
||||
const FileListItem: React.FC<FileListItemProps> = ({
|
||||
@@ -37,12 +40,14 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
||||
onDownload,
|
||||
onDoubleClick,
|
||||
isHistoryFile = false,
|
||||
isLatestVersion = false
|
||||
isLatestVersion = false,
|
||||
isActive = false
|
||||
}) => {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const {expandedFileIds, onToggleExpansion, onUnzipFile } = useFileManagerContext();
|
||||
const { removeFiles } = useFileManagement();
|
||||
|
||||
// Check if this is a ZIP file
|
||||
const isZipFile = zipFileService.isZipFileStub(file);
|
||||
@@ -61,8 +66,10 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
||||
<Box
|
||||
p="sm"
|
||||
style={{
|
||||
cursor: isHistoryFile ? 'default' : 'pointer',
|
||||
backgroundColor: isSelected
|
||||
cursor: isHistoryFile || isActive ? 'default' : 'pointer',
|
||||
backgroundColor: isActive
|
||||
? 'var(--file-active-bg)'
|
||||
: isSelected
|
||||
? 'var(--mantine-color-gray-1)'
|
||||
: (shouldShowHovered ? 'var(--mantine-color-gray-1)' : 'var(--bg-file-list)'),
|
||||
opacity: isSupported ? 1 : 0.5,
|
||||
@@ -74,7 +81,7 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
||||
paddingLeft: isHistoryFile ? '2rem' : '0.75rem', // Indent history files
|
||||
borderLeft: isHistoryFile ? '3px solid var(--mantine-color-blue-4)' : 'none' // Visual indicator for history
|
||||
}}
|
||||
onClick={isHistoryFile ? undefined : (e) => onSelect(e.shiftKey)}
|
||||
onClick={isHistoryFile || isActive ? undefined : (e) => onSelect(e.shiftKey)}
|
||||
onDoubleClick={onDoubleClick}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
@@ -84,14 +91,16 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
||||
<Box>
|
||||
{/* Checkbox for regular files only */}
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
checked={isActive || isSelected}
|
||||
onChange={() => {}} // Handled by parent onClick
|
||||
size="sm"
|
||||
pl="sm"
|
||||
pr="xs"
|
||||
disabled={isActive}
|
||||
color={isActive ? "green" : undefined}
|
||||
styles={{
|
||||
input: {
|
||||
cursor: 'pointer'
|
||||
cursor: isActive ? 'not-allowed' : 'pointer'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@@ -103,6 +112,19 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
||||
<Text size="sm" fw={500} truncate style={{ flex: 1 }}>
|
||||
<PrivateContent>{file.name}</PrivateContent>
|
||||
</Text>
|
||||
{isActive && (
|
||||
<Badge
|
||||
size="xs"
|
||||
variant="light"
|
||||
style={{
|
||||
backgroundColor: 'var(--file-active-badge-bg)',
|
||||
color: 'var(--file-active-badge-fg)',
|
||||
border: '1px solid var(--file-active-badge-border)'
|
||||
}}
|
||||
>
|
||||
{t('fileManager.active', 'Active')}
|
||||
</Badge>
|
||||
)}
|
||||
<Badge size="xs" variant="light" color={"blue"}>
|
||||
v{currentVersion}
|
||||
</Badge>
|
||||
@@ -151,6 +173,22 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
||||
</Menu.Target>
|
||||
|
||||
<Menu.Dropdown>
|
||||
{/* Close file option for active files */}
|
||||
{isActive && (
|
||||
<>
|
||||
<Menu.Item
|
||||
leftSection={<CloseIcon style={{ fontSize: 16 }} />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
removeFiles([file.id]);
|
||||
}}
|
||||
>
|
||||
{t('fileManager.closeFile', 'Close File')}
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
</>
|
||||
)}
|
||||
|
||||
{onDownload && (
|
||||
<Menu.Item
|
||||
leftSection={<DownloadIcon style={{ fontSize: 16 }} />}
|
||||
|
||||
@@ -20,6 +20,7 @@ interface FileManagerContextValue {
|
||||
fileGroups: Map<FileId, StirlingFileStub[]>;
|
||||
loadedHistoryFiles: Map<FileId, StirlingFileStub[]>;
|
||||
isLoading: boolean;
|
||||
activeFileIds: FileId[];
|
||||
|
||||
// Handlers
|
||||
onSourceChange: (source: 'recent' | 'local' | 'drive') => void;
|
||||
@@ -63,6 +64,7 @@ interface FileManagerProviderProps {
|
||||
modalHeight: string;
|
||||
refreshRecentFiles: () => Promise<void>;
|
||||
isLoading: boolean;
|
||||
activeFileIds: FileId[];
|
||||
}
|
||||
|
||||
export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
||||
@@ -77,6 +79,7 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
||||
modalHeight,
|
||||
refreshRecentFiles,
|
||||
isLoading,
|
||||
activeFileIds,
|
||||
}) => {
|
||||
const [activeSource, setActiveSource] = useState<'recent' | 'local' | 'drive'>('recent');
|
||||
const [selectedFileIds, setSelectedFileIds] = useState<FileId[]>([]);
|
||||
@@ -618,6 +621,7 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
||||
fileGroups,
|
||||
loadedHistoryFiles,
|
||||
isLoading,
|
||||
activeFileIds,
|
||||
|
||||
// Handlers
|
||||
onSourceChange: handleSourceChange,
|
||||
@@ -654,6 +658,7 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
||||
fileGroups,
|
||||
loadedHistoryFiles,
|
||||
isLoading,
|
||||
activeFileIds,
|
||||
handleSourceChange,
|
||||
handleLocalFileClick,
|
||||
handleFileSelect,
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
/* Inline highlights in summary */
|
||||
--spdf-compare-inline-removed-bg: rgba(255, 59, 48, 0.25);
|
||||
--spdf-compare-inline-added-bg: rgba(52, 199, 89, 0.25);
|
||||
|
||||
/* File Manager active file colors */
|
||||
--file-active-bg: rgba(59, 130, 246, 0.1); /* Transparent blue for active file background */
|
||||
--file-active-badge-bg: rgba(34, 197, 94, 0.15); /* Transparent green for active badge */
|
||||
--file-active-badge-fg: #15803d; /* Green text for active badge */
|
||||
--file-active-badge-border: rgba(34, 197, 94, 0.3); /* Green border for active badge */
|
||||
}
|
||||
|
||||
/* CSS variables for Tailwind + Mantine integration */
|
||||
|
||||
Reference in New Issue
Block a user