import React, { useCallback, useMemo } from 'react'; import { ActionIcon, Divider } from '@mantine/core'; import './rightRail/RightRail.css'; import { useToolWorkflow } from '../../contexts/ToolWorkflowContext'; import { useRightRail } from '../../contexts/RightRailContext'; import { useFileState, useFileSelection } from '../../contexts/FileContext'; import { useNavigationState } from '../../contexts/NavigationContext'; import { useTranslation } from 'react-i18next'; import LanguageSelector from '../shared/LanguageSelector'; import { useRainbowThemeContext } from '../shared/RainbowThemeProvider'; import { Tooltip } from '../shared/Tooltip'; import { ViewerContext } from '../../contexts/ViewerContext'; import { useSignature } from '../../contexts/SignatureContext'; import LocalIcon from './LocalIcon'; import { useSidebarContext } from '../../contexts/SidebarContext'; import { RightRailButtonConfig, RightRailRenderContext, RightRailSection } from '../../types/rightRail'; const SECTION_ORDER: RightRailSection[] = ['top', 'middle', 'bottom']; function renderWithTooltip( node: React.ReactNode, tooltip: React.ReactNode | undefined ) { if (!tooltip) return node; const portalTarget = typeof document !== 'undefined' ? document.body : undefined; return (
{node}
); } export default function RightRail() { const { sidebarRefs } = useSidebarContext(); const { t } = useTranslation(); const viewerContext = React.useContext(ViewerContext); const { toggleTheme } = useRainbowThemeContext(); const { buttons, actions, allButtonsDisabled } = useRightRail(); const { pageEditorFunctions, toolPanelMode, leftPanelView } = useToolWorkflow(); const disableForFullscreen = toolPanelMode === 'fullscreen' && leftPanelView === 'toolPicker'; const { workbench: currentView } = useNavigationState(); const { selectors } = useFileState(); const { selectedFiles, selectedFileIds } = useFileSelection(); const { signaturesApplied } = useSignature(); const activeFiles = selectors.getFiles(); const pageEditorTotalPages = pageEditorFunctions?.totalPages ?? 0; const pageEditorSelectedCount = pageEditorFunctions?.selectedPageIds?.length ?? 0; const exportState = viewerContext?.getExportState?.(); const totalItems = useMemo(() => { if (currentView === 'pageEditor') return pageEditorTotalPages; return activeFiles.length; }, [currentView, pageEditorTotalPages, activeFiles.length]); const selectedCount = useMemo(() => { if (currentView === 'pageEditor') { return pageEditorSelectedCount; } return selectedFileIds.length; }, [currentView, pageEditorSelectedCount, selectedFileIds.length]); const sectionsWithButtons = useMemo(() => { return SECTION_ORDER .map(section => { const sectionButtons = buttons.filter(btn => (btn.section ?? 'top') === section && (btn.visible ?? true)); return { section, buttons: sectionButtons }; }) .filter(entry => entry.buttons.length > 0); }, [buttons]); const renderButton = useCallback( (btn: RightRailButtonConfig) => { const action = actions[btn.id]; const disabled = Boolean(btn.disabled || allButtonsDisabled || disableForFullscreen); const triggerAction = () => { if (!disabled) action?.(); }; if (btn.render) { const context: RightRailRenderContext = { id: btn.id, disabled, allButtonsDisabled, action, triggerAction, }; return btn.render(context) ?? null; } if (!btn.icon) return null; const ariaLabel = btn.ariaLabel || (typeof btn.tooltip === 'string' ? (btn.tooltip as string) : undefined); const className = ['right-rail-icon', btn.className].filter(Boolean).join(' '); const buttonNode = ( {btn.icon} ); return renderWithTooltip(buttonNode, btn.tooltip); }, [actions, allButtonsDisabled, disableForFullscreen] ); const handleExportAll = useCallback(async () => { if (currentView === 'viewer') { if (!signaturesApplied) { alert('You have unapplied signatures. Please use "Apply Signatures" first before exporting.'); return; } viewerContext?.exportActions?.download(); return; } if (currentView === 'pageEditor') { pageEditorFunctions?.onExportAll?.(); return; } const filesToDownload = selectedFiles.length > 0 ? selectedFiles : activeFiles; filesToDownload.forEach(file => { const link = document.createElement('a'); link.href = URL.createObjectURL(file); link.download = file.name; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(link.href); }); }, [ currentView, selectedFiles, activeFiles, pageEditorFunctions, viewerContext, signaturesApplied ]); const downloadTooltip = useMemo(() => { if (currentView === 'pageEditor') { return t('rightRail.exportAll', 'Export PDF'); } if (selectedCount > 0) { return t('rightRail.downloadSelected', 'Download Selected Files'); } return t('rightRail.downloadAll', 'Download All'); }, [currentView, selectedCount, t]); return (
{sectionsWithButtons.map(({ section, buttons: sectionButtons }) => (
{sectionButtons.map((btn, index) => { const content = renderButton(btn); if (!content) return null; return (
{content}
); })}
))}
{renderWithTooltip( , t('rightRail.toggleTheme', 'Toggle Theme') )} {renderWithTooltip(
, t('rightRail.language', 'Language') )} {renderWithTooltip( , downloadTooltip )}
); }