cleanup
This commit is contained in:
@@ -33,7 +33,6 @@ const AppLayout: React.FC = () => {
|
||||
onToggleSearchIncludeDescendants: documentsPreferences.toggleSearchIncludeDescendants,
|
||||
onSetSearchIncludeDescendants: documentsPreferences.setSearchIncludeDescendants,
|
||||
sortRefreshReadyRef: documentsPreferences.sortRefreshReadyRef,
|
||||
handleDeskExit: documentsPreferences.handleDeskExit,
|
||||
});
|
||||
|
||||
if (['logged-out', 'authenticating', 'selecting-tenant'].includes(appStatus)) {
|
||||
|
||||
@@ -34,8 +34,6 @@ interface DocumentsRouteAppShell {
|
||||
documentsTableProps?: DocumentsTableProps | null;
|
||||
detailPanelProps?: Record<string, unknown> | null;
|
||||
detailPanelOpen?: boolean;
|
||||
documentsViewMode?: string;
|
||||
deskWorkspaceProps?: Record<string, unknown> | null;
|
||||
openTagsModal?: () => void;
|
||||
openCorrespondentsModal?: () => void;
|
||||
previewWorkspaceDocument?: unknown;
|
||||
@@ -55,8 +53,6 @@ const DocumentsRouteContent: React.FC = () => {
|
||||
documentsTableProps,
|
||||
detailPanelProps,
|
||||
detailPanelOpen,
|
||||
documentsViewMode,
|
||||
deskWorkspaceProps,
|
||||
openTagsModal,
|
||||
openCorrespondentsModal,
|
||||
previewWorkspaceDocument,
|
||||
@@ -106,8 +102,6 @@ const DocumentsRouteContent: React.FC = () => {
|
||||
documentsTableProps,
|
||||
detailPanelProps,
|
||||
detailPanelOpen,
|
||||
viewMode: documentsViewMode,
|
||||
deskWorkspaceProps,
|
||||
previewWorkspaceDocument,
|
||||
previewWorkspaceEntry,
|
||||
previewDocumentId,
|
||||
|
||||
@@ -3,7 +3,6 @@ import type { ReactNode } from 'react';
|
||||
import { SidebarExpandIcon } from '../ui/icons';
|
||||
import { createDocumentsSurface } from '../documents/DocumentsPanel';
|
||||
import { createDocumentViewerSurface } from '../preview/DocumentViewerPanel';
|
||||
import createDesktopSurface from '../desktop/createDesktopSurface';
|
||||
import { usePanelManager } from './PanelManagerContext';
|
||||
|
||||
type Identifier = string | number;
|
||||
@@ -16,8 +15,7 @@ type NotifyApiError = (error: unknown, fallbackMessage?: string) => void;
|
||||
|
||||
type DocumentsSurface = ReturnType<typeof createDocumentsSurface> | null;
|
||||
type PreviewSurface = ReturnType<typeof createDocumentViewerSurface> | null;
|
||||
type DesktopSurface = ReturnType<typeof createDesktopSurface> | null;
|
||||
type WorkspaceSurface = DocumentsSurface | PreviewSurface | DesktopSurface | null;
|
||||
type WorkspaceSurface = DocumentsSurface | PreviewSurface | null;
|
||||
|
||||
interface UseWorkspaceSurfaceArgs {
|
||||
sidebarHidden?: boolean;
|
||||
@@ -25,8 +23,6 @@ interface UseWorkspaceSurfaceArgs {
|
||||
documentsTableProps?: Record<string, any> | null;
|
||||
detailPanelProps?: (Record<string, any> & { onClose?: () => void }) | null;
|
||||
detailPanelOpen?: boolean;
|
||||
viewMode?: string;
|
||||
deskWorkspaceProps?: Record<string, any> | null;
|
||||
previewWorkspaceDocument?: unknown;
|
||||
previewWorkspaceEntry?: unknown;
|
||||
previewDocumentId?: Identifier | null;
|
||||
@@ -48,8 +44,6 @@ export const useWorkspaceSurface = ({
|
||||
documentsTableProps,
|
||||
detailPanelProps,
|
||||
detailPanelOpen = false,
|
||||
viewMode = 'list',
|
||||
deskWorkspaceProps,
|
||||
previewWorkspaceDocument,
|
||||
previewWorkspaceEntry,
|
||||
previewDocumentId,
|
||||
@@ -161,33 +155,12 @@ export const useWorkspaceSurface = ({
|
||||
detailPanelProps,
|
||||
]);
|
||||
|
||||
const workspaceSurface = useMemo<DesktopSurface>(() => {
|
||||
if (viewMode !== 'desk') {
|
||||
return null;
|
||||
}
|
||||
return createDesktopSurface({
|
||||
workspaceProps: deskWorkspaceProps,
|
||||
renderSidebarToggle,
|
||||
detailProps: detailPanelProps,
|
||||
detailOpen: detailPanelOpen,
|
||||
});
|
||||
}, [
|
||||
viewMode,
|
||||
deskWorkspaceProps,
|
||||
renderSidebarToggle,
|
||||
detailPanelProps,
|
||||
detailPanelOpen,
|
||||
]);
|
||||
|
||||
const surface = useMemo<WorkspaceSurface>(() => {
|
||||
if (showPreviewWorkspace) {
|
||||
return previewSurface;
|
||||
}
|
||||
if (viewMode === 'desk') {
|
||||
return workspaceSurface;
|
||||
}
|
||||
return documentsSurface;
|
||||
}, [showPreviewWorkspace, viewMode, previewSurface, workspaceSurface, documentsSurface]);
|
||||
}, [showPreviewWorkspace, previewSurface, documentsSurface]);
|
||||
|
||||
return { surface };
|
||||
};
|
||||
|
||||
@@ -130,8 +130,6 @@ interface DesktopWorkspaceProps {
|
||||
activeTagIds?: Array<Identifier | null>;
|
||||
selectedDocumentIds?: Identifier[];
|
||||
onClearSelection?: () => void;
|
||||
detailPanelOpen?: boolean;
|
||||
onCloseDetailPanel?: () => void;
|
||||
tenantId?: Identifier | null;
|
||||
viewId?: string | null;
|
||||
}
|
||||
@@ -175,8 +173,6 @@ interface DesktopWorkspaceViewProps {
|
||||
onPromoteSelection?: DesktopWorkspaceProps['onPromoteSelection'];
|
||||
selectedDocumentIds: Identifier[];
|
||||
onClearSelection?: DesktopWorkspaceProps['onClearSelection'];
|
||||
detailPanelOpen: boolean;
|
||||
onCloseDetailPanel?: DesktopWorkspaceProps['onCloseDetailPanel'];
|
||||
documentLookup: Map<string, DeskDocument>;
|
||||
resolveBaseMetrics: (doc: DeskDocument | null, cardWidth: number, cardHeight: number) => {
|
||||
baseWidth: number;
|
||||
@@ -210,8 +206,6 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
|
||||
activeTagIds = [],
|
||||
selectedDocumentIds = [],
|
||||
onClearSelection = null,
|
||||
detailPanelOpen = false,
|
||||
onCloseDetailPanel = null,
|
||||
tenantId = null,
|
||||
viewId = 'default',
|
||||
previewEntries,
|
||||
@@ -788,8 +782,6 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
|
||||
onPromoteSelection,
|
||||
selectedDocumentIds,
|
||||
onClearSelection,
|
||||
detailPanelOpen,
|
||||
onCloseDetailPanel,
|
||||
documentLookup,
|
||||
resolveBaseMetrics,
|
||||
bringToFront,
|
||||
@@ -831,7 +823,6 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
|
||||
layoutRef,
|
||||
layoutSnapshot,
|
||||
onClearSelection,
|
||||
onCloseDetailPanel,
|
||||
onDocumentStackSelect,
|
||||
onEntryPointer,
|
||||
onPromoteSelection,
|
||||
@@ -847,7 +838,6 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
|
||||
resolveBaseMetrics,
|
||||
setDraggingId,
|
||||
selectedDocumentIds,
|
||||
detailPanelOpen,
|
||||
onInspectDocument,
|
||||
markLayoutDirty,
|
||||
tagDropTargetId,
|
||||
@@ -895,8 +885,6 @@ function DesktopWorkspaceView({
|
||||
onPromoteSelection,
|
||||
selectedDocumentIds,
|
||||
onClearSelection,
|
||||
detailPanelOpen,
|
||||
onCloseDetailPanel,
|
||||
documentLookup,
|
||||
resolveBaseMetrics,
|
||||
bringToFront,
|
||||
@@ -951,8 +939,6 @@ function DesktopWorkspaceView({
|
||||
onPromoteSelection,
|
||||
onInspectDocument,
|
||||
selectedDocumentIds,
|
||||
detailPanelOpen,
|
||||
onCloseDetailPanel,
|
||||
openOverlayForDoc,
|
||||
}) as {
|
||||
getCardPointerHandlers: (doc: DeskDocument) => React.HTMLAttributes<HTMLDivElement>;
|
||||
@@ -970,14 +956,6 @@ function DesktopWorkspaceView({
|
||||
}
|
||||
}, [focusShell, selectedDocumentIds.length]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!detailPanelOpen) {
|
||||
focusShell();
|
||||
}
|
||||
}, [detailPanelOpen, focusShell]);
|
||||
|
||||
|
||||
|
||||
const allSizesReady = items.every((doc) => Boolean(ensureDocumentSize(doc)));
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { SelectionFloatingPanel } from '../documents/SelectionFloatingActions';
|
||||
import { createDocumentsTableHeaderActions } from '../documents/DocumentsPanel';
|
||||
import createWorkspaceSurfaceConfig from '../documents/workspaceHeader';
|
||||
import DocumentViewerPanel from '../preview/DocumentViewerPanel';
|
||||
import DesktopWorkspace from './DesktopWorkspace';
|
||||
|
||||
type WorkspaceProps = Record<string, any>;
|
||||
|
||||
interface DetailProps extends Record<string, any> {
|
||||
onClose?: () => void;
|
||||
onOpenPreview?: () => void;
|
||||
tags?: unknown;
|
||||
}
|
||||
|
||||
interface CreateDesktopSurfaceArgs {
|
||||
workspaceProps?: WorkspaceProps | null;
|
||||
renderSidebarToggle?: () => ReactNode;
|
||||
detailProps?: DetailProps | null;
|
||||
detailOpen?: boolean;
|
||||
}
|
||||
|
||||
const createDesktopSurface = ({
|
||||
workspaceProps,
|
||||
renderSidebarToggle,
|
||||
detailProps = null,
|
||||
detailOpen = false,
|
||||
}: CreateDesktopSurfaceArgs) => {
|
||||
if (!workspaceProps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
currentFolderName,
|
||||
searchResults,
|
||||
onRefresh,
|
||||
viewMode,
|
||||
onViewModeChange,
|
||||
onDeleteSelection,
|
||||
tags,
|
||||
correspondents,
|
||||
documentLookup,
|
||||
tagLookupById,
|
||||
onBulkTagAdd,
|
||||
onBulkTagRemove,
|
||||
onBulkCorrespondentAdd,
|
||||
onBulkCorrespondentRemove,
|
||||
onBulkReanalyze,
|
||||
folderOptions,
|
||||
onMoveDocumentsToFolder,
|
||||
searchIncludeDescendants,
|
||||
onToggleSearchIncludeDescendants,
|
||||
} = workspaceProps;
|
||||
|
||||
const title = Array.isArray(searchResults) ? 'Search results' : currentFolderName;
|
||||
const subtitle = Array.isArray(searchResults)
|
||||
? `${searchResults.length} matching document${searchResults.length === 1 ? '' : 's'}`
|
||||
: null;
|
||||
|
||||
const actions = createDocumentsTableHeaderActions({
|
||||
viewMode: viewMode || 'desk',
|
||||
onViewModeChange,
|
||||
onRefresh,
|
||||
includeDescendants: searchIncludeDescendants,
|
||||
onToggleIncludeDescendants: onToggleSearchIncludeDescendants,
|
||||
});
|
||||
|
||||
const floatingActions = (
|
||||
<SelectionFloatingPanel
|
||||
documentLookup={documentLookup}
|
||||
tags={tags}
|
||||
tagLookupById={tagLookupById}
|
||||
correspondents={correspondents}
|
||||
onBulkTagAdd={onBulkTagAdd}
|
||||
onBulkTagRemove={onBulkTagRemove}
|
||||
onBulkCorrespondentAdd={onBulkCorrespondentAdd}
|
||||
onBulkCorrespondentRemove={onBulkCorrespondentRemove}
|
||||
onBulkReanalyze={onBulkReanalyze}
|
||||
onDeleteSelection={onDeleteSelection}
|
||||
folderOptions={folderOptions}
|
||||
onMoveDocumentsToFolder={onMoveDocumentsToFolder}
|
||||
/>
|
||||
);
|
||||
|
||||
const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null;
|
||||
const detail = detailOpen && detailProps
|
||||
? (() => {
|
||||
const { onClose, onOpenPreview, tags: tagOptions, ...restDetailProps } = detailProps;
|
||||
return (
|
||||
<DocumentViewerPanel
|
||||
variant="sidebar"
|
||||
onCollapsePanel={onClose}
|
||||
onMaximizePanel={onOpenPreview}
|
||||
tagOptions={tagOptions}
|
||||
{...restDetailProps}
|
||||
/>
|
||||
);
|
||||
})()
|
||||
: null;
|
||||
const surfaceConfig = createWorkspaceSurfaceConfig({
|
||||
key: 'workspace',
|
||||
variant: 'workspace',
|
||||
title,
|
||||
subtitle,
|
||||
sidebarToggle,
|
||||
actions,
|
||||
breadcrumbs: workspaceProps?.breadcrumbs || null,
|
||||
selectionLabel: null,
|
||||
floatingActions,
|
||||
content: (
|
||||
<DesktopWorkspace
|
||||
{...workspaceProps}
|
||||
/>
|
||||
),
|
||||
detail,
|
||||
});
|
||||
|
||||
return {
|
||||
...surfaceConfig,
|
||||
supportsDetail: Boolean(detailProps),
|
||||
};
|
||||
};
|
||||
|
||||
export default createDesktopSurface;
|
||||
@@ -37,8 +37,6 @@ export const useDeskPointer = ({
|
||||
onPromoteSelection,
|
||||
onInspectDocument,
|
||||
selectedDocumentIds,
|
||||
detailPanelOpen,
|
||||
onCloseDetailPanel,
|
||||
openOverlayForDoc = null,
|
||||
}) => {
|
||||
const pointerIntentRef = useRef(null);
|
||||
@@ -404,12 +402,8 @@ export const useDeskPointer = ({
|
||||
return;
|
||||
}
|
||||
|
||||
if (detailPanelOpen) {
|
||||
event.preventDefault();
|
||||
safeInvoke(onCloseDetailPanel);
|
||||
}
|
||||
},
|
||||
[detailPanelOpen, onCloseDetailPanel, openOverlayForDoc, selectedDocumentIds],
|
||||
[openOverlayForDoc, selectedDocumentIds],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,255 +0,0 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
type Identifier = string | number;
|
||||
|
||||
type DocumentEntry = { id?: Identifier } & Record<string, unknown>;
|
||||
type InspectTarget = DocumentEntry | Identifier | null;
|
||||
|
||||
type WorkspaceViewMode = 'desk' | 'grid' | 'list' | string;
|
||||
|
||||
type DeskDocumentStackSelectHandler = (docIds: Identifier[]) => void;
|
||||
|
||||
type ResolveRowKey = (id: Identifier) => Identifier | string | null;
|
||||
|
||||
type ApplySelection = (
|
||||
keys: Array<Identifier | string>,
|
||||
options: { anchor?: Identifier | string | null; interactedKeys?: Array<Identifier | string> },
|
||||
) => void;
|
||||
|
||||
interface UseDeskWorkspacePropsArgs {
|
||||
documents?: DocumentEntry[];
|
||||
searchResults?: DocumentEntry[];
|
||||
breadcrumbs?: unknown[];
|
||||
currentFolderName?: string | null;
|
||||
documentsViewMode?: WorkspaceViewMode;
|
||||
handleDocumentsViewModeChange?: (mode: WorkspaceViewMode) => void;
|
||||
handleDeskExit?: () => void;
|
||||
refreshCurrentFolder?: () => Promise<void> | void;
|
||||
inspectDocument?: (doc: InspectTarget) => void;
|
||||
handleEntryPointerCore?: (...args: unknown[]) => void;
|
||||
promoteSelectionOrder?: (...args: unknown[]) => void;
|
||||
currentTenantId?: Identifier | null;
|
||||
selectedDocumentIds?: Identifier[];
|
||||
selectedFolderIds?: Identifier[];
|
||||
clearDocumentSelection?: () => void;
|
||||
detailPanelOpen?: boolean;
|
||||
handleDetailPanelClose?: () => void;
|
||||
resolveThumbnailUrlForDoc?: (doc: DocumentEntry) => string | null;
|
||||
handleDocumentTagDrop?: (...args: unknown[]) => void;
|
||||
ensureAssetUrl?: (...args: unknown[]) => void;
|
||||
getDocumentAsset?: (...args: unknown[]) => unknown;
|
||||
activeTagFilters?: Identifier[];
|
||||
handleDeleteSelection?: () => void;
|
||||
tags?: unknown[];
|
||||
correspondents?: unknown[];
|
||||
documentLookup?: unknown;
|
||||
tagLookupById?: unknown;
|
||||
handleBulkTagAddFromDetail?: (...args: unknown[]) => void;
|
||||
handleBulkTagRemoveFromDetail?: (...args: unknown[]) => void;
|
||||
handleBulkCorrespondentAdd?: (...args: unknown[]) => void;
|
||||
handleBulkCorrespondentRemove?: (...args: unknown[]) => void;
|
||||
handleBulkSelectionReanalyze?: (...args: unknown[]) => void;
|
||||
folderOptions?: unknown[];
|
||||
moveDocumentsToFolder?: (...args: unknown[]) => void;
|
||||
searchIncludeDescendants?: boolean;
|
||||
toggleSearchIncludeDescendants?: () => void;
|
||||
selectedEntries?: Array<Identifier | string>;
|
||||
selectionAnchorRef: { current: Identifier | string | null };
|
||||
applySelection: ApplySelection;
|
||||
resolveDocumentRowKey: ResolveRowKey;
|
||||
showingSearchResults?: boolean;
|
||||
searchQuery?: string;
|
||||
activeCorrespondentFilters?: Identifier[];
|
||||
selectedFolder?: Identifier | string | null;
|
||||
previewEntries?: Map<Identifier, { url?: string | null; contentType?: string | null }>;
|
||||
ensureDownloadUrl?: (documentId: Identifier, options?: { force?: boolean }) => Promise<{ url?: string | null; contentType?: string | null } | null>;
|
||||
}
|
||||
|
||||
const useDeskWorkspaceProps = ({
|
||||
documents,
|
||||
searchResults,
|
||||
breadcrumbs,
|
||||
currentFolderName,
|
||||
documentsViewMode = 'desk',
|
||||
handleDocumentsViewModeChange,
|
||||
handleDeskExit,
|
||||
refreshCurrentFolder,
|
||||
inspectDocument,
|
||||
handleEntryPointerCore,
|
||||
promoteSelectionOrder,
|
||||
currentTenantId,
|
||||
selectedDocumentIds,
|
||||
selectedFolderIds,
|
||||
clearDocumentSelection,
|
||||
detailPanelOpen,
|
||||
handleDetailPanelClose,
|
||||
resolveThumbnailUrlForDoc,
|
||||
handleDocumentTagDrop,
|
||||
ensureAssetUrl,
|
||||
getDocumentAsset,
|
||||
activeTagFilters = [],
|
||||
handleDeleteSelection,
|
||||
tags,
|
||||
correspondents,
|
||||
documentLookup,
|
||||
tagLookupById,
|
||||
handleBulkTagAddFromDetail,
|
||||
handleBulkTagRemoveFromDetail,
|
||||
handleBulkCorrespondentAdd,
|
||||
handleBulkCorrespondentRemove,
|
||||
handleBulkSelectionReanalyze,
|
||||
folderOptions,
|
||||
moveDocumentsToFolder,
|
||||
searchIncludeDescendants,
|
||||
toggleSearchIncludeDescendants,
|
||||
selectedEntries = [],
|
||||
selectionAnchorRef,
|
||||
applySelection,
|
||||
resolveDocumentRowKey,
|
||||
showingSearchResults = false,
|
||||
searchQuery = '',
|
||||
activeCorrespondentFilters = [],
|
||||
selectedFolder,
|
||||
previewEntries,
|
||||
ensureDownloadUrl,
|
||||
}: UseDeskWorkspacePropsArgs) => {
|
||||
const handleDeskDocumentStackSelect: DeskDocumentStackSelectHandler = useCallback(
|
||||
(docIds) => {
|
||||
if (!Array.isArray(docIds) || docIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rowKeys = docIds
|
||||
.map((id) => resolveDocumentRowKey(id))
|
||||
.filter((value): value is Identifier | string => Boolean(value));
|
||||
|
||||
if (!rowKeys.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextKeys = [...selectedEntries];
|
||||
rowKeys.forEach((key) => {
|
||||
if (!nextKeys.includes(key)) {
|
||||
nextKeys.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
const anchor = (rowKeys[0]
|
||||
|| selectionAnchorRef.current
|
||||
|| nextKeys[nextKeys.length - 1]) as Identifier | string | null;
|
||||
|
||||
applySelection(nextKeys, {
|
||||
anchor,
|
||||
interactedKeys: rowKeys,
|
||||
});
|
||||
},
|
||||
[applySelection, resolveDocumentRowKey, selectedEntries, selectionAnchorRef],
|
||||
);
|
||||
|
||||
const deskViewId = useMemo(() => {
|
||||
if (showingSearchResults) {
|
||||
const trimmedQuery = searchQuery.trim();
|
||||
const tagsKey = [...activeTagFilters].sort().join(',');
|
||||
const correspondentsKey = [...activeCorrespondentFilters].sort().join(',');
|
||||
return `search:${trimmedQuery}|tags:${tagsKey}|corr:${correspondentsKey}`;
|
||||
}
|
||||
|
||||
const folderKey = selectedFolder && selectedFolder !== '' ? selectedFolder : 'root';
|
||||
return `folder:${folderKey}`;
|
||||
}, [
|
||||
showingSearchResults,
|
||||
searchQuery,
|
||||
activeTagFilters,
|
||||
activeCorrespondentFilters,
|
||||
selectedFolder,
|
||||
]);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
documents,
|
||||
searchResults,
|
||||
breadcrumbs,
|
||||
currentFolderName,
|
||||
viewMode: documentsViewMode,
|
||||
onViewModeChange: handleDocumentsViewModeChange,
|
||||
onExit: handleDeskExit,
|
||||
onRefresh: refreshCurrentFolder,
|
||||
onInspectDocument: inspectDocument,
|
||||
onEntryPointer: handleEntryPointerCore,
|
||||
onDocumentStackSelect: handleDeskDocumentStackSelect,
|
||||
onPromoteSelection: promoteSelectionOrder,
|
||||
tenantId: currentTenantId,
|
||||
viewId: deskViewId,
|
||||
selectedDocumentIds,
|
||||
selectedFolderIds,
|
||||
onClearSelection: clearDocumentSelection,
|
||||
detailPanelOpen,
|
||||
onCloseDetailPanel: handleDetailPanelClose,
|
||||
resolveThumbnailUrl: resolveThumbnailUrlForDoc,
|
||||
onAssignTagToDocument: handleDocumentTagDrop,
|
||||
ensureAssetUrl,
|
||||
getDocumentAsset,
|
||||
activeTagIds: activeTagFilters,
|
||||
onDeleteSelection: handleDeleteSelection,
|
||||
tags,
|
||||
correspondents,
|
||||
documentLookup,
|
||||
tagLookupById,
|
||||
onBulkTagAdd: handleBulkTagAddFromDetail,
|
||||
onBulkTagRemove: handleBulkTagRemoveFromDetail,
|
||||
onBulkCorrespondentAdd: handleBulkCorrespondentAdd,
|
||||
onBulkCorrespondentRemove: handleBulkCorrespondentRemove,
|
||||
onBulkReanalyze: handleBulkSelectionReanalyze,
|
||||
folderOptions,
|
||||
onMoveDocumentsToFolder: moveDocumentsToFolder,
|
||||
searchIncludeDescendants,
|
||||
onToggleSearchIncludeDescendants: toggleSearchIncludeDescendants,
|
||||
previewEntries,
|
||||
ensureDownloadUrl,
|
||||
}),
|
||||
[
|
||||
documents,
|
||||
searchResults,
|
||||
breadcrumbs,
|
||||
currentFolderName,
|
||||
documentsViewMode,
|
||||
handleDocumentsViewModeChange,
|
||||
handleDeskExit,
|
||||
refreshCurrentFolder,
|
||||
inspectDocument,
|
||||
handleEntryPointerCore,
|
||||
handleDeskDocumentStackSelect,
|
||||
promoteSelectionOrder,
|
||||
currentTenantId,
|
||||
deskViewId,
|
||||
selectedDocumentIds,
|
||||
selectedFolderIds,
|
||||
clearDocumentSelection,
|
||||
detailPanelOpen,
|
||||
handleDetailPanelClose,
|
||||
resolveThumbnailUrlForDoc,
|
||||
handleDocumentTagDrop,
|
||||
ensureAssetUrl,
|
||||
getDocumentAsset,
|
||||
activeTagFilters,
|
||||
handleDeleteSelection,
|
||||
tags,
|
||||
correspondents,
|
||||
documentLookup,
|
||||
tagLookupById,
|
||||
handleBulkTagAddFromDetail,
|
||||
handleBulkTagRemoveFromDetail,
|
||||
handleBulkCorrespondentAdd,
|
||||
handleBulkCorrespondentRemove,
|
||||
handleBulkSelectionReanalyze,
|
||||
folderOptions,
|
||||
moveDocumentsToFolder,
|
||||
searchIncludeDescendants,
|
||||
toggleSearchIncludeDescendants,
|
||||
previewEntries,
|
||||
ensureDownloadUrl,
|
||||
],
|
||||
);
|
||||
};
|
||||
|
||||
export default useDeskWorkspaceProps;
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { ViewListIcon, ViewGridIcon, IconFileStack } from '../../ui/icons';
|
||||
import BreadcrumbTrail from '../../ui/BreadcrumbTrail';
|
||||
import DocumentsGrid from '../DocumentsGrid';
|
||||
import DocumentsList from '../DocumentsList';
|
||||
import DesktopWorkspace from '../../desktop/DesktopWorkspace';
|
||||
import { isTagTransferEvent } from '../tagTransfer';
|
||||
import PreviewZoomOverlay from '../../detail/PreviewZoomOverlay';
|
||||
import { isPointerModifierEvent, isPrimaryPointerEvent } from '../useEntryPointer';
|
||||
@@ -24,9 +23,8 @@ const defaultGetDocumentAsset = (_doc?: unknown, _type?: string) => null;
|
||||
export type PreviewEntryLike = { url?: string | null; contentType?: string | null };
|
||||
|
||||
const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
|
||||
currentFolderName,
|
||||
currentFolderName: _currentFolderName,
|
||||
breadcrumbs,
|
||||
onRefresh,
|
||||
subfolders,
|
||||
documents,
|
||||
searchResults,
|
||||
@@ -54,10 +52,10 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
|
||||
isSearchLoading = false,
|
||||
onDocumentTagDrop,
|
||||
viewMode = 'list',
|
||||
onViewModeChange,
|
||||
showHeader = true,
|
||||
onViewModeChange: _onViewModeChange,
|
||||
previewEntries,
|
||||
ensureDownloadUrl,
|
||||
deskWorkspaceProps = null,
|
||||
}) => {
|
||||
const {
|
||||
selectedEntries,
|
||||
@@ -403,20 +401,7 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
|
||||
],
|
||||
);
|
||||
|
||||
const isListView = viewMode === 'list';
|
||||
const gridIconSize = DEFAULT_GRID_ICON_SIZE;
|
||||
const handleSetViewMode = useCallback(
|
||||
(nextMode) => {
|
||||
if (!onViewModeChange) {
|
||||
return;
|
||||
}
|
||||
onViewModeChange(nextMode);
|
||||
if (scrollRef.current) {
|
||||
scrollRef.current.scrollTop = 0;
|
||||
}
|
||||
},
|
||||
[onViewModeChange],
|
||||
);
|
||||
useEffect(() => {
|
||||
if (scrollRef.current) {
|
||||
scrollRef.current.scrollTop = 0;
|
||||
@@ -590,181 +575,152 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
|
||||
const showDefaultEmptyState = !showingSearchResults && !isFilterActive && entries.length === 0;
|
||||
const showListSearchEmptyState = showingSearchResults && !hasDocumentEntries && !isGridView && !isSearchLoading;
|
||||
const showGridSearchEmptyState = isGridView && showingSearchResults && !hasDocumentEntries && !isSearchLoading;
|
||||
const breadcrumbEntries = useMemo(() => (Array.isArray(breadcrumbs) ? breadcrumbs.filter(Boolean) : []), [breadcrumbs]);
|
||||
const trailEntries = useMemo(() => {
|
||||
if (!breadcrumbEntries.length) {
|
||||
return [{ id: 'current-folder', label: currentFolderName }];
|
||||
}
|
||||
const lastIndex = breadcrumbEntries.length - 1;
|
||||
return breadcrumbEntries.map((crumb, index) => ({
|
||||
id: crumb.id ?? index,
|
||||
label: crumb.name ?? crumb.label ?? crumb.title ?? '',
|
||||
onClick: index < lastIndex && onFolderSelect
|
||||
? () => onFolderSelect(crumb.id)
|
||||
: null,
|
||||
}));
|
||||
}, [breadcrumbEntries, currentFolderName, onFolderSelect]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<section
|
||||
className={`documents-panel documents-panel--view-${isGridView ? 'grid' : 'list'}`}
|
||||
>
|
||||
{showHeader ? (
|
||||
<div className="panel-section__header">
|
||||
<div className="panel-section__titles">
|
||||
<h2 className="documents-panel__title">
|
||||
<BreadcrumbTrail
|
||||
entries={trailEntries}
|
||||
className="documents-panel__breadcrumbs"
|
||||
separator="/"
|
||||
/>
|
||||
</h2>
|
||||
{showingSearchResults && (
|
||||
<div className="panel-section__subtitle">Search results</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="header-actions">
|
||||
<div className="view-toggle" role="group" aria-label="Change view">
|
||||
<button
|
||||
type="button"
|
||||
className={`view-toggle__button${isListView ? ' active' : ''}`}
|
||||
onClick={() => handleSetViewMode('list')}
|
||||
aria-pressed={isListView}
|
||||
title="List view"
|
||||
>
|
||||
<ViewListIcon className="view-toggle__icon" size={18} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`view-toggle__button${isGridView ? ' active' : ''}`}
|
||||
onClick={() => handleSetViewMode('grid')}
|
||||
aria-pressed={isGridView}
|
||||
title="Icons view"
|
||||
>
|
||||
<ViewGridIcon className="view-toggle__icon" size={18} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`view-toggle__button${isDeskView ? ' active' : ''}`}
|
||||
onClick={() => handleSetViewMode('desk')}
|
||||
aria-pressed={isDeskView}
|
||||
title="Desk view"
|
||||
>
|
||||
<IconFileStack className="view-toggle__icon" size={18} />
|
||||
</button>
|
||||
const renderBody = () => {
|
||||
if (isDeskView) {
|
||||
return (
|
||||
<div className="panel-section__body panel-section__body--desk">
|
||||
{deskWorkspaceProps ? (
|
||||
<DesktopWorkspace {...deskWorkspaceProps} />
|
||||
) : (
|
||||
<div className="empty-state empty-state--global">
|
||||
Desk view is unavailable.
|
||||
</div>
|
||||
<button className="secondary" onClick={onRefresh}>
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
{showDefaultEmptyState ? (
|
||||
);
|
||||
}
|
||||
|
||||
if (showDefaultEmptyState) {
|
||||
return (
|
||||
<div className="panel-section__body">
|
||||
<div className="empty-state">
|
||||
Drop files anywhere or onto a folder to upload documents.
|
||||
</div>
|
||||
</div>
|
||||
) : showGridSearchEmptyState ? (
|
||||
);
|
||||
}
|
||||
|
||||
if (showGridSearchEmptyState) {
|
||||
return (
|
||||
<div className="panel-section__body">
|
||||
<div className="empty-state empty-state--global">
|
||||
No documents match the current filters.
|
||||
</div>
|
||||
</div>
|
||||
) : showListSearchEmptyState ? (
|
||||
);
|
||||
}
|
||||
|
||||
if (showListSearchEmptyState) {
|
||||
return (
|
||||
<div className="panel-section__body">
|
||||
<div className="empty-state">No documents match the current filters.</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="panel-section__body">
|
||||
<div
|
||||
ref={assignScrollRef}
|
||||
className="documents-scroll"
|
||||
tabIndex={0}
|
||||
onFocus={(event) => {
|
||||
if (event.target === scrollRef.current) {
|
||||
handlePanelFocus();
|
||||
}
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.target !== scrollRef.current) {
|
||||
return;
|
||||
}
|
||||
handlePanelKeyDown(event);
|
||||
}}
|
||||
onClick={(event) => {
|
||||
if (event.target === event.currentTarget) {
|
||||
clearSelection();
|
||||
}
|
||||
}}
|
||||
aria-activedescendant={isGridView ? undefined : activeDescendantId}
|
||||
>
|
||||
{isGridView ? (
|
||||
<DocumentsGrid
|
||||
entries={entries}
|
||||
draggingDocumentIdsSet={draggingSet}
|
||||
draggedFolderId={draggedFolderId}
|
||||
onFolderClick={handleFolderClick}
|
||||
onFolderSelect={onFolderSelect}
|
||||
onFolderDragOver={onFolderDragOver}
|
||||
onFolderDragLeave={onFolderDragLeave}
|
||||
onFolderDrop={onFolderDrop}
|
||||
onFolderDragStart={onFolderDragStart}
|
||||
onFolderDragEnd={onFolderDragEnd}
|
||||
onDocumentClick={handleDocumentClick}
|
||||
onDocumentActivate={handleDocumentActivate}
|
||||
onDocumentDragStart={handleDocumentDragStartLocal}
|
||||
onDocumentDragEnd={handleDocumentDragEndLocal}
|
||||
onDocumentTagDragOver={handleDocumentTagDragOver}
|
||||
onDocumentTagDragLeave={handleDocumentTagDragLeave}
|
||||
onDocumentTagDrop={handleDocumentTagDrop}
|
||||
ensureAssetUrl={ensureAssetUrl}
|
||||
getDocumentAsset={getDocumentAsset}
|
||||
gridIconSize={gridIconSize}
|
||||
tagLookupById={tagLookupById}
|
||||
onTagClick={onTagClick}
|
||||
scrollRef={scrollRef}
|
||||
onCorrespondentClick={onCorrespondentClick}
|
||||
activeCorrespondentIdSet={activeCorrespondentIdSet}
|
||||
onDocumentRename={onDocumentRename}
|
||||
onFolderRename={onFolderRename}
|
||||
/>
|
||||
) : !showTableRows ? null : (
|
||||
<DocumentsList
|
||||
entries={entries}
|
||||
focusedRowKey={focusedRowKey}
|
||||
draggingDocumentIdsSet={draggingSet}
|
||||
draggedFolderId={draggedFolderId}
|
||||
ensureAssetUrl={ensureAssetUrl}
|
||||
getDocumentAsset={getDocumentAsset}
|
||||
onFolderClick={handleFolderClick}
|
||||
onFolderSelect={onFolderSelect}
|
||||
onFolderDragOver={onFolderDragOver}
|
||||
onFolderDragLeave={onFolderDragLeave}
|
||||
onFolderDrop={onFolderDrop}
|
||||
onFolderDragStart={onFolderDragStart}
|
||||
onFolderDragEnd={onFolderDragEnd}
|
||||
onFolderRename={onFolderRename}
|
||||
onDocumentClick={handleDocumentClick}
|
||||
onDocumentActivate={handleDocumentActivate}
|
||||
onDocumentDragStart={handleDocumentDragStartLocal}
|
||||
onDocumentDragEnd={handleDocumentDragEndLocal}
|
||||
onDocumentTagDragOver={handleDocumentTagDragOver}
|
||||
onDocumentTagDragLeave={handleDocumentTagDragLeave}
|
||||
onDocumentTagDrop={handleDocumentTagDrop}
|
||||
onDocumentRename={onDocumentRename}
|
||||
tagLookupById={tagLookupById}
|
||||
onTagClick={onTagClick}
|
||||
onCorrespondentClick={onCorrespondentClick}
|
||||
activeCorrespondentIdSet={activeCorrespondentIdSet}
|
||||
scrollRef={scrollRef}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!showTableRows) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="panel-section__body">
|
||||
<div
|
||||
ref={assignScrollRef}
|
||||
className="documents-scroll"
|
||||
tabIndex={0}
|
||||
onFocus={(event) => {
|
||||
if (event.target === scrollRef.current) {
|
||||
handlePanelFocus();
|
||||
}
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.target !== scrollRef.current) {
|
||||
return;
|
||||
}
|
||||
handlePanelKeyDown(event);
|
||||
}}
|
||||
onClick={(event) => {
|
||||
if (event.target === event.currentTarget) {
|
||||
clearSelection();
|
||||
}
|
||||
}}
|
||||
aria-activedescendant={isGridView ? undefined : activeDescendantId}
|
||||
>
|
||||
{isGridView ? (
|
||||
<DocumentsGrid
|
||||
entries={entries}
|
||||
draggingDocumentIdsSet={draggingSet}
|
||||
draggedFolderId={draggedFolderId}
|
||||
onFolderClick={handleFolderClick}
|
||||
onFolderSelect={onFolderSelect}
|
||||
onFolderDragOver={onFolderDragOver}
|
||||
onFolderDragLeave={onFolderDragLeave}
|
||||
onFolderDrop={onFolderDrop}
|
||||
onFolderDragStart={onFolderDragStart}
|
||||
onFolderDragEnd={onFolderDragEnd}
|
||||
onDocumentClick={handleDocumentClick}
|
||||
onDocumentActivate={handleDocumentActivate}
|
||||
onDocumentDragStart={handleDocumentDragStartLocal}
|
||||
onDocumentDragEnd={handleDocumentDragEndLocal}
|
||||
onDocumentTagDragOver={handleDocumentTagDragOver}
|
||||
onDocumentTagDragLeave={handleDocumentTagDragLeave}
|
||||
onDocumentTagDrop={handleDocumentTagDrop}
|
||||
ensureAssetUrl={ensureAssetUrl}
|
||||
getDocumentAsset={getDocumentAsset}
|
||||
gridIconSize={gridIconSize}
|
||||
tagLookupById={tagLookupById}
|
||||
onTagClick={onTagClick}
|
||||
scrollRef={scrollRef}
|
||||
onCorrespondentClick={onCorrespondentClick}
|
||||
activeCorrespondentIdSet={activeCorrespondentIdSet}
|
||||
onDocumentRename={onDocumentRename}
|
||||
onFolderRename={onFolderRename}
|
||||
/>
|
||||
) : (
|
||||
<DocumentsList
|
||||
entries={entries}
|
||||
focusedRowKey={focusedRowKey}
|
||||
draggingDocumentIdsSet={draggingSet}
|
||||
draggedFolderId={draggedFolderId}
|
||||
ensureAssetUrl={ensureAssetUrl}
|
||||
getDocumentAsset={getDocumentAsset}
|
||||
onFolderClick={handleFolderClick}
|
||||
onFolderSelect={onFolderSelect}
|
||||
onFolderDragOver={onFolderDragOver}
|
||||
onFolderDragLeave={onFolderDragLeave}
|
||||
onFolderDrop={onFolderDrop}
|
||||
onFolderDragStart={onFolderDragStart}
|
||||
onFolderDragEnd={onFolderDragEnd}
|
||||
onFolderRename={onFolderRename}
|
||||
onDocumentClick={handleDocumentClick}
|
||||
onDocumentActivate={handleDocumentActivate}
|
||||
onDocumentDragStart={handleDocumentDragStartLocal}
|
||||
onDocumentDragEnd={handleDocumentDragEndLocal}
|
||||
onDocumentTagDragOver={handleDocumentTagDragOver}
|
||||
onDocumentTagDragLeave={handleDocumentTagDragLeave}
|
||||
onDocumentTagDrop={handleDocumentTagDrop}
|
||||
onDocumentRename={onDocumentRename}
|
||||
tagLookupById={tagLookupById}
|
||||
onTagClick={onTagClick}
|
||||
onCorrespondentClick={onCorrespondentClick}
|
||||
activeCorrespondentIdSet={activeCorrespondentIdSet}
|
||||
scrollRef={scrollRef}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const panelVariant = isDeskView ? 'desk' : isGridView ? 'grid' : 'list';
|
||||
|
||||
return (
|
||||
<>
|
||||
<section
|
||||
className={`documents-panel documents-panel--view-${panelVariant}`}
|
||||
>
|
||||
{renderBody()}
|
||||
</section>
|
||||
<PreviewZoomOverlay
|
||||
open={Boolean(zoomDisplay?.url)}
|
||||
onClose={closePreviewOverlay}
|
||||
|
||||
@@ -47,6 +47,8 @@ const createDocumentsSurface = ({
|
||||
onInspectDocument,
|
||||
} = tableProps;
|
||||
|
||||
const isDeskView = viewMode === 'desk';
|
||||
|
||||
const title = Array.isArray(searchResults) ? 'Search results' : currentFolderName;
|
||||
const subtitle = Array.isArray(searchResults)
|
||||
? `${searchResults.length} matching document${searchResults.length === 1 ? '' : 's'}`
|
||||
@@ -99,8 +101,8 @@ const createDocumentsSurface = ({
|
||||
: null;
|
||||
|
||||
return createWorkspaceSurfaceConfig({
|
||||
key: 'documents',
|
||||
variant: 'documents',
|
||||
key: isDeskView ? 'workspace' : 'documents',
|
||||
variant: isDeskView ? 'workspace' : 'documents',
|
||||
title,
|
||||
subtitle,
|
||||
sidebarToggle,
|
||||
@@ -111,7 +113,6 @@ const createDocumentsSurface = ({
|
||||
content: (
|
||||
<DocumentsPanel
|
||||
{...tableProps}
|
||||
showHeader={false}
|
||||
onInspectDocument={onInspectDocument}
|
||||
/>
|
||||
),
|
||||
|
||||
@@ -18,7 +18,6 @@ import useDocumentsSelection from '../../documents/hooks/useDocumentsSelection';
|
||||
import useBulkDocumentActions from '../../documents/hooks/useBulkDocumentActions';
|
||||
import useDocumentsPanelProps from '../../documents/hooks/useDocumentsPanelProps';
|
||||
import useDocumentPreview from '../../app/useDocumentPreview';
|
||||
import useDeskWorkspaceProps from '../../desktop/useDeskWorkspaceProps';
|
||||
import useSidebarProps from '../../sidebar/useSidebarProps';
|
||||
import {
|
||||
ASSET_PRESIGN_TTL_MS,
|
||||
@@ -97,7 +96,6 @@ interface UseDocumentsWorkspaceOptions {
|
||||
onToggleSearchIncludeDescendants?: () => void;
|
||||
onSetSearchIncludeDescendants?: (value: boolean) => void;
|
||||
sortRefreshReadyRef?: MutableRefObject<boolean>;
|
||||
handleDeskExit?: () => void;
|
||||
}
|
||||
|
||||
const useDocumentsWorkspace = ({
|
||||
@@ -113,14 +111,12 @@ const useDocumentsWorkspace = ({
|
||||
onToggleSearchIncludeDescendants,
|
||||
onSetSearchIncludeDescendants,
|
||||
sortRefreshReadyRef,
|
||||
handleDeskExit,
|
||||
}: UseDocumentsWorkspaceOptions = {}) => {
|
||||
const handleDocumentsViewModeChange = onDocumentsViewModeChange || noop;
|
||||
const handleDocumentsSortFieldChange = onDocumentsSortFieldChange || noop;
|
||||
const handleDocumentsSortDirectionToggle = onDocumentsSortDirectionToggle || noop;
|
||||
const toggleSearchIncludeDescendants = onToggleSearchIncludeDescendants || noop;
|
||||
const setSearchIncludeDescendants = onSetSearchIncludeDescendants || noop;
|
||||
const handleDeskExitSafe = handleDeskExit || noop;
|
||||
|
||||
const fallbackSortFieldRef = useRef(documentsSortField);
|
||||
const activeSortFieldRef = documentsSortFieldRef || fallbackSortFieldRef;
|
||||
@@ -1236,12 +1232,10 @@ const useDocumentsWorkspace = ({
|
||||
detailPanelProps,
|
||||
detailPanelOpen,
|
||||
openDetailPanel,
|
||||
handleDetailPanelClose,
|
||||
inspectDocument,
|
||||
previewActive,
|
||||
previewWorkspaceDocument,
|
||||
previewWorkspaceEntry,
|
||||
resolveThumbnailUrlForDoc,
|
||||
resolveFolderPath,
|
||||
} = useDetailWorkspace({
|
||||
documents,
|
||||
@@ -1404,7 +1398,97 @@ const useDocumentsWorkspace = ({
|
||||
});
|
||||
|
||||
|
||||
const documentsTableProps = useDocumentsPanelProps({
|
||||
const handleDeskDocumentStackSelect = useCallback(
|
||||
(docIds: Array<Identifier | string>) => {
|
||||
if (!Array.isArray(docIds) || docIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rowKeys = docIds
|
||||
.map((id) => resolveDocumentRowKey(id as Identifier))
|
||||
.filter((value): value is string => typeof value === 'string');
|
||||
|
||||
if (!rowKeys.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextKeys = [...selectedEntries];
|
||||
rowKeys.forEach((key) => {
|
||||
if (!nextKeys.includes(key)) {
|
||||
nextKeys.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
const anchor = (rowKeys[0]
|
||||
|| selectionAnchorRef.current
|
||||
|| nextKeys[nextKeys.length - 1]) as Identifier | string | null;
|
||||
|
||||
applySelection(nextKeys, {
|
||||
anchor,
|
||||
interactedKeys: rowKeys,
|
||||
});
|
||||
},
|
||||
[applySelection, selectedEntries, selectionAnchorRef],
|
||||
);
|
||||
|
||||
const deskViewId = useMemo(() => {
|
||||
if (showingSearchResults) {
|
||||
const trimmedQuery = searchQuery.trim();
|
||||
const tagsKey = [...activeTagFilters].sort().join(',');
|
||||
const correspondentsKey = [...activeCorrespondentFilters].sort().join(',');
|
||||
return `search:${trimmedQuery}|tags:${tagsKey}|corr:${correspondentsKey}`;
|
||||
}
|
||||
|
||||
const folderKey = selectedFolder && selectedFolder !== '' ? selectedFolder : 'root';
|
||||
return `folder:${folderKey}`;
|
||||
}, [
|
||||
showingSearchResults,
|
||||
searchQuery,
|
||||
activeTagFilters,
|
||||
activeCorrespondentFilters,
|
||||
selectedFolder,
|
||||
]);
|
||||
|
||||
const deskWorkspaceProps = useMemo(
|
||||
() => ({
|
||||
documents,
|
||||
searchResults,
|
||||
onInspectDocument: inspectDocumentForDesk,
|
||||
onEntryPointer: handleEntryPointerCore,
|
||||
onDocumentStackSelect: handleDeskDocumentStackSelect,
|
||||
onPromoteSelection: promoteSelectionOrder,
|
||||
onAssignTagToDocument: handleDocumentTagDrop,
|
||||
ensureAssetUrl,
|
||||
getDocumentAsset,
|
||||
activeTagIds: activeTagFilters,
|
||||
selectedDocumentIds,
|
||||
onClearSelection: clearDocumentSelection,
|
||||
tenantId: currentTenantId,
|
||||
viewId: deskViewId,
|
||||
previewEntries,
|
||||
ensureDownloadUrl,
|
||||
}),
|
||||
[
|
||||
documents,
|
||||
searchResults,
|
||||
inspectDocumentForDesk,
|
||||
handleEntryPointerCore,
|
||||
handleDeskDocumentStackSelect,
|
||||
promoteSelectionOrder,
|
||||
handleDocumentTagDrop,
|
||||
ensureAssetUrl,
|
||||
getDocumentAsset,
|
||||
activeTagFilters,
|
||||
selectedDocumentIds,
|
||||
clearDocumentSelection,
|
||||
currentTenantId,
|
||||
deskViewId,
|
||||
previewEntries,
|
||||
ensureDownloadUrl,
|
||||
],
|
||||
);
|
||||
|
||||
const documentsPanelProps = useDocumentsPanelProps({
|
||||
currentFolderName,
|
||||
breadcrumbs,
|
||||
refreshCurrentFolder,
|
||||
@@ -1457,6 +1541,14 @@ const useDocumentsWorkspace = ({
|
||||
ensureDownloadUrl,
|
||||
});
|
||||
|
||||
const documentsTableProps = useMemo(
|
||||
() => ({
|
||||
...documentsPanelProps,
|
||||
deskWorkspaceProps,
|
||||
}),
|
||||
[documentsPanelProps, deskWorkspaceProps],
|
||||
);
|
||||
|
||||
const sidebarProps = useSidebarProps({
|
||||
folderNodes,
|
||||
folderClickHandlers,
|
||||
@@ -1495,57 +1587,6 @@ const useDocumentsWorkspace = ({
|
||||
uploadQueue,
|
||||
});
|
||||
|
||||
|
||||
|
||||
const deskWorkspaceProps = useDeskWorkspaceProps({
|
||||
documents,
|
||||
searchResults,
|
||||
breadcrumbs,
|
||||
currentFolderName,
|
||||
documentsViewMode,
|
||||
handleDocumentsViewModeChange,
|
||||
handleDeskExit: handleDeskExitSafe,
|
||||
refreshCurrentFolder,
|
||||
inspectDocument: inspectDocumentForDesk,
|
||||
handleEntryPointerCore,
|
||||
promoteSelectionOrder,
|
||||
currentTenantId,
|
||||
selectedDocumentIds,
|
||||
selectedFolderIds,
|
||||
clearDocumentSelection,
|
||||
detailPanelOpen,
|
||||
handleDetailPanelClose,
|
||||
resolveThumbnailUrlForDoc,
|
||||
handleDocumentTagDrop,
|
||||
ensureAssetUrl,
|
||||
getDocumentAsset,
|
||||
activeTagFilters,
|
||||
handleDeleteSelection,
|
||||
tags,
|
||||
correspondents,
|
||||
documentLookup,
|
||||
tagLookupById,
|
||||
handleBulkTagAddFromDetail,
|
||||
handleBulkTagRemoveFromDetail,
|
||||
handleBulkCorrespondentAdd,
|
||||
handleBulkCorrespondentRemove,
|
||||
handleBulkSelectionReanalyze,
|
||||
folderOptions,
|
||||
moveDocumentsToFolder,
|
||||
searchIncludeDescendants,
|
||||
toggleSearchIncludeDescendants,
|
||||
selectedEntries,
|
||||
selectionAnchorRef,
|
||||
applySelection,
|
||||
resolveDocumentRowKey,
|
||||
showingSearchResults,
|
||||
searchQuery,
|
||||
activeCorrespondentFilters,
|
||||
selectedFolder,
|
||||
previewEntries,
|
||||
ensureDownloadUrl,
|
||||
});
|
||||
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
token,
|
||||
@@ -1585,7 +1626,6 @@ const useDocumentsWorkspace = ({
|
||||
documentsTableProps,
|
||||
detailPanelProps,
|
||||
documentsViewMode,
|
||||
deskWorkspaceProps,
|
||||
ensurePreviewData,
|
||||
ensureAssetUrl,
|
||||
resolveFolderPath,
|
||||
@@ -1637,7 +1677,6 @@ const useDocumentsWorkspace = ({
|
||||
documentsTableProps,
|
||||
detailPanelProps,
|
||||
documentsViewMode,
|
||||
deskWorkspaceProps,
|
||||
ensurePreviewData,
|
||||
ensureAssetUrl,
|
||||
resolveFolderPath,
|
||||
|
||||
@@ -93,6 +93,19 @@ const srgbChannelToLinear = (value: number) => {
|
||||
return ((normalized + 0.055) / 1.055) ** 2.4;
|
||||
};
|
||||
|
||||
export const hexToRgb = (input?: string): (RgbColor & { hex: string }) | null => {
|
||||
if (!input) return null;
|
||||
const match = HEX_COLOR_PATTERN.exec(input.trim());
|
||||
if (!match) return null;
|
||||
const value = parseInt(match[1], 16);
|
||||
return {
|
||||
r: (value >> 16) & 0xff,
|
||||
g: (value >> 8) & 0xff,
|
||||
b: value & 0xff,
|
||||
hex: `#${match[1].toLowerCase()}`,
|
||||
};
|
||||
};
|
||||
|
||||
const hexToOklch = (input?: string) => {
|
||||
const rgb = hexToRgb(input);
|
||||
if (!rgb) {
|
||||
@@ -150,19 +163,6 @@ const buildTagStyle = (backgroundHex: string) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const hexToRgb = (input?: string): (RgbColor & { hex: string }) | null => {
|
||||
if (!input) return null;
|
||||
const match = HEX_COLOR_PATTERN.exec(input.trim());
|
||||
if (!match) return null;
|
||||
const value = parseInt(match[1], 16);
|
||||
return {
|
||||
r: (value >> 16) & 0xff,
|
||||
g: (value >> 8) & 0xff,
|
||||
b: value & 0xff,
|
||||
hex: `#${match[1].toLowerCase()}`,
|
||||
};
|
||||
};
|
||||
|
||||
export const relativeLuminance = ({ r, g, b }: RgbColor): number => {
|
||||
const toLinear = (channel) => {
|
||||
const normalized = channel / 255;
|
||||
@@ -295,7 +295,6 @@ export const getTagColorStyle = (hex) => {
|
||||
adjustments.forEach((delta) => registerCandidate(baseHsl.l + delta));
|
||||
|
||||
let bestBackground = baseHex;
|
||||
let bestText = baseText;
|
||||
let bestRatio = baseRatio;
|
||||
|
||||
if (bestRatio >= TARGET_RATIO) {
|
||||
@@ -303,7 +302,6 @@ export const getTagColorStyle = (hex) => {
|
||||
}
|
||||
|
||||
let compliantBackground = null;
|
||||
let compliantText = null;
|
||||
let compliantDelta = Infinity;
|
||||
let compliantRatio = -Infinity;
|
||||
|
||||
@@ -314,7 +312,6 @@ export const getTagColorStyle = (hex) => {
|
||||
|
||||
if (ratio > bestRatio) {
|
||||
bestBackground = candidateHex;
|
||||
bestText = textHex;
|
||||
bestRatio = ratio;
|
||||
}
|
||||
|
||||
@@ -330,7 +327,6 @@ export const getTagColorStyle = (hex) => {
|
||||
|| (isSimilarDistance && improvesRatio)
|
||||
) {
|
||||
compliantBackground = candidateHex;
|
||||
compliantText = textHex;
|
||||
compliantDelta = delta;
|
||||
compliantRatio = ratio;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user