introduce a unified document viewer component.
This commit is contained in:
@@ -116,8 +116,8 @@ export const useWorkspaceSurface = ({
|
||||
<DocumentViewerPanel
|
||||
variant="sidebar"
|
||||
sidebarMode={sidebarMode}
|
||||
onClosePanel={onClose}
|
||||
onMaximizePanel={onOpenPreview}
|
||||
onClose={onClose}
|
||||
onMaximize={onOpenPreview}
|
||||
tagOptions={tagOptions}
|
||||
{...restDetailProps}
|
||||
/>
|
||||
@@ -194,7 +194,7 @@ export const useWorkspaceSurface = ({
|
||||
ensurePreviewData={ensurePreviewData}
|
||||
notifyApiError={notifyApiError}
|
||||
sidebarToggle={sidebarToggle}
|
||||
onClosePanel={closeDocumentPreview}
|
||||
onClose={closeDocumentPreview}
|
||||
resolveFolderPath={resolveFolderPath}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import PdfViewer from '../preview/PdfViewer';
|
||||
import MediaViewer from '../preview/MediaViewer';
|
||||
import UnifiedDocumentViewer from '../preview/UnifiedDocumentViewer';
|
||||
import PanelHeader from '../ui/PanelHeader';
|
||||
import { IconX, DownloadIcon, FileInfoIcon } from '../ui/icons';
|
||||
|
||||
@@ -14,16 +13,6 @@ interface PreviewZoomOverlayProps {
|
||||
document?: Document | null;
|
||||
}
|
||||
|
||||
type DisplayKind = 'image' | 'pdf';
|
||||
|
||||
const determineDisplayKind = (doc?: Document | null): DisplayKind => {
|
||||
const type = doc?.mime_type?.toLowerCase?.() || '';
|
||||
if (type.includes('pdf')) {
|
||||
return 'pdf';
|
||||
}
|
||||
return 'image';
|
||||
};
|
||||
|
||||
const PreviewZoomOverlay: React.FC<PreviewZoomOverlayProps> = ({
|
||||
open = false,
|
||||
onClose,
|
||||
@@ -41,7 +30,6 @@ const PreviewZoomOverlay: React.FC<PreviewZoomOverlayProps> = ({
|
||||
}
|
||||
|
||||
const downloadUrl = lastDocumentRef.current?.current_version?.download?.url;
|
||||
const displayKind = determineDisplayKind(lastDocumentRef.current);
|
||||
const documentTitle = lastDocumentRef.current?.title || undefined;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -172,23 +160,10 @@ const PreviewZoomOverlay: React.FC<PreviewZoomOverlayProps> = ({
|
||||
ref={scrollRef}
|
||||
tabIndex={-1}
|
||||
>
|
||||
{displayKind === 'pdf' ? (
|
||||
<PdfViewer
|
||||
src={downloadUrl}
|
||||
title={documentTitle}
|
||||
className="preview-zoom__pdf-viewer"
|
||||
viewportRef={scrollRef}
|
||||
/>
|
||||
) : (
|
||||
<MediaViewer
|
||||
src={downloadUrl}
|
||||
alt={documentTitle}
|
||||
className="preview-zoom__image"
|
||||
draggable={false}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
mimeType={lastDocumentRef.current?.mime_type || undefined}
|
||||
/>
|
||||
)}
|
||||
<UnifiedDocumentViewer
|
||||
document={lastDocumentRef.current}
|
||||
viewportRef={scrollRef}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useCallback, useMemo, useRef } from 'react';
|
||||
import type { JSX } from 'react';
|
||||
import DocumentInfoPanel from '../documents/DocumentInfoPanel';
|
||||
import PdfViewer from './PdfViewer';
|
||||
import MediaViewer from './MediaViewer';
|
||||
import UnifiedDocumentViewer from './UnifiedDocumentViewer';
|
||||
|
||||
import type { Document } from '../types/documents';
|
||||
|
||||
@@ -33,7 +32,6 @@ interface DocumentViewerLayoutProps {
|
||||
previewLoadingMessage?: string;
|
||||
layoutMode?: LayoutMode;
|
||||
}
|
||||
|
||||
const DocumentViewerLayout = ({
|
||||
document,
|
||||
summaryProps = {},
|
||||
@@ -49,43 +47,12 @@ const DocumentViewerLayout = ({
|
||||
const isStacked = layoutMode === 'stacked';
|
||||
const viewportRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const previewContent = useMemo(() => {
|
||||
if (!document) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const downloadUrl = document.current_version?.download?.url;
|
||||
if (!downloadUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalizedMimeType = (document.mime_type || '').toLowerCase();
|
||||
const normalizedFilename = document.filename;
|
||||
|
||||
const isPdf = normalizedMimeType === 'application/pdf'
|
||||
|| normalizedMimeType === 'application/x-pdf';
|
||||
|
||||
if (isPdf) {
|
||||
const documentTitle = document.title;
|
||||
|
||||
return (
|
||||
<PdfViewer
|
||||
src={downloadUrl}
|
||||
title={documentTitle ? `Preview of ${documentTitle}` : undefined}
|
||||
viewportRef={viewportRef}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MediaViewer
|
||||
src={downloadUrl}
|
||||
mimeType={normalizedMimeType}
|
||||
filename={normalizedFilename}
|
||||
alt={`Preview of ${document.title}`}
|
||||
/>
|
||||
);
|
||||
}, [document, viewportRef]);
|
||||
const previewContent = useMemo(() => (
|
||||
<UnifiedDocumentViewer
|
||||
document={document}
|
||||
viewportRef={viewportRef}
|
||||
/>
|
||||
), [document, viewportRef]);
|
||||
|
||||
const renderViewportPane = useCallback(() => (
|
||||
<div className="document-viewer__viewport" ref={viewportRef}>
|
||||
|
||||
@@ -39,10 +39,10 @@ interface DocumentViewerPanelProps extends DocumentSummarySectionProps {
|
||||
ensurePreviewData?: (docId: DocumentId, options?: { signal?: AbortSignal }) => Promise<Document | null>;
|
||||
notifyApiError?: (error: unknown, fallbackMessage?: string) => void;
|
||||
sidebarToggle?: ReactNode;
|
||||
onClosePanel?: () => void;
|
||||
onClose?: () => void;
|
||||
resolveFolderPath?: (doc: Document | null) => Array<{ id?: string; name?: string }>;
|
||||
variant?: 'viewer' | 'sidebar';
|
||||
onMaximizePanel?: (args: { documentIds: Array<string> }) => void;
|
||||
onMaximize?: (args: { documentIds: Array<string> }) => void;
|
||||
sidebarMode?: SidebarMode;
|
||||
}
|
||||
|
||||
@@ -95,10 +95,10 @@ const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
|
||||
ensurePreviewData,
|
||||
notifyApiError,
|
||||
sidebarToggle = null,
|
||||
onClosePanel,
|
||||
onClose,
|
||||
resolveFolderPath,
|
||||
variant = 'viewer',
|
||||
onMaximizePanel,
|
||||
onMaximize,
|
||||
sidebarMode = 'overlay',
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
@@ -299,7 +299,7 @@ const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
|
||||
canZoom: Boolean(document),
|
||||
});
|
||||
|
||||
const maximizeButton = isSidebarVariant && onMaximizePanel
|
||||
const maximizeButton = isSidebarVariant && onMaximize
|
||||
? (
|
||||
<button
|
||||
type="button"
|
||||
@@ -310,7 +310,7 @@ const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
|
||||
if (targetId == null) {
|
||||
return;
|
||||
}
|
||||
onMaximizePanel?.({ documentIds: [targetId] });
|
||||
onMaximize?.({ documentIds: [targetId] });
|
||||
}}
|
||||
aria-label="Maximize"
|
||||
title="Maximize"
|
||||
@@ -320,12 +320,12 @@ const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
|
||||
)
|
||||
: null;
|
||||
|
||||
const closeButton = onClosePanel
|
||||
const closeButton = onClose
|
||||
? (
|
||||
<button
|
||||
type="button"
|
||||
className="icon-button"
|
||||
onClick={() => onClosePanel?.()}
|
||||
onClick={() => onClose?.()}
|
||||
aria-label="Close preview"
|
||||
title="Close preview"
|
||||
>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import PdfViewer from './PdfViewer';
|
||||
import MediaViewer from './MediaViewer';
|
||||
import type { Document } from '../types/documents';
|
||||
|
||||
interface UnifiedDocumentViewerProps {
|
||||
document: Document | null;
|
||||
viewportRef?: React.RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
const UnifiedDocumentViewer: React.FC<UnifiedDocumentViewerProps> = ({
|
||||
document,
|
||||
viewportRef,
|
||||
}) => {
|
||||
const content = useMemo(() => {
|
||||
if (!document) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const downloadUrl = document.current_version?.download?.url;
|
||||
if (!downloadUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalizedMimeType = (document.mime_type || '').toLowerCase();
|
||||
const normalizedFilename = document.filename;
|
||||
|
||||
const isPdf = normalizedMimeType === 'application/pdf'
|
||||
|| normalizedMimeType === 'application/x-pdf';
|
||||
|
||||
if (isPdf) {
|
||||
return (
|
||||
<PdfViewer
|
||||
src={downloadUrl}
|
||||
title={document.title}
|
||||
viewportRef={viewportRef}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MediaViewer
|
||||
src={downloadUrl}
|
||||
mimeType={normalizedMimeType}
|
||||
filename={normalizedFilename}
|
||||
alt={document.title}
|
||||
/>
|
||||
);
|
||||
}, [
|
||||
document,
|
||||
viewportRef,
|
||||
]);
|
||||
|
||||
return content;
|
||||
};
|
||||
|
||||
export default UnifiedDocumentViewer;
|
||||
@@ -32,7 +32,7 @@
|
||||
--header-clearance: calc(2.5rem + 0.5rem);
|
||||
}
|
||||
|
||||
.preview-zoom__image {
|
||||
.preview-zoom__scroll .document-viewer__object--image {
|
||||
width: auto;
|
||||
height: auto;
|
||||
box-shadow: 0 32px 120px var(--shadow-deep);
|
||||
@@ -59,10 +59,7 @@
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.preview-zoom__pdf-viewer {
|
||||
.preview-zoom__scroll .document-viewer__object--pdf {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user