@muhgholy/next-drive 4.23.23 → 4.23.25
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/{chunk-IVSBLEMY.cjs → chunk-45FZNILV.cjs} +170 -1031
- package/dist/chunk-45FZNILV.cjs.map +1 -0
- package/dist/{chunk-CPMMHIQM.js → chunk-VBFXHZBY.js} +159 -1026
- package/dist/chunk-VBFXHZBY.js.map +1 -0
- package/dist/chunk-VTCBYFAK.cjs +765 -0
- package/dist/chunk-VTCBYFAK.cjs.map +1 -0
- package/dist/chunk-ZNWS7VJP.js +755 -0
- package/dist/chunk-ZNWS7VJP.js.map +1 -0
- package/dist/client/components/drive/dnd/context.d.ts.map +1 -1
- package/dist/client/components/drive/explorer.d.ts.map +1 -1
- package/dist/client/components/drive/file-grid.d.ts.map +1 -1
- package/dist/client/hooks/use-marquee.d.ts +14 -0
- package/dist/client/hooks/use-marquee.d.ts.map +1 -0
- package/dist/client/index.cjs +170 -150
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.js +169 -150
- package/dist/client/index.js.map +1 -1
- package/dist/client/upload.d.ts +9 -4
- package/dist/client/upload.d.ts.map +1 -1
- package/dist/server/actions/drive.d.ts.map +1 -1
- package/dist/server/controllers/drive.d.ts.map +1 -1
- package/dist/server/express.cjs +15 -14
- package/dist/server/express.cjs.map +1 -1
- package/dist/server/express.js +4 -2
- package/dist/server/express.js.map +1 -1
- package/dist/server/hono.cjs +15 -14
- package/dist/server/hono.cjs.map +1 -1
- package/dist/server/hono.js +4 -2
- package/dist/server/hono.js.map +1 -1
- package/dist/server/index.cjs +28 -27
- package/dist/server/index.js +2 -1
- package/dist/server/tus.d.ts +17 -0
- package/dist/server/tus.d.ts.map +1 -0
- package/dist/server/zod/schemas.d.ts +0 -53
- package/dist/server/zod/schemas.d.ts.map +1 -1
- package/dist/tus-6OAOA454.js +3 -0
- package/dist/tus-6OAOA454.js.map +1 -0
- package/dist/tus-P67YULWB.cjs +16 -0
- package/dist/tus-P67YULWB.cjs.map +1 -0
- package/dist/types/server/api.d.ts +1 -1
- package/dist/types/server/api.d.ts.map +1 -1
- package/package.json +4 -1
- package/dist/chunk-CPMMHIQM.js.map +0 -1
- package/dist/chunk-IVSBLEMY.cjs.map +0 -1
package/dist/client/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export { driveFileSchemaZod } from '../chunk-FJRGKB3U.js';
|
|
|
3
3
|
import * as React3 from 'react';
|
|
4
4
|
import React3__default, { createContext, useContext, useState, useMemo, useCallback, useRef, useEffect } from 'react';
|
|
5
5
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
6
|
+
import * as tus from 'tus-js-client';
|
|
6
7
|
import { Slot } from '@radix-ui/react-slot';
|
|
7
8
|
import { cva } from 'class-variance-authority';
|
|
8
9
|
import * as ProgressPrimitive from '@radix-ui/react-progress';
|
|
@@ -306,8 +307,6 @@ var useDrive = () => {
|
|
|
306
307
|
if (!context) throw new Error("useDrive must be used within a DriveProvider");
|
|
307
308
|
return context;
|
|
308
309
|
};
|
|
309
|
-
|
|
310
|
-
// src/client/upload.ts
|
|
311
310
|
var MAX_CONCURRENT_UPLOADS = 2;
|
|
312
311
|
var getChunkSize = (fileSize) => {
|
|
313
312
|
if (fileSize < 50 * 1024 * 1024) return 2 * 1024 * 1024;
|
|
@@ -341,161 +340,92 @@ var uploadFile = (options) => {
|
|
|
341
340
|
onComplete
|
|
342
341
|
} = options;
|
|
343
342
|
const id = options.id ?? generateId();
|
|
344
|
-
const endpoint = apiEndpoint.replace(/\/$/, "")
|
|
345
|
-
const uploadUrl = `${endpoint}?action=upload`;
|
|
346
|
-
const controller = new AbortController();
|
|
347
|
-
let activeXhr = null;
|
|
348
|
-
let driveId;
|
|
349
|
-
let completedItem;
|
|
350
|
-
let cancelled = false;
|
|
351
|
-
const totalBytes = file.size;
|
|
343
|
+
const endpoint = `${apiEndpoint.replace(/\/$/, "")}?action=upload`;
|
|
352
344
|
const chunkSize = getChunkSize(file.size);
|
|
353
|
-
const totalChunks = Math.ceil(file.size / chunkSize);
|
|
345
|
+
const totalChunks = Math.max(1, Math.ceil(file.size / chunkSize));
|
|
354
346
|
const log = (type, message) => onLog?.({ id, type, message, timestamp: Date.now() });
|
|
355
|
-
|
|
356
|
-
const percent = totalBytes === 0 ? 100 : Math.min(100, Math.round(uploadedBytes / totalBytes * 100));
|
|
357
|
-
onProgress?.({ id, percent, uploadedBytes, totalBytes, currentChunk, totalChunks });
|
|
347
|
+
let settle = () => {
|
|
358
348
|
};
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
}
|
|
384
|
-
return;
|
|
349
|
+
const promise = new Promise((resolve) => {
|
|
350
|
+
settle = resolve;
|
|
351
|
+
});
|
|
352
|
+
let completedItem;
|
|
353
|
+
let cancelled = false;
|
|
354
|
+
const metadata = {
|
|
355
|
+
filename: file.name,
|
|
356
|
+
filetype: file.type || "application/octet-stream",
|
|
357
|
+
folderId: folderId || "root"
|
|
358
|
+
};
|
|
359
|
+
if (conflictAction) metadata.conflictAction = conflictAction;
|
|
360
|
+
const upload = new tus.Upload(file, {
|
|
361
|
+
endpoint,
|
|
362
|
+
chunkSize,
|
|
363
|
+
// ** Retry with backoff. On a dropped connection tus first re-checks the server offset (HEAD)
|
|
364
|
+
// ** and resumes from there — this is what prevents the "session expired" failures at ~100%.
|
|
365
|
+
retryDelays: [0, 1e3, 3e3, 5e3, 1e4],
|
|
366
|
+
removeFingerprintOnSuccess: true,
|
|
367
|
+
metadata,
|
|
368
|
+
headers: accountId ? { "x-drive-account": accountId } : {},
|
|
369
|
+
onBeforeRequest(req) {
|
|
370
|
+
if (withCredentials) {
|
|
371
|
+
const xhr = req.getUnderlyingObject();
|
|
372
|
+
if (xhr && "withCredentials" in xhr) xhr.withCredentials = true;
|
|
385
373
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}
|
|
393
|
-
log("success", `Chunk uploaded successfully`);
|
|
394
|
-
resolve([true, data.data, false]);
|
|
395
|
-
} catch {
|
|
396
|
-
log("error", `Failed to parse response: ${text.slice(0, 100)}`);
|
|
397
|
-
resolve([false, "Invalid server response", true]);
|
|
374
|
+
},
|
|
375
|
+
onShouldRetry(err) {
|
|
376
|
+
const status = err?.originalResponse?.getStatus?.() ?? 0;
|
|
377
|
+
if ([400, 401, 403, 413].includes(status)) {
|
|
378
|
+
log("error", `Non-retryable error (status ${status})`);
|
|
379
|
+
return false;
|
|
398
380
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
for (let i = 0; i < totalChunks; i++) {
|
|
420
|
-
if (controller.signal.aborted) throw new Error("Cancelled");
|
|
421
|
-
log("info", `Processing chunk ${i + 1}/${totalChunks}`);
|
|
422
|
-
const start = i * chunkSize;
|
|
423
|
-
const end = Math.min(start + chunkSize, file.size);
|
|
424
|
-
const chunk = file.slice(start, end);
|
|
425
|
-
const formData = new FormData();
|
|
426
|
-
formData.append("chunk", chunk);
|
|
427
|
-
formData.append("chunkIndex", String(i));
|
|
428
|
-
formData.append("totalChunks", String(totalChunks));
|
|
429
|
-
formData.append("fileName", file.name);
|
|
430
|
-
formData.append("fileSize", String(file.size));
|
|
431
|
-
formData.append("fileType", file.type);
|
|
432
|
-
formData.append("folderId", folderId || "root");
|
|
433
|
-
if (conflictAction) formData.append("conflictAction", conflictAction);
|
|
434
|
-
if (driveId) formData.append("driveId", driveId);
|
|
435
|
-
let attempts = 0;
|
|
436
|
-
let success = false;
|
|
437
|
-
while (!success && attempts < 3 && !controller.signal.aborted) {
|
|
438
|
-
const [ok, result, canRetry] = await sendChunk(i, formData, start);
|
|
439
|
-
if (ok) {
|
|
440
|
-
success = true;
|
|
441
|
-
emitProgress(i + 1, end);
|
|
442
|
-
if (result.type === "UPLOAD_STARTED" && result.driveId) {
|
|
443
|
-
driveId = result.driveId;
|
|
444
|
-
log("success", `Upload session started with ID: ${driveId}`);
|
|
445
|
-
} else if (result.type === "UPLOAD_COMPLETE") {
|
|
446
|
-
if (result.driveId) driveId = result.driveId;
|
|
447
|
-
log("success", `Upload completed successfully`);
|
|
448
|
-
completedItem = result.item;
|
|
449
|
-
}
|
|
450
|
-
} else {
|
|
451
|
-
if (result === "Cancelled") throw new Error("Cancelled");
|
|
452
|
-
if (!canRetry) {
|
|
453
|
-
log("error", `Non-retryable error: ${result}`);
|
|
454
|
-
throw new Error(result);
|
|
455
|
-
}
|
|
456
|
-
attempts++;
|
|
457
|
-
log("warning", `Retry attempt ${attempts}/3 after error`);
|
|
458
|
-
if (attempts === 3) {
|
|
459
|
-
log("error", `Max retry attempts reached: ${result}`);
|
|
460
|
-
throw new Error(result);
|
|
461
|
-
}
|
|
462
|
-
await new Promise((r) => setTimeout(r, 1e3 * attempts));
|
|
463
|
-
}
|
|
381
|
+
log("warning", `Upload interrupted (status ${status}), retrying`);
|
|
382
|
+
return true;
|
|
383
|
+
},
|
|
384
|
+
onProgress(bytesUploaded, bytesTotal) {
|
|
385
|
+
const percent = bytesTotal === 0 ? 100 : Math.min(100, Math.round(bytesUploaded / bytesTotal * 100));
|
|
386
|
+
onProgress?.({
|
|
387
|
+
id,
|
|
388
|
+
percent,
|
|
389
|
+
uploadedBytes: bytesUploaded,
|
|
390
|
+
totalBytes: bytesTotal,
|
|
391
|
+
currentChunk: Math.min(totalChunks, Math.floor(bytesUploaded / chunkSize)),
|
|
392
|
+
totalChunks
|
|
393
|
+
});
|
|
394
|
+
},
|
|
395
|
+
onAfterResponse(_req, res) {
|
|
396
|
+
const header = res.getHeader("X-Drive-Item");
|
|
397
|
+
if (header) {
|
|
398
|
+
try {
|
|
399
|
+
completedItem = JSON.parse(decodeURIComponent(header));
|
|
400
|
+
} catch {
|
|
464
401
|
}
|
|
465
|
-
if (!success && controller.signal.aborted) throw new Error("Cancelled");
|
|
466
402
|
}
|
|
467
|
-
|
|
468
|
-
|
|
403
|
+
},
|
|
404
|
+
onSuccess() {
|
|
405
|
+
log("success", "Upload completed successfully");
|
|
469
406
|
if (completedItem !== void 0) onComplete?.(completedItem);
|
|
470
|
-
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
}
|
|
477
|
-
log("error", `Upload failed: ${errorMessage}`);
|
|
478
|
-
return { id, status: "error", driveId: driveId ?? null, error: errorMessage };
|
|
407
|
+
settle({ id, status: "complete", driveId: completedItem?.id ?? "", file: toDriveFile(completedItem), item: completedItem });
|
|
408
|
+
},
|
|
409
|
+
onError(error) {
|
|
410
|
+
const message = error instanceof Error ? error.message : "Upload failed";
|
|
411
|
+
log("error", `Upload failed: ${message}`);
|
|
412
|
+
settle({ id, status: "error", driveId: null, error: message });
|
|
479
413
|
}
|
|
480
|
-
};
|
|
414
|
+
});
|
|
481
415
|
const cancel = () => {
|
|
482
416
|
if (cancelled) return;
|
|
483
417
|
cancelled = true;
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
fetch(`${endpoint}?action=cancel&id=${driveId}`, {
|
|
488
|
-
method: "POST",
|
|
489
|
-
credentials: withCredentials ? "include" : "same-origin"
|
|
490
|
-
}).catch(() => {
|
|
491
|
-
});
|
|
492
|
-
}
|
|
418
|
+
upload.abort(true).catch(() => {
|
|
419
|
+
});
|
|
420
|
+
settle({ id, status: "cancelled", driveId: null });
|
|
493
421
|
};
|
|
494
422
|
if (signal) {
|
|
495
423
|
if (signal.aborted) cancel();
|
|
496
424
|
else signal.addEventListener("abort", cancel, { once: true });
|
|
497
425
|
}
|
|
498
|
-
|
|
426
|
+
log("info", `Starting upload: ${file.name} (${file.size} bytes)`);
|
|
427
|
+
upload.start();
|
|
428
|
+
return { id, promise, cancel };
|
|
499
429
|
};
|
|
500
430
|
var uploadFiles = (files, options) => {
|
|
501
431
|
const { concurrency = MAX_CONCURRENT_UPLOADS, onFileComplete, ...base } = options;
|
|
@@ -1530,9 +1460,16 @@ var DriveDndContext = createContext({
|
|
|
1530
1460
|
var useDriveDnd = () => useContext(DriveDndContext);
|
|
1531
1461
|
var DriveDndProvider = (props) => {
|
|
1532
1462
|
const { children } = props;
|
|
1533
|
-
const { items, setItems, sortBy, setSortBy, moveItem, callAPI, currentView } = useDrive();
|
|
1463
|
+
const { items, setItems, sortBy, setSortBy, moveItem, callAPI, currentView, selectedFileIds, setSelectedFiles } = useDrive();
|
|
1534
1464
|
const [dragOverFolderId, setDragOverFolderId] = useState(null);
|
|
1535
1465
|
const [draggingItemId, setDraggingItemId] = useState(null);
|
|
1466
|
+
const moveSelection = useCallback(async (activeId, targetFolderId) => {
|
|
1467
|
+
const ids = selectedFileIds.includes(activeId) && selectedFileIds.length > 1 ? selectedFileIds : [activeId];
|
|
1468
|
+
if (ids.length === 1) return moveItem(ids[0], targetFolderId);
|
|
1469
|
+
setItems((prev) => prev.filter((i) => !ids.includes(i.id)));
|
|
1470
|
+
setSelectedFiles([]);
|
|
1471
|
+
await callAPI("move", { method: "POST", body: JSON.stringify({ ids, targetFolderId }) });
|
|
1472
|
+
}, [selectedFileIds, moveItem, setItems, setSelectedFiles, callAPI]);
|
|
1536
1473
|
const sensors = useSensors(
|
|
1537
1474
|
useSensor(PointerSensor, { activationConstraint: { distance: 8 } }),
|
|
1538
1475
|
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
|
|
@@ -1567,12 +1504,12 @@ var DriveDndProvider = (props) => {
|
|
|
1567
1504
|
if (overId.startsWith("path-")) {
|
|
1568
1505
|
const pathId = overId.slice(5);
|
|
1569
1506
|
const targetFolderId = pathId === "root" || pathId === "null" ? "root" : pathId;
|
|
1570
|
-
await
|
|
1507
|
+
await moveSelection(active.id, targetFolderId);
|
|
1571
1508
|
return;
|
|
1572
1509
|
}
|
|
1573
1510
|
const overItem = items.find((i) => i.id === over.id);
|
|
1574
1511
|
if (overItem?.information.type === "FOLDER") {
|
|
1575
|
-
await
|
|
1512
|
+
await moveSelection(active.id, over.id);
|
|
1576
1513
|
return;
|
|
1577
1514
|
}
|
|
1578
1515
|
const oldIndex = items.findIndex((i) => i.id === active.id);
|
|
@@ -1582,7 +1519,7 @@ var DriveDndProvider = (props) => {
|
|
|
1582
1519
|
setItems(reordered);
|
|
1583
1520
|
if (sortBy.field !== "order") setSortBy({ field: "order", order: 1 });
|
|
1584
1521
|
await callAPI("reorder", { method: "POST", body: JSON.stringify({ ids: reordered.map((i) => i.id) }) });
|
|
1585
|
-
}, [items, setItems, sortBy, setSortBy,
|
|
1522
|
+
}, [items, setItems, sortBy, setSortBy, moveSelection, callAPI]);
|
|
1586
1523
|
const enableDnd = currentView === "BROWSE";
|
|
1587
1524
|
return /* @__PURE__ */ jsx(DriveDndContext.Provider, { value: { dragOverFolderId, draggingItemId }, children: enableDnd ? /* @__PURE__ */ jsx(
|
|
1588
1525
|
DndContext,
|
|
@@ -1948,6 +1885,49 @@ var DriveItemRename = (props) => {
|
|
|
1948
1885
|
}
|
|
1949
1886
|
) });
|
|
1950
1887
|
};
|
|
1888
|
+
var useMarquee = (onSelectIds) => {
|
|
1889
|
+
const [box, setBox] = useState(null);
|
|
1890
|
+
const onMouseDown = (e) => {
|
|
1891
|
+
if (e.button !== 0 || e.target.closest("[data-marquee-id]")) return;
|
|
1892
|
+
const container = e.currentTarget;
|
|
1893
|
+
const start = { x: e.clientX, y: e.clientY };
|
|
1894
|
+
let moved = false;
|
|
1895
|
+
const handleMove = (ev) => {
|
|
1896
|
+
if (!moved && Math.abs(ev.clientX - start.x) < 4 && Math.abs(ev.clientY - start.y) < 4) return;
|
|
1897
|
+
moved = true;
|
|
1898
|
+
const left = Math.min(start.x, ev.clientX);
|
|
1899
|
+
const top = Math.min(start.y, ev.clientY);
|
|
1900
|
+
const right = Math.max(start.x, ev.clientX);
|
|
1901
|
+
const bottom = Math.max(start.y, ev.clientY);
|
|
1902
|
+
const cRect = container.getBoundingClientRect();
|
|
1903
|
+
setBox({
|
|
1904
|
+
left: left - cRect.left + container.scrollLeft,
|
|
1905
|
+
top: top - cRect.top + container.scrollTop,
|
|
1906
|
+
width: right - left,
|
|
1907
|
+
height: bottom - top
|
|
1908
|
+
});
|
|
1909
|
+
const ids = [];
|
|
1910
|
+
container.querySelectorAll("[data-marquee-id]").forEach((el) => {
|
|
1911
|
+
const r = el.getBoundingClientRect();
|
|
1912
|
+
if (r.left < right && r.right > left && r.top < bottom && r.bottom > top) ids.push(el.dataset.marqueeId);
|
|
1913
|
+
});
|
|
1914
|
+
onSelectIds(ids);
|
|
1915
|
+
};
|
|
1916
|
+
const handleUp = () => {
|
|
1917
|
+
window.removeEventListener("mousemove", handleMove);
|
|
1918
|
+
window.removeEventListener("mouseup", handleUp);
|
|
1919
|
+
if (!moved) onSelectIds([]);
|
|
1920
|
+
setBox(null);
|
|
1921
|
+
};
|
|
1922
|
+
window.addEventListener("mousemove", handleMove);
|
|
1923
|
+
window.addEventListener("mouseup", handleUp);
|
|
1924
|
+
};
|
|
1925
|
+
const overlay = box ? React3__default.createElement("div", {
|
|
1926
|
+
className: "nd:absolute nd:z-50 nd:rounded-sm nd:border nd:border-primary nd:bg-primary/10 nd:pointer-events-none",
|
|
1927
|
+
style: { left: box.left, top: box.top, width: box.width, height: box.height }
|
|
1928
|
+
}) : null;
|
|
1929
|
+
return { onMouseDown, overlay };
|
|
1930
|
+
};
|
|
1951
1931
|
var SortableItem = (props) => {
|
|
1952
1932
|
const { id, children, disabled, isDragOverTarget } = props;
|
|
1953
1933
|
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id, disabled });
|
|
@@ -1958,7 +1938,7 @@ var SortableItem = (props) => {
|
|
|
1958
1938
|
zIndex: isDragging ? 50 : "auto",
|
|
1959
1939
|
position: "relative"
|
|
1960
1940
|
};
|
|
1961
|
-
return /* @__PURE__ */ jsx("div", { ref: setNodeRef, style, ...attributes, ...listeners, children });
|
|
1941
|
+
return /* @__PURE__ */ jsx("div", { ref: setNodeRef, "data-marquee-id": id, style, ...attributes, ...listeners, children });
|
|
1962
1942
|
};
|
|
1963
1943
|
var FileItem = (props) => {
|
|
1964
1944
|
const { item, isSelected, isDragOver, onSelect, onDoubleClick, onRename, onDelete, onRestore } = props;
|
|
@@ -2045,6 +2025,8 @@ var DriveFileGrid = (props) => {
|
|
|
2045
2025
|
sortBy,
|
|
2046
2026
|
selectedFileIds,
|
|
2047
2027
|
toggleSelected,
|
|
2028
|
+
setSelectedFiles,
|
|
2029
|
+
selectionMode,
|
|
2048
2030
|
navigateToFolder,
|
|
2049
2031
|
hasMore,
|
|
2050
2032
|
loadMore,
|
|
@@ -2056,6 +2038,19 @@ var DriveFileGrid = (props) => {
|
|
|
2056
2038
|
restoreItem
|
|
2057
2039
|
} = useDrive();
|
|
2058
2040
|
const { dragOverFolderId } = useDriveDnd();
|
|
2041
|
+
const { onMouseDown: marqueeMouseDown, overlay: marqueeOverlay } = useMarquee((ids) => {
|
|
2042
|
+
let files = items.filter((i) => ids.includes(i.id)).map((i) => ({
|
|
2043
|
+
id: i.id,
|
|
2044
|
+
file: {
|
|
2045
|
+
name: i.name,
|
|
2046
|
+
mime: i.information.type === "FILE" ? i.information.mime : "",
|
|
2047
|
+
size: i.information.type === "FILE" ? i.information.sizeInBytes : 0
|
|
2048
|
+
}
|
|
2049
|
+
}));
|
|
2050
|
+
if (selectionMode.type === "SINGLE") files = files.slice(0, 1);
|
|
2051
|
+
else if (selectionMode.maxFile) files = files.slice(0, selectionMode.maxFile);
|
|
2052
|
+
setSelectedFiles(files);
|
|
2053
|
+
});
|
|
2059
2054
|
const [dialogs, setDialogs] = React3__default.useState({ newFolder: false, rename: false, delete: false });
|
|
2060
2055
|
const [itemToDelete, setItemToDelete] = React3__default.useState(null);
|
|
2061
2056
|
const [itemToRename, setItemToRename] = React3__default.useState(null);
|
|
@@ -2178,7 +2173,8 @@ var DriveFileGrid = (props) => {
|
|
|
2178
2173
|
indicatorClassName: "nd:bg-primary"
|
|
2179
2174
|
}
|
|
2180
2175
|
) }),
|
|
2181
|
-
/* @__PURE__ */ jsxs("div", { className: cn("nd:flex-1 nd:overflow-y-auto nd:min-h-0 nd:p-2 nd:sm:p-3 nd:md:p-4", className), children: [
|
|
2176
|
+
/* @__PURE__ */ jsxs("div", { className: cn("nd:flex-1 nd:overflow-y-auto nd:min-h-0 nd:relative nd:p-2 nd:sm:p-3 nd:md:p-4", className), onMouseDown: marqueeMouseDown, children: [
|
|
2177
|
+
marqueeOverlay,
|
|
2182
2178
|
/* @__PURE__ */ jsx("div", { className: "nd:space-y-4 nd:sm:space-y-6", children: Object.entries(groupedItems).map(([groupName, groupItems]) => /* @__PURE__ */ jsxs("div", { children: [
|
|
2183
2179
|
groupBy !== "NONE" && /* @__PURE__ */ jsxs("h3", { className: "nd:text-xs nd:font-semibold nd:text-muted-foreground nd:uppercase nd:tracking-wide nd:mb-2 nd:sm:mb-3 nd:px-1", children: [
|
|
2184
2180
|
groupName,
|
|
@@ -3829,7 +3825,7 @@ var SortableItem2 = (props) => {
|
|
|
3829
3825
|
zIndex: isDragging ? 50 : "auto",
|
|
3830
3826
|
position: "relative"
|
|
3831
3827
|
};
|
|
3832
|
-
return /* @__PURE__ */ jsx("div", { ref: setNodeRef, style, ...attributes, ...listeners, children });
|
|
3828
|
+
return /* @__PURE__ */ jsx("div", { ref: setNodeRef, "data-marquee-id": id, style, ...attributes, ...listeners, children });
|
|
3833
3829
|
};
|
|
3834
3830
|
var DriveExplorer = (props) => {
|
|
3835
3831
|
const { onItemClick, onItemDoubleClick, mimeFilter, className, selectableFolders = false } = props;
|
|
@@ -3846,6 +3842,8 @@ var DriveExplorer = (props) => {
|
|
|
3846
3842
|
setSortBy,
|
|
3847
3843
|
selectedFileIds,
|
|
3848
3844
|
toggleSelected,
|
|
3845
|
+
setSelectedFiles,
|
|
3846
|
+
selectionMode,
|
|
3849
3847
|
navigateToFolder,
|
|
3850
3848
|
hasMore,
|
|
3851
3849
|
loadMore,
|
|
@@ -3875,6 +3873,13 @@ var DriveExplorer = (props) => {
|
|
|
3875
3873
|
const handleDragStart = (event) => {
|
|
3876
3874
|
setDraggingItemId(event.active.id);
|
|
3877
3875
|
};
|
|
3876
|
+
const moveSelection = async (activeId, targetFolderId) => {
|
|
3877
|
+
const ids = selectedFileIds.includes(activeId) && selectedFileIds.length > 1 ? selectedFileIds : [activeId];
|
|
3878
|
+
if (ids.length === 1) return moveItem(ids[0], targetFolderId);
|
|
3879
|
+
setItems((prev) => prev.filter((i) => !ids.includes(i.id)));
|
|
3880
|
+
setSelectedFiles([]);
|
|
3881
|
+
await callAPI("move", { method: "POST", body: JSON.stringify({ ids, targetFolderId }) });
|
|
3882
|
+
};
|
|
3878
3883
|
const handleDragOver = (event) => {
|
|
3879
3884
|
const { over } = event;
|
|
3880
3885
|
if (!over) {
|
|
@@ -3898,13 +3903,13 @@ var DriveExplorer = (props) => {
|
|
|
3898
3903
|
const overId = over.id;
|
|
3899
3904
|
if (overId.startsWith("path-")) {
|
|
3900
3905
|
const targetFolderId = overId.replace("path-", "");
|
|
3901
|
-
await
|
|
3906
|
+
await moveSelection(active.id, targetFolderId === "root" ? "root" : targetFolderId);
|
|
3902
3907
|
return;
|
|
3903
3908
|
}
|
|
3904
3909
|
const overItem = items.find((i) => i.id === over.id);
|
|
3905
3910
|
const activeItem = items.find((i) => i.id === active.id);
|
|
3906
3911
|
if (overItem?.information.type === "FOLDER" && activeItem) {
|
|
3907
|
-
await
|
|
3912
|
+
await moveSelection(active.id, over.id);
|
|
3908
3913
|
return;
|
|
3909
3914
|
}
|
|
3910
3915
|
setItems((prevItems) => {
|
|
@@ -4008,6 +4013,19 @@ var DriveExplorer = (props) => {
|
|
|
4008
4013
|
} else onItemDoubleClick?.(item);
|
|
4009
4014
|
};
|
|
4010
4015
|
const enableDrag = currentView === "BROWSE";
|
|
4016
|
+
const { onMouseDown: marqueeMouseDown, overlay: marqueeOverlay } = useMarquee((ids) => {
|
|
4017
|
+
let files = items.filter((i) => ids.includes(i.id)).map((i) => ({
|
|
4018
|
+
id: i.id,
|
|
4019
|
+
file: {
|
|
4020
|
+
name: i.name,
|
|
4021
|
+
mime: i.information.type === "FILE" ? i.information.mime : "",
|
|
4022
|
+
size: i.information.type === "FILE" ? i.information.sizeInBytes : 0
|
|
4023
|
+
}
|
|
4024
|
+
}));
|
|
4025
|
+
if (selectionMode.type === "SINGLE") files = files.slice(0, 1);
|
|
4026
|
+
else if (selectionMode.maxFile) files = files.slice(0, selectionMode.maxFile);
|
|
4027
|
+
setSelectedFiles(files);
|
|
4028
|
+
});
|
|
4011
4029
|
const stateContent = (() => {
|
|
4012
4030
|
if (isLoading && items.length === 0) {
|
|
4013
4031
|
return /* @__PURE__ */ jsx("div", { className: "nd:flex nd:items-center nd:justify-center nd:py-12 nd:flex-1", children: /* @__PURE__ */ jsx(Loader2, { className: "nd:size-6 nd:animate-spin nd:text-muted-foreground" }) });
|
|
@@ -4038,7 +4056,8 @@ var DriveExplorer = (props) => {
|
|
|
4038
4056
|
] }),
|
|
4039
4057
|
/* @__PURE__ */ jsx("div", { className: "nd:lg:hidden nd:px-3 nd:py-2 nd:border-b nd:bg-background/95 nd:dark:bg-background/80 nd:backdrop-blur-sm", children: /* @__PURE__ */ jsx(DrivePathBar, {}) }),
|
|
4040
4058
|
stateContent || /* @__PURE__ */ jsxs(ContextMenu, { children: [
|
|
4041
|
-
/* @__PURE__ */ jsx(ContextMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: cn("nd:flex-1 nd:overflow-y-auto nd:min-h-0 nd:container nd:mx-auto nd:p-2 nd:sm:p-3 nd:md:p-4", className), children: [
|
|
4059
|
+
/* @__PURE__ */ jsx(ContextMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: cn("nd:flex-1 nd:overflow-y-auto nd:min-h-0 nd:relative nd:container nd:mx-auto nd:p-2 nd:sm:p-3 nd:md:p-4", className), onMouseDown: marqueeMouseDown, children: [
|
|
4060
|
+
marqueeOverlay,
|
|
4042
4061
|
/* @__PURE__ */ jsx("div", { className: "nd:space-y-4 nd:sm:space-y-6 nd:pb-8 nd:sm:pb-12", children: Object.entries(groupedItems).map(([groupName, groupItems]) => /* @__PURE__ */ jsxs("div", { className: "nd:space-y-3", children: [
|
|
4043
4062
|
groupBy !== "NONE" && /* @__PURE__ */ jsxs("h3", { className: "nd:text-sm nd:font-medium nd:text-muted-foreground nd:flex nd:items-center nd:gap-2", children: [
|
|
4044
4063
|
groupName,
|