mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-03-04 02:20:19 +01:00
various viewer pill fixes (#5714)
This commit is contained in:
@@ -3,11 +3,12 @@ import { Box } from '@mantine/core';
|
||||
import { useRainbowThemeContext } from '@app/components/shared/RainbowThemeProvider';
|
||||
import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext';
|
||||
import { useFileHandler } from '@app/hooks/useFileHandler';
|
||||
import { useFileState } from '@app/contexts/FileContext';
|
||||
import { useFileState, useFileActions } from '@app/contexts/FileContext';
|
||||
import { useNavigationState, useNavigationActions, useNavigationGuard } from '@app/contexts/NavigationContext';
|
||||
import { isBaseWorkbench } from '@app/types/workbench';
|
||||
import { useViewer } from '@app/contexts/ViewerContext';
|
||||
import { useAppConfig } from '@app/contexts/AppConfigContext';
|
||||
import { FileId } from '@app/types/file';
|
||||
import styles from '@app/components/layout/Workbench.module.css';
|
||||
|
||||
import TopControls from '@app/components/shared/TopControls';
|
||||
@@ -26,6 +27,7 @@ export default function Workbench() {
|
||||
|
||||
// Use context-based hooks to eliminate all prop drilling
|
||||
const { selectors } = useFileState();
|
||||
const { actions: fileActions } = useFileActions();
|
||||
const { workbench: currentView } = useNavigationState();
|
||||
const { actions: navActions } = useNavigationActions();
|
||||
const setCurrentView = navActions.setWorkbench;
|
||||
@@ -61,13 +63,17 @@ export default function Workbench() {
|
||||
const handleFileSelect = useCallback((index: number) => {
|
||||
// Don't do anything if selecting the same file
|
||||
if (index === activeFileIndex) return;
|
||||
|
||||
|
||||
// requestNavigation handles the unsaved changes check internally
|
||||
requestNavigation(() => {
|
||||
setActiveFileIndex(index);
|
||||
});
|
||||
}, [activeFileIndex, requestNavigation, setActiveFileIndex]);
|
||||
|
||||
const handleFileRemove = useCallback(async (fileId: FileId) => {
|
||||
await fileActions.removeFiles([fileId], false); // false = don't delete from IndexedDB, just remove from context
|
||||
}, [fileActions]);
|
||||
|
||||
const handlePreviewClose = () => {
|
||||
setPreviewFile(null);
|
||||
const previousMode = sessionStorage.getItem('previousMode');
|
||||
@@ -201,6 +207,7 @@ export default function Workbench() {
|
||||
})}
|
||||
currentFileIndex={activeFileIndex}
|
||||
onFileSelect={handleFileSelect}
|
||||
onFileRemove={handleFileRemove}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
import React from 'react';
|
||||
import { Menu, Loader, Group, Text } from '@mantine/core';
|
||||
import VisibilityIcon from '@mui/icons-material/Visibility';
|
||||
import { Menu, Loader, Group, Text, ActionIcon, Tooltip } from '@mantine/core';
|
||||
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import FitText from '@app/components/shared/FitText';
|
||||
import { PrivateContent } from '@app/components/shared/PrivateContent';
|
||||
import { FileId } from '@app/types/file';
|
||||
|
||||
// Truncate text from the center: "very-long-filename.pdf" -> "very-lo...ame.pdf"
|
||||
function truncateCenter(text: string, maxLength: number = 25): string {
|
||||
if (text.length <= maxLength) return text;
|
||||
const ellipsis = '...';
|
||||
const charsToShow = maxLength - ellipsis.length;
|
||||
const frontChars = Math.ceil(charsToShow / 2);
|
||||
const backChars = Math.floor(charsToShow / 2);
|
||||
return text.substring(0, frontChars) + ellipsis + text.substring(text.length - backChars);
|
||||
}
|
||||
|
||||
interface FileDropdownMenuProps {
|
||||
displayName: string;
|
||||
activeFiles: Array<{ fileId: string; name: string; versionNumber?: number }>;
|
||||
currentFileIndex: number;
|
||||
onFileSelect?: (index: number) => void;
|
||||
onFileRemove?: (fileId: FileId) => void;
|
||||
switchingTo?: string | null;
|
||||
viewOptionStyle: React.CSSProperties;
|
||||
pillRef?: React.RefObject<HTMLDivElement>;
|
||||
@@ -20,22 +33,27 @@ export const FileDropdownMenu: React.FC<FileDropdownMenuProps> = ({
|
||||
activeFiles,
|
||||
currentFileIndex,
|
||||
onFileSelect,
|
||||
onFileRemove,
|
||||
switchingTo,
|
||||
viewOptionStyle,
|
||||
}) => {
|
||||
return (
|
||||
<Menu trigger="click" position="bottom" width="30rem">
|
||||
<Menu.Target>
|
||||
<div style={{...viewOptionStyle, cursor: 'pointer'}}>
|
||||
<div style={{...viewOptionStyle, cursor: 'pointer', maxWidth: '100%'}}>
|
||||
{switchingTo === "viewer" ? (
|
||||
<Loader size="xs" />
|
||||
) : (
|
||||
<VisibilityIcon fontSize="small" />
|
||||
<InsertDriveFileIcon fontSize="small" style={{ flexShrink: 0 }} />
|
||||
)}
|
||||
<PrivateContent>
|
||||
<FitText text={displayName} fontSize={14} minimumFontScale={0.6} />
|
||||
<FitText
|
||||
text={truncateCenter(displayName, 30)}
|
||||
minimumFontScale={0.6}
|
||||
style={{ maxWidth: '12rem', display: 'inline-block' }}
|
||||
/>
|
||||
</PrivateContent>
|
||||
<KeyboardArrowDownIcon fontSize="small" />
|
||||
<KeyboardArrowDownIcon fontSize="small" style={{ flexShrink: 0 }} />
|
||||
</div>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown style={{
|
||||
@@ -65,14 +83,36 @@ export const FileDropdownMenu: React.FC<FileDropdownMenuProps> = ({
|
||||
<Group gap="xs" style={{ width: '100%', justifyContent: 'space-between' }}>
|
||||
<div style={{ flex: 1, textAlign: 'left', minWidth: 0 }}>
|
||||
<PrivateContent>
|
||||
<FitText text={itemName} fontSize={14} minimumFontScale={0.7} />
|
||||
<FitText
|
||||
text={truncateCenter(itemName, 50)}
|
||||
minimumFontScale={0.7}
|
||||
style={{ display: 'block', width: '100%' }}
|
||||
/>
|
||||
</PrivateContent>
|
||||
</div>
|
||||
{file.versionNumber && file.versionNumber > 1 && (
|
||||
<Text size="xs" c="dimmed">
|
||||
v{file.versionNumber}
|
||||
</Text>
|
||||
)}
|
||||
<Group gap="xs" style={{ flexShrink: 0 }}>
|
||||
{file.versionNumber && file.versionNumber > 1 && (
|
||||
<Text size="xs" c="dimmed">
|
||||
v{file.versionNumber}
|
||||
</Text>
|
||||
)}
|
||||
{onFileRemove && (
|
||||
<Tooltip label="Close file" withArrow>
|
||||
<ActionIcon
|
||||
size="xs"
|
||||
variant="subtle"
|
||||
color="red"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onFileRemove(file.fileId as FileId);
|
||||
}}
|
||||
style={{ flexShrink: 0 }}
|
||||
>
|
||||
<CloseIcon style={{ fontSize: 14 }} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Group>
|
||||
</Group>
|
||||
</Menu.Item>
|
||||
);
|
||||
|
||||
@@ -127,7 +127,7 @@ const FileMenuItem: React.FC<FileMenuItemProps> = ({
|
||||
/>
|
||||
<div style={{ flex: 1, textAlign: 'left', minWidth: 0 }}>
|
||||
<PrivateContent>
|
||||
<FitText text={itemName} fontSize={14} minimumFontScale={0.7} />
|
||||
<FitText text={itemName} minimumFontScale={0.7} />
|
||||
</PrivateContent>
|
||||
</div>
|
||||
{file.versionNumber && file.versionNumber > 1 && (
|
||||
|
||||
@@ -11,6 +11,7 @@ import { PageEditorFileDropdown } from '@app/components/shared/PageEditorFileDro
|
||||
import type { CustomWorkbenchViewInstance } from '@app/contexts/ToolWorkflowContext';
|
||||
import { FileDropdownMenu } from '@app/components/shared/FileDropdownMenu';
|
||||
import { usePageEditorDropdownState, PageEditorDropdownState } from '@app/components/pageEditor/hooks/usePageEditorDropdownState';
|
||||
import { FileId } from '@app/types/file';
|
||||
|
||||
|
||||
const viewOptionStyle: React.CSSProperties = {
|
||||
@@ -29,6 +30,7 @@ const createViewOptions = (
|
||||
activeFiles: Array<{ fileId: string; name: string; versionNumber?: number }>,
|
||||
currentFileIndex: number,
|
||||
onFileSelect?: (index: number) => void,
|
||||
onFileRemove?: (fileId: FileId) => void,
|
||||
pageEditorState?: PageEditorDropdownState,
|
||||
customViews?: CustomWorkbenchViewInstance[]
|
||||
) => {
|
||||
@@ -37,8 +39,7 @@ const createViewOptions = (
|
||||
const isInViewer = currentView === 'viewer';
|
||||
const fileName = currentFile?.name || '';
|
||||
const viewerDisplayName = isInViewer && fileName ? fileName : 'Viewer';
|
||||
const hasMultipleFiles = activeFiles.length > 1;
|
||||
const showViewerDropdown = isInViewer && hasMultipleFiles;
|
||||
const showViewerDropdown = isInViewer;
|
||||
|
||||
const viewerOption = {
|
||||
label: showViewerDropdown ? (
|
||||
@@ -47,6 +48,7 @@ const createViewOptions = (
|
||||
activeFiles={activeFiles}
|
||||
currentFileIndex={currentFileIndex}
|
||||
onFileSelect={onFileSelect}
|
||||
onFileRemove={onFileRemove}
|
||||
switchingTo={switchingTo}
|
||||
viewOptionStyle={viewOptionStyle}
|
||||
/>
|
||||
@@ -132,6 +134,7 @@ interface TopControlsProps {
|
||||
activeFiles?: Array<{ fileId: string; name: string; versionNumber?: number }>;
|
||||
currentFileIndex?: number;
|
||||
onFileSelect?: (index: number) => void;
|
||||
onFileRemove?: (fileId: FileId) => void;
|
||||
}
|
||||
|
||||
const TopControls = ({
|
||||
@@ -141,6 +144,7 @@ const TopControls = ({
|
||||
activeFiles = [],
|
||||
currentFileIndex = 0,
|
||||
onFileSelect,
|
||||
onFileRemove,
|
||||
}: TopControlsProps) => {
|
||||
const { isRainbowMode } = useRainbowThemeContext();
|
||||
const [switchingTo, setSwitchingTo] = useState<WorkbenchType | null>(null);
|
||||
@@ -176,9 +180,10 @@ const TopControls = ({
|
||||
activeFiles,
|
||||
currentFileIndex,
|
||||
onFileSelect,
|
||||
onFileRemove,
|
||||
pageEditorState,
|
||||
customViews
|
||||
), [currentView, switchingTo, activeFiles, currentFileIndex, onFileSelect, pageEditorState, customViews]);
|
||||
), [currentView, switchingTo, activeFiles, currentFileIndex, onFileSelect, onFileRemove, pageEditorState, customViews]);
|
||||
|
||||
return (
|
||||
<div className="absolute left-0 w-full top-0 z-[100] pointer-events-none">
|
||||
|
||||
Reference in New Issue
Block a user