cleanup
This commit is contained in:
@@ -33,7 +33,6 @@ const AppLayout: React.FC = () => {
|
|||||||
onToggleSearchIncludeDescendants: documentsPreferences.toggleSearchIncludeDescendants,
|
onToggleSearchIncludeDescendants: documentsPreferences.toggleSearchIncludeDescendants,
|
||||||
onSetSearchIncludeDescendants: documentsPreferences.setSearchIncludeDescendants,
|
onSetSearchIncludeDescendants: documentsPreferences.setSearchIncludeDescendants,
|
||||||
sortRefreshReadyRef: documentsPreferences.sortRefreshReadyRef,
|
sortRefreshReadyRef: documentsPreferences.sortRefreshReadyRef,
|
||||||
handleDeskExit: documentsPreferences.handleDeskExit,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (['logged-out', 'authenticating', 'selecting-tenant'].includes(appStatus)) {
|
if (['logged-out', 'authenticating', 'selecting-tenant'].includes(appStatus)) {
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ interface DocumentsRouteAppShell {
|
|||||||
documentsTableProps?: DocumentsTableProps | null;
|
documentsTableProps?: DocumentsTableProps | null;
|
||||||
detailPanelProps?: Record<string, unknown> | null;
|
detailPanelProps?: Record<string, unknown> | null;
|
||||||
detailPanelOpen?: boolean;
|
detailPanelOpen?: boolean;
|
||||||
documentsViewMode?: string;
|
|
||||||
deskWorkspaceProps?: Record<string, unknown> | null;
|
|
||||||
openTagsModal?: () => void;
|
openTagsModal?: () => void;
|
||||||
openCorrespondentsModal?: () => void;
|
openCorrespondentsModal?: () => void;
|
||||||
previewWorkspaceDocument?: unknown;
|
previewWorkspaceDocument?: unknown;
|
||||||
@@ -55,8 +53,6 @@ const DocumentsRouteContent: React.FC = () => {
|
|||||||
documentsTableProps,
|
documentsTableProps,
|
||||||
detailPanelProps,
|
detailPanelProps,
|
||||||
detailPanelOpen,
|
detailPanelOpen,
|
||||||
documentsViewMode,
|
|
||||||
deskWorkspaceProps,
|
|
||||||
openTagsModal,
|
openTagsModal,
|
||||||
openCorrespondentsModal,
|
openCorrespondentsModal,
|
||||||
previewWorkspaceDocument,
|
previewWorkspaceDocument,
|
||||||
@@ -106,8 +102,6 @@ const DocumentsRouteContent: React.FC = () => {
|
|||||||
documentsTableProps,
|
documentsTableProps,
|
||||||
detailPanelProps,
|
detailPanelProps,
|
||||||
detailPanelOpen,
|
detailPanelOpen,
|
||||||
viewMode: documentsViewMode,
|
|
||||||
deskWorkspaceProps,
|
|
||||||
previewWorkspaceDocument,
|
previewWorkspaceDocument,
|
||||||
previewWorkspaceEntry,
|
previewWorkspaceEntry,
|
||||||
previewDocumentId,
|
previewDocumentId,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type { ReactNode } from 'react';
|
|||||||
import { SidebarExpandIcon } from '../ui/icons';
|
import { SidebarExpandIcon } from '../ui/icons';
|
||||||
import { createDocumentsSurface } from '../documents/DocumentsPanel';
|
import { createDocumentsSurface } from '../documents/DocumentsPanel';
|
||||||
import { createDocumentViewerSurface } from '../preview/DocumentViewerPanel';
|
import { createDocumentViewerSurface } from '../preview/DocumentViewerPanel';
|
||||||
import createDesktopSurface from '../desktop/createDesktopSurface';
|
|
||||||
import { usePanelManager } from './PanelManagerContext';
|
import { usePanelManager } from './PanelManagerContext';
|
||||||
|
|
||||||
type Identifier = string | number;
|
type Identifier = string | number;
|
||||||
@@ -16,8 +15,7 @@ type NotifyApiError = (error: unknown, fallbackMessage?: string) => void;
|
|||||||
|
|
||||||
type DocumentsSurface = ReturnType<typeof createDocumentsSurface> | null;
|
type DocumentsSurface = ReturnType<typeof createDocumentsSurface> | null;
|
||||||
type PreviewSurface = ReturnType<typeof createDocumentViewerSurface> | null;
|
type PreviewSurface = ReturnType<typeof createDocumentViewerSurface> | null;
|
||||||
type DesktopSurface = ReturnType<typeof createDesktopSurface> | null;
|
type WorkspaceSurface = DocumentsSurface | PreviewSurface | null;
|
||||||
type WorkspaceSurface = DocumentsSurface | PreviewSurface | DesktopSurface | null;
|
|
||||||
|
|
||||||
interface UseWorkspaceSurfaceArgs {
|
interface UseWorkspaceSurfaceArgs {
|
||||||
sidebarHidden?: boolean;
|
sidebarHidden?: boolean;
|
||||||
@@ -25,8 +23,6 @@ interface UseWorkspaceSurfaceArgs {
|
|||||||
documentsTableProps?: Record<string, any> | null;
|
documentsTableProps?: Record<string, any> | null;
|
||||||
detailPanelProps?: (Record<string, any> & { onClose?: () => void }) | null;
|
detailPanelProps?: (Record<string, any> & { onClose?: () => void }) | null;
|
||||||
detailPanelOpen?: boolean;
|
detailPanelOpen?: boolean;
|
||||||
viewMode?: string;
|
|
||||||
deskWorkspaceProps?: Record<string, any> | null;
|
|
||||||
previewWorkspaceDocument?: unknown;
|
previewWorkspaceDocument?: unknown;
|
||||||
previewWorkspaceEntry?: unknown;
|
previewWorkspaceEntry?: unknown;
|
||||||
previewDocumentId?: Identifier | null;
|
previewDocumentId?: Identifier | null;
|
||||||
@@ -48,8 +44,6 @@ export const useWorkspaceSurface = ({
|
|||||||
documentsTableProps,
|
documentsTableProps,
|
||||||
detailPanelProps,
|
detailPanelProps,
|
||||||
detailPanelOpen = false,
|
detailPanelOpen = false,
|
||||||
viewMode = 'list',
|
|
||||||
deskWorkspaceProps,
|
|
||||||
previewWorkspaceDocument,
|
previewWorkspaceDocument,
|
||||||
previewWorkspaceEntry,
|
previewWorkspaceEntry,
|
||||||
previewDocumentId,
|
previewDocumentId,
|
||||||
@@ -161,33 +155,12 @@ export const useWorkspaceSurface = ({
|
|||||||
detailPanelProps,
|
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>(() => {
|
const surface = useMemo<WorkspaceSurface>(() => {
|
||||||
if (showPreviewWorkspace) {
|
if (showPreviewWorkspace) {
|
||||||
return previewSurface;
|
return previewSurface;
|
||||||
}
|
}
|
||||||
if (viewMode === 'desk') {
|
|
||||||
return workspaceSurface;
|
|
||||||
}
|
|
||||||
return documentsSurface;
|
return documentsSurface;
|
||||||
}, [showPreviewWorkspace, viewMode, previewSurface, workspaceSurface, documentsSurface]);
|
}, [showPreviewWorkspace, previewSurface, documentsSurface]);
|
||||||
|
|
||||||
return { surface };
|
return { surface };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -130,8 +130,6 @@ interface DesktopWorkspaceProps {
|
|||||||
activeTagIds?: Array<Identifier | null>;
|
activeTagIds?: Array<Identifier | null>;
|
||||||
selectedDocumentIds?: Identifier[];
|
selectedDocumentIds?: Identifier[];
|
||||||
onClearSelection?: () => void;
|
onClearSelection?: () => void;
|
||||||
detailPanelOpen?: boolean;
|
|
||||||
onCloseDetailPanel?: () => void;
|
|
||||||
tenantId?: Identifier | null;
|
tenantId?: Identifier | null;
|
||||||
viewId?: string | null;
|
viewId?: string | null;
|
||||||
}
|
}
|
||||||
@@ -175,8 +173,6 @@ interface DesktopWorkspaceViewProps {
|
|||||||
onPromoteSelection?: DesktopWorkspaceProps['onPromoteSelection'];
|
onPromoteSelection?: DesktopWorkspaceProps['onPromoteSelection'];
|
||||||
selectedDocumentIds: Identifier[];
|
selectedDocumentIds: Identifier[];
|
||||||
onClearSelection?: DesktopWorkspaceProps['onClearSelection'];
|
onClearSelection?: DesktopWorkspaceProps['onClearSelection'];
|
||||||
detailPanelOpen: boolean;
|
|
||||||
onCloseDetailPanel?: DesktopWorkspaceProps['onCloseDetailPanel'];
|
|
||||||
documentLookup: Map<string, DeskDocument>;
|
documentLookup: Map<string, DeskDocument>;
|
||||||
resolveBaseMetrics: (doc: DeskDocument | null, cardWidth: number, cardHeight: number) => {
|
resolveBaseMetrics: (doc: DeskDocument | null, cardWidth: number, cardHeight: number) => {
|
||||||
baseWidth: number;
|
baseWidth: number;
|
||||||
@@ -210,8 +206,6 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
|
|||||||
activeTagIds = [],
|
activeTagIds = [],
|
||||||
selectedDocumentIds = [],
|
selectedDocumentIds = [],
|
||||||
onClearSelection = null,
|
onClearSelection = null,
|
||||||
detailPanelOpen = false,
|
|
||||||
onCloseDetailPanel = null,
|
|
||||||
tenantId = null,
|
tenantId = null,
|
||||||
viewId = 'default',
|
viewId = 'default',
|
||||||
previewEntries,
|
previewEntries,
|
||||||
@@ -788,8 +782,6 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
|
|||||||
onPromoteSelection,
|
onPromoteSelection,
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
onClearSelection,
|
onClearSelection,
|
||||||
detailPanelOpen,
|
|
||||||
onCloseDetailPanel,
|
|
||||||
documentLookup,
|
documentLookup,
|
||||||
resolveBaseMetrics,
|
resolveBaseMetrics,
|
||||||
bringToFront,
|
bringToFront,
|
||||||
@@ -831,7 +823,6 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
|
|||||||
layoutRef,
|
layoutRef,
|
||||||
layoutSnapshot,
|
layoutSnapshot,
|
||||||
onClearSelection,
|
onClearSelection,
|
||||||
onCloseDetailPanel,
|
|
||||||
onDocumentStackSelect,
|
onDocumentStackSelect,
|
||||||
onEntryPointer,
|
onEntryPointer,
|
||||||
onPromoteSelection,
|
onPromoteSelection,
|
||||||
@@ -847,7 +838,6 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
|
|||||||
resolveBaseMetrics,
|
resolveBaseMetrics,
|
||||||
setDraggingId,
|
setDraggingId,
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
detailPanelOpen,
|
|
||||||
onInspectDocument,
|
onInspectDocument,
|
||||||
markLayoutDirty,
|
markLayoutDirty,
|
||||||
tagDropTargetId,
|
tagDropTargetId,
|
||||||
@@ -895,8 +885,6 @@ function DesktopWorkspaceView({
|
|||||||
onPromoteSelection,
|
onPromoteSelection,
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
onClearSelection,
|
onClearSelection,
|
||||||
detailPanelOpen,
|
|
||||||
onCloseDetailPanel,
|
|
||||||
documentLookup,
|
documentLookup,
|
||||||
resolveBaseMetrics,
|
resolveBaseMetrics,
|
||||||
bringToFront,
|
bringToFront,
|
||||||
@@ -951,8 +939,6 @@ function DesktopWorkspaceView({
|
|||||||
onPromoteSelection,
|
onPromoteSelection,
|
||||||
onInspectDocument,
|
onInspectDocument,
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
detailPanelOpen,
|
|
||||||
onCloseDetailPanel,
|
|
||||||
openOverlayForDoc,
|
openOverlayForDoc,
|
||||||
}) as {
|
}) as {
|
||||||
getCardPointerHandlers: (doc: DeskDocument) => React.HTMLAttributes<HTMLDivElement>;
|
getCardPointerHandlers: (doc: DeskDocument) => React.HTMLAttributes<HTMLDivElement>;
|
||||||
@@ -970,14 +956,6 @@ function DesktopWorkspaceView({
|
|||||||
}
|
}
|
||||||
}, [focusShell, selectedDocumentIds.length]);
|
}, [focusShell, selectedDocumentIds.length]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!detailPanelOpen) {
|
|
||||||
focusShell();
|
|
||||||
}
|
|
||||||
}, [detailPanelOpen, focusShell]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const allSizesReady = items.every((doc) => Boolean(ensureDocumentSize(doc)));
|
const allSizesReady = items.every((doc) => Boolean(ensureDocumentSize(doc)));
|
||||||
|
|
||||||
return (
|
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,
|
onPromoteSelection,
|
||||||
onInspectDocument,
|
onInspectDocument,
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
detailPanelOpen,
|
|
||||||
onCloseDetailPanel,
|
|
||||||
openOverlayForDoc = null,
|
openOverlayForDoc = null,
|
||||||
}) => {
|
}) => {
|
||||||
const pointerIntentRef = useRef(null);
|
const pointerIntentRef = useRef(null);
|
||||||
@@ -404,12 +402,8 @@ export const useDeskPointer = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (detailPanelOpen) {
|
|
||||||
event.preventDefault();
|
|
||||||
safeInvoke(onCloseDetailPanel);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[detailPanelOpen, onCloseDetailPanel, openOverlayForDoc, selectedDocumentIds],
|
[openOverlayForDoc, selectedDocumentIds],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
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 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 DocumentsGrid from '../DocumentsGrid';
|
||||||
import DocumentsList from '../DocumentsList';
|
import DocumentsList from '../DocumentsList';
|
||||||
|
import DesktopWorkspace from '../../desktop/DesktopWorkspace';
|
||||||
import { isTagTransferEvent } from '../tagTransfer';
|
import { isTagTransferEvent } from '../tagTransfer';
|
||||||
import PreviewZoomOverlay from '../../detail/PreviewZoomOverlay';
|
import PreviewZoomOverlay from '../../detail/PreviewZoomOverlay';
|
||||||
import { isPointerModifierEvent, isPrimaryPointerEvent } from '../useEntryPointer';
|
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 };
|
export type PreviewEntryLike = { url?: string | null; contentType?: string | null };
|
||||||
|
|
||||||
const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
|
const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
|
||||||
currentFolderName,
|
currentFolderName: _currentFolderName,
|
||||||
breadcrumbs,
|
breadcrumbs,
|
||||||
onRefresh,
|
|
||||||
subfolders,
|
subfolders,
|
||||||
documents,
|
documents,
|
||||||
searchResults,
|
searchResults,
|
||||||
@@ -54,10 +52,10 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
|
|||||||
isSearchLoading = false,
|
isSearchLoading = false,
|
||||||
onDocumentTagDrop,
|
onDocumentTagDrop,
|
||||||
viewMode = 'list',
|
viewMode = 'list',
|
||||||
onViewModeChange,
|
onViewModeChange: _onViewModeChange,
|
||||||
showHeader = true,
|
|
||||||
previewEntries,
|
previewEntries,
|
||||||
ensureDownloadUrl,
|
ensureDownloadUrl,
|
||||||
|
deskWorkspaceProps = null,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
selectedEntries,
|
selectedEntries,
|
||||||
@@ -403,20 +401,7 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const isListView = viewMode === 'list';
|
|
||||||
const gridIconSize = DEFAULT_GRID_ICON_SIZE;
|
const gridIconSize = DEFAULT_GRID_ICON_SIZE;
|
||||||
const handleSetViewMode = useCallback(
|
|
||||||
(nextMode) => {
|
|
||||||
if (!onViewModeChange) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onViewModeChange(nextMode);
|
|
||||||
if (scrollRef.current) {
|
|
||||||
scrollRef.current.scrollTop = 0;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[onViewModeChange],
|
|
||||||
);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (scrollRef.current) {
|
if (scrollRef.current) {
|
||||||
scrollRef.current.scrollTop = 0;
|
scrollRef.current.scrollTop = 0;
|
||||||
@@ -590,93 +575,54 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
|
|||||||
const showDefaultEmptyState = !showingSearchResults && !isFilterActive && entries.length === 0;
|
const showDefaultEmptyState = !showingSearchResults && !isFilterActive && entries.length === 0;
|
||||||
const showListSearchEmptyState = showingSearchResults && !hasDocumentEntries && !isGridView && !isSearchLoading;
|
const showListSearchEmptyState = showingSearchResults && !hasDocumentEntries && !isGridView && !isSearchLoading;
|
||||||
const showGridSearchEmptyState = isGridView && showingSearchResults && !hasDocumentEntries && !isSearchLoading;
|
const showGridSearchEmptyState = isGridView && showingSearchResults && !hasDocumentEntries && !isSearchLoading;
|
||||||
const breadcrumbEntries = useMemo(() => (Array.isArray(breadcrumbs) ? breadcrumbs.filter(Boolean) : []), [breadcrumbs]);
|
const renderBody = () => {
|
||||||
const trailEntries = useMemo(() => {
|
if (isDeskView) {
|
||||||
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 (
|
return (
|
||||||
<>
|
<div className="panel-section__body panel-section__body--desk">
|
||||||
<section
|
{deskWorkspaceProps ? (
|
||||||
className={`documents-panel documents-panel--view-${isGridView ? 'grid' : 'list'}`}
|
<DesktopWorkspace {...deskWorkspaceProps} />
|
||||||
>
|
) : (
|
||||||
{showHeader ? (
|
<div className="empty-state empty-state--global">
|
||||||
<div className="panel-section__header">
|
Desk view is unavailable.
|
||||||
<div className="panel-section__titles">
|
</div>
|
||||||
<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>
|
||||||
<div className="header-actions">
|
);
|
||||||
<div className="view-toggle" role="group" aria-label="Change view">
|
}
|
||||||
<button
|
|
||||||
type="button"
|
if (showDefaultEmptyState) {
|
||||||
className={`view-toggle__button${isListView ? ' active' : ''}`}
|
return (
|
||||||
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>
|
|
||||||
</div>
|
|
||||||
<button className="secondary" onClick={onRefresh}>
|
|
||||||
Refresh
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{showDefaultEmptyState ? (
|
|
||||||
<div className="panel-section__body">
|
<div className="panel-section__body">
|
||||||
<div className="empty-state">
|
<div className="empty-state">
|
||||||
Drop files anywhere or onto a folder to upload documents.
|
Drop files anywhere or onto a folder to upload documents.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : showGridSearchEmptyState ? (
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showGridSearchEmptyState) {
|
||||||
|
return (
|
||||||
<div className="panel-section__body">
|
<div className="panel-section__body">
|
||||||
<div className="empty-state empty-state--global">
|
<div className="empty-state empty-state--global">
|
||||||
No documents match the current filters.
|
No documents match the current filters.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : showListSearchEmptyState ? (
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showListSearchEmptyState) {
|
||||||
|
return (
|
||||||
<div className="panel-section__body">
|
<div className="panel-section__body">
|
||||||
<div className="empty-state">No documents match the current filters.</div>
|
<div className="empty-state">No documents match the current filters.</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!showTableRows) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
<div className="panel-section__body">
|
<div className="panel-section__body">
|
||||||
<div
|
<div
|
||||||
ref={assignScrollRef}
|
ref={assignScrollRef}
|
||||||
@@ -730,7 +676,7 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
|
|||||||
onDocumentRename={onDocumentRename}
|
onDocumentRename={onDocumentRename}
|
||||||
onFolderRename={onFolderRename}
|
onFolderRename={onFolderRename}
|
||||||
/>
|
/>
|
||||||
) : !showTableRows ? null : (
|
) : (
|
||||||
<DocumentsList
|
<DocumentsList
|
||||||
entries={entries}
|
entries={entries}
|
||||||
focusedRowKey={focusedRowKey}
|
focusedRowKey={focusedRowKey}
|
||||||
@@ -763,7 +709,17 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const panelVariant = isDeskView ? 'desk' : isGridView ? 'grid' : 'list';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<section
|
||||||
|
className={`documents-panel documents-panel--view-${panelVariant}`}
|
||||||
|
>
|
||||||
|
{renderBody()}
|
||||||
</section>
|
</section>
|
||||||
<PreviewZoomOverlay
|
<PreviewZoomOverlay
|
||||||
open={Boolean(zoomDisplay?.url)}
|
open={Boolean(zoomDisplay?.url)}
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ const createDocumentsSurface = ({
|
|||||||
onInspectDocument,
|
onInspectDocument,
|
||||||
} = tableProps;
|
} = tableProps;
|
||||||
|
|
||||||
|
const isDeskView = viewMode === 'desk';
|
||||||
|
|
||||||
const title = Array.isArray(searchResults) ? 'Search results' : currentFolderName;
|
const title = Array.isArray(searchResults) ? 'Search results' : currentFolderName;
|
||||||
const subtitle = Array.isArray(searchResults)
|
const subtitle = Array.isArray(searchResults)
|
||||||
? `${searchResults.length} matching document${searchResults.length === 1 ? '' : 's'}`
|
? `${searchResults.length} matching document${searchResults.length === 1 ? '' : 's'}`
|
||||||
@@ -99,8 +101,8 @@ const createDocumentsSurface = ({
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
return createWorkspaceSurfaceConfig({
|
return createWorkspaceSurfaceConfig({
|
||||||
key: 'documents',
|
key: isDeskView ? 'workspace' : 'documents',
|
||||||
variant: 'documents',
|
variant: isDeskView ? 'workspace' : 'documents',
|
||||||
title,
|
title,
|
||||||
subtitle,
|
subtitle,
|
||||||
sidebarToggle,
|
sidebarToggle,
|
||||||
@@ -111,7 +113,6 @@ const createDocumentsSurface = ({
|
|||||||
content: (
|
content: (
|
||||||
<DocumentsPanel
|
<DocumentsPanel
|
||||||
{...tableProps}
|
{...tableProps}
|
||||||
showHeader={false}
|
|
||||||
onInspectDocument={onInspectDocument}
|
onInspectDocument={onInspectDocument}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import useDocumentsSelection from '../../documents/hooks/useDocumentsSelection';
|
|||||||
import useBulkDocumentActions from '../../documents/hooks/useBulkDocumentActions';
|
import useBulkDocumentActions from '../../documents/hooks/useBulkDocumentActions';
|
||||||
import useDocumentsPanelProps from '../../documents/hooks/useDocumentsPanelProps';
|
import useDocumentsPanelProps from '../../documents/hooks/useDocumentsPanelProps';
|
||||||
import useDocumentPreview from '../../app/useDocumentPreview';
|
import useDocumentPreview from '../../app/useDocumentPreview';
|
||||||
import useDeskWorkspaceProps from '../../desktop/useDeskWorkspaceProps';
|
|
||||||
import useSidebarProps from '../../sidebar/useSidebarProps';
|
import useSidebarProps from '../../sidebar/useSidebarProps';
|
||||||
import {
|
import {
|
||||||
ASSET_PRESIGN_TTL_MS,
|
ASSET_PRESIGN_TTL_MS,
|
||||||
@@ -97,7 +96,6 @@ interface UseDocumentsWorkspaceOptions {
|
|||||||
onToggleSearchIncludeDescendants?: () => void;
|
onToggleSearchIncludeDescendants?: () => void;
|
||||||
onSetSearchIncludeDescendants?: (value: boolean) => void;
|
onSetSearchIncludeDescendants?: (value: boolean) => void;
|
||||||
sortRefreshReadyRef?: MutableRefObject<boolean>;
|
sortRefreshReadyRef?: MutableRefObject<boolean>;
|
||||||
handleDeskExit?: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useDocumentsWorkspace = ({
|
const useDocumentsWorkspace = ({
|
||||||
@@ -113,14 +111,12 @@ const useDocumentsWorkspace = ({
|
|||||||
onToggleSearchIncludeDescendants,
|
onToggleSearchIncludeDescendants,
|
||||||
onSetSearchIncludeDescendants,
|
onSetSearchIncludeDescendants,
|
||||||
sortRefreshReadyRef,
|
sortRefreshReadyRef,
|
||||||
handleDeskExit,
|
|
||||||
}: UseDocumentsWorkspaceOptions = {}) => {
|
}: UseDocumentsWorkspaceOptions = {}) => {
|
||||||
const handleDocumentsViewModeChange = onDocumentsViewModeChange || noop;
|
const handleDocumentsViewModeChange = onDocumentsViewModeChange || noop;
|
||||||
const handleDocumentsSortFieldChange = onDocumentsSortFieldChange || noop;
|
const handleDocumentsSortFieldChange = onDocumentsSortFieldChange || noop;
|
||||||
const handleDocumentsSortDirectionToggle = onDocumentsSortDirectionToggle || noop;
|
const handleDocumentsSortDirectionToggle = onDocumentsSortDirectionToggle || noop;
|
||||||
const toggleSearchIncludeDescendants = onToggleSearchIncludeDescendants || noop;
|
const toggleSearchIncludeDescendants = onToggleSearchIncludeDescendants || noop;
|
||||||
const setSearchIncludeDescendants = onSetSearchIncludeDescendants || noop;
|
const setSearchIncludeDescendants = onSetSearchIncludeDescendants || noop;
|
||||||
const handleDeskExitSafe = handleDeskExit || noop;
|
|
||||||
|
|
||||||
const fallbackSortFieldRef = useRef(documentsSortField);
|
const fallbackSortFieldRef = useRef(documentsSortField);
|
||||||
const activeSortFieldRef = documentsSortFieldRef || fallbackSortFieldRef;
|
const activeSortFieldRef = documentsSortFieldRef || fallbackSortFieldRef;
|
||||||
@@ -1236,12 +1232,10 @@ const useDocumentsWorkspace = ({
|
|||||||
detailPanelProps,
|
detailPanelProps,
|
||||||
detailPanelOpen,
|
detailPanelOpen,
|
||||||
openDetailPanel,
|
openDetailPanel,
|
||||||
handleDetailPanelClose,
|
|
||||||
inspectDocument,
|
inspectDocument,
|
||||||
previewActive,
|
previewActive,
|
||||||
previewWorkspaceDocument,
|
previewWorkspaceDocument,
|
||||||
previewWorkspaceEntry,
|
previewWorkspaceEntry,
|
||||||
resolveThumbnailUrlForDoc,
|
|
||||||
resolveFolderPath,
|
resolveFolderPath,
|
||||||
} = useDetailWorkspace({
|
} = useDetailWorkspace({
|
||||||
documents,
|
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,
|
currentFolderName,
|
||||||
breadcrumbs,
|
breadcrumbs,
|
||||||
refreshCurrentFolder,
|
refreshCurrentFolder,
|
||||||
@@ -1457,6 +1541,14 @@ const useDocumentsWorkspace = ({
|
|||||||
ensureDownloadUrl,
|
ensureDownloadUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const documentsTableProps = useMemo(
|
||||||
|
() => ({
|
||||||
|
...documentsPanelProps,
|
||||||
|
deskWorkspaceProps,
|
||||||
|
}),
|
||||||
|
[documentsPanelProps, deskWorkspaceProps],
|
||||||
|
);
|
||||||
|
|
||||||
const sidebarProps = useSidebarProps({
|
const sidebarProps = useSidebarProps({
|
||||||
folderNodes,
|
folderNodes,
|
||||||
folderClickHandlers,
|
folderClickHandlers,
|
||||||
@@ -1495,57 +1587,6 @@ const useDocumentsWorkspace = ({
|
|||||||
uploadQueue,
|
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(
|
const contextValue = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
token,
|
token,
|
||||||
@@ -1585,7 +1626,6 @@ const useDocumentsWorkspace = ({
|
|||||||
documentsTableProps,
|
documentsTableProps,
|
||||||
detailPanelProps,
|
detailPanelProps,
|
||||||
documentsViewMode,
|
documentsViewMode,
|
||||||
deskWorkspaceProps,
|
|
||||||
ensurePreviewData,
|
ensurePreviewData,
|
||||||
ensureAssetUrl,
|
ensureAssetUrl,
|
||||||
resolveFolderPath,
|
resolveFolderPath,
|
||||||
@@ -1637,7 +1677,6 @@ const useDocumentsWorkspace = ({
|
|||||||
documentsTableProps,
|
documentsTableProps,
|
||||||
detailPanelProps,
|
detailPanelProps,
|
||||||
documentsViewMode,
|
documentsViewMode,
|
||||||
deskWorkspaceProps,
|
|
||||||
ensurePreviewData,
|
ensurePreviewData,
|
||||||
ensureAssetUrl,
|
ensureAssetUrl,
|
||||||
resolveFolderPath,
|
resolveFolderPath,
|
||||||
|
|||||||
@@ -93,6 +93,19 @@ const srgbChannelToLinear = (value: number) => {
|
|||||||
return ((normalized + 0.055) / 1.055) ** 2.4;
|
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 hexToOklch = (input?: string) => {
|
||||||
const rgb = hexToRgb(input);
|
const rgb = hexToRgb(input);
|
||||||
if (!rgb) {
|
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 => {
|
export const relativeLuminance = ({ r, g, b }: RgbColor): number => {
|
||||||
const toLinear = (channel) => {
|
const toLinear = (channel) => {
|
||||||
const normalized = channel / 255;
|
const normalized = channel / 255;
|
||||||
@@ -295,7 +295,6 @@ export const getTagColorStyle = (hex) => {
|
|||||||
adjustments.forEach((delta) => registerCandidate(baseHsl.l + delta));
|
adjustments.forEach((delta) => registerCandidate(baseHsl.l + delta));
|
||||||
|
|
||||||
let bestBackground = baseHex;
|
let bestBackground = baseHex;
|
||||||
let bestText = baseText;
|
|
||||||
let bestRatio = baseRatio;
|
let bestRatio = baseRatio;
|
||||||
|
|
||||||
if (bestRatio >= TARGET_RATIO) {
|
if (bestRatio >= TARGET_RATIO) {
|
||||||
@@ -303,7 +302,6 @@ export const getTagColorStyle = (hex) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let compliantBackground = null;
|
let compliantBackground = null;
|
||||||
let compliantText = null;
|
|
||||||
let compliantDelta = Infinity;
|
let compliantDelta = Infinity;
|
||||||
let compliantRatio = -Infinity;
|
let compliantRatio = -Infinity;
|
||||||
|
|
||||||
@@ -314,7 +312,6 @@ export const getTagColorStyle = (hex) => {
|
|||||||
|
|
||||||
if (ratio > bestRatio) {
|
if (ratio > bestRatio) {
|
||||||
bestBackground = candidateHex;
|
bestBackground = candidateHex;
|
||||||
bestText = textHex;
|
|
||||||
bestRatio = ratio;
|
bestRatio = ratio;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,7 +327,6 @@ export const getTagColorStyle = (hex) => {
|
|||||||
|| (isSimilarDistance && improvesRatio)
|
|| (isSimilarDistance && improvesRatio)
|
||||||
) {
|
) {
|
||||||
compliantBackground = candidateHex;
|
compliantBackground = candidateHex;
|
||||||
compliantText = textHex;
|
|
||||||
compliantDelta = delta;
|
compliantDelta = delta;
|
||||||
compliantRatio = ratio;
|
compliantRatio = ratio;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user