mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-14 10:34:06 +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",
|
"download": "Download",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"unsupported": "Unsupported",
|
"unsupported": "Unsupported",
|
||||||
|
"active": "Active",
|
||||||
"addToUpload": "Add to Upload",
|
"addToUpload": "Add to Upload",
|
||||||
|
"closeFile": "Close File",
|
||||||
"deleteAll": "Delete All",
|
"deleteAll": "Delete All",
|
||||||
"loadingFiles": "Loading files...",
|
"loadingFiles": "Loading files...",
|
||||||
"noFiles": "No files available",
|
"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 { Z_INDEX_FILE_MANAGER_MODAL } from '@app/styles/zIndex';
|
||||||
import { isGoogleDriveConfigured } from '@app/services/googleDrivePickerService';
|
import { isGoogleDriveConfigured } from '@app/services/googleDrivePickerService';
|
||||||
import { loadScript } from '@app/utils/scriptLoader';
|
import { loadScript } from '@app/utils/scriptLoader';
|
||||||
|
import { useAllFiles } from '@app/contexts/FileContext';
|
||||||
|
|
||||||
interface FileManagerProps {
|
interface FileManagerProps {
|
||||||
selectedTool?: Tool | null;
|
selectedTool?: Tool | null;
|
||||||
@@ -25,6 +26,9 @@ const FileManager: React.FC<FileManagerProps> = ({ selectedTool }) => {
|
|||||||
|
|
||||||
const { loadRecentFiles, handleRemoveFile, loading } = useFileManager();
|
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
|
// File management handlers
|
||||||
const isFileSupported = useCallback((fileName: string) => {
|
const isFileSupported = useCallback((fileName: string) => {
|
||||||
if (!selectedTool?.supportedFormats) return true;
|
if (!selectedTool?.supportedFormats) return true;
|
||||||
@@ -175,6 +179,7 @@ const FileManager: React.FC<FileManagerProps> = ({ selectedTool }) => {
|
|||||||
modalHeight={modalHeight}
|
modalHeight={modalHeight}
|
||||||
refreshRecentFiles={refreshRecentFiles}
|
refreshRecentFiles={refreshRecentFiles}
|
||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
|
activeFileIds={activeFileIds}
|
||||||
>
|
>
|
||||||
{isMobile ? <MobileLayout /> : <DesktopLayout />}
|
{isMobile ? <MobileLayout /> : <DesktopLayout />}
|
||||||
</FileManagerProvider>
|
</FileManagerProvider>
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const FileListArea: React.FC<FileListAreaProps> = ({
|
|||||||
onDownloadSingle,
|
onDownloadSingle,
|
||||||
isFileSupported,
|
isFileSupported,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
activeFileIds,
|
||||||
} = useFileManagerContext();
|
} = useFileManagerContext();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -55,6 +56,7 @@ const FileListArea: React.FC<FileListAreaProps> = ({
|
|||||||
// All files in filteredFiles are now leaf files only
|
// All files in filteredFiles are now leaf files only
|
||||||
const historyFiles = loadedHistoryFiles.get(file.id) || [];
|
const historyFiles = loadedHistoryFiles.get(file.id) || [];
|
||||||
const isExpanded = expandedFileIds.has(file.id);
|
const isExpanded = expandedFileIds.has(file.id);
|
||||||
|
const isActive = activeFileIds.includes(file.id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={file.id}>
|
<React.Fragment key={file.id}>
|
||||||
@@ -68,6 +70,7 @@ const FileListArea: React.FC<FileListAreaProps> = ({
|
|||||||
onDoubleClick={() => onFileDoubleClick(file)}
|
onDoubleClick={() => onFileDoubleClick(file)}
|
||||||
isHistoryFile={false} // All files here are leaf files
|
isHistoryFile={false} // All files here are leaf files
|
||||||
isLatestVersion={true} // All files here are the latest versions
|
isLatestVersion={true} // All files here are the latest versions
|
||||||
|
isActive={isActive}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FileHistoryGroup
|
<FileHistoryGroup
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import DownloadIcon from '@mui/icons-material/Download';
|
|||||||
import HistoryIcon from '@mui/icons-material/History';
|
import HistoryIcon from '@mui/icons-material/History';
|
||||||
import RestoreIcon from '@mui/icons-material/Restore';
|
import RestoreIcon from '@mui/icons-material/Restore';
|
||||||
import UnarchiveIcon from '@mui/icons-material/Unarchive';
|
import UnarchiveIcon from '@mui/icons-material/Unarchive';
|
||||||
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { getFileSize, getFileDate } from '@app/utils/fileUtils';
|
import { getFileSize, getFileDate } from '@app/utils/fileUtils';
|
||||||
import { FileId, StirlingFileStub } from '@app/types/fileContext';
|
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 ToolChain from '@app/components/shared/ToolChain';
|
||||||
import { Z_INDEX_OVER_FILE_MANAGER_MODAL } from '@app/styles/zIndex';
|
import { Z_INDEX_OVER_FILE_MANAGER_MODAL } from '@app/styles/zIndex';
|
||||||
import { PrivateContent } from '@app/components/shared/PrivateContent';
|
import { PrivateContent } from '@app/components/shared/PrivateContent';
|
||||||
|
import { useFileManagement } from '@app/contexts/FileContext';
|
||||||
|
|
||||||
interface FileListItemProps {
|
interface FileListItemProps {
|
||||||
file: StirlingFileStub;
|
file: StirlingFileStub;
|
||||||
@@ -26,6 +28,7 @@ interface FileListItemProps {
|
|||||||
isLast?: boolean;
|
isLast?: boolean;
|
||||||
isHistoryFile?: boolean; // Whether this is a history file (indented)
|
isHistoryFile?: boolean; // Whether this is a history file (indented)
|
||||||
isLatestVersion?: boolean; // Whether this is the latest version (shows chevron)
|
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> = ({
|
const FileListItem: React.FC<FileListItemProps> = ({
|
||||||
@@ -37,12 +40,14 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
|||||||
onDownload,
|
onDownload,
|
||||||
onDoubleClick,
|
onDoubleClick,
|
||||||
isHistoryFile = false,
|
isHistoryFile = false,
|
||||||
isLatestVersion = false
|
isLatestVersion = false,
|
||||||
|
isActive = false
|
||||||
}) => {
|
}) => {
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {expandedFileIds, onToggleExpansion, onUnzipFile } = useFileManagerContext();
|
const {expandedFileIds, onToggleExpansion, onUnzipFile } = useFileManagerContext();
|
||||||
|
const { removeFiles } = useFileManagement();
|
||||||
|
|
||||||
// Check if this is a ZIP file
|
// Check if this is a ZIP file
|
||||||
const isZipFile = zipFileService.isZipFileStub(file);
|
const isZipFile = zipFileService.isZipFileStub(file);
|
||||||
@@ -61,8 +66,10 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
|||||||
<Box
|
<Box
|
||||||
p="sm"
|
p="sm"
|
||||||
style={{
|
style={{
|
||||||
cursor: isHistoryFile ? 'default' : 'pointer',
|
cursor: isHistoryFile || isActive ? 'default' : 'pointer',
|
||||||
backgroundColor: isSelected
|
backgroundColor: isActive
|
||||||
|
? 'var(--file-active-bg)'
|
||||||
|
: isSelected
|
||||||
? 'var(--mantine-color-gray-1)'
|
? 'var(--mantine-color-gray-1)'
|
||||||
: (shouldShowHovered ? 'var(--mantine-color-gray-1)' : 'var(--bg-file-list)'),
|
: (shouldShowHovered ? 'var(--mantine-color-gray-1)' : 'var(--bg-file-list)'),
|
||||||
opacity: isSupported ? 1 : 0.5,
|
opacity: isSupported ? 1 : 0.5,
|
||||||
@@ -74,7 +81,7 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
|||||||
paddingLeft: isHistoryFile ? '2rem' : '0.75rem', // Indent history files
|
paddingLeft: isHistoryFile ? '2rem' : '0.75rem', // Indent history files
|
||||||
borderLeft: isHistoryFile ? '3px solid var(--mantine-color-blue-4)' : 'none' // Visual indicator for history
|
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}
|
onDoubleClick={onDoubleClick}
|
||||||
onMouseEnter={() => setIsHovered(true)}
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
onMouseLeave={() => setIsHovered(false)}
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
@@ -84,14 +91,16 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
|||||||
<Box>
|
<Box>
|
||||||
{/* Checkbox for regular files only */}
|
{/* Checkbox for regular files only */}
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isSelected}
|
checked={isActive || isSelected}
|
||||||
onChange={() => {}} // Handled by parent onClick
|
onChange={() => {}} // Handled by parent onClick
|
||||||
size="sm"
|
size="sm"
|
||||||
pl="sm"
|
pl="sm"
|
||||||
pr="xs"
|
pr="xs"
|
||||||
|
disabled={isActive}
|
||||||
|
color={isActive ? "green" : undefined}
|
||||||
styles={{
|
styles={{
|
||||||
input: {
|
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 }}>
|
<Text size="sm" fw={500} truncate style={{ flex: 1 }}>
|
||||||
<PrivateContent>{file.name}</PrivateContent>
|
<PrivateContent>{file.name}</PrivateContent>
|
||||||
</Text>
|
</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"}>
|
<Badge size="xs" variant="light" color={"blue"}>
|
||||||
v{currentVersion}
|
v{currentVersion}
|
||||||
</Badge>
|
</Badge>
|
||||||
@@ -151,6 +173,22 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
|||||||
</Menu.Target>
|
</Menu.Target>
|
||||||
|
|
||||||
<Menu.Dropdown>
|
<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 && (
|
{onDownload && (
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
leftSection={<DownloadIcon style={{ fontSize: 16 }} />}
|
leftSection={<DownloadIcon style={{ fontSize: 16 }} />}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ interface FileManagerContextValue {
|
|||||||
fileGroups: Map<FileId, StirlingFileStub[]>;
|
fileGroups: Map<FileId, StirlingFileStub[]>;
|
||||||
loadedHistoryFiles: Map<FileId, StirlingFileStub[]>;
|
loadedHistoryFiles: Map<FileId, StirlingFileStub[]>;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
|
activeFileIds: FileId[];
|
||||||
|
|
||||||
// Handlers
|
// Handlers
|
||||||
onSourceChange: (source: 'recent' | 'local' | 'drive') => void;
|
onSourceChange: (source: 'recent' | 'local' | 'drive') => void;
|
||||||
@@ -63,6 +64,7 @@ interface FileManagerProviderProps {
|
|||||||
modalHeight: string;
|
modalHeight: string;
|
||||||
refreshRecentFiles: () => Promise<void>;
|
refreshRecentFiles: () => Promise<void>;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
|
activeFileIds: FileId[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
||||||
@@ -77,6 +79,7 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
|||||||
modalHeight,
|
modalHeight,
|
||||||
refreshRecentFiles,
|
refreshRecentFiles,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
activeFileIds,
|
||||||
}) => {
|
}) => {
|
||||||
const [activeSource, setActiveSource] = useState<'recent' | 'local' | 'drive'>('recent');
|
const [activeSource, setActiveSource] = useState<'recent' | 'local' | 'drive'>('recent');
|
||||||
const [selectedFileIds, setSelectedFileIds] = useState<FileId[]>([]);
|
const [selectedFileIds, setSelectedFileIds] = useState<FileId[]>([]);
|
||||||
@@ -618,6 +621,7 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
|||||||
fileGroups,
|
fileGroups,
|
||||||
loadedHistoryFiles,
|
loadedHistoryFiles,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
activeFileIds,
|
||||||
|
|
||||||
// Handlers
|
// Handlers
|
||||||
onSourceChange: handleSourceChange,
|
onSourceChange: handleSourceChange,
|
||||||
@@ -654,6 +658,7 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
|||||||
fileGroups,
|
fileGroups,
|
||||||
loadedHistoryFiles,
|
loadedHistoryFiles,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
activeFileIds,
|
||||||
handleSourceChange,
|
handleSourceChange,
|
||||||
handleLocalFileClick,
|
handleLocalFileClick,
|
||||||
handleFileSelect,
|
handleFileSelect,
|
||||||
|
|||||||
@@ -12,6 +12,12 @@
|
|||||||
/* Inline highlights in summary */
|
/* Inline highlights in summary */
|
||||||
--spdf-compare-inline-removed-bg: rgba(255, 59, 48, 0.25);
|
--spdf-compare-inline-removed-bg: rgba(255, 59, 48, 0.25);
|
||||||
--spdf-compare-inline-added-bg: rgba(52, 199, 89, 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 */
|
/* CSS variables for Tailwind + Mantine integration */
|
||||||
|
|||||||
Reference in New Issue
Block a user