refactor
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
import React from 'react';
|
||||
import { FolderIcon } from '../ui/icons';
|
||||
import DocumentThumbnailImage from './DocumentThumbnailImage';
|
||||
import CorrespondentLinks from './CorrespondentLinks';
|
||||
import { getTagColorStyle } from '../utils/colors';
|
||||
import { resolveCorrespondents } from './correspondents';
|
||||
|
||||
const DocumentsGrid = ({
|
||||
entries,
|
||||
selectedDocumentIdsSet,
|
||||
selectedFolderIdsSet,
|
||||
draggingDocumentIdsSet,
|
||||
draggedFolderId,
|
||||
onFolderClick,
|
||||
onFolderSelect,
|
||||
onFolderDragOver,
|
||||
onFolderDragLeave,
|
||||
onFolderDrop,
|
||||
onFolderDragStart,
|
||||
onFolderDragEnd,
|
||||
onDocumentClick,
|
||||
onDocumentOpen,
|
||||
onDocumentDragStart,
|
||||
onDocumentDragEnd,
|
||||
onDocumentTagDragOver,
|
||||
onDocumentTagDragLeave,
|
||||
onDocumentTagDrop,
|
||||
ensureAssetUrl,
|
||||
getDocumentAsset,
|
||||
gridIconSize,
|
||||
tagLookupById,
|
||||
onTagClick,
|
||||
scrollRef,
|
||||
onCorrespondentClick,
|
||||
activeCorrespondentIdSet,
|
||||
onClearSelection,
|
||||
}) => (
|
||||
<div
|
||||
className="documents-grid"
|
||||
role="list"
|
||||
style={{ '--documents-grid-icon-size': `${gridIconSize}px` }}
|
||||
onClick={(event) => {
|
||||
if (event.target === event.currentTarget) {
|
||||
onClearSelection?.();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{entries.map((entry) => {
|
||||
if (entry.type === 'folder') {
|
||||
const folder = entry.folder;
|
||||
if (!folder) {
|
||||
return null;
|
||||
}
|
||||
const canDragFolder = folder.id !== 'root';
|
||||
const isDraggingFolder = draggedFolderId === folder.id;
|
||||
const isSelectedFolder = selectedFolderIdsSet?.has(folder.id);
|
||||
const classes = ['document-card', 'folder-card'];
|
||||
if (isDraggingFolder) classes.push('is-dragging');
|
||||
if (isSelectedFolder) classes.push('selected');
|
||||
|
||||
return (
|
||||
<div
|
||||
key={entry.key}
|
||||
className={classes.join(' ')}
|
||||
role="listitem"
|
||||
id={`folder-card-${folder.id}`}
|
||||
draggable={canDragFolder}
|
||||
onClick={(event) => onFolderClick?.(folder, event)}
|
||||
onDoubleClick={(event) => {
|
||||
event.preventDefault();
|
||||
onFolderSelect?.(folder.id);
|
||||
}}
|
||||
onDragOver={(event) => onFolderDragOver?.(event, folder.id)}
|
||||
onDragLeave={onFolderDragLeave}
|
||||
onDrop={(event) => onFolderDrop?.(event, folder.id)}
|
||||
onDragStart={(event) => {
|
||||
if (canDragFolder) {
|
||||
onFolderDragStart?.(event, folder.id);
|
||||
}
|
||||
}}
|
||||
onDragEnd={(event) => {
|
||||
if (canDragFolder) {
|
||||
onFolderDragEnd?.(event);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="folder-card__icon">
|
||||
<FolderIcon className="folder-card__icon-svg" size={gridIconSize} />
|
||||
</div>
|
||||
<div className="folder-card__meta">
|
||||
<div className="folder-card__name" title={folder.name}>
|
||||
{folder.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const doc = entry.document;
|
||||
if (!doc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isSelected = selectedDocumentIdsSet?.has(doc.id);
|
||||
const isDraggingDoc = draggingDocumentIdsSet?.has(doc.id);
|
||||
const tagList = Array.isArray(doc.tags) ? doc.tags : [];
|
||||
const visibleTags = tagList.slice(0, 3);
|
||||
const remainingTagCount = tagList.length > 3 ? tagList.length - 3 : 0;
|
||||
const correspondents = resolveCorrespondents(doc);
|
||||
const cardClasses = ['document-card', 'document'];
|
||||
if (isSelected) cardClasses.push('selected');
|
||||
if (isDraggingDoc) cardClasses.push('is-dragging');
|
||||
const titleText = doc.title || doc.original_name;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={entry.key}
|
||||
className={cardClasses.join(' ')}
|
||||
role="listitem"
|
||||
id={`document-card-${doc.id}`}
|
||||
data-doc-id={doc.id}
|
||||
onClick={(event) => onDocumentClick?.(doc, event)}
|
||||
onDoubleClick={() => onDocumentOpen?.(doc.id)}
|
||||
draggable
|
||||
onDragStart={(event) => onDocumentDragStart?.(event, doc)}
|
||||
onDragEnd={(event) => onDocumentDragEnd?.(event)}
|
||||
onDragOver={(event) => onDocumentTagDragOver?.(event)}
|
||||
onDragOverCapture={(event) => onDocumentTagDragOver?.(event)}
|
||||
onDragLeave={onDocumentTagDragLeave}
|
||||
onDragLeaveCapture={onDocumentTagDragLeave}
|
||||
onDrop={(event) => onDocumentTagDrop?.(event, doc.id)}
|
||||
onDropCapture={(event) => onDocumentTagDrop?.(event, doc.id)}
|
||||
>
|
||||
<DocumentThumbnailImage
|
||||
document={doc}
|
||||
ensureAssetUrl={ensureAssetUrl}
|
||||
getDocumentAsset={getDocumentAsset}
|
||||
alt={`Thumbnail for ${titleText}`}
|
||||
maxSize={gridIconSize}
|
||||
scrollRootRef={scrollRef}
|
||||
/>
|
||||
<div className="document-card__meta">
|
||||
<div className="document-card__title" title={titleText}>
|
||||
{correspondents.length > 0 ? (
|
||||
<span className="doc-correspondents">
|
||||
<CorrespondentLinks
|
||||
correspondents={correspondents}
|
||||
activeCorrespondentIdSet={activeCorrespondentIdSet}
|
||||
onCorrespondentClick={onCorrespondentClick}
|
||||
/>
|
||||
</span>
|
||||
) : null}
|
||||
<span className="doc-name__primary">{titleText}</span>
|
||||
</div>
|
||||
{visibleTags.length > 0 && (
|
||||
<div className="document-card__tags">
|
||||
{visibleTags.map((tag) => {
|
||||
const colorSource = tag?.color || tagLookupById?.get(tag.id)?.color;
|
||||
const style = getTagColorStyle(colorSource);
|
||||
return (
|
||||
<span
|
||||
key={tag.id}
|
||||
className="badge tag-chip"
|
||||
style={style || undefined}
|
||||
title={tag.label}
|
||||
role="button"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onTagClick?.(tag.id);
|
||||
}}
|
||||
draggable
|
||||
onDragStart={(event) => {
|
||||
event.stopPropagation();
|
||||
try {
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.effectAllowed = 'copyMove';
|
||||
}
|
||||
const payload = JSON.stringify({
|
||||
id: tag.id,
|
||||
label: tag.label,
|
||||
sourceDocId: doc.id,
|
||||
});
|
||||
event.dataTransfer?.setData('application/x-papercrate-tag', payload);
|
||||
event.dataTransfer?.setData('text/papercrate-tag', payload);
|
||||
event.dataTransfer?.setData('text/plain', tag.label || 'Tag');
|
||||
} catch (error) {
|
||||
console.warn('[documents] Failed to configure tag drag payload', error);
|
||||
}
|
||||
}}
|
||||
onDragEnd={(event) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onTagClick?.(tag.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{tag.label}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
{remainingTagCount > 0 && (
|
||||
<span className="badge tag-chip tag-chip--more">+{remainingTagCount}</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default DocumentsGrid;
|
||||
Reference in New Issue
Block a user