This commit is contained in:
Reece Browne 2025-09-15 16:05:19 +01:00
parent 85a74c1d46
commit c17dd25069
6 changed files with 147 additions and 32 deletions

View File

@ -16,6 +16,7 @@
"@embedpdf/plugin-loader": "^1.1.1",
"@embedpdf/plugin-pan": "^1.1.1",
"@embedpdf/plugin-render": "^1.1.1",
"@embedpdf/plugin-rotate": "^1.1.1",
"@embedpdf/plugin-scroll": "^1.1.1",
"@embedpdf/plugin-search": "^1.1.1",
"@embedpdf/plugin-selection": "^1.1.1",
@ -715,6 +716,21 @@
"vue": ">=3.2.0"
}
},
"node_modules/@embedpdf/plugin-rotate": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-rotate/-/plugin-rotate-1.1.1.tgz",
"integrity": "sha512-xxM62dv4TAoTEfCNxyC0UbGryT3ucAH4txQAoab7tfvnfqbAIxTonH1PzQMsBmzN0WETCGjUBm1Ympb95cDx2Q==",
"dependencies": {
"@embedpdf/models": "1.1.1"
},
"peerDependencies": {
"@embedpdf/core": "1.1.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
"vue": ">=3.2.0"
}
},
"node_modules/@embedpdf/plugin-scroll": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-scroll/-/plugin-scroll-1.1.1.tgz",

View File

@ -12,6 +12,7 @@
"@embedpdf/plugin-loader": "^1.1.1",
"@embedpdf/plugin-pan": "^1.1.1",
"@embedpdf/plugin-render": "^1.1.1",
"@embedpdf/plugin-rotate": "^1.1.1",
"@embedpdf/plugin-scroll": "^1.1.1",
"@embedpdf/plugin-search": "^1.1.1",
"@embedpdf/plugin-selection": "^1.1.1",

View File

@ -7,6 +7,7 @@ import { useRightRail } from '../../contexts/RightRailContext';
import { useFileState, useFileSelection, useFileManagement } from '../../contexts/FileContext';
import { useNavigationState } from '../../contexts/NavigationContext';
import { useTranslation } from 'react-i18next';
import '../../types/embedPdf';
import LanguageSelector from '../shared/LanguageSelector';
import { useRainbowThemeContext } from '../shared/RainbowThemeProvider';
@ -17,6 +18,7 @@ import { SearchInterface } from '../viewer/SearchInterface';
export default function RightRail() {
const { t } = useTranslation();
const [isPanning, setIsPanning] = useState(false);
const [currentRotation, setCurrentRotation] = useState(0);
const { toggleTheme } = useRainbowThemeContext();
const { buttons, actions } = useRightRail();
const topButtons = useMemo(() => buttons.filter(b => (b.section || 'top') === 'top' && (b.visible ?? true)), [buttons]);
@ -30,6 +32,24 @@ export default function RightRail() {
// Navigation view
const { workbench: currentView } = useNavigationState();
// Sync rotation state with EmbedPDF API
useEffect(() => {
if (currentView === 'viewer' && window.embedPdfRotate) {
const updateRotation = () => {
const rotation = window.embedPdfRotate?.getRotation() || 0;
setCurrentRotation(rotation * 90); // Convert enum to degrees
};
// Update rotation immediately
updateRotation();
// Set up periodic updates to keep state in sync
const interval = setInterval(updateRotation, 1000);
return () => clearInterval(interval);
}
}, [currentView]);
// File state and selection
const { state, selectors } = useFileState();
const { selectedFiles, selectedFileIds, setSelectedFiles } = useFileSelection();
@ -249,7 +269,7 @@ export default function RightRail() {
radius="md"
className="right-rail-icon"
onClick={() => {
(window as any).embedPdfPan?.togglePan();
window.embedPdfPan?.togglePan();
setIsPanning(!isPanning);
}}
disabled={currentView !== 'viewer'}
@ -264,20 +284,50 @@ export default function RightRail() {
variant="subtle"
radius="md"
className="right-rail-icon"
onClick={() => (window as any).embedPdfControls?.pointer()}
onClick={() => window.embedPdfControls?.pointer()}
disabled={currentView !== 'viewer'}
>
<LocalIcon icon="mouse-pointer" width="1.5rem" height="1.5rem" />
</ActionIcon>
</Tooltip>
{/* Rotate Left */}
<Tooltip content={t('rightRail.rotateLeft', 'Rotate Left')} position="left" offset={12} arrow>
<ActionIcon
variant="subtle"
radius="md"
className="right-rail-icon"
onClick={() => {
window.embedPdfRotate?.rotateBackward();
}}
disabled={currentView !== 'viewer'}
>
<LocalIcon icon="rotate-left" width="1.5rem" height="1.5rem" />
</ActionIcon>
</Tooltip>
{/* Rotate Right */}
<Tooltip content={t('rightRail.rotateRight', 'Rotate Right')} position="left" offset={12} arrow>
<ActionIcon
variant="subtle"
radius="md"
className="right-rail-icon"
onClick={() => {
window.embedPdfRotate?.rotateForward();
}}
disabled={currentView !== 'viewer'}
>
<LocalIcon icon="rotate-right" width="1.5rem" height="1.5rem" />
</ActionIcon>
</Tooltip>
{/* Sidebar Toggle */}
<Tooltip content={t('rightRail.toggleSidebar', 'Toggle Sidebar')} position="left" offset={12} arrow>
<ActionIcon
variant="subtle"
radius="md"
className="right-rail-icon"
onClick={() => (window as any).toggleThumbnailSidebar?.()}
onClick={() => window.toggleThumbnailSidebar?.()}
disabled={currentView !== 'viewer'}
>
<LocalIcon icon="view-list" width="1.5rem" height="1.5rem" />

View File

@ -16,6 +16,8 @@ import { PanPluginPackage } from '@embedpdf/plugin-pan/react';
import { SpreadPluginPackage, SpreadMode } from '@embedpdf/plugin-spread/react';
import { SearchPluginPackage } from '@embedpdf/plugin-search/react';
import { ThumbnailPluginPackage } from '@embedpdf/plugin-thumbnail/react';
import { RotatePluginPackage, Rotate } from '@embedpdf/plugin-rotate/react';
import { Rotation } from '@embedpdf/models';
import { CustomSearchLayer } from './CustomSearchLayer';
import { ZoomAPIBridge } from './ZoomAPIBridge';
import { ScrollAPIBridge } from './ScrollAPIBridge';
@ -24,6 +26,7 @@ import { PanAPIBridge } from './PanAPIBridge';
import { SpreadAPIBridge } from './SpreadAPIBridge';
import { SearchAPIBridge } from './SearchAPIBridge';
import { ThumbnailAPIBridge } from './ThumbnailAPIBridge';
import { RotateAPIBridge } from './RotateAPIBridge';
interface LocalEmbedPDFProps {
file?: File | Blob;
@ -106,6 +109,11 @@ export function LocalEmbedPDF({ file, url, colorScheme }: LocalEmbedPDFProps) {
// Register thumbnail plugin for page thumbnails
createPluginRegistration(ThumbnailPluginPackage),
// Register rotate plugin
createPluginRegistration(RotatePluginPackage, {
defaultRotation: Rotation.Degree0, // Start with no rotation
}),
];
}, [pdfUrl]);
@ -187,6 +195,7 @@ export function LocalEmbedPDF({ file, url, colorScheme }: LocalEmbedPDFProps) {
<SpreadAPIBridge />
<SearchAPIBridge />
<ThumbnailAPIBridge />
<RotateAPIBridge />
<GlobalPointerProvider>
<Viewport
style={{
@ -205,6 +214,7 @@ export function LocalEmbedPDF({ file, url, colorScheme }: LocalEmbedPDFProps) {
>
<Scroller
renderPage={({ width, height, pageIndex, scale, rotation }: { width: number; height: number; pageIndex: number; scale: number; rotation?: number }) => (
<Rotate pageSize={{ width, height }}>
<PagePointerProvider {...{ pageWidth: width, pageHeight: height, pageIndex, scale, rotation: rotation || 0 }}>
<div
style={{
@ -234,6 +244,7 @@ export function LocalEmbedPDF({ file, url, colorScheme }: LocalEmbedPDFProps) {
<SelectionLayer pageIndex={pageIndex} scale={scale} />
</div>
</PagePointerProvider>
</Rotate>
)}
/>
</Viewport>

View File

@ -0,0 +1,23 @@
import { useEffect } from 'react';
import { useRotate } from '@embedpdf/plugin-rotate/react';
/**
* Component that runs inside EmbedPDF context and exports rotate controls globally
*/
export function RotateAPIBridge() {
const { provides: rotate, rotation } = useRotate();
useEffect(() => {
if (rotate) {
// Export rotate controls to global window for right rail access
window.embedPdfRotate = {
rotateForward: () => rotate.rotateForward(),
rotateBackward: () => rotate.rotateBackward(),
setRotation: (rotationValue: number) => rotate.setRotation(rotationValue),
getRotation: () => rotation,
};
}
}, [rotate, rotation]);
return null;
}

View File

@ -17,12 +17,24 @@ export interface EmbedPdfScrollAPI {
export interface EmbedPdfPanAPI {
isPanning: boolean;
togglePan: () => void;
}
export interface EmbedPdfSpreadAPI {
toggleSpreadMode: () => void;
}
export interface EmbedPdfRotateAPI {
rotateForward: () => void;
rotateBackward: () => void;
setRotation: (rotation: number) => void;
getRotation: () => number;
}
export interface EmbedPdfControlsAPI {
pointer: () => void;
}
export interface EmbedPdfThumbnailAPI {
thumbnailAPI: {
renderThumb: (pageIndex: number, scale: number) => {
@ -37,6 +49,8 @@ declare global {
embedPdfScroll?: EmbedPdfScrollAPI;
embedPdfPan?: EmbedPdfPanAPI;
embedPdfSpread?: EmbedPdfSpreadAPI;
embedPdfRotate?: EmbedPdfRotateAPI;
embedPdfControls?: EmbedPdfControlsAPI;
embedPdfThumbnail?: EmbedPdfThumbnailAPI;
toggleThumbnailSidebar?: () => void;
}