typescript

This commit is contained in:
2025-11-13 01:07:55 +01:00
parent b812d748ea
commit ada089c05b
147 changed files with 7427 additions and 2534 deletions
@@ -1,11 +1,56 @@
import React, { useCallback, useMemo } from 'react';
import { useCallback, useMemo } from 'react';
import type { JSX } from 'react';
import DocumentInfoPanel from '../documents/DocumentInfoPanel';
import { DownloadIcon } from '../ui/icons';
interface DocumentLike {
id?: string | number;
title?: string;
content_type?: string;
filename?: string;
original_name?: string;
[key: string]: unknown;
}
interface PreviewEntry {
url?: string;
contentType?: string;
filename?: string;
}
interface ContentTabConfig {
id?: string;
label?: string;
enabled?: boolean;
forceDisplay?: boolean;
loadContent?: (options?: { signal?: AbortSignal }) => unknown | Promise<unknown>;
loadingMessage?: string;
emptyMessage?: string;
unavailableMessage?: string;
errorMessage?: string;
[key: string]: unknown;
}
type LayoutMode = 'split' | 'stacked' | (string & {});
interface DocumentViewerLayoutProps {
document?: DocumentLike | null;
previewEntry?: PreviewEntry | null;
summaryProps?: Record<string, unknown>;
metadataPayload?: unknown;
contentTabConfig?: ContentTabConfig | null;
resetKey?: string | number | null;
classNamePrefix?: string;
defaultTabId?: string;
infoPanelProps?: Record<string, unknown>;
previewLoadingMessage?: string;
layoutMode?: LayoutMode;
}
const DocumentViewerLayout = ({
document,
previewEntry,
summaryProps,
summaryProps = {},
metadataPayload,
contentTabConfig,
resetKey,
@@ -14,7 +59,7 @@ const DocumentViewerLayout = ({
infoPanelProps = {},
previewLoadingMessage = 'Preparing preview…',
layoutMode = 'split',
}) => {
}: DocumentViewerLayoutProps): JSX.Element => {
const isStacked = layoutMode === 'stacked';
const previewContent = useMemo(() => {
@@ -103,7 +148,7 @@ const DocumentViewerLayout = ({
const resolvedDefaultTabId = isStacked ? 'preview' : defaultTabId;
const summaryPlacement = 'tabs';
const tabsPlacement = isStacked ? 'bottom' : 'top';
const tabsPlacement = 'bottom';
const summaryLayout = 'compact';
const detailsPane = (
@@ -5,12 +5,13 @@ import React, {
useRef,
useState,
} from 'react';
import type { ReactNode } from 'react';
import { useNavigate } from 'react-router-dom';
import {
DownloadIcon,
CloseIcon,
IconZoomInArea,
DetailPanelCollapseIcon,
IconX,
WindowMaximizeIcon,
} from '../ui/icons';
import PreviewZoomOverlay from '../detail/PreviewZoomOverlay';
@@ -18,6 +19,7 @@ import {
buildCorrespondentOptions,
sortCorrespondents,
} from '../documents/DocumentSummarySection';
import type { DocumentSummarySectionProps } from '../documents/DocumentSummarySection';
import { extractDocumentMetadataPayload } from '../documents/documentMetadata';
import { createDocumentActionState } from '../documents/documentActions';
import { resolveDocumentAssetUrl } from '../asset_manager';
@@ -28,6 +30,44 @@ import DocumentViewerLayout from './DocumentViewerLayout';
import useViewerLayoutMode from './useViewerLayoutMode';
import { usePanelResizeBindings } from '../app/PanelManagerContext';
interface DocumentLike {
id?: string | number;
title?: string;
issued_at?: string | null;
correspondents?: Array<{ id?: string | number; name?: string }>;
[key: string]: unknown;
}
interface AssetLike {
id?: string | number;
url?: string | null;
metadata?: Record<string, unknown> | null;
[key: string]: unknown;
}
interface DocumentViewerPanelProps extends DocumentSummarySectionProps {
document: DocumentLike | null;
previewEntry?: {
url?: string;
canGoPrev?: boolean;
canGoNext?: boolean;
goPrev?: () => void;
goNext?: () => void;
} | null;
hydrateDocument?: (doc: DocumentLike | null) => DocumentLike | null;
ensureAssetUrl?: (docId: string | number, asset: AssetLike, options?: { start?: number; limit?: number }) => Promise<unknown>;
getDocumentAsset?: (doc: DocumentLike | null, type: string) => AssetLike | null | undefined;
ensurePreviewData?: (docId: string | number, options?: { signal?: AbortSignal }) => Promise<DocumentLike | null | undefined>;
resolveApiPath?: (path: string) => string;
notifyApiError?: (error: unknown, fallbackMessage?: string) => void;
sidebarToggle?: ReactNode;
onClosePanel?: () => void;
resolveFolderPath?: (doc: DocumentLike | null) => Array<{ id?: string | number; name?: string }>;
variant?: 'viewer' | 'sidebar';
onCollapsePanel?: () => void;
onMaximizePanel?: () => void;
}
export const createDocumentViewerHeaderActions = ({
document,
@@ -74,7 +114,7 @@ export const createDocumentViewerHeaderActions = ({
);
};
const DocumentViewerPanel = ({
const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
document,
previewEntry,
hydrateDocument,
@@ -239,7 +279,7 @@ const DocumentViewerPanel = ({
}
}, [hydrateDocument, document?.id]);
const panelRef = useRef(null);
const panelRef = useRef<HTMLDivElement | HTMLFormElement | HTMLElement | null>(null);
const isStackedLayout = useViewerLayoutMode(panelRef, document?.id);
const {
@@ -366,7 +406,7 @@ const DocumentViewerPanel = ({
aria-label="Close detail panel"
title="Close detail panel"
>
<DetailPanelCollapseIcon />
<IconX />
</button>
)
: null;
@@ -1,4 +1,4 @@
import { useLayoutEffect, useState } from 'react';
import { MutableRefObject, useLayoutEffect, useState } from 'react';
export const DOCUMENT_VIEWER_PORTRAIT_HEIGHT_RATIO = 0.4;
const PORTRAIT_RATIO_STYLE_ID = 'document-viewer-portrait-ratio-style';
@@ -19,9 +19,14 @@ const ensurePortraitRatioStyle = () => {
}
};
const computeStackedLayoutBreakpoint = () => window.innerWidth / 2;
const MIN_STACKED_BREAKPOINT = 480;
export const useViewerLayoutMode = (ref, dependency) => {
const computeStackedLayoutBreakpoint = () => Math.max(window.innerWidth / 2, MIN_STACKED_BREAKPOINT);
export const useViewerLayoutMode = (
ref: MutableRefObject<HTMLElement | null> | null,
dependency?: unknown,
) => {
const [isStacked, setIsStacked] = useState(false);
useLayoutEffect(() => {
@@ -35,8 +40,8 @@ export const useViewerLayoutMode = (ref, dependency) => {
return undefined;
}
let frame = null;
const commitMeasure = (width) => {
let frame: number | null = null;
const commitMeasure = (width: number) => {
if (frame) {
cancelAnimationFrame(frame);
}