@vryzel/file-next-ui 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +134 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +134 -85
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -200,6 +200,15 @@ interface FileExplorerProps {
|
|
|
200
200
|
onSelect: (file: FileNode) => void;
|
|
201
201
|
};
|
|
202
202
|
readonly protectedIds?: ReadonlyArray<string>;
|
|
203
|
+
/**
|
|
204
|
+
* Per-item rename/delete locks, resolved by the consumer (permissions,
|
|
205
|
+
* business rules). Locked actions are disabled in the context menu, blocked
|
|
206
|
+
* on F2/Delete, and excluded from the selection toast.
|
|
207
|
+
*/
|
|
208
|
+
readonly itemLocks?: (file: FileNode) => {
|
|
209
|
+
rename?: boolean;
|
|
210
|
+
delete?: boolean;
|
|
211
|
+
};
|
|
203
212
|
readonly labels?: Partial<ExplorerLabels>;
|
|
204
213
|
}
|
|
205
214
|
declare function FileExplorer(props: FileExplorerProps): React.ReactElement;
|
|
@@ -280,6 +289,8 @@ interface ExplorerContextMenuProps {
|
|
|
280
289
|
readonly mode?: "browse" | "trash";
|
|
281
290
|
readonly actions: ExplorerContextMenuActions;
|
|
282
291
|
readonly protectedItem?: boolean;
|
|
292
|
+
readonly renameLocked?: boolean;
|
|
293
|
+
readonly deleteLocked?: boolean;
|
|
283
294
|
readonly onClose: () => void;
|
|
284
295
|
readonly onPreview?: (file: FileNode) => void;
|
|
285
296
|
readonly onDownload?: (file: FileNode) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -200,6 +200,15 @@ interface FileExplorerProps {
|
|
|
200
200
|
onSelect: (file: FileNode) => void;
|
|
201
201
|
};
|
|
202
202
|
readonly protectedIds?: ReadonlyArray<string>;
|
|
203
|
+
/**
|
|
204
|
+
* Per-item rename/delete locks, resolved by the consumer (permissions,
|
|
205
|
+
* business rules). Locked actions are disabled in the context menu, blocked
|
|
206
|
+
* on F2/Delete, and excluded from the selection toast.
|
|
207
|
+
*/
|
|
208
|
+
readonly itemLocks?: (file: FileNode) => {
|
|
209
|
+
rename?: boolean;
|
|
210
|
+
delete?: boolean;
|
|
211
|
+
};
|
|
203
212
|
readonly labels?: Partial<ExplorerLabels>;
|
|
204
213
|
}
|
|
205
214
|
declare function FileExplorer(props: FileExplorerProps): React.ReactElement;
|
|
@@ -280,6 +289,8 @@ interface ExplorerContextMenuProps {
|
|
|
280
289
|
readonly mode?: "browse" | "trash";
|
|
281
290
|
readonly actions: ExplorerContextMenuActions;
|
|
282
291
|
readonly protectedItem?: boolean;
|
|
292
|
+
readonly renameLocked?: boolean;
|
|
293
|
+
readonly deleteLocked?: boolean;
|
|
283
294
|
readonly onClose: () => void;
|
|
284
295
|
readonly onPreview?: (file: FileNode) => void;
|
|
285
296
|
readonly onDownload?: (file: FileNode) => void;
|
package/dist/index.js
CHANGED
|
@@ -1039,6 +1039,8 @@ function ExplorerContextMenu(props) {
|
|
|
1039
1039
|
mode = "browse",
|
|
1040
1040
|
actions,
|
|
1041
1041
|
protectedItem = false,
|
|
1042
|
+
renameLocked = false,
|
|
1043
|
+
deleteLocked = false,
|
|
1042
1044
|
onClose,
|
|
1043
1045
|
onPreview,
|
|
1044
1046
|
onDownload,
|
|
@@ -1087,7 +1089,7 @@ function ExplorerContextMenu(props) {
|
|
|
1087
1089
|
swallowClickThrough();
|
|
1088
1090
|
onClose();
|
|
1089
1091
|
};
|
|
1090
|
-
const itemClass = "flex w-full min-h-11 cursor-pointer items-center gap-2 rounded-[10px] px-3 py-2.5 text-left text-sm outline-none hover:bg-accent disabled:opacity-40 sm:min-h-0 sm:px-2.5 sm:py-1.5";
|
|
1092
|
+
const itemClass = "flex w-full min-h-11 cursor-pointer items-center gap-2 rounded-[10px] px-3 py-2.5 text-left text-sm outline-none hover:bg-accent disabled:cursor-not-allowed disabled:opacity-40 sm:min-h-0 sm:px-2.5 sm:py-1.5";
|
|
1091
1093
|
return /* @__PURE__ */ jsx(
|
|
1092
1094
|
"div",
|
|
1093
1095
|
{
|
|
@@ -1164,13 +1166,24 @@ function ExplorerContextMenu(props) {
|
|
|
1164
1166
|
close();
|
|
1165
1167
|
extraFileAction.onSelect(target.file);
|
|
1166
1168
|
}, children: extraFileAction.label }) : null,
|
|
1167
|
-
onRename ? /* @__PURE__ */ jsxs(
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1169
|
+
onRename ? /* @__PURE__ */ jsxs(
|
|
1170
|
+
"button",
|
|
1171
|
+
{
|
|
1172
|
+
type: "button",
|
|
1173
|
+
role: "menuitem",
|
|
1174
|
+
className: itemClass,
|
|
1175
|
+
disabled: renameLocked,
|
|
1176
|
+
onClick: () => {
|
|
1177
|
+
if (renameLocked) return;
|
|
1178
|
+
close();
|
|
1179
|
+
onRename(target.file);
|
|
1180
|
+
},
|
|
1181
|
+
children: [
|
|
1182
|
+
/* @__PURE__ */ jsx(Pencil, { "aria-hidden": "true", className: "size-4" }),
|
|
1183
|
+
labels.rename
|
|
1184
|
+
]
|
|
1185
|
+
}
|
|
1186
|
+
) : null,
|
|
1174
1187
|
onCopy ? /* @__PURE__ */ jsxs("button", { type: "button", role: "menuitem", className: itemClass, onClick: () => {
|
|
1175
1188
|
close();
|
|
1176
1189
|
onCopy(target.file);
|
|
@@ -1209,10 +1222,10 @@ function ExplorerContextMenu(props) {
|
|
|
1209
1222
|
{
|
|
1210
1223
|
type: "button",
|
|
1211
1224
|
role: "menuitem",
|
|
1212
|
-
disabled: protectedItem,
|
|
1225
|
+
disabled: protectedItem || deleteLocked,
|
|
1213
1226
|
className: cn(itemClass, "text-destructive"),
|
|
1214
1227
|
onClick: () => {
|
|
1215
|
-
if (protectedItem) return;
|
|
1228
|
+
if (protectedItem || deleteLocked) return;
|
|
1216
1229
|
close();
|
|
1217
1230
|
if (onRequestDelete) onRequestDelete(target.file);
|
|
1218
1231
|
else void actions.deleteFile({ id: target.file.id });
|
|
@@ -1964,10 +1977,15 @@ function FileExplorerInner(props) {
|
|
|
1964
1977
|
onPreview,
|
|
1965
1978
|
onDownload,
|
|
1966
1979
|
extraFileAction,
|
|
1967
|
-
protectedIds
|
|
1980
|
+
protectedIds,
|
|
1981
|
+
itemLocks
|
|
1968
1982
|
} = props;
|
|
1969
1983
|
const labels = useExplorerLabels();
|
|
1970
1984
|
const protectedSet = useMemo(() => new Set(protectedIds ?? []), [protectedIds]);
|
|
1985
|
+
const locksFor = useCallback(
|
|
1986
|
+
(file) => itemLocks?.(file) ?? {},
|
|
1987
|
+
[itemLocks]
|
|
1988
|
+
);
|
|
1971
1989
|
const explorer = useFileExplorer({
|
|
1972
1990
|
listFiles: ((input) => listFiles({ parentId: input.parentId, limit: PAGE_SIZE })),
|
|
1973
1991
|
tenantId,
|
|
@@ -1989,6 +2007,7 @@ function FileExplorerInner(props) {
|
|
|
1989
2007
|
const browseCursor = useRef(void 0);
|
|
1990
2008
|
const overlayCursor = useRef(void 0);
|
|
1991
2009
|
const loadingMoreRef = useRef(false);
|
|
2010
|
+
const openingFolderIdRef = useRef(null);
|
|
1992
2011
|
const overlayMoreRef = useRef(false);
|
|
1993
2012
|
const browseGen = useRef(0);
|
|
1994
2013
|
const [overlayFiles, setOverlayFiles] = useState(null);
|
|
@@ -2061,6 +2080,7 @@ function FileExplorerInner(props) {
|
|
|
2061
2080
|
setLoadingMore(false);
|
|
2062
2081
|
return;
|
|
2063
2082
|
}
|
|
2083
|
+
if (mode === "reset") openingFolderIdRef.current = null;
|
|
2064
2084
|
if (result.ok) {
|
|
2065
2085
|
browseCursor.current = result.value.nextCursor;
|
|
2066
2086
|
setBrowseHasMore(Boolean(result.value.nextCursor));
|
|
@@ -2180,14 +2200,18 @@ function FileExplorerInner(props) {
|
|
|
2180
2200
|
() => [...sourceFiles].sort((a, b) => compareFiles(a, b, sortKey, sortDir)),
|
|
2181
2201
|
[sourceFiles, sortKey, sortDir]
|
|
2182
2202
|
);
|
|
2203
|
+
const refreshSeen = useRef(refreshKey);
|
|
2183
2204
|
useEffect(() => {
|
|
2184
|
-
if (refreshKey === void 0) return;
|
|
2205
|
+
if (refreshKey === void 0 || refreshSeen.current === refreshKey) return;
|
|
2206
|
+
refreshSeen.current = refreshKey;
|
|
2185
2207
|
void reload();
|
|
2186
|
-
}, [refreshKey]);
|
|
2208
|
+
}, [refreshKey, reload]);
|
|
2187
2209
|
const handleActivate = useCallback(
|
|
2188
2210
|
(file) => {
|
|
2189
2211
|
if (file.id === DRAFT_FOLDER_ID) return;
|
|
2190
2212
|
if (file.kind === "folder") {
|
|
2213
|
+
if (openingFolderIdRef.current === file.id) return;
|
|
2214
|
+
openingFolderIdRef.current = file.id;
|
|
2191
2215
|
onOpenFolder?.(file);
|
|
2192
2216
|
return;
|
|
2193
2217
|
}
|
|
@@ -2318,6 +2342,7 @@ function FileExplorerInner(props) {
|
|
|
2318
2342
|
}
|
|
2319
2343
|
if (event.key === "F2" && selected && !trashOpen) {
|
|
2320
2344
|
event.preventDefault();
|
|
2345
|
+
if (locksFor(selected).rename) return;
|
|
2321
2346
|
setRenameTarget(selected);
|
|
2322
2347
|
return;
|
|
2323
2348
|
}
|
|
@@ -2327,7 +2352,8 @@ function FileExplorerInner(props) {
|
|
|
2327
2352
|
if (actions.purgeNode) setPurgeTargets(selectedFiles);
|
|
2328
2353
|
return;
|
|
2329
2354
|
}
|
|
2330
|
-
if (selectedFiles.some((file) => protectedSet.has(file.id)))
|
|
2355
|
+
if (selectedFiles.some((file) => protectedSet.has(file.id) || locksFor(file).delete))
|
|
2356
|
+
return;
|
|
2331
2357
|
setDeleteTargets(selectedFiles);
|
|
2332
2358
|
return;
|
|
2333
2359
|
}
|
|
@@ -2341,6 +2367,7 @@ function FileExplorerInner(props) {
|
|
|
2341
2367
|
clearSelection,
|
|
2342
2368
|
copyToClipboard,
|
|
2343
2369
|
handleActivate,
|
|
2370
|
+
locksFor,
|
|
2344
2371
|
pasteClipboard,
|
|
2345
2372
|
protectedSet,
|
|
2346
2373
|
selected,
|
|
@@ -2379,6 +2406,7 @@ function FileExplorerInner(props) {
|
|
|
2379
2406
|
...sortedFiles
|
|
2380
2407
|
];
|
|
2381
2408
|
}, [draftFolderName, parentId, sortedFiles, tenantId]);
|
|
2409
|
+
const listing = overlayLoading || pageStatus === "loading";
|
|
2382
2410
|
const renamingId = draftFolderName != null ? DRAFT_FOLDER_ID : renameTarget?.id ?? null;
|
|
2383
2411
|
const openUpload = () => uploadInputRef.current?.click();
|
|
2384
2412
|
return /* @__PURE__ */ jsxs(
|
|
@@ -2443,79 +2471,95 @@ function FileExplorerInner(props) {
|
|
|
2443
2471
|
}
|
|
2444
2472
|
)
|
|
2445
2473
|
] }),
|
|
2446
|
-
/* @__PURE__ */
|
|
2447
|
-
/* @__PURE__ */ jsx("p", { className: "font-mono text-[10px] uppercase tracking-[0.12em] text-muted-foreground", children: trashOpen ? labels.emptyTrash : query.trim() ? labels.noMatches : labels.emptyFolder }),
|
|
2448
|
-
trashOpen ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: labels.trashRetention }) : !query.trim() ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: labels.dropHint }) : null
|
|
2449
|
-
] }) : explorer.view === "list" ? /* @__PURE__ */ jsx(
|
|
2450
|
-
"div",
|
|
2451
|
-
{
|
|
2452
|
-
onClick: (event) => {
|
|
2453
|
-
if (event.target.closest("[data-file-id]")) return;
|
|
2454
|
-
clearSelection();
|
|
2455
|
-
},
|
|
2456
|
-
children: /* @__PURE__ */ jsx(
|
|
2457
|
-
ExplorerListView,
|
|
2458
|
-
{
|
|
2459
|
-
files: displayFiles,
|
|
2460
|
-
protectedIds: protectedSet,
|
|
2461
|
-
selectedIds,
|
|
2462
|
-
onSelect: handleSelect,
|
|
2463
|
-
onActivate: handleActivate,
|
|
2464
|
-
onContextMenu: handleContextMenu,
|
|
2465
|
-
onDragStart: explorer.beginDrag,
|
|
2466
|
-
onDragEnd: explorer.endDrag,
|
|
2467
|
-
onDrop: handleDropOnFolder,
|
|
2468
|
-
draggingId: explorer.draggingId,
|
|
2469
|
-
draggingIds,
|
|
2470
|
-
dropTargetId: explorer.dropTargetId,
|
|
2471
|
-
onDragOverRow: explorer.setDropTarget,
|
|
2472
|
-
sortKey,
|
|
2473
|
-
sortDirection: sortDir,
|
|
2474
|
-
onSortChange: handleSortChange,
|
|
2475
|
-
renamingId,
|
|
2476
|
-
onRenameCommit: commitRename,
|
|
2477
|
-
onRenameCancel: cancelRename,
|
|
2478
|
-
scrollRef,
|
|
2479
|
-
hasMore,
|
|
2480
|
-
loadingMore: loadingMore || overlayMore,
|
|
2481
|
-
onLoadMore: loadNext
|
|
2482
|
-
}
|
|
2483
|
-
)
|
|
2484
|
-
}
|
|
2485
|
-
) : /* @__PURE__ */ jsx(
|
|
2474
|
+
/* @__PURE__ */ jsxs(
|
|
2486
2475
|
"div",
|
|
2487
2476
|
{
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2477
|
+
ref: scrollRef,
|
|
2478
|
+
"aria-busy": listing,
|
|
2479
|
+
className: cn(
|
|
2480
|
+
"min-h-0 flex-1 overflow-y-auto overflow-x-auto overscroll-x-contain",
|
|
2481
|
+
// Stale rows stay mounted while the next listing loads, so dim them and
|
|
2482
|
+
// swallow clicks instead of letting the user re-open the same folder.
|
|
2483
|
+
listing && displayFiles.length > 0 && "pointer-events-none opacity-60"
|
|
2484
|
+
),
|
|
2485
|
+
children: [
|
|
2486
|
+
listing && displayFiles.length > 0 ? /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "h-0.5 w-full overflow-hidden bg-muted", children: /* @__PURE__ */ jsx("div", { className: "h-full w-1/3 animate-pulse bg-primary" }) }) : null,
|
|
2487
|
+
listing && displayFiles.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-4 py-12 text-center font-mono text-[10px] uppercase tracking-[0.12em] text-muted-foreground", children: labels.loading }) : overlayError || pageStatus === "error" && pageError ? /* @__PURE__ */ jsx("div", { className: "border-b border-destructive/30 bg-destructive/5 px-4 py-3 text-sm text-destructive", children: (overlayError ?? pageError)?.message ?? labels.folderCreateFailed }) : displayFiles.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex min-h-full flex-col justify-center px-4 py-16 text-center", children: [
|
|
2488
|
+
/* @__PURE__ */ jsx("p", { className: "font-mono text-[10px] uppercase tracking-[0.12em] text-muted-foreground", children: trashOpen ? labels.emptyTrash : query.trim() ? labels.noMatches : labels.emptyFolder }),
|
|
2489
|
+
trashOpen ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: labels.trashRetention }) : !query.trim() ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: labels.dropHint }) : null
|
|
2490
|
+
] }) : explorer.view === "list" ? /* @__PURE__ */ jsx(
|
|
2491
|
+
"div",
|
|
2492
|
+
{
|
|
2493
|
+
onClick: (event) => {
|
|
2494
|
+
if (event.target.closest("[data-file-id]")) return;
|
|
2495
|
+
clearSelection();
|
|
2496
|
+
},
|
|
2497
|
+
children: /* @__PURE__ */ jsx(
|
|
2498
|
+
ExplorerListView,
|
|
2499
|
+
{
|
|
2500
|
+
files: displayFiles,
|
|
2501
|
+
protectedIds: protectedSet,
|
|
2502
|
+
selectedIds,
|
|
2503
|
+
onSelect: handleSelect,
|
|
2504
|
+
onActivate: handleActivate,
|
|
2505
|
+
onContextMenu: handleContextMenu,
|
|
2506
|
+
onDragStart: explorer.beginDrag,
|
|
2507
|
+
onDragEnd: explorer.endDrag,
|
|
2508
|
+
onDrop: handleDropOnFolder,
|
|
2509
|
+
draggingId: explorer.draggingId,
|
|
2510
|
+
draggingIds,
|
|
2511
|
+
dropTargetId: explorer.dropTargetId,
|
|
2512
|
+
onDragOverRow: explorer.setDropTarget,
|
|
2513
|
+
sortKey,
|
|
2514
|
+
sortDirection: sortDir,
|
|
2515
|
+
onSortChange: handleSortChange,
|
|
2516
|
+
renamingId,
|
|
2517
|
+
onRenameCommit: commitRename,
|
|
2518
|
+
onRenameCancel: cancelRename,
|
|
2519
|
+
scrollRef,
|
|
2520
|
+
hasMore,
|
|
2521
|
+
loadingMore: loadingMore || overlayMore,
|
|
2522
|
+
onLoadMore: loadNext
|
|
2523
|
+
}
|
|
2524
|
+
)
|
|
2525
|
+
}
|
|
2526
|
+
) : /* @__PURE__ */ jsx(
|
|
2527
|
+
"div",
|
|
2528
|
+
{
|
|
2529
|
+
onClick: (event) => {
|
|
2530
|
+
if (event.target.closest("[data-file-id]")) return;
|
|
2531
|
+
clearSelection();
|
|
2532
|
+
},
|
|
2533
|
+
children: /* @__PURE__ */ jsx(
|
|
2534
|
+
ExplorerGridView,
|
|
2535
|
+
{
|
|
2536
|
+
files: displayFiles,
|
|
2537
|
+
protectedIds: protectedSet,
|
|
2538
|
+
selectedIds,
|
|
2539
|
+
onSelect: handleSelect,
|
|
2540
|
+
onActivate: handleActivate,
|
|
2541
|
+
onContextMenu: handleContextMenu,
|
|
2542
|
+
onDragStart: explorer.beginDrag,
|
|
2543
|
+
onDragEnd: explorer.endDrag,
|
|
2544
|
+
onDrop: handleDropOnFolder,
|
|
2545
|
+
draggingId: explorer.draggingId,
|
|
2546
|
+
draggingIds,
|
|
2547
|
+
dropTargetId: explorer.dropTargetId,
|
|
2548
|
+
onDragOverRow: explorer.setDropTarget,
|
|
2549
|
+
renamingId,
|
|
2550
|
+
onRenameCommit: commitRename,
|
|
2551
|
+
onRenameCancel: cancelRename,
|
|
2552
|
+
scrollRef,
|
|
2553
|
+
hasMore,
|
|
2554
|
+
loadingMore: loadingMore || overlayMore,
|
|
2555
|
+
onLoadMore: loadNext
|
|
2556
|
+
}
|
|
2557
|
+
)
|
|
2558
|
+
}
|
|
2559
|
+
)
|
|
2560
|
+
]
|
|
2517
2561
|
}
|
|
2518
|
-
)
|
|
2562
|
+
),
|
|
2519
2563
|
/* @__PURE__ */ jsxs("footer", { className: "flex shrink-0 items-center justify-between gap-3 border-t border-border px-4 py-2", children: [
|
|
2520
2564
|
/* @__PURE__ */ jsxs("span", { className: "font-mono text-[10px] uppercase tracking-[0.12em] text-muted-foreground", children: [
|
|
2521
2565
|
/* @__PURE__ */ jsxs("span", { className: "text-primary tabular-nums", children: [
|
|
@@ -2536,6 +2580,8 @@ function FileExplorerInner(props) {
|
|
|
2536
2580
|
target: menu,
|
|
2537
2581
|
mode: trashOpen ? "trash" : "browse",
|
|
2538
2582
|
protectedItem: menu?.kind === "item" ? protectedSet.has(menu.file.id) : false,
|
|
2583
|
+
renameLocked: menu?.kind === "item" ? Boolean(locksFor(menu.file).rename) : false,
|
|
2584
|
+
deleteLocked: menu?.kind === "item" ? Boolean(locksFor(menu.file).delete) : false,
|
|
2539
2585
|
extraFileAction,
|
|
2540
2586
|
actions: {
|
|
2541
2587
|
deleteFile: async (input) => {
|
|
@@ -2570,7 +2616,10 @@ function FileExplorerInner(props) {
|
|
|
2570
2616
|
ExplorerSelectionToast,
|
|
2571
2617
|
{
|
|
2572
2618
|
files: selectedFiles,
|
|
2573
|
-
protectedIds:
|
|
2619
|
+
protectedIds: /* @__PURE__ */ new Set([
|
|
2620
|
+
...protectedSet,
|
|
2621
|
+
...selectedFiles.filter((file) => locksFor(file).delete).map((file) => file.id)
|
|
2622
|
+
]),
|
|
2574
2623
|
trashOpen,
|
|
2575
2624
|
onDownload,
|
|
2576
2625
|
onCopy: trashOpen ? void 0 : () => copyToClipboard(selectedFiles),
|