159 lines
4.6 KiB
TypeScript
159 lines
4.6 KiB
TypeScript
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
|
|
interface FolderEntry {
|
|
id: string | number;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
interface DocumentEntry {
|
|
id: string | number;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
interface NavigableRow {
|
|
key: string;
|
|
type: 'folder' | 'document';
|
|
id: string | number;
|
|
}
|
|
|
|
interface UseDocumentsSelectionOptions {
|
|
showingSearchResults: boolean;
|
|
currentSubfolders: FolderEntry[];
|
|
visibleDocuments: DocumentEntry[];
|
|
resolveFolderRowKey: (id: string | number) => string | null;
|
|
resolveDocumentRowKey: (id: string | number) => string | null;
|
|
configureSelectionEnvironment: (config: { visibleRowKeySet: Set<string>; navigableRowKeys: string[] }) => void;
|
|
visibleRowKeySet: Set<string>;
|
|
selectedEntries: string[];
|
|
selectionAnchorRef: { current: string | null };
|
|
promoteSelectionOrderRaw: (id: string | number) => void;
|
|
setFocusedDocumentId: (id: string | number | null) => void;
|
|
setActivePreviewId: (id: string | number | null) => void;
|
|
clearSelection: () => void;
|
|
focusedDocumentId: string | number | null;
|
|
setFocusedRowKey: (value: string | null | ((current: string | null) => string | null)) => void;
|
|
focusedRowKey: string | null;
|
|
isFolderRowKey: (key: string | null) => boolean;
|
|
}
|
|
|
|
const useDocumentsSelection = ({
|
|
showingSearchResults,
|
|
currentSubfolders,
|
|
visibleDocuments,
|
|
resolveFolderRowKey,
|
|
resolveDocumentRowKey,
|
|
configureSelectionEnvironment,
|
|
visibleRowKeySet,
|
|
selectedEntries,
|
|
selectionAnchorRef,
|
|
promoteSelectionOrderRaw,
|
|
setFocusedDocumentId,
|
|
setActivePreviewId,
|
|
clearSelection,
|
|
focusedDocumentId,
|
|
setFocusedRowKey,
|
|
focusedRowKey,
|
|
isFolderRowKey,
|
|
}: UseDocumentsSelectionOptions) => {
|
|
const navigableRows = useMemo<NavigableRow[]>(() => {
|
|
const entries: NavigableRow[] = [];
|
|
if (!showingSearchResults) {
|
|
currentSubfolders.forEach((folder) => {
|
|
const key = resolveFolderRowKey(folder.id);
|
|
if (key) {
|
|
entries.push({ key, type: 'folder', id: folder.id });
|
|
}
|
|
});
|
|
}
|
|
visibleDocuments.forEach((doc) => {
|
|
const key = resolveDocumentRowKey(doc.id);
|
|
if (key) {
|
|
entries.push({ key, type: 'document', id: doc.id });
|
|
}
|
|
});
|
|
return entries;
|
|
}, [showingSearchResults, currentSubfolders, visibleDocuments, resolveFolderRowKey, resolveDocumentRowKey]);
|
|
|
|
const navigableRowKeys = useMemo(
|
|
() => navigableRows.map((entry) => entry.key),
|
|
[navigableRows],
|
|
);
|
|
|
|
useEffect(() => {
|
|
configureSelectionEnvironment({
|
|
visibleRowKeySet,
|
|
navigableRowKeys,
|
|
});
|
|
}, [configureSelectionEnvironment, visibleRowKeySet, navigableRowKeys]);
|
|
|
|
const promoteSelectionOrder = useCallback(
|
|
(docId: string | number | null) => {
|
|
if (!docId) return;
|
|
promoteSelectionOrderRaw(docId);
|
|
const rowKey = resolveDocumentRowKey(docId);
|
|
if (rowKey) {
|
|
selectionAnchorRef.current = rowKey;
|
|
}
|
|
setFocusedDocumentId(docId);
|
|
setActivePreviewId(docId);
|
|
},
|
|
[promoteSelectionOrderRaw, resolveDocumentRowKey, selectionAnchorRef, setFocusedDocumentId, setActivePreviewId],
|
|
);
|
|
|
|
const clearDocumentSelection = useCallback(() => {
|
|
clearSelection();
|
|
}, [clearSelection]);
|
|
|
|
const prevFocusedDocIdRef = useRef<string | number | null>(focusedDocumentId);
|
|
useEffect(() => {
|
|
const previous = prevFocusedDocIdRef.current;
|
|
if (previous === focusedDocumentId) {
|
|
return;
|
|
}
|
|
prevFocusedDocIdRef.current = focusedDocumentId;
|
|
if (focusedDocumentId) {
|
|
setFocusedRowKey(resolveDocumentRowKey(focusedDocumentId));
|
|
} else {
|
|
setFocusedRowKey((current) => (isFolderRowKey(current) ? current : null));
|
|
}
|
|
}, [focusedDocumentId, resolveDocumentRowKey, setFocusedRowKey, isFolderRowKey]);
|
|
|
|
useEffect(() => {
|
|
if (!navigableRowKeys.length) {
|
|
if (focusedRowKey) {
|
|
setFocusedRowKey(null);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (focusedRowKey && navigableRowKeys.includes(focusedRowKey)) {
|
|
return;
|
|
}
|
|
|
|
const docKey = focusedDocumentId ? resolveDocumentRowKey(focusedDocumentId) : null;
|
|
if (docKey && navigableRowKeys.includes(docKey)) {
|
|
setFocusedRowKey(docKey);
|
|
return;
|
|
}
|
|
|
|
const selectedKey = selectedEntries.find((key) => navigableRowKeys.includes(key));
|
|
if (selectedKey) {
|
|
setFocusedRowKey(selectedKey);
|
|
return;
|
|
}
|
|
|
|
if (focusedRowKey) {
|
|
setFocusedRowKey(null);
|
|
}
|
|
}, [focusedRowKey, focusedDocumentId, navigableRowKeys, selectedEntries, setFocusedRowKey, resolveDocumentRowKey]);
|
|
|
|
return {
|
|
navigableRows,
|
|
navigableRowKeys,
|
|
promoteSelectionOrder,
|
|
clearDocumentSelection,
|
|
};
|
|
};
|
|
|
|
export default useDocumentsSelection;
|