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 selectionAnchorRef = useRef<string | null>(null);
|
||||||
const selectionInitializedRef = useRef(false);
|
const selectionInitializedRef = useRef(false);
|
||||||
const [focusedDocumentId, setFocusedDocumentId] = useState<DocumentId | null>(null);
|
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 visibleEntryKeySetRef = useRef<Set<string>>(new Set());
|
||||||
const navigableRowKeysRef = useRef<string[]>([]);
|
const navigableEntryKeysRef = useRef<string[]>([]);
|
||||||
|
|
||||||
const configureSelectionEnvironment = useCallback(({
|
const configureSelectionEnvironment = useCallback(({
|
||||||
visibleRowKeySet,
|
visibleEntryKeySet,
|
||||||
navigableRowKeys,
|
navigableEntryKeys,
|
||||||
}: {
|
}: {
|
||||||
visibleRowKeySet?: Set<string>;
|
visibleEntryKeySet?: Set<string>;
|
||||||
navigableRowKeys?: string[];
|
navigableEntryKeys?: string[];
|
||||||
}) => {
|
}) => {
|
||||||
if (visibleRowKeySet) {
|
if (visibleEntryKeySet) {
|
||||||
visibleRowKeySetRef.current = visibleRowKeySet;
|
visibleEntryKeySetRef.current = visibleEntryKeySet;
|
||||||
}
|
}
|
||||||
if (Array.isArray(navigableRowKeys)) {
|
if (Array.isArray(navigableEntryKeys)) {
|
||||||
navigableRowKeysRef.current = navigableRowKeys;
|
navigableEntryKeysRef.current = navigableEntryKeys;
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -88,16 +88,16 @@ export const useDocumentSelection = ({
|
|||||||
|
|
||||||
const applySelection = useCallback(
|
const applySelection = useCallback(
|
||||||
(
|
(
|
||||||
rowKeys: Array<string | null>,
|
entryKeys: Array<string | null>,
|
||||||
{ anchor = null, interactedKeys = [] }: ApplySelectionOptions = { anchor: null, interactedKeys: [] },
|
{ anchor = null, interactedKeys = [] }: ApplySelectionOptions = { anchor: null, interactedKeys: [] },
|
||||||
) => {
|
) => {
|
||||||
const visibleRowKeySet = visibleRowKeySetRef.current;
|
const visibleEntryKeySet = visibleEntryKeySetRef.current;
|
||||||
const unique: string[] = [];
|
const unique: string[] = [];
|
||||||
|
|
||||||
(rowKeys || []).forEach((key) => {
|
(entryKeys || []).forEach((key) => {
|
||||||
if (!key) return;
|
if (!key) return;
|
||||||
let canonicalKey: string | null = null;
|
let canonicalKey: string | null = null;
|
||||||
if (visibleRowKeySet.has(key)) {
|
if (visibleEntryKeySet.has(key)) {
|
||||||
canonicalKey = key;
|
canonicalKey = key;
|
||||||
} else if (isDocumentEntry(key)) {
|
} else if (isDocumentEntry(key)) {
|
||||||
const id = getEntryId(key);
|
const id = getEntryId(key);
|
||||||
@@ -107,7 +107,7 @@ export const useDocumentSelection = ({
|
|||||||
canonicalKey = id ? createFolderEntryKey(id) : null;
|
canonicalKey = id ? createFolderEntryKey(id) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!canonicalKey || !visibleRowKeySet.has(canonicalKey)) {
|
if (!canonicalKey || !visibleEntryKeySet.has(canonicalKey)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,19 +159,25 @@ export const useDocumentSelection = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const clearSelection = useCallback(() => {
|
const clearSelection = useCallback(() => {
|
||||||
setFocusedRowKey(null);
|
setFocusedEntryKey(null);
|
||||||
applySelection([], { anchor: null, interactedKeys: [] });
|
applySelection([], { anchor: null, interactedKeys: [] });
|
||||||
}, [applySelection]);
|
}, [applySelection]);
|
||||||
|
|
||||||
const handleEntrySelection = useCallback(
|
const handleEntrySelection = useCallback(
|
||||||
(rowKey: string, event?: SelectionEventLike) => {
|
(entryKeyOrKeys: string | string[], event?: SelectionEventLike) => {
|
||||||
const visibleRowKeySet = visibleRowKeySetRef.current;
|
const visibleEntryKeySet = visibleEntryKeySetRef.current;
|
||||||
const navigableRowKeys = navigableRowKeysRef.current;
|
const navigableEntryKeys = navigableEntryKeysRef.current;
|
||||||
if (!rowKey || !visibleRowKeySet.has(rowKey)) {
|
|
||||||
|
const entryKeys = Array.isArray(entryKeyOrKeys) ? entryKeyOrKeys : [entryKeyOrKeys];
|
||||||
|
const validKeys = entryKeys.filter((key) => key && visibleEntryKeySet.has(key));
|
||||||
|
|
||||||
|
if (validKeys.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFocusedRowKey(rowKey);
|
// Focus the last valid key
|
||||||
|
const lastKey = validKeys[validKeys.length - 1];
|
||||||
|
setFocusedEntryKey(lastKey);
|
||||||
|
|
||||||
const shiftKey = Boolean(event?.shiftKey);
|
const shiftKey = Boolean(event?.shiftKey);
|
||||||
const metaKey = Boolean(event?.metaKey);
|
const metaKey = Boolean(event?.metaKey);
|
||||||
@@ -187,44 +193,57 @@ export const useDocumentSelection = ({
|
|||||||
anchorKey = selectedEntries[selectedEntries.length - 1];
|
anchorKey = selectedEntries[selectedEntries.length - 1];
|
||||||
}
|
}
|
||||||
if (!anchorKey) {
|
if (!anchorKey) {
|
||||||
anchorKey = rowKey;
|
anchorKey = lastKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
let nextKeys: string[] = [];
|
let nextKeys: string[] = [];
|
||||||
let interactedKeys: string[] = [];
|
let interactedKeys: string[] = [];
|
||||||
|
|
||||||
if (shiftKey && anchorKey) {
|
// Shift selection logic (range) - primarily for single click + shift
|
||||||
const anchorIndex = navigableRowKeys.indexOf(anchorKey);
|
if (shiftKey && anchorKey && validKeys.length === 1) {
|
||||||
const targetIndex = navigableRowKeys.indexOf(rowKey);
|
const entryKey = validKeys[0];
|
||||||
|
const anchorIndex = navigableEntryKeys.indexOf(anchorKey);
|
||||||
|
const targetIndex = navigableEntryKeys.indexOf(entryKey);
|
||||||
if (anchorIndex !== -1 && targetIndex !== -1) {
|
if (anchorIndex !== -1 && targetIndex !== -1) {
|
||||||
const [start, end] = anchorIndex <= targetIndex
|
const [start, end] = anchorIndex <= targetIndex
|
||||||
? [anchorIndex, targetIndex]
|
? [anchorIndex, targetIndex]
|
||||||
: [targetIndex, anchorIndex];
|
: [targetIndex, anchorIndex];
|
||||||
const range = navigableRowKeys.slice(start, end + 1);
|
const range = navigableEntryKeys.slice(start, end + 1);
|
||||||
nextKeys = range;
|
nextKeys = range;
|
||||||
|
|
||||||
const previousSet = new Set(selectedEntries);
|
const previousSet = new Set(selectedEntries);
|
||||||
interactedKeys = range.filter((key) => key === rowKey || !previousSet.has(key));
|
interactedKeys = range.filter((key) => key === entryKey || !previousSet.has(key));
|
||||||
if (!interactedKeys.includes(rowKey)) {
|
if (!interactedKeys.includes(entryKey)) {
|
||||||
interactedKeys.push(rowKey);
|
interactedKeys.push(entryKey);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nextKeys = [rowKey];
|
nextKeys = [entryKey];
|
||||||
interactedKeys = [rowKey];
|
interactedKeys = [entryKey];
|
||||||
}
|
}
|
||||||
} else if (additive) {
|
} else if (additive) {
|
||||||
if (selectedEntries.includes(rowKey)) {
|
// Additive batch
|
||||||
nextKeys = selectedEntries.filter((key) => key !== rowKey);
|
const previousSet = new Set(selectedEntries);
|
||||||
interactedKeys = [];
|
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 {
|
} else {
|
||||||
nextKeys = [...selectedEntries, rowKey];
|
// Batch add
|
||||||
interactedKeys = [rowKey];
|
validKeys.forEach(key => previousSet.add(key));
|
||||||
|
nextKeys = Array.from(previousSet) as string[];
|
||||||
|
interactedKeys = validKeys;
|
||||||
}
|
}
|
||||||
anchorKey = rowKey;
|
anchorKey = lastKey;
|
||||||
} else {
|
} else {
|
||||||
nextKeys = [rowKey];
|
// Replace with batch
|
||||||
interactedKeys = [rowKey];
|
nextKeys = validKeys;
|
||||||
anchorKey = rowKey;
|
interactedKeys = validKeys;
|
||||||
|
anchorKey = lastKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
applySelection(nextKeys, { anchor: anchorKey, interactedKeys });
|
applySelection(nextKeys, { anchor: anchorKey, interactedKeys });
|
||||||
@@ -235,14 +254,14 @@ export const useDocumentSelection = ({
|
|||||||
const promoteSelectionOrder = useCallback(
|
const promoteSelectionOrder = useCallback(
|
||||||
(docId?: DocumentId | null) => {
|
(docId?: DocumentId | null) => {
|
||||||
if (!docId) return;
|
if (!docId) return;
|
||||||
const rowKey = createDocumentEntryKey(docId);
|
const entryKey = createDocumentEntryKey(docId);
|
||||||
if (!rowKey) return;
|
if (!entryKey) return;
|
||||||
|
|
||||||
if (!selectedEntries.includes(rowKey)) {
|
if (!selectedEntries.includes(entryKey)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSelectionOrder(selectedEntries, [rowKey]);
|
updateSelectionOrder(selectedEntries, [entryKey]);
|
||||||
},
|
},
|
||||||
[selectedEntries, updateSelectionOrder],
|
[selectedEntries, updateSelectionOrder],
|
||||||
);
|
);
|
||||||
@@ -257,8 +276,8 @@ export const useDocumentSelection = ({
|
|||||||
selectionInitializedRef,
|
selectionInitializedRef,
|
||||||
focusedDocumentId,
|
focusedDocumentId,
|
||||||
setFocusedDocumentId,
|
setFocusedDocumentId,
|
||||||
focusedRowKey,
|
focusedEntryKey,
|
||||||
setFocusedRowKey,
|
setFocusedEntryKey,
|
||||||
applySelection,
|
applySelection,
|
||||||
clearSelection,
|
clearSelection,
|
||||||
handleEntrySelection,
|
handleEntrySelection,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { isDocumentEntry, isFolderEntry, getEntryId } from './entryKey';
|
|||||||
interface SelectionEntry {
|
interface SelectionEntry {
|
||||||
entryKey?: string;
|
entryKey?: string;
|
||||||
// Legacy field for compatibility
|
// Legacy field for compatibility
|
||||||
rowKey?: string;
|
// rowKey?: string; // Removed as part of refactor
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,8 +32,8 @@ export const useWorkspaceSelection = ({
|
|||||||
selectionInitializedRef,
|
selectionInitializedRef,
|
||||||
focusedDocumentId,
|
focusedDocumentId,
|
||||||
setFocusedDocumentId,
|
setFocusedDocumentId,
|
||||||
focusedRowKey,
|
focusedEntryKey,
|
||||||
setFocusedRowKey,
|
setFocusedEntryKey,
|
||||||
applySelection,
|
applySelection,
|
||||||
clearSelection,
|
clearSelection,
|
||||||
handleEntrySelection,
|
handleEntrySelection,
|
||||||
@@ -60,12 +60,18 @@ export const useWorkspaceSelection = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const selectEntry = useCallback(
|
const selectEntry = useCallback(
|
||||||
(entry: SelectionEntry | string, event?: unknown) => {
|
(entryOrEntries: SelectionEntry | string | Array<SelectionEntry | string>, event?: unknown) => {
|
||||||
const rowKey = entry && Object(entry) === entry
|
const entries = Array.isArray(entryOrEntries) ? entryOrEntries : [entryOrEntries];
|
||||||
? (entry as SelectionEntry).rowKey ?? undefined
|
const entryKeys = entries
|
||||||
: (entry as string);
|
.map((entry) => {
|
||||||
if (!rowKey) return;
|
return entry && Object(entry) === entry
|
||||||
handleEntrySelection(rowKey, event);
|
? (entry as SelectionEntry).entryKey ?? undefined
|
||||||
|
: (entry as string);
|
||||||
|
})
|
||||||
|
.filter((key): key is string => Boolean(key));
|
||||||
|
|
||||||
|
if (entryKeys.length === 0) return;
|
||||||
|
handleEntrySelection(entryKeys, event);
|
||||||
},
|
},
|
||||||
[handleEntrySelection],
|
[handleEntrySelection],
|
||||||
);
|
);
|
||||||
@@ -96,8 +102,8 @@ export const useWorkspaceSelection = ({
|
|||||||
selectionInitializedRef,
|
selectionInitializedRef,
|
||||||
focusedDocumentId,
|
focusedDocumentId,
|
||||||
setFocusedDocumentId,
|
setFocusedDocumentId,
|
||||||
focusedRowKey,
|
focusedEntryKey,
|
||||||
setFocusedRowKey,
|
setFocusedEntryKey,
|
||||||
applySelection,
|
applySelection,
|
||||||
clearSelection,
|
clearSelection,
|
||||||
handleEntrySelection: selectEntry,
|
handleEntrySelection: selectEntry,
|
||||||
|
|||||||
@@ -171,6 +171,8 @@ interface DesktopWorkspaceViewProps extends Omit<DocumentsViewProps, 'entries' |
|
|||||||
recalcVisibleDocIds: () => void;
|
recalcVisibleDocIds: () => void;
|
||||||
dragSettings: DragSettings;
|
dragSettings: DragSettings;
|
||||||
markLayoutDirty: () => void;
|
markLayoutDirty: () => void;
|
||||||
|
onSelect?: (descriptor: unknown, event?: unknown) => void;
|
||||||
|
onPromoteSelection?: (docId: Identifier, event?: unknown) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultGetDocumentAsset: GetAsset = () => null;
|
const defaultGetDocumentAsset: GetAsset = () => null;
|
||||||
@@ -192,6 +194,7 @@ const DesktopWorkspace: React.FC<DocumentsViewProps> = ({
|
|||||||
clearSelection,
|
clearSelection,
|
||||||
handleEntrySelection,
|
handleEntrySelection,
|
||||||
promoteSelectionOrder,
|
promoteSelectionOrder,
|
||||||
|
configureSelectionEnvironment,
|
||||||
} = useWorkspaceSelectionContext();
|
} = useWorkspaceSelectionContext();
|
||||||
const items = useMemo<DeskDocument[]>(
|
const items = useMemo<DeskDocument[]>(
|
||||||
() => {
|
() => {
|
||||||
@@ -213,36 +216,16 @@ const DesktopWorkspace: React.FC<DocumentsViewProps> = ({
|
|||||||
[entries],
|
[entries],
|
||||||
);
|
);
|
||||||
|
|
||||||
const getDocRowKey = useCallback((id: Identifier | null) => (id != null ? `document:${id}` : null), []);
|
const getDocEntryKey = useCallback((id: Identifier | null) => (id != null ? `document:${id}` : null), []);
|
||||||
|
|
||||||
const handleStackSelect = useCallback(
|
|
||||||
(docIds: Identifier[], event?: PointerEvent | MouseEvent | null) => {
|
|
||||||
if (!Array.isArray(docIds) || docIds.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const syntheticEvent = event || ({
|
|
||||||
metaKey: true,
|
|
||||||
ctrlKey: true,
|
|
||||||
preventDefault: () => { },
|
|
||||||
} as unknown as PointerEvent);
|
|
||||||
docIds.forEach((id) => {
|
|
||||||
const key = getDocRowKey(id);
|
|
||||||
if (key) {
|
|
||||||
handleEntrySelection(key, syntheticEvent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[getDocRowKey, handleEntrySelection],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handlePromoteSelection = useCallback(
|
const handlePromoteSelection = useCallback(
|
||||||
(docId: Identifier | null) => {
|
(docId: Identifier | null) => {
|
||||||
const key = getDocRowKey(docId);
|
const key = getDocEntryKey(docId);
|
||||||
if (key && promoteSelectionOrder) {
|
if (key) {
|
||||||
promoteSelectionOrder(key);
|
promoteSelectionOrder(docId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[getDocRowKey, promoteSelectionOrder],
|
[getDocEntryKey, promoteSelectionOrder],
|
||||||
);
|
);
|
||||||
|
|
||||||
const allowLayoutPersistence = Boolean(tenantId && viewId && viewId.startsWith('folder:'));
|
const allowLayoutPersistence = Boolean(tenantId && viewId && viewId.startsWith('folder:'));
|
||||||
@@ -307,10 +290,6 @@ const DesktopWorkspace: React.FC<DocumentsViewProps> = ({
|
|||||||
engine.setDocumentLookup(map);
|
engine.setDocumentLookup(map);
|
||||||
}, [engine, items]);
|
}, [engine, items]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
engine.setEnsureDocumentSize(ensureDocumentSize);
|
|
||||||
}, [engine, ensureDocumentSize]);
|
|
||||||
|
|
||||||
const workspaceSnapshot = useWorkspaceSnapshot(engine, useSyncExternalStore) as WorkspaceSnapshotState;
|
const workspaceSnapshot = useWorkspaceSnapshot(engine, useSyncExternalStore) as WorkspaceSnapshotState;
|
||||||
const {
|
const {
|
||||||
layout: layoutSnapshot,
|
layout: layoutSnapshot,
|
||||||
@@ -323,6 +302,19 @@ const DesktopWorkspace: React.FC<DocumentsViewProps> = ({
|
|||||||
initialLoadDone,
|
initialLoadDone,
|
||||||
} = workspaceSnapshot;
|
} = workspaceSnapshot;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const visibleEntryKeySet = new Set<string>();
|
||||||
|
visibleDocIds.forEach((id) => {
|
||||||
|
const key = getDocEntryKey(id);
|
||||||
|
if (key) visibleEntryKeySet.add(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
configureSelectionEnvironment({
|
||||||
|
visibleEntryKeySet,
|
||||||
|
navigableEntryKeys: [], // Desktop doesn't have linear navigation yet
|
||||||
|
});
|
||||||
|
}, [visibleDocIds, getDocEntryKey, configureSelectionEnvironment]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const shouldWaitForPersisted = allowLayoutPersistence && !initialLoadDone;
|
const shouldWaitForPersisted = allowLayoutPersistence && !initialLoadDone;
|
||||||
|
|
||||||
@@ -794,10 +786,22 @@ const DesktopWorkspace: React.FC<DocumentsViewProps> = ({
|
|||||||
overlayOriginRect,
|
overlayOriginRect,
|
||||||
overlayOriginTransform,
|
overlayOriginTransform,
|
||||||
overlayDocument,
|
overlayDocument,
|
||||||
onDocumentStackSelect: handleStackSelect,
|
|
||||||
onPromoteSelection: handlePromoteSelection,
|
onPromoteSelection: handlePromoteSelection,
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
onClearSelection: clearSelection,
|
onClearSelection: clearSelection,
|
||||||
|
onDocumentClick: passThroughProps.onDocumentClick,
|
||||||
|
onSelect: (descriptorOrDescriptors: any, event: any) => {
|
||||||
|
const descriptors = Array.isArray(descriptorOrDescriptors)
|
||||||
|
? descriptorOrDescriptors
|
||||||
|
: [descriptorOrDescriptors];
|
||||||
|
const keys = descriptors
|
||||||
|
.map((d: any) => getDocEntryKey(d.id))
|
||||||
|
.filter((k: any) => k);
|
||||||
|
|
||||||
|
if (keys.length > 0) {
|
||||||
|
handleEntrySelection(keys, event);
|
||||||
|
}
|
||||||
|
},
|
||||||
documentLookup,
|
documentLookup,
|
||||||
resolveBaseMetrics,
|
resolveBaseMetrics,
|
||||||
bringToFront,
|
bringToFront,
|
||||||
@@ -836,7 +840,6 @@ function DesktopWorkspaceView({
|
|||||||
overlayOriginTransform,
|
overlayOriginTransform,
|
||||||
overlayDocument,
|
overlayDocument,
|
||||||
onDocumentClick,
|
onDocumentClick,
|
||||||
onDocumentStackSelect,
|
|
||||||
onPromoteSelection,
|
onPromoteSelection,
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
onClearSelection,
|
onClearSelection,
|
||||||
@@ -850,6 +853,7 @@ function DesktopWorkspaceView({
|
|||||||
dragSettings,
|
dragSettings,
|
||||||
onDocumentActivate,
|
onDocumentActivate,
|
||||||
markLayoutDirty,
|
markLayoutDirty,
|
||||||
|
onSelect,
|
||||||
}: DesktopWorkspaceViewProps) {
|
}: DesktopWorkspaceViewProps) {
|
||||||
const handleDeskDocumentActivate = useCallback(
|
const handleDeskDocumentActivate = useCallback(
|
||||||
(docId: Identifier) => {
|
(docId: Identifier) => {
|
||||||
@@ -888,7 +892,6 @@ function DesktopWorkspaceView({
|
|||||||
settings: dragSettings,
|
settings: dragSettings,
|
||||||
containerRef,
|
containerRef,
|
||||||
onDocumentActivate: handleDeskDocumentActivate,
|
onDocumentActivate: handleDeskDocumentActivate,
|
||||||
selectedDocumentIds,
|
|
||||||
markLayoutDirty,
|
markLayoutDirty,
|
||||||
}) as {
|
}) as {
|
||||||
handlePointerDown: React.PointerEventHandler<HTMLElement>;
|
handlePointerDown: React.PointerEventHandler<HTMLElement>;
|
||||||
@@ -908,11 +911,11 @@ function DesktopWorkspaceView({
|
|||||||
handlePointerUp,
|
handlePointerUp,
|
||||||
handlePointerCancel,
|
handlePointerCancel,
|
||||||
onDocumentClick: handleDeskDocumentClick,
|
onDocumentClick: handleDeskDocumentClick,
|
||||||
onDocumentStackSelect,
|
|
||||||
onPromoteSelection,
|
onPromoteSelection,
|
||||||
onDocumentActivate: handleDeskDocumentActivate,
|
onDocumentActivate: handleDeskDocumentActivate,
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
openOverlayForDoc,
|
openOverlayForDoc,
|
||||||
|
onSelect,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export interface PointerIntent {
|
|||||||
clickSelectionApplied: boolean;
|
clickSelectionApplied: boolean;
|
||||||
stackSelectionApplied: boolean;
|
stackSelectionApplied: boolean;
|
||||||
longPressTriggered: boolean;
|
longPressTriggered: boolean;
|
||||||
|
optimisticSelection: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createPointerIntent = ({
|
export const createPointerIntent = ({
|
||||||
@@ -61,9 +62,9 @@ export const createPointerIntent = ({
|
|||||||
|
|
||||||
if (metaKey) {
|
if (metaKey) {
|
||||||
clickAction = CLICK_ACTIONS.addStack;
|
clickAction = CLICK_ACTIONS.addStack;
|
||||||
dragAction = DRAG_ACTIONS.dragSelectStack;
|
dragAction = DRAG_ACTIONS.dragSelection;
|
||||||
} else if (alreadySelected) {
|
} else if (alreadySelected) {
|
||||||
clickAction = CLICK_ACTIONS.selectSingle;
|
clickAction = CLICK_ACTIONS.openDetail;
|
||||||
dragAction = selectionCount > 1 ? DRAG_ACTIONS.dragSelection : DRAG_ACTIONS.dragSelectSingle;
|
dragAction = selectionCount > 1 ? DRAG_ACTIONS.dragSelection : DRAG_ACTIONS.dragSelectSingle;
|
||||||
} else {
|
} else {
|
||||||
clickAction = CLICK_ACTIONS.selectSingle;
|
clickAction = CLICK_ACTIONS.selectSingle;
|
||||||
@@ -74,8 +75,23 @@ export const createPointerIntent = ({
|
|||||||
? stackHits.map((value) => String(value))
|
? stackHits.map((value) => String(value))
|
||||||
: [String(doc.id)];
|
: [String(doc.id)];
|
||||||
|
|
||||||
const stackDocIdsForDrag = dragAction === DRAG_ACTIONS.dragSelectStack ? stackList : null;
|
|
||||||
const stackDocIdsForClick = clickAction === CLICK_ACTIONS.addStack ? stackList : null;
|
const stackDocIdsForClick = clickAction === CLICK_ACTIONS.addStack ? stackList : null;
|
||||||
|
const stackDocIdsForDrag = metaKey ? stackList : null;
|
||||||
|
|
||||||
|
// Calculate optimistic selection
|
||||||
|
let optimisticSelection: string[] = [];
|
||||||
|
if (metaKey) {
|
||||||
|
// Additive selection (stack or single)
|
||||||
|
const currentSelection = new Set(selectedDocumentIds);
|
||||||
|
stackList.forEach(id => currentSelection.add(id));
|
||||||
|
optimisticSelection = Array.from(currentSelection);
|
||||||
|
} else if (alreadySelected) {
|
||||||
|
// Already selected: keep current selection
|
||||||
|
optimisticSelection = [...selectedDocumentIds];
|
||||||
|
} else {
|
||||||
|
// New single selection
|
||||||
|
optimisticSelection = [doc.id];
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
docId: doc.id,
|
docId: doc.id,
|
||||||
@@ -90,33 +106,53 @@ export const createPointerIntent = ({
|
|||||||
stackDocIdsForDrag,
|
stackDocIdsForDrag,
|
||||||
stackDocIdsForClick,
|
stackDocIdsForClick,
|
||||||
stackReplaceOnClick: clickAction === CLICK_ACTIONS.addStack,
|
stackReplaceOnClick: clickAction === CLICK_ACTIONS.addStack,
|
||||||
stackReplaceOnDrag: dragAction === DRAG_ACTIONS.dragSelectStack,
|
stackReplaceOnDrag: false,
|
||||||
clickSelectionApplied: false,
|
clickSelectionApplied: false,
|
||||||
stackSelectionApplied: false,
|
stackSelectionApplied: false,
|
||||||
longPressTriggered: false,
|
longPressTriggered: false,
|
||||||
|
optimisticSelection,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const applyClickPlanImmediately = ({ intent, event, onEntryPointer, onDocumentStackSelect }: {
|
export const applyClickPlanImmediately = ({ intent, event, onEntryPointer, onDocumentStackSelect, onSelect }: {
|
||||||
intent: PointerIntent;
|
intent: PointerIntent;
|
||||||
event?: unknown;
|
event?: unknown;
|
||||||
onEntryPointer?: (descriptor: unknown, event?: unknown) => void;
|
onEntryPointer?: (descriptor: unknown, event?: unknown) => void;
|
||||||
onDocumentStackSelect?: (docIds: string[], event?: unknown, options?: { replace?: boolean }) => void;
|
onDocumentStackSelect?: (docIds: string[], event?: unknown, options?: { replace?: boolean }) => void;
|
||||||
|
onSelect?: (descriptor: unknown, event?: unknown) => void;
|
||||||
}) => {
|
}) => {
|
||||||
switch (intent.clickAction) {
|
switch (intent.clickAction) {
|
||||||
case CLICK_ACTIONS.selectSingle:
|
case CLICK_ACTIONS.selectSingle:
|
||||||
case CLICK_ACTIONS.addCard:
|
case CLICK_ACTIONS.addCard:
|
||||||
safeInvoke(onEntryPointer, intent.entryDescriptor, event);
|
if (onSelect) {
|
||||||
|
safeInvoke(onSelect, intent.entryDescriptor, event);
|
||||||
|
} else {
|
||||||
|
safeInvoke(onEntryPointer, intent.entryDescriptor, event);
|
||||||
|
}
|
||||||
intent.clickSelectionApplied = true;
|
intent.clickSelectionApplied = true;
|
||||||
break;
|
break;
|
||||||
case CLICK_ACTIONS.addStack:
|
case CLICK_ACTIONS.addStack:
|
||||||
if (Array.isArray(intent.stackDocIdsForClick) && intent.stackDocIdsForClick.length > 0) {
|
if (Array.isArray(intent.stackDocIdsForClick) && intent.stackDocIdsForClick.length > 0) {
|
||||||
safeInvoke(
|
// Use onSelect for stack selection (batch)
|
||||||
onDocumentStackSelect,
|
if (onSelect) {
|
||||||
intent.stackDocIdsForClick,
|
// Map docIds to descriptors if necessary, or just pass IDs if onSelect handles it.
|
||||||
event,
|
// The current onSelect adapter in DesktopWorkspace expects { id } objects or just IDs?
|
||||||
{ replace: intent.stackReplaceOnClick },
|
// Let's assume it expects descriptors like selectSingle.
|
||||||
);
|
const descriptors = intent.stackDocIdsForClick.map(id => ({
|
||||||
|
type: 'document',
|
||||||
|
id,
|
||||||
|
key: `document:${id}`,
|
||||||
|
}));
|
||||||
|
safeInvoke(onSelect, descriptors, event);
|
||||||
|
} else {
|
||||||
|
// Fallback to legacy if onSelect not provided (shouldn't happen in new flow)
|
||||||
|
safeInvoke(
|
||||||
|
onDocumentStackSelect,
|
||||||
|
intent.stackDocIdsForClick,
|
||||||
|
event,
|
||||||
|
{ replace: intent.stackReplaceOnClick },
|
||||||
|
);
|
||||||
|
}
|
||||||
intent.clickSelectionApplied = true;
|
intent.clickSelectionApplied = true;
|
||||||
intent.stackSelectionApplied = true;
|
intent.stackSelectionApplied = true;
|
||||||
}
|
}
|
||||||
@@ -128,17 +164,18 @@ export const applyClickPlanImmediately = ({ intent, event, onEntryPointer, onDoc
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const finalizeClickSelection = ({ intent, event, onEntryPointer, onDocumentStackSelect }: {
|
export const finalizeClickSelection = ({ intent, event, onEntryPointer, onDocumentStackSelect, onSelect }: {
|
||||||
intent: PointerIntent;
|
intent: PointerIntent;
|
||||||
event?: unknown;
|
event?: unknown;
|
||||||
onEntryPointer?: (descriptor: unknown, event?: unknown) => void;
|
onEntryPointer?: (descriptor: unknown, event?: unknown) => void;
|
||||||
onDocumentStackSelect?: (docIds: string[], event?: unknown, options?: { replace?: boolean }) => void;
|
onDocumentStackSelect?: (docIds: string[], event?: unknown, options?: { replace?: boolean }) => void;
|
||||||
|
onSelect?: (descriptor: unknown, event?: unknown) => void;
|
||||||
}) => {
|
}) => {
|
||||||
if (!intent || intent.clickSelectionApplied) {
|
if (!intent || intent.clickSelectionApplied) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyClickPlanImmediately({ intent, event, onEntryPointer, onDocumentStackSelect });
|
applyClickPlanImmediately({ intent, event, onEntryPointer, onDocumentStackSelect, onSelect });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const applyLongPressSelection = ({ intent, stackDocIds, syntheticEvent, onDocumentStackSelect }: {
|
export const applyLongPressSelection = ({ intent, stackDocIds, syntheticEvent, onDocumentStackSelect }: {
|
||||||
|
|||||||
@@ -33,11 +33,11 @@ export const useDeskPointer = ({
|
|||||||
handlePointerUp,
|
handlePointerUp,
|
||||||
handlePointerCancel,
|
handlePointerCancel,
|
||||||
onDocumentClick,
|
onDocumentClick,
|
||||||
onDocumentStackSelect,
|
|
||||||
onPromoteSelection,
|
onPromoteSelection,
|
||||||
onDocumentActivate,
|
onDocumentActivate,
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
openOverlayForDoc = null,
|
openOverlayForDoc = null,
|
||||||
|
onSelect = null,
|
||||||
}) => {
|
}) => {
|
||||||
const pointerIntentRef = useRef(null);
|
const pointerIntentRef = useRef(null);
|
||||||
const pointerStartRef = useRef({ x: 0, y: 0 });
|
const pointerStartRef = useRef({ x: 0, y: 0 });
|
||||||
@@ -203,13 +203,13 @@ export const useDeskPointer = ({
|
|||||||
intent,
|
intent,
|
||||||
stackDocIds: stackHits,
|
stackDocIds: stackHits,
|
||||||
syntheticEvent,
|
syntheticEvent,
|
||||||
onDocumentStackSelect,
|
onDocumentStackSelect: null, // Deprecated, handled by onSelect if needed, or long press needs update
|
||||||
});
|
});
|
||||||
pointerIntentRef.current = intent;
|
pointerIntentRef.current = intent;
|
||||||
resetLongPressState();
|
resetLongPressState();
|
||||||
}, LONG_PRESS_DURATION_MS);
|
}, LONG_PRESS_DURATION_MS);
|
||||||
},
|
},
|
||||||
[onDocumentStackSelect, resolveStackDocIds, resetLongPressState],
|
[resolveStackDocIds, resetLongPressState],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => () => resetLongPressState(), [resetLongPressState]);
|
useEffect(() => () => resetLongPressState(), [resetLongPressState]);
|
||||||
@@ -242,25 +242,24 @@ export const useDeskPointer = ({
|
|||||||
stackHits,
|
stackHits,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (intent.selectedAtDown) {
|
if (intent.selectedAtDown) {
|
||||||
safeInvoke(onPromoteSelection, doc.id, event);
|
safeInvoke(onPromoteSelection, doc.id, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
applyClickPlanImmediately({
|
applyClickPlanImmediately({
|
||||||
intent,
|
intent,
|
||||||
event,
|
event,
|
||||||
onEntryPointer: onDocumentClick,
|
onEntryPointer: onDocumentClick,
|
||||||
onDocumentStackSelect,
|
onSelect,
|
||||||
});
|
});
|
||||||
|
|
||||||
pointerIntentRef.current = intent;
|
pointerIntentRef.current = intent;
|
||||||
|
|
||||||
handlePointerDown(event, doc.id, {
|
handlePointerDown(event, doc.id, {
|
||||||
stackDocIds: intent.stackDocIdsForDrag,
|
draggedDocIds: intent.optimisticSelection,
|
||||||
stackSelectionApplied: intent.stackSelectionApplied,
|
stackSelectionApplied: intent.stackSelectionApplied,
|
||||||
wasSelected: intent.selectedAtDown,
|
wasSelected: intent.selectedAtDown,
|
||||||
modifierActive,
|
modifierActive,
|
||||||
stackReplace: intent.stackReplaceOnDrag,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
scheduleLongPress({
|
scheduleLongPress({
|
||||||
@@ -273,7 +272,7 @@ export const useDeskPointer = ({
|
|||||||
handlePointerDown,
|
handlePointerDown,
|
||||||
onPromoteSelection,
|
onPromoteSelection,
|
||||||
onDocumentClick,
|
onDocumentClick,
|
||||||
onDocumentStackSelect,
|
onSelect,
|
||||||
resolveStackDocIds,
|
resolveStackDocIds,
|
||||||
resetLongPressState,
|
resetLongPressState,
|
||||||
scheduleLongPress,
|
scheduleLongPress,
|
||||||
@@ -309,7 +308,7 @@ export const useDeskPointer = ({
|
|||||||
intent: pointerState,
|
intent: pointerState,
|
||||||
event,
|
event,
|
||||||
onEntryPointer: onDocumentClick,
|
onEntryPointer: onDocumentClick,
|
||||||
onDocumentStackSelect,
|
onSelect,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -336,8 +335,8 @@ export const useDeskPointer = ({
|
|||||||
[
|
[
|
||||||
handlePointerUp,
|
handlePointerUp,
|
||||||
onDocumentActivate,
|
onDocumentActivate,
|
||||||
onDocumentStackSelect,
|
|
||||||
onDocumentClick,
|
onDocumentClick,
|
||||||
|
onSelect,
|
||||||
resetLongPressState,
|
resetLongPressState,
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
type RefObject,
|
type RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import type { PointerEvent as ReactPointerEvent } from 'react';
|
import type { PointerEvent as ReactPointerEvent } from 'react';
|
||||||
import { preventAll, safeInvoke } from './events';
|
import { preventAll } from './events';
|
||||||
import { clamp } from '../utils/math';
|
import { clamp } from '../utils/math';
|
||||||
import usePointerTap from '../ui/usePointerTap';
|
import usePointerTap from '../ui/usePointerTap';
|
||||||
import {
|
import {
|
||||||
@@ -83,11 +83,9 @@ interface DragSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface PointerDownOptions {
|
interface PointerDownOptions {
|
||||||
stackDocIds?: Array<Identifier | null>;
|
draggedDocIds?: string[];
|
||||||
stackSelectionApplied?: boolean;
|
stackSelectionApplied?: boolean;
|
||||||
wasSelected?: boolean;
|
|
||||||
modifierActive?: boolean;
|
modifierActive?: boolean;
|
||||||
stackReplace?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UseDocumentDragOptions {
|
interface UseDocumentDragOptions {
|
||||||
@@ -109,12 +107,6 @@ interface UseDocumentDragOptions {
|
|||||||
settings?: DragSettings;
|
settings?: DragSettings;
|
||||||
containerRef?: RefObject<HTMLElement>;
|
containerRef?: RefObject<HTMLElement>;
|
||||||
onDocumentActivate?: (docId: Identifier | null, event?: PointerEvent | ReactPointerEvent) => void;
|
onDocumentActivate?: (docId: Identifier | null, event?: PointerEvent | ReactPointerEvent) => void;
|
||||||
onDocumentStackSelect?: (
|
|
||||||
docIds: Identifier[],
|
|
||||||
event: PointerEvent | ReactPointerEvent,
|
|
||||||
options?: { replace?: boolean },
|
|
||||||
) => void;
|
|
||||||
selectedDocumentIds?: Array<Identifier | null>;
|
|
||||||
markLayoutDirty?: () => void;
|
markLayoutDirty?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,9 +144,7 @@ interface DragStateInternal extends EngineDragState {
|
|||||||
activeDocIds: string[];
|
activeDocIds: string[];
|
||||||
groupItems: DragGroupItemInternal[];
|
groupItems: DragGroupItemInternal[];
|
||||||
groupElevated: boolean;
|
groupElevated: boolean;
|
||||||
stackDocIds: string[] | null;
|
|
||||||
stackSelectionApplied: boolean;
|
stackSelectionApplied: boolean;
|
||||||
stackReplace: boolean;
|
|
||||||
massGrams: number;
|
massGrams: number;
|
||||||
pointerRadiusScale: number;
|
pointerRadiusScale: number;
|
||||||
lastPointerCanvasX: number;
|
lastPointerCanvasX: number;
|
||||||
@@ -209,8 +199,6 @@ const useDocumentDrag = (options: UseDocumentDragOptions) => {
|
|||||||
settings,
|
settings,
|
||||||
containerRef: providedContainerRef,
|
containerRef: providedContainerRef,
|
||||||
onDocumentActivate,
|
onDocumentActivate,
|
||||||
onDocumentStackSelect,
|
|
||||||
selectedDocumentIds = [],
|
|
||||||
markLayoutDirty,
|
markLayoutDirty,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
@@ -342,50 +330,20 @@ const useDocumentDrag = (options: UseDocumentDragOptions) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const massGrams = computeDocumentMassGrams(doc);
|
const massGrams = computeDocumentMassGrams(doc);
|
||||||
|
const draggedDocIds = options?.draggedDocIds;
|
||||||
|
|
||||||
const stackDocIdsOptionRaw = options?.stackDocIds;
|
let selectionIds: string[] = [];
|
||||||
const stackDocIdsOption = Array.isArray(stackDocIdsOptionRaw)
|
|
||||||
? stackDocIdsOptionRaw
|
|
||||||
.map((value) => (value != null ? String(value) : null))
|
|
||||||
.filter((value): value is string => Boolean(value))
|
|
||||||
: null;
|
|
||||||
const stackSelectionAppliedInitial = Boolean(options?.stackSelectionApplied);
|
|
||||||
const wasSelectedAtPointerDown = Boolean(options?.wasSelected);
|
|
||||||
const pointerModifierActive =
|
|
||||||
options?.modifierActive ?? Boolean(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey);
|
|
||||||
const stackReplace = Boolean(options?.stackReplace);
|
|
||||||
|
|
||||||
let selectionIds: string[] = Array.isArray(selectedDocumentIds)
|
if (Array.isArray(draggedDocIds) && draggedDocIds.length > 0) {
|
||||||
? selectedDocumentIds
|
// Use explicitly provided IDs for the drag operation
|
||||||
.map((id) => (id != null ? String(id) : null))
|
selectionIds = draggedDocIds;
|
||||||
.filter((id): id is string => Boolean(id))
|
} else {
|
||||||
: [];
|
// Fallback (should ideally not happen if useDeskPointer is correct)
|
||||||
|
|
||||||
if (!stackDocIdsOption && !pointerModifierActive && !wasSelectedAtPointerDown) {
|
|
||||||
selectionIds = [docKey];
|
selectionIds = [docKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stackDocIdsOption && stackDocIdsOption.length) {
|
|
||||||
const selectionSet = new Set(selectionIds);
|
|
||||||
stackDocIdsOption.forEach((value) => {
|
|
||||||
if (value != null) {
|
|
||||||
selectionSet.add(String(value));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
selectionIds = Array.from(selectionSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
const metaOrCtrl = event.metaKey || event.ctrlKey;
|
|
||||||
if (!stackDocIdsOption && metaOrCtrl && !selectionIds.includes(docKey)) {
|
|
||||||
selectionIds = [...selectionIds, docKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
selectionIds = selectionIds.filter((id, index, array) => array.indexOf(id) === index && documentLookup.has(id));
|
selectionIds = selectionIds.filter((id, index, array) => array.indexOf(id) === index && documentLookup.has(id));
|
||||||
|
|
||||||
if (!selectionIds.includes(docKey)) {
|
|
||||||
selectionIds.unshift(docKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!selectionIds.length) {
|
if (!selectionIds.length) {
|
||||||
selectionIds = [docKey];
|
selectionIds = [docKey];
|
||||||
}
|
}
|
||||||
@@ -413,7 +371,7 @@ const useDocumentDrag = (options: UseDocumentDragOptions) => {
|
|||||||
const centerX = Number.isFinite(entry?.centerX) ? entry.centerX : defaultCenterX;
|
const centerX = Number.isFinite(entry?.centerX) ? entry.centerX : defaultCenterX;
|
||||||
const centerY = Number.isFinite(entry?.centerY) ? entry.centerY : defaultCenterY;
|
const centerY = Number.isFinite(entry?.centerY) ? entry.centerY : defaultCenterY;
|
||||||
|
|
||||||
const modifierPressed = pointerModifierActive;
|
const modifierPressed = Boolean(options?.modifierActive);
|
||||||
if (!modifierPressed) {
|
if (!modifierPressed) {
|
||||||
if (isGroupDrag) {
|
if (isGroupDrag) {
|
||||||
const layout = layoutRef.current;
|
const layout = layoutRef.current;
|
||||||
@@ -498,8 +456,6 @@ const useDocumentDrag = (options: UseDocumentDragOptions) => {
|
|||||||
? performance.now()
|
? performance.now()
|
||||||
: Date.now();
|
: Date.now();
|
||||||
|
|
||||||
const hasStackSource = Array.isArray(stackDocIdsOption) && stackDocIdsOption.length > 1;
|
|
||||||
|
|
||||||
dragStateRef.current = {
|
dragStateRef.current = {
|
||||||
docId: docKey,
|
docId: docKey,
|
||||||
docKey,
|
docKey,
|
||||||
@@ -532,9 +488,7 @@ const useDocumentDrag = (options: UseDocumentDragOptions) => {
|
|||||||
activeDocIds: selectionIds,
|
activeDocIds: selectionIds,
|
||||||
groupItems,
|
groupItems,
|
||||||
groupElevated: !isGroupDrag,
|
groupElevated: !isGroupDrag,
|
||||||
stackDocIds: hasStackSource ? stackDocIdsOption : null,
|
stackSelectionApplied: true,
|
||||||
stackSelectionApplied: stackSelectionAppliedInitial || !hasStackSource,
|
|
||||||
stackReplace,
|
|
||||||
massGrams,
|
massGrams,
|
||||||
pointerRadiusScale: 1,
|
pointerRadiusScale: 1,
|
||||||
lastPointerCanvasX: pointerCanvasX,
|
lastPointerCanvasX: pointerCanvasX,
|
||||||
@@ -595,7 +549,6 @@ const useDocumentDrag = (options: UseDocumentDragOptions) => {
|
|||||||
ensureDocumentSize,
|
ensureDocumentSize,
|
||||||
layoutRef,
|
layoutRef,
|
||||||
resolveBaseMetrics,
|
resolveBaseMetrics,
|
||||||
selectedDocumentIds,
|
|
||||||
setDraggingId,
|
setDraggingId,
|
||||||
debugDrag,
|
debugDrag,
|
||||||
itemRefs,
|
itemRefs,
|
||||||
@@ -690,12 +643,7 @@ const useDocumentDrag = (options: UseDocumentDragOptions) => {
|
|||||||
state.moved = true;
|
state.moved = true;
|
||||||
if (
|
if (
|
||||||
!state.stackSelectionApplied
|
!state.stackSelectionApplied
|
||||||
&& Array.isArray(state.stackDocIds)
|
|
||||||
&& state.stackDocIds.length > 0
|
|
||||||
) {
|
) {
|
||||||
safeInvoke(onDocumentStackSelect, state.stackDocIds as Identifier[], event, {
|
|
||||||
replace: state.stackReplace,
|
|
||||||
});
|
|
||||||
state.stackSelectionApplied = true;
|
state.stackSelectionApplied = true;
|
||||||
}
|
}
|
||||||
if (!state.groupElevated) {
|
if (!state.groupElevated) {
|
||||||
@@ -973,7 +921,6 @@ const useDocumentDrag = (options: UseDocumentDragOptions) => {
|
|||||||
layoutRef,
|
layoutRef,
|
||||||
itemRefs,
|
itemRefs,
|
||||||
debugDrag,
|
debugDrag,
|
||||||
onDocumentStackSelect,
|
|
||||||
setDragTransform,
|
setDragTransform,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||||
|
import type { Dispatch, SetStateAction } from 'react';
|
||||||
import { createDocumentEntryKey, createFolderEntryKey, isFolderEntry } from '../../app/entryKey';
|
import { createDocumentEntryKey, createFolderEntryKey, isFolderEntry } from '../../app/entryKey';
|
||||||
import type { DocumentId, FolderId } from '../../types/identifiers';
|
import type { DocumentId, FolderId } from '../../types/identifiers';
|
||||||
|
|
||||||
@@ -22,8 +23,8 @@ interface UseDocumentsSelectionOptions {
|
|||||||
showingSearchResults?: boolean;
|
showingSearchResults?: boolean;
|
||||||
currentSubfolders?: FolderEntry[];
|
currentSubfolders?: FolderEntry[];
|
||||||
visibleDocuments?: DocumentEntry[];
|
visibleDocuments?: DocumentEntry[];
|
||||||
configureSelectionEnvironment: (config: { visibleRowKeySet: Set<string>; navigableRowKeys: string[] }) => void;
|
configureSelectionEnvironment: (config: { visibleEntryKeySet: Set<string>; navigableEntryKeys: string[] }) => void;
|
||||||
visibleRowKeySet: Set<string>;
|
visibleEntryKeySet: Set<string>;
|
||||||
selectedEntries: string[];
|
selectedEntries: string[];
|
||||||
selectionAnchorRef: { current: string | null };
|
selectionAnchorRef: { current: string | null };
|
||||||
promoteSelectionOrderRaw: (id: DocumentId) => void;
|
promoteSelectionOrderRaw: (id: DocumentId) => void;
|
||||||
@@ -31,8 +32,8 @@ interface UseDocumentsSelectionOptions {
|
|||||||
setActivePreviewId: (id: DocumentId | null) => void;
|
setActivePreviewId: (id: DocumentId | null) => void;
|
||||||
clearSelection: () => void;
|
clearSelection: () => void;
|
||||||
focusedDocumentId: DocumentId | null;
|
focusedDocumentId: DocumentId | null;
|
||||||
setFocusedRowKey: (value: string | null | ((current: string | null) => string | null)) => void;
|
setFocusedEntryKey: Dispatch<SetStateAction<string | null>>;
|
||||||
focusedRowKey: string | null;
|
focusedEntryKey: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useDocumentsSelection = ({
|
const useDocumentsSelection = ({
|
||||||
@@ -40,7 +41,7 @@ const useDocumentsSelection = ({
|
|||||||
currentSubfolders = [],
|
currentSubfolders = [],
|
||||||
visibleDocuments = [],
|
visibleDocuments = [],
|
||||||
configureSelectionEnvironment,
|
configureSelectionEnvironment,
|
||||||
visibleRowKeySet,
|
visibleEntryKeySet,
|
||||||
selectedEntries,
|
selectedEntries,
|
||||||
selectionAnchorRef,
|
selectionAnchorRef,
|
||||||
promoteSelectionOrderRaw,
|
promoteSelectionOrderRaw,
|
||||||
@@ -48,8 +49,8 @@ const useDocumentsSelection = ({
|
|||||||
setActivePreviewId,
|
setActivePreviewId,
|
||||||
clearSelection,
|
clearSelection,
|
||||||
focusedDocumentId,
|
focusedDocumentId,
|
||||||
setFocusedRowKey,
|
setFocusedEntryKey,
|
||||||
focusedRowKey,
|
focusedEntryKey,
|
||||||
}: UseDocumentsSelectionOptions) => {
|
}: UseDocumentsSelectionOptions) => {
|
||||||
const navigableRows = useMemo<NavigableRow[]>(() => {
|
const navigableRows = useMemo<NavigableRow[]>(() => {
|
||||||
const entries: NavigableRow[] = [];
|
const entries: NavigableRow[] = [];
|
||||||
@@ -77,10 +78,10 @@ const useDocumentsSelection = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
configureSelectionEnvironment({
|
configureSelectionEnvironment({
|
||||||
visibleRowKeySet,
|
visibleEntryKeySet,
|
||||||
navigableRowKeys,
|
navigableEntryKeys: navigableRowKeys,
|
||||||
});
|
});
|
||||||
}, [configureSelectionEnvironment, visibleRowKeySet, navigableRowKeys]);
|
}, [configureSelectionEnvironment, visibleEntryKeySet, navigableRowKeys]);
|
||||||
|
|
||||||
const promoteSelectionOrder = useCallback(
|
const promoteSelectionOrder = useCallback(
|
||||||
(docId: DocumentId | null) => {
|
(docId: DocumentId | null) => {
|
||||||
@@ -108,40 +109,40 @@ const useDocumentsSelection = ({
|
|||||||
}
|
}
|
||||||
prevFocusedDocIdRef.current = focusedDocumentId;
|
prevFocusedDocIdRef.current = focusedDocumentId;
|
||||||
if (focusedDocumentId) {
|
if (focusedDocumentId) {
|
||||||
setFocusedRowKey(createDocumentEntryKey(focusedDocumentId));
|
setFocusedEntryKey(createDocumentEntryKey(focusedDocumentId));
|
||||||
} else {
|
} else {
|
||||||
setFocusedRowKey((current) => (current && isFolderEntry(current) ? current : null));
|
setFocusedEntryKey((current) => (current && isFolderEntry(current) ? current : null));
|
||||||
}
|
}
|
||||||
}, [focusedDocumentId, setFocusedRowKey]);
|
}, [focusedDocumentId, setFocusedEntryKey]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!navigableRowKeys.length) {
|
if (!navigableRowKeys.length) {
|
||||||
if (focusedRowKey) {
|
if (focusedEntryKey) {
|
||||||
setFocusedRowKey(null);
|
setFocusedEntryKey(null);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (focusedRowKey && navigableRowKeys.includes(focusedRowKey)) {
|
if (focusedEntryKey && navigableRowKeys.includes(focusedEntryKey)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const docKey = focusedDocumentId ? createDocumentEntryKey(focusedDocumentId) : null;
|
const docKey = focusedDocumentId ? createDocumentEntryKey(focusedDocumentId) : null;
|
||||||
if (docKey && navigableRowKeys.includes(docKey)) {
|
if (docKey && navigableRowKeys.includes(docKey)) {
|
||||||
setFocusedRowKey(docKey);
|
setFocusedEntryKey(docKey);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedKey = selectedEntries.find((key) => navigableRowKeys.includes(key));
|
const selectedKey = selectedEntries.find((key) => navigableRowKeys.includes(key));
|
||||||
if (selectedKey) {
|
if (selectedKey) {
|
||||||
setFocusedRowKey(selectedKey);
|
setFocusedEntryKey(selectedKey);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (focusedRowKey) {
|
if (focusedEntryKey) {
|
||||||
setFocusedRowKey(null);
|
setFocusedEntryKey(null);
|
||||||
}
|
}
|
||||||
}, [focusedRowKey, focusedDocumentId, navigableRowKeys, selectedEntries, setFocusedRowKey]);
|
}, [focusedEntryKey, focusedDocumentId, navigableRowKeys, selectedEntries, setFocusedEntryKey]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
navigableRows,
|
navigableRows,
|
||||||
|
|||||||
@@ -142,8 +142,8 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
|||||||
}): ReactNode => {
|
}): ReactNode => {
|
||||||
const {
|
const {
|
||||||
selectedEntries,
|
selectedEntries,
|
||||||
focusedRowKey,
|
focusedEntryKey,
|
||||||
setFocusedRowKey,
|
setFocusedEntryKey,
|
||||||
handleEntrySelection,
|
handleEntrySelection,
|
||||||
clearSelection,
|
clearSelection,
|
||||||
selectionAnchorRef,
|
selectionAnchorRef,
|
||||||
@@ -177,28 +177,28 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rowKeys = docIds
|
const entryKeys = docIds
|
||||||
.map((id) => createDocumentEntryKey(id as Identifier))
|
.map((id) => createDocumentEntryKey(id as Identifier))
|
||||||
.filter((value): value is string => typeof value === 'string');
|
.filter((value): value is string => typeof value === 'string');
|
||||||
|
|
||||||
if (!rowKeys.length) {
|
if (!entryKeys.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextKeys = [...selectedEntries];
|
const nextKeys = [...selectedEntries];
|
||||||
rowKeys.forEach((key) => {
|
entryKeys.forEach((key) => {
|
||||||
if (!nextKeys.includes(key)) {
|
if (!nextKeys.includes(key)) {
|
||||||
nextKeys.push(key);
|
nextKeys.push(key);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const anchor = (rowKeys[0]
|
const anchor = (entryKeys[0]
|
||||||
|| selectionAnchorRef.current
|
|| selectionAnchorRef.current
|
||||||
|| nextKeys[nextKeys.length - 1]) as string | null;
|
|| nextKeys[nextKeys.length - 1]) as string | null;
|
||||||
|
|
||||||
applySelection(nextKeys, {
|
applySelection(nextKeys, {
|
||||||
anchor,
|
anchor,
|
||||||
interactedKeys: rowKeys,
|
interactedKeys: entryKeys,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[applySelection, selectedEntries, selectionAnchorRef],
|
[applySelection, selectedEntries, selectionAnchorRef],
|
||||||
@@ -482,24 +482,24 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
|||||||
() => entries.map((entry) => ({ key: entry.key, type: entry.type, id: entry.id })),
|
() => entries.map((entry) => ({ key: entry.key, type: entry.type, id: entry.id })),
|
||||||
[entries],
|
[entries],
|
||||||
);
|
);
|
||||||
const navigableRowKeys = useMemo(() => navigableRows.map((row) => row.key), [navigableRows]);
|
const navigableEntryKeys = useMemo(() => navigableRows.map((row) => row.key), [navigableRows]);
|
||||||
|
|
||||||
const getEntryByKey = useCallback(
|
const getEntryByKey = useCallback(
|
||||||
(rowKey) => entries.find((entry) => entry.key === rowKey) || null,
|
(entryKey) => entries.find((entry) => entry.key === entryKey) || null,
|
||||||
[entries],
|
[entries],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handlePanelFocus = useCallback(() => {
|
const handlePanelFocus = useCallback(() => {
|
||||||
let resolvedKey = null;
|
let resolvedKey = null;
|
||||||
|
|
||||||
if (focusedRowKey && navigableRowKeys.includes(focusedRowKey)) {
|
if (focusedEntryKey && navigableEntryKeys.includes(focusedEntryKey)) {
|
||||||
resolvedKey = focusedRowKey;
|
resolvedKey = focusedEntryKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resolvedKey) {
|
if (!resolvedKey) {
|
||||||
for (let index = selectedEntries.length - 1; index >= 0; index -= 1) {
|
for (let index = selectedEntries.length - 1; index >= 0; index -= 1) {
|
||||||
const candidate = selectedEntries[index];
|
const candidate = selectedEntries[index];
|
||||||
if (navigableRowKeys.includes(candidate)) {
|
if (navigableEntryKeys.includes(candidate)) {
|
||||||
resolvedKey = candidate;
|
resolvedKey = candidate;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -519,12 +519,12 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFocusedRowKey(resolvedKey);
|
setFocusedEntryKey(resolvedKey);
|
||||||
}, [
|
}, [
|
||||||
focusedRowKey,
|
focusedEntryKey,
|
||||||
navigableRowKeys,
|
navigableEntryKeys,
|
||||||
navigableRows,
|
navigableRows,
|
||||||
setFocusedRowKey,
|
setFocusedEntryKey,
|
||||||
selectedEntries,
|
selectedEntries,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -543,15 +543,15 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
let activeKey =
|
let activeKey =
|
||||||
focusedRowKey && navigableRowKeys.includes(focusedRowKey)
|
focusedEntryKey && navigableEntryKeys.includes(focusedEntryKey)
|
||||||
? focusedRowKey
|
? focusedEntryKey
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (!activeKey) {
|
if (!activeKey) {
|
||||||
if (selectedEntries.length) {
|
if (selectedEntries.length) {
|
||||||
for (let index = selectedEntries.length - 1; index >= 0; index -= 1) {
|
for (let index = selectedEntries.length - 1; index >= 0; index -= 1) {
|
||||||
const candidate = selectedEntries[index];
|
const candidate = selectedEntries[index];
|
||||||
if (navigableRowKeys.includes(candidate)) {
|
if (navigableEntryKeys.includes(candidate)) {
|
||||||
activeKey = candidate;
|
activeKey = candidate;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -559,11 +559,11 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!activeKey) {
|
if (!activeKey) {
|
||||||
activeKey = key === 'ArrowUp' ? navigableRowKeys[navigableRowKeys.length - 1] : navigableRowKeys[0];
|
activeKey = key === 'ArrowUp' ? navigableEntryKeys[navigableEntryKeys.length - 1] : navigableEntryKeys[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentIndex = navigableRowKeys.indexOf(activeKey);
|
const currentIndex = navigableEntryKeys.indexOf(activeKey);
|
||||||
const activeRow = currentIndex === -1 ? null : navigableRows[currentIndex];
|
const activeRow = currentIndex === -1 ? null : navigableRows[currentIndex];
|
||||||
|
|
||||||
if (key === 'Enter' || key === ' ' || key === 'Space' || key === 'Spacebar') {
|
if (key === 'Enter' || key === ' ' || key === 'Space' || key === 'Spacebar') {
|
||||||
@@ -601,22 +601,22 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFocusedRowKey(targetRow.key);
|
setFocusedEntryKey(targetRow.key);
|
||||||
handleEntrySelection(targetRow.key, {
|
handleEntrySelection(targetRow.key, {
|
||||||
shiftKey,
|
shiftKey,
|
||||||
preventDefault: () => { },
|
preventDefault: () => { },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
focusedRowKey,
|
focusedEntryKey,
|
||||||
getEntryByKey,
|
getEntryByKey,
|
||||||
navigableRowKeys,
|
navigableEntryKeys,
|
||||||
navigableRows,
|
navigableRows,
|
||||||
onFolderSelect,
|
onFolderSelect,
|
||||||
selectedEntries,
|
selectedEntries,
|
||||||
handleDocumentPreviewZoom,
|
handleDocumentPreviewZoom,
|
||||||
handleEntrySelection,
|
handleEntrySelection,
|
||||||
setFocusedRowKey,
|
setFocusedEntryKey,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -648,14 +648,14 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
|||||||
|
|
||||||
const isTagDragEvent = useCallback((event) => isTagTransferEvent(event), []);
|
const isTagDragEvent = useCallback((event) => isTagTransferEvent(event), []);
|
||||||
const ensureFocusedRowVisible = useCallback(() => {
|
const ensureFocusedRowVisible = useCallback(() => {
|
||||||
if (!focusedRowKey) return;
|
if (!focusedEntryKey) return;
|
||||||
const container = scrollRef.current;
|
const container = scrollRef.current;
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
let selector = null;
|
let selector = null;
|
||||||
if (focusedRowKey.startsWith('document:')) {
|
if (focusedEntryKey.startsWith('document:')) {
|
||||||
selector = `#document-row-${focusedRowKey.slice('document:'.length)}`;
|
selector = `#document-row-${focusedEntryKey.slice('document:'.length)}`;
|
||||||
} else if (focusedRowKey.startsWith('folder:')) {
|
} else if (focusedEntryKey.startsWith('folder:')) {
|
||||||
selector = `#folder-row-${focusedRowKey.slice('folder:'.length)}`;
|
selector = `#folder-row-${focusedEntryKey.slice('folder:'.length)}`;
|
||||||
}
|
}
|
||||||
if (!selector) {
|
if (!selector) {
|
||||||
return;
|
return;
|
||||||
@@ -681,22 +681,22 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
|||||||
const nextScrollTop = rowBottom - container.clientHeight;
|
const nextScrollTop = rowBottom - container.clientHeight;
|
||||||
container.scrollTop = Math.max(nextScrollTop, 0);
|
container.scrollTop = Math.max(nextScrollTop, 0);
|
||||||
}
|
}
|
||||||
}, [focusedRowKey]);
|
}, [focusedEntryKey]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ensureFocusedRowVisible();
|
ensureFocusedRowVisible();
|
||||||
}, [ensureFocusedRowVisible]);
|
}, [ensureFocusedRowVisible]);
|
||||||
|
|
||||||
const activeDescendantId = useMemo(() => {
|
const activeDescendantId = useMemo(() => {
|
||||||
if (!focusedRowKey) return undefined;
|
if (!focusedEntryKey) return undefined;
|
||||||
if (focusedRowKey.startsWith('document:')) {
|
if (focusedEntryKey.startsWith('document:')) {
|
||||||
return `document-row-${focusedRowKey.slice('document:'.length)}`;
|
return `document-row-${focusedEntryKey.slice('document:'.length)}`;
|
||||||
}
|
}
|
||||||
if (focusedRowKey.startsWith('folder:')) {
|
if (focusedEntryKey.startsWith('folder:')) {
|
||||||
return `folder-row-${focusedRowKey.slice('folder:'.length)}`;
|
return `folder-row-${focusedEntryKey.slice('folder:'.length)}`;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}, [focusedRowKey]);
|
}, [focusedEntryKey]);
|
||||||
|
|
||||||
const handleDocumentTagDragOver = useCallback(
|
const handleDocumentTagDragOver = useCallback(
|
||||||
(event) => {
|
(event) => {
|
||||||
@@ -756,10 +756,10 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
|||||||
&& scrollRef.current
|
&& scrollRef.current
|
||||||
) {
|
) {
|
||||||
scrollRef.current.focus({ preventScroll: true });
|
scrollRef.current.focus({ preventScroll: true });
|
||||||
setFocusedRowKey(`folder:${folder.id}`);
|
setFocusedEntryKey(`folder:${folder.id}`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onEntryPointer, setFocusedRowKey],
|
[onEntryPointer, setFocusedEntryKey],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDocumentDragStartLocal = useCallback(
|
const handleDocumentDragStartLocal = useCallback(
|
||||||
|
|||||||
@@ -123,8 +123,8 @@ interface UseDocumentMutationsArgs {
|
|||||||
selectionAnchorRef: MutableRefObject<string | null>;
|
selectionAnchorRef: MutableRefObject<string | null>;
|
||||||
setFocusedDocumentId: Dispatch<SetStateAction<DocumentId | null>>;
|
setFocusedDocumentId: Dispatch<SetStateAction<DocumentId | null>>;
|
||||||
focusedDocumentId: DocumentId | null;
|
focusedDocumentId: DocumentId | null;
|
||||||
setFocusedRowKey: Dispatch<SetStateAction<string | null>>;
|
setFocusedEntryKey: Dispatch<SetStateAction<string | null>>;
|
||||||
focusedRowKey: string | null;
|
focusedEntryKey: string | null;
|
||||||
notifyApiError: NotifyApiError;
|
notifyApiError: NotifyApiError;
|
||||||
setStatusMessage: SetStatusMessage;
|
setStatusMessage: SetStatusMessage;
|
||||||
mapDocumentCaches: MapDocumentCaches;
|
mapDocumentCaches: MapDocumentCaches;
|
||||||
@@ -197,8 +197,8 @@ const useDocumentMutations = ({
|
|||||||
selectionAnchorRef,
|
selectionAnchorRef,
|
||||||
setFocusedDocumentId,
|
setFocusedDocumentId,
|
||||||
focusedDocumentId,
|
focusedDocumentId,
|
||||||
setFocusedRowKey,
|
setFocusedEntryKey,
|
||||||
focusedRowKey,
|
focusedEntryKey,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
setStatusMessage,
|
||||||
mapDocumentCaches,
|
mapDocumentCaches,
|
||||||
@@ -357,11 +357,11 @@ const useDocumentMutations = ({
|
|||||||
setFocusedDocumentId(null);
|
setFocusedDocumentId(null);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
focusedRowKey &&
|
focusedEntryKey &&
|
||||||
isDocumentEntry(focusedRowKey) &&
|
isDocumentEntry(focusedEntryKey) &&
|
||||||
uniqueIdSet.has(getEntryId(focusedRowKey) as DocumentId)
|
uniqueIdSet.has(getEntryId(focusedEntryKey) as DocumentId)
|
||||||
) {
|
) {
|
||||||
setFocusedRowKey(null);
|
setFocusedEntryKey(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,8 +387,8 @@ const useDocumentMutations = ({
|
|||||||
selectionAnchorRef,
|
selectionAnchorRef,
|
||||||
setFocusedDocumentId,
|
setFocusedDocumentId,
|
||||||
focusedDocumentId,
|
focusedDocumentId,
|
||||||
setFocusedRowKey,
|
setFocusedEntryKey,
|
||||||
focusedRowKey,
|
focusedEntryKey,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
setStatusMessage,
|
||||||
mapDocumentCaches,
|
mapDocumentCaches,
|
||||||
|
|||||||
@@ -259,8 +259,8 @@ const useDocumentsWorkspace = ({
|
|||||||
selectionInitializedRef,
|
selectionInitializedRef,
|
||||||
focusedDocumentId,
|
focusedDocumentId,
|
||||||
setFocusedDocumentId,
|
setFocusedDocumentId,
|
||||||
focusedRowKey,
|
focusedEntryKey,
|
||||||
setFocusedRowKey,
|
setFocusedEntryKey,
|
||||||
applySelection,
|
applySelection,
|
||||||
handleEntrySelection,
|
handleEntrySelection,
|
||||||
clearSelection,
|
clearSelection,
|
||||||
@@ -416,14 +416,14 @@ const useDocumentsWorkspace = ({
|
|||||||
[showingSearchResults, currentSubfolders],
|
[showingSearchResults, currentSubfolders],
|
||||||
);
|
);
|
||||||
|
|
||||||
const visibleRowKeys = useMemo(
|
const visibleEntryKeys = useMemo(
|
||||||
() => [...visibleFolderKeys, ...visibleDocumentKeys],
|
() => [...visibleFolderKeys, ...visibleDocumentKeys],
|
||||||
[visibleFolderKeys, visibleDocumentKeys],
|
[visibleFolderKeys, visibleDocumentKeys],
|
||||||
);
|
);
|
||||||
|
|
||||||
const visibleRowKeySet = useMemo(
|
const visibleEntryKeySet = useMemo(
|
||||||
() => new Set(visibleRowKeys),
|
() => new Set(visibleEntryKeys),
|
||||||
[visibleRowKeys],
|
[visibleEntryKeys],
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -750,8 +750,8 @@ const useDocumentsWorkspace = ({
|
|||||||
selectionAnchorRef,
|
selectionAnchorRef,
|
||||||
setFocusedDocumentId,
|
setFocusedDocumentId,
|
||||||
focusedDocumentId,
|
focusedDocumentId,
|
||||||
setFocusedRowKey,
|
setFocusedEntryKey,
|
||||||
focusedRowKey,
|
focusedEntryKey,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
setStatusMessage,
|
||||||
mapDocumentCaches,
|
mapDocumentCaches,
|
||||||
@@ -812,7 +812,7 @@ const useDocumentsWorkspace = ({
|
|||||||
currentSubfolders,
|
currentSubfolders,
|
||||||
visibleDocuments: viewDocuments,
|
visibleDocuments: viewDocuments,
|
||||||
configureSelectionEnvironment,
|
configureSelectionEnvironment,
|
||||||
visibleRowKeySet,
|
visibleEntryKeySet,
|
||||||
selectedEntries,
|
selectedEntries,
|
||||||
selectionAnchorRef,
|
selectionAnchorRef,
|
||||||
promoteSelectionOrderRaw,
|
promoteSelectionOrderRaw,
|
||||||
@@ -820,8 +820,8 @@ const useDocumentsWorkspace = ({
|
|||||||
setActivePreviewId,
|
setActivePreviewId,
|
||||||
clearSelection,
|
clearSelection,
|
||||||
focusedDocumentId,
|
focusedDocumentId,
|
||||||
setFocusedRowKey,
|
setFocusedEntryKey,
|
||||||
focusedRowKey,
|
focusedEntryKey,
|
||||||
});
|
});
|
||||||
const initializeAfterLogin = useCallback(async () => {
|
const initializeAfterLogin = useCallback(async () => {
|
||||||
await Promise.all([refreshTags(), refreshCorrespondents()]);
|
await Promise.all([refreshTags(), refreshCorrespondents()]);
|
||||||
|
|||||||
Reference in New Issue
Block a user