feat: Enable moving and dragging selected folders, including mixed document and folder selections, with updated drag preview visuals and minor UI styling.
This commit is contained in:
@@ -360,12 +360,13 @@ const SelectionFloatingActions: React.FC<SelectionFloatingActionsProps> = ({
|
||||
|
||||
const handleMoveSelectionToFolder = useCallback(
|
||||
async (folderId: DocumentId | null) => {
|
||||
if (!documentIdList.length || !onMoveDocumentsToFolder) {
|
||||
const itemsToMove = [...documentIdList, ...folderIdList];
|
||||
if (!itemsToMove.length || !onMoveDocumentsToFolder) {
|
||||
return;
|
||||
}
|
||||
await onMoveDocumentsToFolder(documentIdList, folderId);
|
||||
await onMoveDocumentsToFolder(itemsToMove, folderId);
|
||||
},
|
||||
[documentIdList, onMoveDocumentsToFolder],
|
||||
[documentIdList, folderIdList, onMoveDocumentsToFolder],
|
||||
);
|
||||
|
||||
const summaryNode = totalCount > 0 ? (
|
||||
@@ -395,7 +396,7 @@ const SelectionFloatingActions: React.FC<SelectionFloatingActionsProps> = ({
|
||||
placeholder="Search folders…"
|
||||
emptyMessage="No folders"
|
||||
onSelectFolder={handleMoveSelectionToFolder}
|
||||
disabled={!documentCount}
|
||||
disabled={!documentCount && !folderCount}
|
||||
onOpenMenu={handleMoveMenuOpen}
|
||||
rootTitle={rootTitle}
|
||||
/>
|
||||
|
||||
@@ -58,7 +58,7 @@ const useDocumentDragHandlers = ({
|
||||
useEffect(() => destroyDragPreview, [destroyDragPreview]);
|
||||
|
||||
const createDragPreview = useCallback(
|
||||
({ documents = [], folders = [] }: { documents?: Document[]; folders?: FolderIdentifier[] } = {}) => {
|
||||
({ documents = [], folders = [], prioritizeFolders = false }: { documents?: Document[]; folders?: FolderIdentifier[]; prioritizeFolders?: boolean } = {}) => {
|
||||
destroyDragPreview();
|
||||
|
||||
const docEntries = (documents || []).filter(Boolean);
|
||||
@@ -72,15 +72,44 @@ const useDocumentDragHandlers = ({
|
||||
const size = 64;
|
||||
const canvasSize = Math.round(size * 1.6);
|
||||
|
||||
const visibleItems = [];
|
||||
docEntries.slice(0, maxVisible).forEach((doc) => {
|
||||
visibleItems.push({ type: 'document', payload: doc });
|
||||
});
|
||||
const visibleItems: Array<{ type: 'document' | 'folder'; payload: any }> = [];
|
||||
|
||||
if (visibleItems.length < maxVisible) {
|
||||
folderEntries
|
||||
.slice(0, maxVisible - visibleItems.length)
|
||||
.forEach((folderId) => visibleItems.push({ type: 'folder', payload: folderId }));
|
||||
let takeDocs = 0;
|
||||
let takeFolders = 0;
|
||||
|
||||
if (docEntries.length > 0 && folderEntries.length > 0) {
|
||||
if (prioritizeFolders) {
|
||||
// Folders on top (added last)
|
||||
takeDocs = Math.min(docEntries.length, maxVisible - 1);
|
||||
takeFolders = Math.min(folderEntries.length, maxVisible - takeDocs);
|
||||
} else {
|
||||
// Docs on top (added last)
|
||||
takeFolders = Math.min(folderEntries.length, maxVisible - 1);
|
||||
takeDocs = Math.min(docEntries.length, maxVisible - takeFolders);
|
||||
}
|
||||
} else {
|
||||
takeDocs = Math.min(docEntries.length, maxVisible);
|
||||
takeFolders = Math.min(folderEntries.length, maxVisible - takeDocs);
|
||||
}
|
||||
|
||||
if (prioritizeFolders) {
|
||||
// Docs at bottom
|
||||
docEntries.slice(0, takeDocs).forEach((doc) => {
|
||||
visibleItems.push({ type: 'document', payload: doc });
|
||||
});
|
||||
// Folders at top
|
||||
folderEntries.slice(0, takeFolders).forEach((folderId) => {
|
||||
visibleItems.push({ type: 'folder', payload: folderId });
|
||||
});
|
||||
} else {
|
||||
// Folders at bottom
|
||||
folderEntries.slice(0, takeFolders).forEach((folderId) => {
|
||||
visibleItems.push({ type: 'folder', payload: folderId });
|
||||
});
|
||||
// Docs at top
|
||||
docEntries.slice(0, takeDocs).forEach((doc) => {
|
||||
visibleItems.push({ type: 'document', payload: doc });
|
||||
});
|
||||
}
|
||||
|
||||
const wrapper = document.createElement('div');
|
||||
@@ -152,16 +181,18 @@ const useDocumentDragHandlers = ({
|
||||
layer.style.height = `${size}px`;
|
||||
layer.classList.add('document-drag-preview__item--folder');
|
||||
|
||||
let content: HTMLElement | null = null;
|
||||
let content: HTMLElement | SVGElement | null = null;
|
||||
if (iconEl instanceof HTMLElement) {
|
||||
const cloneSource = iconEl.classList.contains('folder-card__icon')
|
||||
? iconEl.querySelector('svg') || iconEl
|
||||
: iconEl;
|
||||
const clone = cloneSource.cloneNode(true);
|
||||
if (clone instanceof HTMLElement) {
|
||||
content = clone;
|
||||
if (clone instanceof HTMLElement || clone instanceof SVGElement) {
|
||||
content = clone as HTMLElement | SVGElement;
|
||||
content.classList.add('document-drag-preview__folder-thumb');
|
||||
const svg = content.querySelector('svg');
|
||||
const svg = content.nodeName.toLowerCase() === 'svg'
|
||||
? content
|
||||
: content.querySelector('svg');
|
||||
if (svg) {
|
||||
svg.setAttribute('width', '48');
|
||||
svg.setAttribute('height', '48');
|
||||
@@ -213,7 +244,11 @@ const useDocumentDragHandlers = ({
|
||||
const selection: Identifier[] = isAlreadySelected
|
||||
? [...selectedDocumentIds]
|
||||
: [documentId];
|
||||
const folderSelection: FolderIdentifier[] = [];
|
||||
|
||||
// If the document is part of the selection, we also want to include any selected folders
|
||||
const folderSelection: FolderIdentifier[] = isAlreadySelected
|
||||
? normalizedFolderIds
|
||||
: [];
|
||||
|
||||
if (!isAlreadySelected) {
|
||||
applySelection([documentKey], {
|
||||
@@ -228,6 +263,7 @@ const useDocumentDragHandlers = ({
|
||||
const previewNode = createDragPreview({
|
||||
documents: previewDocs,
|
||||
folders: folderSelection,
|
||||
prioritizeFolders: false,
|
||||
});
|
||||
|
||||
setDraggedDocumentIds(selection);
|
||||
@@ -266,6 +302,7 @@ const useDocumentDragHandlers = ({
|
||||
createDragPreview,
|
||||
setDraggedFolderId,
|
||||
setDraggedDocumentIds,
|
||||
normalizedFolderIds,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -331,6 +368,7 @@ const useDocumentDragHandlers = ({
|
||||
.map((id) => documentLookup.get(id) || documentLookup.get(String(id)) || null)
|
||||
.filter(Boolean),
|
||||
folders: uniqueFolders,
|
||||
prioritizeFolders: true,
|
||||
});
|
||||
event.currentTarget.classList.add('dragging');
|
||||
|
||||
|
||||
@@ -852,4 +852,9 @@
|
||||
.menu .selection-assignment__item--empty:focus-visible {
|
||||
background: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.selection-assignment__item--empty .selection-assignment__item-content {
|
||||
opacity: 0.5;
|
||||
cursor: default;
|
||||
}
|
||||
@@ -254,8 +254,8 @@
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.documents-panel--view-grid {
|
||||
padding: 0.35rem 0.5rem 1rem;
|
||||
.documents-panel .documents-grid {
|
||||
padding: 1rem 0.5rem;
|
||||
}
|
||||
|
||||
.documents-panel th.thumb-column,
|
||||
|
||||
Reference in New Issue
Block a user