feat: refactor frontend document sorting preferences and sidebar utilities.

This commit is contained in:
2025-12-07 22:55:26 +01:00
parent 53664f2421
commit b29e856225
9 changed files with 160 additions and 314 deletions
+3 -6
View File
@@ -1,5 +1,5 @@
import React, { useCallback, useEffect } from 'react';
import type { ReactNode, ComponentProps } from 'react';
import type { ReactNode } from 'react';
import { useNavigate } from 'react-router-dom';
import {
DocumentsFilterProvider,
@@ -15,8 +15,7 @@ import useDocumentsShell from './useDocumentsShell';
const DocumentsInner: React.FC<{
surfaceConfig: any;
onNavigate: (documentId: string) => void;
sidebarProps: ComponentProps<typeof Sidebar> | null;
}> = ({ surfaceConfig, onNavigate, sidebarProps }) => {
}> = ({ surfaceConfig, onNavigate }) => {
const { openPreview } = usePreviewContext();
const { collapsed: sidebarCollapsed } = useSidebarContext();
const {
@@ -57,7 +56,7 @@ const DocumentsInner: React.FC<{
const renderSurface = () => {
const detailMode = surface?.detailMode ?? null;
const layoutClass = `documents-main${sidebarHidden ? ' documents-main--sidebar-hidden' : ''}${detailMode === 'overlay' ? ' documents-main--overlay-detail' : ''}`;
const sidebarNode = !sidebarHidden && sidebarProps ? <Sidebar {...sidebarProps} /> : null;
const sidebarNode = !sidebarHidden ? <Sidebar /> : null;
const surfaceDetail = surface && (surface as { detail?: ReactNode }).detail ? (surface as { detail?: ReactNode }).detail : null;
const surfaceBody = surface ? surface.content : null;
@@ -85,7 +84,6 @@ const DocumentsInner: React.FC<{
const DocumentsRouteContent: React.FC = () => {
const {
sidebarProps,
surfaceConfig,
documentsFilter,
} = useDocumentsShell();
@@ -103,7 +101,6 @@ const DocumentsRouteContent: React.FC = () => {
<DocumentsInner
surfaceConfig={surfaceConfig}
onNavigate={handleDocumentNavigate}
sidebarProps={sidebarProps}
/>
</PreviewProvider>
</DocumentsFilterProvider>
+1 -9
View File
@@ -60,9 +60,7 @@ export const useDocumentsPreferences = () => {
const stored = readSessionStorage(SORT_FIELD_STORAGE_KEY);
return SORT_FIELD_VALUES.includes(stored) ? stored : DEFAULT_SORT_FIELD;
});
const documentsSortFieldRef = useRef(documentsSortField);
useEffect(() => {
documentsSortFieldRef.current = documentsSortField;
writeSessionStorage(SORT_FIELD_STORAGE_KEY, documentsSortField);
}, [documentsSortField]);
@@ -70,9 +68,8 @@ export const useDocumentsPreferences = () => {
const stored = readSessionStorage(SORT_DIRECTION_STORAGE_KEY);
return stored === 'desc' || stored === 'asc' ? stored : DEFAULT_SORT_DIRECTION;
});
const documentsSortDirectionRef = useRef(documentsSortDirection);
useEffect(() => {
documentsSortDirectionRef.current = documentsSortDirection;
writeSessionStorage(SORT_DIRECTION_STORAGE_KEY, documentsSortDirection);
}, [documentsSortDirection]);
@@ -102,21 +99,16 @@ export const useDocumentsPreferences = () => {
setSearchIncludeDescendants((previous) => !previous);
}, []);
const sortRefreshReadyRef = useRef(false);
return {
documentsViewMode,
handleDocumentsViewModeChange: setDocumentsViewMode,
handleDeskExit,
documentsSortField,
documentsSortDirection,
documentsSortFieldRef,
documentsSortDirectionRef,
handleDocumentsSortFieldChange,
handleDocumentsSortDirectionToggle,
searchIncludeDescendants,
setSearchIncludeDescendants,
toggleSearchIncludeDescendants,
sortRefreshReadyRef,
};
};
+6 -16
View File
@@ -1,11 +1,11 @@
import { useMemo } from 'react';
import type { ComponentProps } from 'react';
import { useAppShell } from '../lib/context/AppShellContext';
import type { DocumentsFilterValue } from '../documents/context/DocumentsFilterContext';
import type Sidebar from '../sidebar/Sidebar';
import type { UseWorkspaceSurfaceArgs } from './useWorkspaceSurface';
import type { Identifier } from '../types/identifiers';
import useDocumentsPanelProps from '../documents/logic/useDocumentsPanelProps';
type WorkspaceSurfaceConfig = Omit<UseWorkspaceSurfaceArgs, 'sidebarHidden' | 'onExpandSidebar'> & {
openDetailPanel?: (documentId: Identifier) => void;
closeDetailPanel?: () => void;
@@ -13,20 +13,19 @@ type WorkspaceSurfaceConfig = Omit<UseWorkspaceSurfaceArgs, 'sidebarHidden' | 'o
};
interface DocumentsShellView {
sidebarProps: ComponentProps<typeof Sidebar> | null;
surfaceConfig: WorkspaceSurfaceConfig;
documentsFilter: DocumentsFilterValue;
documentsManager: any;
foldersManager: any; // Using any to avoid circular dependency
foldersManager: any;
}
const useDocumentsShell = (): DocumentsShellView => {
const shell = useAppShell();
const documentsPanelProps = useDocumentsPanelProps(shell as any);
return useMemo(() => {
const sidebar = (shell.sidebarProps as ComponentProps<typeof Sidebar> | undefined) || null;
const surfaceConfig: WorkspaceSurfaceConfig = {
documentsTableProps: (shell.documentsTableProps ?? null) as WorkspaceSurfaceConfig['documentsTableProps'],
documentsTableProps: documentsPanelProps,
detailPanelProps: (shell.detailPanelProps ?? null) as WorkspaceSurfaceConfig['detailPanelProps'],
detailPanelOpen: Boolean(shell.detailPanelOpen),
openDetailPanel: shell.openDetailPanel as WorkspaceSurfaceConfig['openDetailPanel'],
@@ -41,22 +40,13 @@ const useDocumentsShell = (): DocumentsShellView => {
handleBreadcrumbNavigate: shell.handleBreadcrumbNavigate as WorkspaceSurfaceConfig['handleBreadcrumbNavigate'],
};
const sidebarWithActions = sidebar
? {
...sidebar,
onManageTags: shell.openTagsModal as (() => void) | undefined,
onManageCorrespondents: shell.openCorrespondentsModal as (() => void) | undefined,
}
: null;
return {
sidebarProps: sidebarWithActions,
surfaceConfig,
documentsFilter: shell.documentsFilter as DocumentsFilterValue,
documentsManager: shell.documentsManager,
foldersManager: shell.foldersManager,
};
}, [shell]);
}, [shell, documentsPanelProps]);
};
export default useDocumentsShell;
+1 -48
View File
@@ -1,6 +1,4 @@
import {
Dispatch,
SetStateAction,
useCallback,
useEffect,
useRef,
@@ -10,18 +8,11 @@ import DocumentsManager from '../DocumentsManager';
import type { DocumentId } from '../../types/identifiers';
import type { Document } from '../../types/documents';
interface FolderContentsEntry {
documents?: Document[];
[key: string]: unknown;
}
interface UseDocumentsOptions {
setFolderContents: Dispatch<SetStateAction<Map<string, FolderContentsEntry>>>;
fetchDocumentById?: (id: DocumentId) => Promise<Document | null>;
}
const useDocuments = ({
setFolderContents,
fetchDocumentById,
}: UseDocumentsOptions) => {
const managerRef = useRef(
@@ -75,46 +66,8 @@ const useDocuments = ({
});
return changed ? next : prev;
});
setFolderContents((prev) => {
if (!prev.size) {
return prev;
}
let changed = false;
const next = new Map();
prev.forEach((contents, key) => {
const docs = Array.isArray(contents?.documents) ? contents.documents : null;
if (!docs || docs.length === 0) {
next.set(key, contents);
return;
}
let docsChanged = false;
const updatedDocs = docs.map((doc) => {
const id = doc?.id;
if (id != null && lookupSnapshot.has(id as DocumentId)) {
const canonical = lookupSnapshot.get(id as DocumentId) as Document;
if (canonical !== doc) {
docsChanged = true;
}
return canonical;
}
const updated = mapper(doc);
const nextDoc = updated === undefined ? doc : updated;
if (nextDoc !== doc) {
docsChanged = true;
}
return nextDoc;
});
if (docsChanged) {
changed = true;
next.set(key, { ...contents, documents: updatedDocs });
} else {
next.set(key, contents);
}
});
return changed ? next : prev;
});
},
[setFolderContents],
[],
);
const updateDocumentCaches = useCallback(
@@ -1,5 +1,4 @@
import {
MutableRefObject,
useCallback,
useEffect,
useMemo,
@@ -25,9 +24,7 @@ import { useEntryPointer as useEntryPointerCore } from '../features/selection/us
import { isTagTransferEvent } from '../features/tagging/tagTransfer';
import useDocumentsSelection from '../features/selection/useDocumentsSelection';
import useBulkDocumentActions from './useBulkDocumentActions';
import useDocumentsPanelProps from '../logic/useDocumentsPanelProps';
import useDocumentPreview from '../../app/useDocumentPreview';
import useSidebarProps from '../../sidebar/useSidebarProps';
import {
DEFAULT_SORT_DIRECTION,
DEFAULT_SORT_FIELD,
@@ -69,19 +66,8 @@ const EntryType = Object.freeze({
const noop = () => { };
import type { Document } from '../../types/documents';
interface FolderContentsEntry {
folder?: { id?: FolderNodeId; name?: string | null } | null;
documents?: Document[];
subfolders?: Array<{ id?: FolderNodeId; name?: string | null;[key: string]: unknown }>;
__includesDocuments?: boolean;
__sortField?: string | null;
__sortDirection?: string | null;
[key: string]: unknown;
}
interface TenantOption {
id?: Identifier | null;
name?: string | null;
@@ -93,8 +79,6 @@ interface UseDocumentsWorkspaceOptions {
documentsViewMode?: string;
documentsSortField?: string;
documentsSortDirection?: string;
documentsSortFieldRef?: MutableRefObject<string>;
documentsSortDirectionRef?: MutableRefObject<string>;
onDocumentsViewModeChange?: (mode: string) => void;
onDocumentsSortFieldChange?: (field: string) => void;
onDocumentsSortDirectionToggle?: () => void;
@@ -106,8 +90,6 @@ const useDocumentsWorkspace = ({
documentsViewMode = 'list',
documentsSortField = DEFAULT_SORT_FIELD,
documentsSortDirection = DEFAULT_SORT_DIRECTION,
documentsSortFieldRef,
documentsSortDirectionRef,
onDocumentsViewModeChange,
onDocumentsSortFieldChange,
onDocumentsSortDirectionToggle,
@@ -119,17 +101,15 @@ const useDocumentsWorkspace = ({
const handleDocumentsSortDirectionToggle = onDocumentsSortDirectionToggle || noop;
const setSearchIncludeDescendants = onSetSearchIncludeDescendants || noop;
const fallbackSortFieldRef = useRef(documentsSortField);
const activeSortFieldRef = documentsSortFieldRef || fallbackSortFieldRef;
const activeSortFieldRef = useRef(documentsSortField);
useEffect(() => {
activeSortFieldRef.current = documentsSortField;
}, [documentsSortField, activeSortFieldRef]);
}, [documentsSortField]);
const fallbackSortDirectionRef = useRef(documentsSortDirection);
const activeSortDirectionRef = documentsSortDirectionRef || fallbackSortDirectionRef;
const activeSortDirectionRef = useRef(documentsSortDirection);
useEffect(() => {
activeSortDirectionRef.current = documentsSortDirection;
}, [documentsSortDirection, activeSortDirectionRef]);
}, [documentsSortDirection]);
const navigate = useNavigate();
const location = useLocation();
@@ -172,7 +152,7 @@ const useDocumentsWorkspace = ({
},
[showToast],
);
const status = null; // No longer used, kept for backward compatibility
const handleApiReport = useCallback(
({ message, variant }) => setStatusMessage(message, variant),
[setStatusMessage],
@@ -193,7 +173,7 @@ const useDocumentsWorkspace = ({
setStatusMessage,
});
const breadcrumbFetchRef = useRef(new Set());
const tagRemovalCursorActiveRef = useRef(false);
const tenantIdRef = useRef(currentTenantId);
const detailPanelControlRef = useRef({ open: () => { }, close: () => { } });
@@ -282,14 +262,6 @@ const useDocumentsWorkspace = ({
configureSelectionEnvironment,
} = selection;
const [folderContents, setFolderContents] = useState<Map<FolderNodeId, FolderContentsEntry>>(
() => new Map(),
);
const folderContentsRef = useRef(folderContents);
useEffect(() => {
folderContentsRef.current = folderContents;
}, [folderContents]);
const {
documents,
setDocuments,
@@ -298,7 +270,6 @@ const useDocumentsWorkspace = ({
updateDocumentCaches,
documentsManager,
} = useDocuments({
setFolderContents,
fetchDocumentById,
});
@@ -328,7 +299,7 @@ const useDocumentsWorkspace = ({
foldersManager,
});
const [currentSubfolders, setCurrentSubfolders] = useState<FolderContentsEntry['subfolders']>([]);
const [currentSubfolders, setCurrentSubfolders] = useState<Array<{ id?: FolderNodeId; name?: string | null;[key: string]: unknown }>>([]);
const ensureFolderData = useCallback(
async (
@@ -699,7 +670,7 @@ const useDocumentsWorkspace = ({
resetPreviewState();
resetUploadsState();
clearUploadQueue();
breadcrumbFetchRef.current = new Set();
detailFolderFetchRef.current = new Set();
bootstrapInitializedRef.current = false;
selectionInitializedRef.current = false;
@@ -1166,94 +1137,61 @@ const useDocumentsWorkspace = ({
tenantIdRef,
});
const documentsPanelProps = useDocumentsPanelProps({
currentFolderName,
breadcrumbs,
refreshCurrentFolder,
currentSubfolders,
documents: viewDocuments,
searchQuery,
searchResultIds,
folderClickHandlers,
selectedFolder,
handleFolderDragStart,
handleFolderDragEnd,
draggedFolderId,
handleFolderRename,
handleDocumentTitleUpdate,
draggedDocumentIds,
handleDocumentDragStart,
handleDocumentDragEnd,
searchLoading,
tagLookupById,
activeTagFilters,
activeCorrespondentFilters,
ensureAssetUrl,
getDocumentAsset,
handleDocumentTagAttach,
handleDocumentTagDetach: handleTagRemove,
documentsViewMode,
documentsSortField,
documentsSortDirection,
handleDocumentsSortFieldChange,
handleDocumentsSortDirectionToggle,
handleDocumentsViewModeChange,
clearDocumentSelection,
handleDeleteSelection,
handleEntryPointerCore,
tags,
correspondents,
documentLookup,
handleBulkTagAddFromDetail,
handleBulkTagRemoveFromDetail,
handleBulkCorrespondentAdd,
handleBulkCorrespondentRemove,
handleBulkSelectionReanalyze,
folderOptions,
moveDocumentsToFolder,
selectFolder,
selectionValue: selection,
});
const documentsTableProps = useMemo(
() => ({
...documentsPanelProps,
}),
[documentsPanelProps],
);
const sidebarProps = useSidebarProps({
folderClickHandlers,
handleFolderDelete,
handleFolderRename,
selectedFolder,
handleFolderDragStart,
handleFolderDragEnd,
draggedFolderId,
handlePromptCreateFolder,
creatingFolder,
});
const contextValue = {
const sessionContext = {
token,
appStatus,
status,
setStatusMessage,
dropOverlayState,
handleLogout,
sidebarProps,
tenant: tenantRecord,
tenants: tenantOptions,
tenantOptions,
handleTenantSelect,
};
const uiContext = {
setStatusMessage,
notifyApiError,
settingsOpen,
openSettings,
closeSettings,
managementModals,
};
const uploadContext = {
dropOverlayState,
uploadQueue,
clearUploadQueue,
handleFileSelection,
};
const tagsContext = {
tags,
refreshTags,
tagLookupById,
activeTagFilters,
handleTagUpdate,
handleTagDelete,
handleDocumentTagDrop: handleDocumentTagAttach,
handleBulkTagAddFromDetail,
handleBulkTagRemoveFromDetail,
openTagsModal,
};
const correspondentsContext = {
correspondents,
refreshCorrespondents,
activeCorrespondentFilters,
handleCorrespondentUpdate,
handleCorrespondentCreate,
handleCorrespondentDelete,
handleDocumentCorrespondentAttach,
handleCorrespondentRemove,
handleCorrespondentAdd,
handleBulkCorrespondentAdd,
handleBulkCorrespondentRemove,
openCorrespondentsModal,
};
const passkeysContext = {
passkeys,
passkeysSupported,
passkeysLoading,
@@ -1262,34 +1200,94 @@ const useDocumentsWorkspace = ({
refreshPasskeys,
registerPasskey,
revokePasskey,
};
const previewContext = {
previewActive,
previewWorkspaceDocument,
previewDocumentId,
closeDocumentPreview,
handleThumbnailRegeneration,
documentsTableProps,
detailPanelProps,
documentsViewMode,
ensureAssetUrl,
resolveFolderPath,
getDocumentAsset,
notifyApiError,
openTagsModal,
openCorrespondentsModal,
openSettings,
handleThumbnailRegeneration,
};
const detailPanelContext = {
detailPanelProps,
detailPanelOpen,
openDetailPanel,
uploadQueue,
clearUploadQueue,
};
const searchContext = {
searchQuery,
documentsFilter,
documentsManager,
searchLoading,
documentsViewMode,
handleDocumentsViewModeChange,
documentsSortField,
documentsSortDirection,
handleDocumentsSortFieldChange,
handleDocumentsSortDirectionToggle,
searchResultIds,
documents: viewDocuments,
};
const folderTreeContext = {
foldersManager,
selectedFolder,
currentFolderName,
folderOptions,
handleBreadcrumbNavigate,
tenant: tenantRecord,
tenants: tenantOptions,
tenantOptions,
handleTenantSelect,
handleFileSelection,
resolveFolderPath,
selectFolder,
moveDocumentsToFolder,
folderClickHandlers,
handleFolderRename,
handleFolderDelete,
handleFolderDragStart,
handleFolderDragEnd,
draggedFolderId,
handlePromptCreateFolder,
creatingFolder,
currentSubfolders,
breadcrumbs,
refreshCurrentFolder,
};
const selectionContext = {
clearDocumentSelection,
handleDeleteSelection,
handleEntryPointerCore,
handleBulkSelectionReanalyze,
selectionValue: selection,
};
const documentMutations = {
handleDocumentTitleUpdate,
handleDocumentDragStart,
handleDocumentDragEnd,
draggedDocumentIds,
};
const managers = {
documentsManager,
documentLookup,
};
const contextValue = {
...sessionContext,
...uiContext,
...uploadContext,
...tagsContext,
...correspondentsContext,
...passkeysContext,
...previewContext,
...detailPanelContext,
...searchContext,
...folderTreeContext,
...selectionContext,
...documentMutations,
...managers,
};
// hook callers handle rendering / routing
+3 -5
View File
@@ -1,13 +1,11 @@
import type { FolderNode } from '../../types/documents';
import type { FolderNodeId } from '../../types/identifiers';
export type Breadcrumb = FolderNode;
export const resolveBreadcrumbs = (
startFolderId: FolderNodeId,
folderNodes: Map<FolderNodeId, FolderNode>
): Breadcrumb[] => {
const chain: Breadcrumb[] = [];
): FolderNode[] => {
const chain: FolderNode[] = [];
const seen = new Set<FolderNodeId>();
let currentId: FolderNodeId | null = startFolderId;
let guard = 0;
@@ -25,7 +23,7 @@ export const resolveBreadcrumbs = (
}
}
const ordered: Breadcrumb[] = [];
const ordered: FolderNode[] = [];
const seenOrdered = new Set<FolderNodeId>();
for (let i = chain.length - 1; i >= 0; i--) {
-3
View File
@@ -37,14 +37,11 @@ const AppLayout: React.FC = () => {
documentsViewMode: documentsPreferences.documentsViewMode,
documentsSortField: documentsPreferences.documentsSortField,
documentsSortDirection: documentsPreferences.documentsSortDirection,
documentsSortFieldRef: documentsPreferences.documentsSortFieldRef,
documentsSortDirectionRef: documentsPreferences.documentsSortDirectionRef,
onDocumentsViewModeChange: documentsPreferences.handleDocumentsViewModeChange,
onDocumentsSortFieldChange: documentsPreferences.handleDocumentsSortFieldChange,
onDocumentsSortDirectionToggle: documentsPreferences.handleDocumentsSortDirectionToggle,
searchIncludeDescendants: documentsPreferences.searchIncludeDescendants,
onSetSearchIncludeDescendants: documentsPreferences.setSearchIncludeDescendants,
sortRefreshReadyRef: documentsPreferences.sortRefreshReadyRef,
});
if (['logged-out', 'authenticating', 'selecting-tenant'].includes(appStatus)) {
+22 -37
View File
@@ -1,44 +1,14 @@
import React, { useRef } from 'react';
import { useAppShell } from '../lib/context/AppShellContext';
import { usePanelResizeBindings } from '../app/PanelManagerContext';
import type { Identifier } from '../types/identifiers';
import SidebarFolderList from './components/SidebarFolderList';
import SidebarTagList from './components/SidebarTagList';
import SidebarCorrespondentList from './components/SidebarCorrespondentList';
import { FolderIdentifier } from './components/SidebarFolderNode';
import { TenantOption } from './components/SidebarMenu';
import SidebarHeader from './components/SidebarHeader';
import SidebarSearch from './components/SidebarSearch';
interface SidebarProps {
onSelect: (folderId: FolderIdentifier) => void;
onDrop: (event: React.DragEvent<HTMLDivElement>, folderId: FolderIdentifier) => void;
onDragOver: (event: React.DragEvent<HTMLDivElement>, folderId: FolderIdentifier) => void;
onDragLeave: (event: React.DragEvent<HTMLDivElement>) => void;
onDeleteFolder: (folderId: FolderIdentifier) => void;
onRenameFolder?: (folderId: FolderIdentifier, name: string) => void;
selectedFolder?: FolderIdentifier | null;
onFolderDragStart?: (event: React.DragEvent<HTMLDivElement>, folderId: FolderIdentifier) => void;
onFolderDragEnd?: (event: React.DragEvent<HTMLDivElement>) => void;
draggedFolderId?: FolderIdentifier | null;
onCreateFolder?: (parentId?: Identifier | null) => void;
creatingFolder?: boolean;
}
const Sidebar: React.FC<SidebarProps> = ({
onSelect,
onDrop,
onDragOver,
onDragLeave,
onDeleteFolder,
onRenameFolder,
selectedFolder = null,
onFolderDragStart,
onFolderDragEnd,
draggedFolderId = null,
onCreateFolder,
creatingFolder = false,
}) => {
const Sidebar: React.FC = () => {
const {
sidebarSuppressed,
openTagsModal,
@@ -52,7 +22,22 @@ const Sidebar: React.FC<SidebarProps> = ({
handleTenantSelect,
openSettings,
handleFileSelection, // Used for upload
} = useAppShell();
// Folder Tree Context Props
folderClickHandlers = {},
handleFolderDelete,
handleFolderRename,
selectedFolder = null,
handleFolderDragStart,
handleFolderDragEnd,
draggedFolderId = null,
handlePromptCreateFolder,
creatingFolder = false,
} = useAppShell() as any;
const onSelect = folderClickHandlers.onSelect;
const onDrop = folderClickHandlers.onDrop;
const onDragOver = folderClickHandlers.onDragOver;
const onDragLeave = folderClickHandlers.onDragLeave;
const onManageTags = openTagsModal;
const onManageCorrespondents = openCorrespondentsModal;
@@ -110,12 +95,12 @@ const Sidebar: React.FC<SidebarProps> = ({
onDrop={onDrop}
onDragOver={onDragOver}
onDragLeave={onDragLeave}
onDeleteFolder={onDeleteFolder}
onRenameFolder={onRenameFolder}
onFolderDragStart={onFolderDragStart}
onFolderDragEnd={onFolderDragEnd}
onDeleteFolder={handleFolderDelete}
onRenameFolder={handleFolderRename}
onFolderDragStart={handleFolderDragStart}
onFolderDragEnd={handleFolderDragEnd}
draggedFolderId={draggedFolderId}
onCreateFolder={onCreateFolder}
onCreateFolder={handlePromptCreateFolder}
creatingFolder={creatingFolder}
/>
-64
View File
@@ -1,64 +0,0 @@
import { useMemo } from 'react';
import type { DragEvent } from 'react';
import type { Identifier } from '../types/identifiers';
type FolderDropHandler = (
event: DragEvent<HTMLElement>,
folderId: Identifier,
) => void | Promise<void>;
interface FolderClickHandlers {
onSelect: (folderId: Identifier | null) => void;
onDrop: FolderDropHandler;
onDragOver: (event: DragEvent<HTMLElement>, folderId: Identifier) => void;
onDragLeave: (event: DragEvent<HTMLElement>) => void;
}
interface UseSidebarPropsArgs {
folderClickHandlers: FolderClickHandlers;
handleFolderDelete: (folderId: Identifier) => void | Promise<void>;
handleFolderRename: (folderId: Identifier, name: string) => void | Promise<void>;
selectedFolder: Identifier | null;
handleFolderDragStart: (event: DragEvent<HTMLElement>, folderId: Identifier) => void;
handleFolderDragEnd: (event: DragEvent<HTMLElement>) => void;
draggedFolderId: Identifier | null;
handlePromptCreateFolder?: (parentId?: Identifier | null) => void;
creatingFolder: boolean;
}
interface SidebarHookResult {
onSelect: FolderClickHandlers['onSelect'];
onDrop: FolderClickHandlers['onDrop'];
onDragOver: FolderClickHandlers['onDragOver'];
onDragLeave: FolderClickHandlers['onDragLeave'];
onDeleteFolder: UseSidebarPropsArgs['handleFolderDelete'];
onRenameFolder: UseSidebarPropsArgs['handleFolderRename'];
selectedFolder: Identifier | null;
onFolderDragStart: UseSidebarPropsArgs['handleFolderDragStart'];
onFolderDragEnd: UseSidebarPropsArgs['handleFolderDragEnd'];
draggedFolderId: Identifier | null;
onCreateFolder: UseSidebarPropsArgs['handlePromptCreateFolder'];
creatingFolder: boolean;
}
const useSidebarProps = (args: UseSidebarPropsArgs): SidebarHookResult =>
useMemo(
() => ({
onSelect: args.folderClickHandlers.onSelect,
onDrop: args.folderClickHandlers.onDrop,
onDragOver: args.folderClickHandlers.onDragOver,
onDragLeave: args.folderClickHandlers.onDragLeave,
onDeleteFolder: args.handleFolderDelete,
onRenameFolder: args.handleFolderRename,
selectedFolder: args.selectedFolder,
onFolderDragStart: args.handleFolderDragStart,
onFolderDragEnd: args.handleFolderDragEnd,
draggedFolderId: args.draggedFolderId,
onCreateFolder: (parentId) => args.handlePromptCreateFolder?.(parentId),
creatingFolder: args.creatingFolder,
}),
[args],
);
export default useSidebarProps;