This commit is contained in:
2025-10-24 15:13:37 +02:00
parent e33ab71fac
commit dc633783e0
10 changed files with 283 additions and 237 deletions
+66 -60
View File
@@ -1,7 +1,7 @@
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import { getAssetFromVersion, resolveDocumentAssetUrl, createAssetView } from '../asset_manager';
import { getTagColorStyle } from '../utils/colors';
import { DownloadIcon, EditIcon, ViewListIcon, ViewGridIcon, FolderIcon } from '../ui/icons';
import { DownloadIcon, EditIcon, ViewListIcon, ViewGridIcon, FolderIcon, TrashIcon } from '../ui/icons';
const TAG_MIME_TYPES = ['application/x-papercrate-tag', 'text/papercrate-tag'];
const DEFAULT_GRID_ICON_SIZE = 96;
@@ -263,16 +263,6 @@ const DocumentsTable = ({
[isTagDragEvent, onDocumentTagDrop],
);
const handleGridBackgroundClick = useCallback(
(event) => {
if (event.target !== event.currentTarget) {
return;
}
onClearSelection?.();
},
[onClearSelection],
);
const showDefaultEmptyState = !showingSearchResults && !subfolders.length && rows.length === 0;
const showListSearchEmptyState =
showingSearchResults && rows.length === 0 && !isGridView && !isSearchLoading;
@@ -376,13 +366,22 @@ const DocumentsTable = ({
onDocumentListKeyDown(event);
}
}}
onClick={(event) => {
if (event.target === event.currentTarget) {
onClearSelection?.();
}
}}
aria-activedescendant={isGridView ? undefined : activeDescendantId}
>
{showDefaultEmptyState ? null : isGridView ? (
<div
className="documents-grid"
role="list"
onClick={handleGridBackgroundClick}
onClick={(event) => {
if (event.target === event.currentTarget) {
onClearSelection?.();
}
}}
style={{ '--documents-grid-icon-size': `${gridIconSize}px` }}
>
{!showingSearchResults &&
@@ -619,13 +618,20 @@ const DocumentsTable = ({
<td className="doc-list__name">
<div className="doc-list__name-content">
<span>{folder.name}</span>
{folder.id !== 'root' && (
</div>
</td>
<td>Folder</td>
<td></td>
<td className="actions">
<div className="action-buttons">
{folder.id !== 'root' && onFolderRename && (
<button
type="button"
className="icon-button ghost doc-list__icon-button"
className="icon-button ghost"
title="Rename"
aria-label={`Rename folder ${folder.name}`}
onClick={(event) => {
event.stopPropagation();
if (!onFolderRename) return;
const nextName = window.prompt('Rename folder', folder.name || '');
if (!nextName) {
return;
@@ -636,28 +642,24 @@ const DocumentsTable = ({
}
onFolderRename(folder.id, trimmed);
}}
title="Rename folder"
aria-label={`Rename folder ${folder.name}`}
>
<EditIcon className="doc-list__icon" size={16} />
<EditIcon className="icon-inline" />
</button>
)}
<button
type="button"
className="icon-button danger"
title="Delete"
aria-label={`Delete folder ${folder.name}`}
onClick={(event) => {
event.stopPropagation();
onFolderDelete(folder.id);
}}
>
<TrashIcon className="icon-inline" />
</button>
</div>
</td>
<td>Folder</td>
<td></td>
<td className="actions">
<button
type="button"
className="icon-button danger"
onClick={(event) => {
event.stopPropagation();
onFolderDelete(folder.id);
}}
>
Delete
</button>
</td>
</tr>
);
})}
@@ -696,30 +698,6 @@ const DocumentsTable = ({
<div className="doc-name">
<div className="doc-list__name-content">
<span className="doc-name__title">{doc.title || doc.original_name}</span>
<button
type="button"
className="icon-button ghost doc-list__icon-button"
onClick={(event) => {
event.stopPropagation();
if (!onDocumentRename) return;
const nextName = window.prompt(
'Rename document',
doc.title || doc.original_name || '',
);
if (!nextName) {
return;
}
const trimmed = nextName.trim();
if (!trimmed || trimmed === (doc.title || doc.original_name)) {
return;
}
onDocumentRename(doc.id, trimmed);
}}
title="Rename document"
aria-label={`Rename document ${doc.title || doc.original_name}`}
>
<EditIcon className="doc-list__icon" size={16} />
</button>
</div>
{(doc.tags || []).length > 0 && (
<div className="doc-name__tags">
@@ -788,31 +766,59 @@ const DocumentsTable = ({
</td>
<td className="actions">
<div className="action-buttons">
{onDocumentRename && (
<button
type="button"
className="icon-button ghost"
title="Rename"
aria-label={`Rename document ${doc.title || doc.original_name}`}
onClick={(event) => {
event.stopPropagation();
const nextName = window.prompt(
'Rename document',
doc.title || doc.original_name || '',
);
if (!nextName) {
return;
}
const trimmed = nextName.trim();
if (!trimmed || trimmed === (doc.title || doc.original_name)) {
return;
}
onDocumentRename(doc.id, trimmed);
}}
>
<EditIcon className="icon-inline" />
</button>
)}
{downloadHref ? (
<a
className="button-link with-icon"
className="icon-button"
href={downloadHref}
target="_blank"
rel="noopener noreferrer"
title="Download"
aria-label="Download document"
onClick={(event) => event.stopPropagation()}
onAuxClick={(event) => event.stopPropagation()}
onContextMenu={(event) => event.stopPropagation()}
>
<DownloadIcon className="icon-inline" />
<span>Download</span>
</a>
) : (
<span className="meta">No download</span>
)}
<button
type="button"
className="danger"
className="icon-button danger"
title="Delete"
aria-label="Delete document"
onClick={(event) => {
event.stopPropagation();
onDocumentDelete?.(doc.id);
}}
>
Delete
<TrashIcon className="icon-inline" />
</button>
</div>
</td>