refactor: Unify document selection and drag interactions by replacing stack-specific callbacks and simplifying drag state.
This commit is contained in:
@@ -35,23 +35,23 @@ export const useDocumentSelection = ({
|
||||
const selectionAnchorRef = useRef<string | null>(null);
|
||||
const selectionInitializedRef = useRef(false);
|
||||
const [focusedDocumentId, setFocusedDocumentId] = useState<DocumentId | null>(null);
|
||||
const [focusedRowKey, setFocusedRowKey] = useState<string | null>(null);
|
||||
const [focusedEntryKey, setFocusedEntryKey] = useState<string | null>(null);
|
||||
|
||||
const visibleRowKeySetRef = useRef<Set<string>>(new Set());
|
||||
const navigableRowKeysRef = useRef<string[]>([]);
|
||||
const visibleEntryKeySetRef = useRef<Set<string>>(new Set());
|
||||
const navigableEntryKeysRef = useRef<string[]>([]);
|
||||
|
||||
const configureSelectionEnvironment = useCallback(({
|
||||
visibleRowKeySet,
|
||||
navigableRowKeys,
|
||||
visibleEntryKeySet,
|
||||
navigableEntryKeys,
|
||||
}: {
|
||||
visibleRowKeySet?: Set<string>;
|
||||
navigableRowKeys?: string[];
|
||||
visibleEntryKeySet?: Set<string>;
|
||||
navigableEntryKeys?: string[];
|
||||
}) => {
|
||||
if (visibleRowKeySet) {
|
||||
visibleRowKeySetRef.current = visibleRowKeySet;
|
||||
if (visibleEntryKeySet) {
|
||||
visibleEntryKeySetRef.current = visibleEntryKeySet;
|
||||
}
|
||||
if (Array.isArray(navigableRowKeys)) {
|
||||
navigableRowKeysRef.current = navigableRowKeys;
|
||||
if (Array.isArray(navigableEntryKeys)) {
|
||||
navigableEntryKeysRef.current = navigableEntryKeys;
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -88,16 +88,16 @@ export const useDocumentSelection = ({
|
||||
|
||||
const applySelection = useCallback(
|
||||
(
|
||||
rowKeys: Array<string | null>,
|
||||
entryKeys: Array<string | null>,
|
||||
{ anchor = null, interactedKeys = [] }: ApplySelectionOptions = { anchor: null, interactedKeys: [] },
|
||||
) => {
|
||||
const visibleRowKeySet = visibleRowKeySetRef.current;
|
||||
const visibleEntryKeySet = visibleEntryKeySetRef.current;
|
||||
const unique: string[] = [];
|
||||
|
||||
(rowKeys || []).forEach((key) => {
|
||||
(entryKeys || []).forEach((key) => {
|
||||
if (!key) return;
|
||||
let canonicalKey: string | null = null;
|
||||
if (visibleRowKeySet.has(key)) {
|
||||
if (visibleEntryKeySet.has(key)) {
|
||||
canonicalKey = key;
|
||||
} else if (isDocumentEntry(key)) {
|
||||
const id = getEntryId(key);
|
||||
@@ -107,7 +107,7 @@ export const useDocumentSelection = ({
|
||||
canonicalKey = id ? createFolderEntryKey(id) : null;
|
||||
}
|
||||
|
||||
if (!canonicalKey || !visibleRowKeySet.has(canonicalKey)) {
|
||||
if (!canonicalKey || !visibleEntryKeySet.has(canonicalKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -159,19 +159,25 @@ export const useDocumentSelection = ({
|
||||
);
|
||||
|
||||
const clearSelection = useCallback(() => {
|
||||
setFocusedRowKey(null);
|
||||
setFocusedEntryKey(null);
|
||||
applySelection([], { anchor: null, interactedKeys: [] });
|
||||
}, [applySelection]);
|
||||
|
||||
const handleEntrySelection = useCallback(
|
||||
(rowKey: string, event?: SelectionEventLike) => {
|
||||
const visibleRowKeySet = visibleRowKeySetRef.current;
|
||||
const navigableRowKeys = navigableRowKeysRef.current;
|
||||
if (!rowKey || !visibleRowKeySet.has(rowKey)) {
|
||||
(entryKeyOrKeys: string | string[], event?: SelectionEventLike) => {
|
||||
const visibleEntryKeySet = visibleEntryKeySetRef.current;
|
||||
const navigableEntryKeys = navigableEntryKeysRef.current;
|
||||
|
||||
const entryKeys = Array.isArray(entryKeyOrKeys) ? entryKeyOrKeys : [entryKeyOrKeys];
|
||||
const validKeys = entryKeys.filter((key) => key && visibleEntryKeySet.has(key));
|
||||
|
||||
if (validKeys.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
setFocusedRowKey(rowKey);
|
||||
// Focus the last valid key
|
||||
const lastKey = validKeys[validKeys.length - 1];
|
||||
setFocusedEntryKey(lastKey);
|
||||
|
||||
const shiftKey = Boolean(event?.shiftKey);
|
||||
const metaKey = Boolean(event?.metaKey);
|
||||
@@ -187,44 +193,57 @@ export const useDocumentSelection = ({
|
||||
anchorKey = selectedEntries[selectedEntries.length - 1];
|
||||
}
|
||||
if (!anchorKey) {
|
||||
anchorKey = rowKey;
|
||||
anchorKey = lastKey;
|
||||
}
|
||||
|
||||
let nextKeys: string[] = [];
|
||||
let interactedKeys: string[] = [];
|
||||
|
||||
if (shiftKey && anchorKey) {
|
||||
const anchorIndex = navigableRowKeys.indexOf(anchorKey);
|
||||
const targetIndex = navigableRowKeys.indexOf(rowKey);
|
||||
// Shift selection logic (range) - primarily for single click + shift
|
||||
if (shiftKey && anchorKey && validKeys.length === 1) {
|
||||
const entryKey = validKeys[0];
|
||||
const anchorIndex = navigableEntryKeys.indexOf(anchorKey);
|
||||
const targetIndex = navigableEntryKeys.indexOf(entryKey);
|
||||
if (anchorIndex !== -1 && targetIndex !== -1) {
|
||||
const [start, end] = anchorIndex <= targetIndex
|
||||
? [anchorIndex, targetIndex]
|
||||
: [targetIndex, anchorIndex];
|
||||
const range = navigableRowKeys.slice(start, end + 1);
|
||||
const range = navigableEntryKeys.slice(start, end + 1);
|
||||
nextKeys = range;
|
||||
|
||||
const previousSet = new Set(selectedEntries);
|
||||
interactedKeys = range.filter((key) => key === rowKey || !previousSet.has(key));
|
||||
if (!interactedKeys.includes(rowKey)) {
|
||||
interactedKeys.push(rowKey);
|
||||
interactedKeys = range.filter((key) => key === entryKey || !previousSet.has(key));
|
||||
if (!interactedKeys.includes(entryKey)) {
|
||||
interactedKeys.push(entryKey);
|
||||
}
|
||||
} else {
|
||||
nextKeys = [rowKey];
|
||||
interactedKeys = [rowKey];
|
||||
nextKeys = [entryKey];
|
||||
interactedKeys = [entryKey];
|
||||
}
|
||||
} else if (additive) {
|
||||
if (selectedEntries.includes(rowKey)) {
|
||||
nextKeys = selectedEntries.filter((key) => key !== rowKey);
|
||||
interactedKeys = [];
|
||||
// Additive batch
|
||||
const previousSet = new Set(selectedEntries);
|
||||
if (validKeys.length === 1) {
|
||||
const entryKey = validKeys[0];
|
||||
if (previousSet.has(entryKey)) {
|
||||
nextKeys = selectedEntries.filter((key) => key !== entryKey);
|
||||
interactedKeys = [];
|
||||
} else {
|
||||
nextKeys = [...selectedEntries, entryKey];
|
||||
interactedKeys = [entryKey];
|
||||
}
|
||||
} else {
|
||||
nextKeys = [...selectedEntries, rowKey];
|
||||
interactedKeys = [rowKey];
|
||||
// Batch add
|
||||
validKeys.forEach(key => previousSet.add(key));
|
||||
nextKeys = Array.from(previousSet) as string[];
|
||||
interactedKeys = validKeys;
|
||||
}
|
||||
anchorKey = rowKey;
|
||||
anchorKey = lastKey;
|
||||
} else {
|
||||
nextKeys = [rowKey];
|
||||
interactedKeys = [rowKey];
|
||||
anchorKey = rowKey;
|
||||
// Replace with batch
|
||||
nextKeys = validKeys;
|
||||
interactedKeys = validKeys;
|
||||
anchorKey = lastKey;
|
||||
}
|
||||
|
||||
applySelection(nextKeys, { anchor: anchorKey, interactedKeys });
|
||||
@@ -235,14 +254,14 @@ export const useDocumentSelection = ({
|
||||
const promoteSelectionOrder = useCallback(
|
||||
(docId?: DocumentId | null) => {
|
||||
if (!docId) return;
|
||||
const rowKey = createDocumentEntryKey(docId);
|
||||
if (!rowKey) return;
|
||||
const entryKey = createDocumentEntryKey(docId);
|
||||
if (!entryKey) return;
|
||||
|
||||
if (!selectedEntries.includes(rowKey)) {
|
||||
if (!selectedEntries.includes(entryKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateSelectionOrder(selectedEntries, [rowKey]);
|
||||
updateSelectionOrder(selectedEntries, [entryKey]);
|
||||
},
|
||||
[selectedEntries, updateSelectionOrder],
|
||||
);
|
||||
@@ -257,8 +276,8 @@ export const useDocumentSelection = ({
|
||||
selectionInitializedRef,
|
||||
focusedDocumentId,
|
||||
setFocusedDocumentId,
|
||||
focusedRowKey,
|
||||
setFocusedRowKey,
|
||||
focusedEntryKey,
|
||||
setFocusedEntryKey,
|
||||
applySelection,
|
||||
clearSelection,
|
||||
handleEntrySelection,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { isDocumentEntry, isFolderEntry, getEntryId } from './entryKey';
|
||||
interface SelectionEntry {
|
||||
entryKey?: string;
|
||||
// Legacy field for compatibility
|
||||
rowKey?: string;
|
||||
// rowKey?: string; // Removed as part of refactor
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ export const useWorkspaceSelection = ({
|
||||
selectionInitializedRef,
|
||||
focusedDocumentId,
|
||||
setFocusedDocumentId,
|
||||
focusedRowKey,
|
||||
setFocusedRowKey,
|
||||
focusedEntryKey,
|
||||
setFocusedEntryKey,
|
||||
applySelection,
|
||||
clearSelection,
|
||||
handleEntrySelection,
|
||||
@@ -60,12 +60,18 @@ export const useWorkspaceSelection = ({
|
||||
);
|
||||
|
||||
const selectEntry = useCallback(
|
||||
(entry: SelectionEntry | string, event?: unknown) => {
|
||||
const rowKey = entry && Object(entry) === entry
|
||||
? (entry as SelectionEntry).rowKey ?? undefined
|
||||
: (entry as string);
|
||||
if (!rowKey) return;
|
||||
handleEntrySelection(rowKey, event);
|
||||
(entryOrEntries: SelectionEntry | string | Array<SelectionEntry | string>, event?: unknown) => {
|
||||
const entries = Array.isArray(entryOrEntries) ? entryOrEntries : [entryOrEntries];
|
||||
const entryKeys = entries
|
||||
.map((entry) => {
|
||||
return entry && Object(entry) === entry
|
||||
? (entry as SelectionEntry).entryKey ?? undefined
|
||||
: (entry as string);
|
||||
})
|
||||
.filter((key): key is string => Boolean(key));
|
||||
|
||||
if (entryKeys.length === 0) return;
|
||||
handleEntrySelection(entryKeys, event);
|
||||
},
|
||||
[handleEntrySelection],
|
||||
);
|
||||
@@ -96,8 +102,8 @@ export const useWorkspaceSelection = ({
|
||||
selectionInitializedRef,
|
||||
focusedDocumentId,
|
||||
setFocusedDocumentId,
|
||||
focusedRowKey,
|
||||
setFocusedRowKey,
|
||||
focusedEntryKey,
|
||||
setFocusedEntryKey,
|
||||
applySelection,
|
||||
clearSelection,
|
||||
handleEntrySelection: selectEntry,
|
||||
|
||||
Reference in New Issue
Block a user