@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.cjs
CHANGED
|
@@ -4,6 +4,7 @@ var chunkHQTC3554_cjs = require('../chunk-HQTC3554.cjs');
|
|
|
4
4
|
var chunkA65ZAA2Z_cjs = require('../chunk-A65ZAA2Z.cjs');
|
|
5
5
|
var React3 = require('react');
|
|
6
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
var tus = require('tus-js-client');
|
|
7
8
|
var reactSlot = require('@radix-ui/react-slot');
|
|
8
9
|
var classVarianceAuthority = require('class-variance-authority');
|
|
9
10
|
var ProgressPrimitive = require('@radix-ui/react-progress');
|
|
@@ -37,6 +38,7 @@ function _interopNamespace(e) {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
var React3__namespace = /*#__PURE__*/_interopNamespace(React3);
|
|
41
|
+
var tus__namespace = /*#__PURE__*/_interopNamespace(tus);
|
|
40
42
|
var ProgressPrimitive__namespace = /*#__PURE__*/_interopNamespace(ProgressPrimitive);
|
|
41
43
|
var SheetPrimitive__namespace = /*#__PURE__*/_interopNamespace(SheetPrimitive);
|
|
42
44
|
var DropdownMenuPrimitive__namespace = /*#__PURE__*/_interopNamespace(DropdownMenuPrimitive);
|
|
@@ -332,8 +334,6 @@ var useDrive = () => {
|
|
|
332
334
|
if (!context) throw new Error("useDrive must be used within a DriveProvider");
|
|
333
335
|
return context;
|
|
334
336
|
};
|
|
335
|
-
|
|
336
|
-
// src/client/upload.ts
|
|
337
337
|
var MAX_CONCURRENT_UPLOADS = 2;
|
|
338
338
|
var getChunkSize = (fileSize) => {
|
|
339
339
|
if (fileSize < 50 * 1024 * 1024) return 2 * 1024 * 1024;
|
|
@@ -367,161 +367,92 @@ var uploadFile = (options) => {
|
|
|
367
367
|
onComplete
|
|
368
368
|
} = options;
|
|
369
369
|
const id = options.id ?? generateId();
|
|
370
|
-
const endpoint = apiEndpoint.replace(/\/$/, "")
|
|
371
|
-
const uploadUrl = `${endpoint}?action=upload`;
|
|
372
|
-
const controller = new AbortController();
|
|
373
|
-
let activeXhr = null;
|
|
374
|
-
let driveId;
|
|
375
|
-
let completedItem;
|
|
376
|
-
let cancelled = false;
|
|
377
|
-
const totalBytes = file.size;
|
|
370
|
+
const endpoint = `${apiEndpoint.replace(/\/$/, "")}?action=upload`;
|
|
378
371
|
const chunkSize = getChunkSize(file.size);
|
|
379
|
-
const totalChunks = Math.ceil(file.size / chunkSize);
|
|
372
|
+
const totalChunks = Math.max(1, Math.ceil(file.size / chunkSize));
|
|
380
373
|
const log = (type, message) => onLog?.({ id, type, message, timestamp: Date.now() });
|
|
381
|
-
|
|
382
|
-
const percent = totalBytes === 0 ? 100 : Math.min(100, Math.round(uploadedBytes / totalBytes * 100));
|
|
383
|
-
onProgress?.({ id, percent, uploadedBytes, totalBytes, currentChunk, totalChunks });
|
|
374
|
+
let settle = () => {
|
|
384
375
|
};
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
return;
|
|
376
|
+
const promise = new Promise((resolve) => {
|
|
377
|
+
settle = resolve;
|
|
378
|
+
});
|
|
379
|
+
let completedItem;
|
|
380
|
+
let cancelled = false;
|
|
381
|
+
const metadata = {
|
|
382
|
+
filename: file.name,
|
|
383
|
+
filetype: file.type || "application/octet-stream",
|
|
384
|
+
folderId: folderId || "root"
|
|
385
|
+
};
|
|
386
|
+
if (conflictAction) metadata.conflictAction = conflictAction;
|
|
387
|
+
const upload = new tus__namespace.Upload(file, {
|
|
388
|
+
endpoint,
|
|
389
|
+
chunkSize,
|
|
390
|
+
// ** Retry with backoff. On a dropped connection tus first re-checks the server offset (HEAD)
|
|
391
|
+
// ** and resumes from there — this is what prevents the "session expired" failures at ~100%.
|
|
392
|
+
retryDelays: [0, 1e3, 3e3, 5e3, 1e4],
|
|
393
|
+
removeFingerprintOnSuccess: true,
|
|
394
|
+
metadata,
|
|
395
|
+
headers: accountId ? { "x-drive-account": accountId } : {},
|
|
396
|
+
onBeforeRequest(req) {
|
|
397
|
+
if (withCredentials) {
|
|
398
|
+
const xhr = req.getUnderlyingObject();
|
|
399
|
+
if (xhr && "withCredentials" in xhr) xhr.withCredentials = true;
|
|
411
400
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}
|
|
419
|
-
log("success", `Chunk uploaded successfully`);
|
|
420
|
-
resolve([true, data.data, false]);
|
|
421
|
-
} catch {
|
|
422
|
-
log("error", `Failed to parse response: ${text.slice(0, 100)}`);
|
|
423
|
-
resolve([false, "Invalid server response", true]);
|
|
401
|
+
},
|
|
402
|
+
onShouldRetry(err) {
|
|
403
|
+
const status = err?.originalResponse?.getStatus?.() ?? 0;
|
|
404
|
+
if ([400, 401, 403, 413].includes(status)) {
|
|
405
|
+
log("error", `Non-retryable error (status ${status})`);
|
|
406
|
+
return false;
|
|
424
407
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
for (let i = 0; i < totalChunks; i++) {
|
|
446
|
-
if (controller.signal.aborted) throw new Error("Cancelled");
|
|
447
|
-
log("info", `Processing chunk ${i + 1}/${totalChunks}`);
|
|
448
|
-
const start = i * chunkSize;
|
|
449
|
-
const end = Math.min(start + chunkSize, file.size);
|
|
450
|
-
const chunk = file.slice(start, end);
|
|
451
|
-
const formData = new FormData();
|
|
452
|
-
formData.append("chunk", chunk);
|
|
453
|
-
formData.append("chunkIndex", String(i));
|
|
454
|
-
formData.append("totalChunks", String(totalChunks));
|
|
455
|
-
formData.append("fileName", file.name);
|
|
456
|
-
formData.append("fileSize", String(file.size));
|
|
457
|
-
formData.append("fileType", file.type);
|
|
458
|
-
formData.append("folderId", folderId || "root");
|
|
459
|
-
if (conflictAction) formData.append("conflictAction", conflictAction);
|
|
460
|
-
if (driveId) formData.append("driveId", driveId);
|
|
461
|
-
let attempts = 0;
|
|
462
|
-
let success = false;
|
|
463
|
-
while (!success && attempts < 3 && !controller.signal.aborted) {
|
|
464
|
-
const [ok, result, canRetry] = await sendChunk(i, formData, start);
|
|
465
|
-
if (ok) {
|
|
466
|
-
success = true;
|
|
467
|
-
emitProgress(i + 1, end);
|
|
468
|
-
if (result.type === "UPLOAD_STARTED" && result.driveId) {
|
|
469
|
-
driveId = result.driveId;
|
|
470
|
-
log("success", `Upload session started with ID: ${driveId}`);
|
|
471
|
-
} else if (result.type === "UPLOAD_COMPLETE") {
|
|
472
|
-
if (result.driveId) driveId = result.driveId;
|
|
473
|
-
log("success", `Upload completed successfully`);
|
|
474
|
-
completedItem = result.item;
|
|
475
|
-
}
|
|
476
|
-
} else {
|
|
477
|
-
if (result === "Cancelled") throw new Error("Cancelled");
|
|
478
|
-
if (!canRetry) {
|
|
479
|
-
log("error", `Non-retryable error: ${result}`);
|
|
480
|
-
throw new Error(result);
|
|
481
|
-
}
|
|
482
|
-
attempts++;
|
|
483
|
-
log("warning", `Retry attempt ${attempts}/3 after error`);
|
|
484
|
-
if (attempts === 3) {
|
|
485
|
-
log("error", `Max retry attempts reached: ${result}`);
|
|
486
|
-
throw new Error(result);
|
|
487
|
-
}
|
|
488
|
-
await new Promise((r) => setTimeout(r, 1e3 * attempts));
|
|
489
|
-
}
|
|
408
|
+
log("warning", `Upload interrupted (status ${status}), retrying`);
|
|
409
|
+
return true;
|
|
410
|
+
},
|
|
411
|
+
onProgress(bytesUploaded, bytesTotal) {
|
|
412
|
+
const percent = bytesTotal === 0 ? 100 : Math.min(100, Math.round(bytesUploaded / bytesTotal * 100));
|
|
413
|
+
onProgress?.({
|
|
414
|
+
id,
|
|
415
|
+
percent,
|
|
416
|
+
uploadedBytes: bytesUploaded,
|
|
417
|
+
totalBytes: bytesTotal,
|
|
418
|
+
currentChunk: Math.min(totalChunks, Math.floor(bytesUploaded / chunkSize)),
|
|
419
|
+
totalChunks
|
|
420
|
+
});
|
|
421
|
+
},
|
|
422
|
+
onAfterResponse(_req, res) {
|
|
423
|
+
const header = res.getHeader("X-Drive-Item");
|
|
424
|
+
if (header) {
|
|
425
|
+
try {
|
|
426
|
+
completedItem = JSON.parse(decodeURIComponent(header));
|
|
427
|
+
} catch {
|
|
490
428
|
}
|
|
491
|
-
if (!success && controller.signal.aborted) throw new Error("Cancelled");
|
|
492
429
|
}
|
|
493
|
-
|
|
494
|
-
|
|
430
|
+
},
|
|
431
|
+
onSuccess() {
|
|
432
|
+
log("success", "Upload completed successfully");
|
|
495
433
|
if (completedItem !== void 0) onComplete?.(completedItem);
|
|
496
|
-
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
log("error", `Upload failed: ${errorMessage}`);
|
|
504
|
-
return { id, status: "error", driveId: driveId ?? null, error: errorMessage };
|
|
434
|
+
settle({ id, status: "complete", driveId: completedItem?.id ?? "", file: toDriveFile(completedItem), item: completedItem });
|
|
435
|
+
},
|
|
436
|
+
onError(error) {
|
|
437
|
+
const message = error instanceof Error ? error.message : "Upload failed";
|
|
438
|
+
log("error", `Upload failed: ${message}`);
|
|
439
|
+
settle({ id, status: "error", driveId: null, error: message });
|
|
505
440
|
}
|
|
506
|
-
};
|
|
441
|
+
});
|
|
507
442
|
const cancel = () => {
|
|
508
443
|
if (cancelled) return;
|
|
509
444
|
cancelled = true;
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
fetch(`${endpoint}?action=cancel&id=${driveId}`, {
|
|
514
|
-
method: "POST",
|
|
515
|
-
credentials: withCredentials ? "include" : "same-origin"
|
|
516
|
-
}).catch(() => {
|
|
517
|
-
});
|
|
518
|
-
}
|
|
445
|
+
upload.abort(true).catch(() => {
|
|
446
|
+
});
|
|
447
|
+
settle({ id, status: "cancelled", driveId: null });
|
|
519
448
|
};
|
|
520
449
|
if (signal) {
|
|
521
450
|
if (signal.aborted) cancel();
|
|
522
451
|
else signal.addEventListener("abort", cancel, { once: true });
|
|
523
452
|
}
|
|
524
|
-
|
|
453
|
+
log("info", `Starting upload: ${file.name} (${file.size} bytes)`);
|
|
454
|
+
upload.start();
|
|
455
|
+
return { id, promise, cancel };
|
|
525
456
|
};
|
|
526
457
|
var uploadFiles = (files, options) => {
|
|
527
458
|
const { concurrency = MAX_CONCURRENT_UPLOADS, onFileComplete, ...base } = options;
|
|
@@ -1556,9 +1487,16 @@ var DriveDndContext = React3.createContext({
|
|
|
1556
1487
|
var useDriveDnd = () => React3.useContext(DriveDndContext);
|
|
1557
1488
|
var DriveDndProvider = (props) => {
|
|
1558
1489
|
const { children } = props;
|
|
1559
|
-
const { items, setItems, sortBy, setSortBy, moveItem, callAPI, currentView } = useDrive();
|
|
1490
|
+
const { items, setItems, sortBy, setSortBy, moveItem, callAPI, currentView, selectedFileIds, setSelectedFiles } = useDrive();
|
|
1560
1491
|
const [dragOverFolderId, setDragOverFolderId] = React3.useState(null);
|
|
1561
1492
|
const [draggingItemId, setDraggingItemId] = React3.useState(null);
|
|
1493
|
+
const moveSelection = React3.useCallback(async (activeId, targetFolderId) => {
|
|
1494
|
+
const ids = selectedFileIds.includes(activeId) && selectedFileIds.length > 1 ? selectedFileIds : [activeId];
|
|
1495
|
+
if (ids.length === 1) return moveItem(ids[0], targetFolderId);
|
|
1496
|
+
setItems((prev) => prev.filter((i) => !ids.includes(i.id)));
|
|
1497
|
+
setSelectedFiles([]);
|
|
1498
|
+
await callAPI("move", { method: "POST", body: JSON.stringify({ ids, targetFolderId }) });
|
|
1499
|
+
}, [selectedFileIds, moveItem, setItems, setSelectedFiles, callAPI]);
|
|
1562
1500
|
const sensors = core.useSensors(
|
|
1563
1501
|
core.useSensor(core.PointerSensor, { activationConstraint: { distance: 8 } }),
|
|
1564
1502
|
core.useSensor(core.KeyboardSensor, { coordinateGetter: sortable.sortableKeyboardCoordinates })
|
|
@@ -1593,12 +1531,12 @@ var DriveDndProvider = (props) => {
|
|
|
1593
1531
|
if (overId.startsWith("path-")) {
|
|
1594
1532
|
const pathId = overId.slice(5);
|
|
1595
1533
|
const targetFolderId = pathId === "root" || pathId === "null" ? "root" : pathId;
|
|
1596
|
-
await
|
|
1534
|
+
await moveSelection(active.id, targetFolderId);
|
|
1597
1535
|
return;
|
|
1598
1536
|
}
|
|
1599
1537
|
const overItem = items.find((i) => i.id === over.id);
|
|
1600
1538
|
if (overItem?.information.type === "FOLDER") {
|
|
1601
|
-
await
|
|
1539
|
+
await moveSelection(active.id, over.id);
|
|
1602
1540
|
return;
|
|
1603
1541
|
}
|
|
1604
1542
|
const oldIndex = items.findIndex((i) => i.id === active.id);
|
|
@@ -1608,7 +1546,7 @@ var DriveDndProvider = (props) => {
|
|
|
1608
1546
|
setItems(reordered);
|
|
1609
1547
|
if (sortBy.field !== "order") setSortBy({ field: "order", order: 1 });
|
|
1610
1548
|
await callAPI("reorder", { method: "POST", body: JSON.stringify({ ids: reordered.map((i) => i.id) }) });
|
|
1611
|
-
}, [items, setItems, sortBy, setSortBy,
|
|
1549
|
+
}, [items, setItems, sortBy, setSortBy, moveSelection, callAPI]);
|
|
1612
1550
|
const enableDnd = currentView === "BROWSE";
|
|
1613
1551
|
return /* @__PURE__ */ jsxRuntime.jsx(DriveDndContext.Provider, { value: { dragOverFolderId, draggingItemId }, children: enableDnd ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1614
1552
|
core.DndContext,
|
|
@@ -1974,6 +1912,49 @@ var DriveItemRename = (props) => {
|
|
|
1974
1912
|
}
|
|
1975
1913
|
) });
|
|
1976
1914
|
};
|
|
1915
|
+
var useMarquee = (onSelectIds) => {
|
|
1916
|
+
const [box, setBox] = React3.useState(null);
|
|
1917
|
+
const onMouseDown = (e) => {
|
|
1918
|
+
if (e.button !== 0 || e.target.closest("[data-marquee-id]")) return;
|
|
1919
|
+
const container = e.currentTarget;
|
|
1920
|
+
const start = { x: e.clientX, y: e.clientY };
|
|
1921
|
+
let moved = false;
|
|
1922
|
+
const handleMove = (ev) => {
|
|
1923
|
+
if (!moved && Math.abs(ev.clientX - start.x) < 4 && Math.abs(ev.clientY - start.y) < 4) return;
|
|
1924
|
+
moved = true;
|
|
1925
|
+
const left = Math.min(start.x, ev.clientX);
|
|
1926
|
+
const top = Math.min(start.y, ev.clientY);
|
|
1927
|
+
const right = Math.max(start.x, ev.clientX);
|
|
1928
|
+
const bottom = Math.max(start.y, ev.clientY);
|
|
1929
|
+
const cRect = container.getBoundingClientRect();
|
|
1930
|
+
setBox({
|
|
1931
|
+
left: left - cRect.left + container.scrollLeft,
|
|
1932
|
+
top: top - cRect.top + container.scrollTop,
|
|
1933
|
+
width: right - left,
|
|
1934
|
+
height: bottom - top
|
|
1935
|
+
});
|
|
1936
|
+
const ids = [];
|
|
1937
|
+
container.querySelectorAll("[data-marquee-id]").forEach((el) => {
|
|
1938
|
+
const r = el.getBoundingClientRect();
|
|
1939
|
+
if (r.left < right && r.right > left && r.top < bottom && r.bottom > top) ids.push(el.dataset.marqueeId);
|
|
1940
|
+
});
|
|
1941
|
+
onSelectIds(ids);
|
|
1942
|
+
};
|
|
1943
|
+
const handleUp = () => {
|
|
1944
|
+
window.removeEventListener("mousemove", handleMove);
|
|
1945
|
+
window.removeEventListener("mouseup", handleUp);
|
|
1946
|
+
if (!moved) onSelectIds([]);
|
|
1947
|
+
setBox(null);
|
|
1948
|
+
};
|
|
1949
|
+
window.addEventListener("mousemove", handleMove);
|
|
1950
|
+
window.addEventListener("mouseup", handleUp);
|
|
1951
|
+
};
|
|
1952
|
+
const overlay = box ? React3__namespace.default.createElement("div", {
|
|
1953
|
+
className: "nd:absolute nd:z-50 nd:rounded-sm nd:border nd:border-primary nd:bg-primary/10 nd:pointer-events-none",
|
|
1954
|
+
style: { left: box.left, top: box.top, width: box.width, height: box.height }
|
|
1955
|
+
}) : null;
|
|
1956
|
+
return { onMouseDown, overlay };
|
|
1957
|
+
};
|
|
1977
1958
|
var SortableItem = (props) => {
|
|
1978
1959
|
const { id, children, disabled, isDragOverTarget } = props;
|
|
1979
1960
|
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = sortable.useSortable({ id, disabled });
|
|
@@ -1984,7 +1965,7 @@ var SortableItem = (props) => {
|
|
|
1984
1965
|
zIndex: isDragging ? 50 : "auto",
|
|
1985
1966
|
position: "relative"
|
|
1986
1967
|
};
|
|
1987
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: setNodeRef, style, ...attributes, ...listeners, children });
|
|
1968
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: setNodeRef, "data-marquee-id": id, style, ...attributes, ...listeners, children });
|
|
1988
1969
|
};
|
|
1989
1970
|
var FileItem = (props) => {
|
|
1990
1971
|
const { item, isSelected, isDragOver, onSelect, onDoubleClick, onRename, onDelete, onRestore } = props;
|
|
@@ -2071,6 +2052,8 @@ var DriveFileGrid = (props) => {
|
|
|
2071
2052
|
sortBy,
|
|
2072
2053
|
selectedFileIds,
|
|
2073
2054
|
toggleSelected,
|
|
2055
|
+
setSelectedFiles,
|
|
2056
|
+
selectionMode,
|
|
2074
2057
|
navigateToFolder,
|
|
2075
2058
|
hasMore,
|
|
2076
2059
|
loadMore,
|
|
@@ -2082,6 +2065,19 @@ var DriveFileGrid = (props) => {
|
|
|
2082
2065
|
restoreItem
|
|
2083
2066
|
} = useDrive();
|
|
2084
2067
|
const { dragOverFolderId } = useDriveDnd();
|
|
2068
|
+
const { onMouseDown: marqueeMouseDown, overlay: marqueeOverlay } = useMarquee((ids) => {
|
|
2069
|
+
let files = items.filter((i) => ids.includes(i.id)).map((i) => ({
|
|
2070
|
+
id: i.id,
|
|
2071
|
+
file: {
|
|
2072
|
+
name: i.name,
|
|
2073
|
+
mime: i.information.type === "FILE" ? i.information.mime : "",
|
|
2074
|
+
size: i.information.type === "FILE" ? i.information.sizeInBytes : 0
|
|
2075
|
+
}
|
|
2076
|
+
}));
|
|
2077
|
+
if (selectionMode.type === "SINGLE") files = files.slice(0, 1);
|
|
2078
|
+
else if (selectionMode.maxFile) files = files.slice(0, selectionMode.maxFile);
|
|
2079
|
+
setSelectedFiles(files);
|
|
2080
|
+
});
|
|
2085
2081
|
const [dialogs, setDialogs] = React3__namespace.default.useState({ newFolder: false, rename: false, delete: false });
|
|
2086
2082
|
const [itemToDelete, setItemToDelete] = React3__namespace.default.useState(null);
|
|
2087
2083
|
const [itemToRename, setItemToRename] = React3__namespace.default.useState(null);
|
|
@@ -2204,7 +2200,8 @@ var DriveFileGrid = (props) => {
|
|
|
2204
2200
|
indicatorClassName: "nd:bg-primary"
|
|
2205
2201
|
}
|
|
2206
2202
|
) }),
|
|
2207
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: chunkHQTC3554_cjs.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: [
|
|
2203
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: chunkHQTC3554_cjs.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: [
|
|
2204
|
+
marqueeOverlay,
|
|
2208
2205
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "nd:space-y-4 nd:sm:space-y-6", children: Object.entries(groupedItems).map(([groupName, groupItems]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2209
2206
|
groupBy !== "NONE" && /* @__PURE__ */ jsxRuntime.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: [
|
|
2210
2207
|
groupName,
|
|
@@ -3855,7 +3852,7 @@ var SortableItem2 = (props) => {
|
|
|
3855
3852
|
zIndex: isDragging ? 50 : "auto",
|
|
3856
3853
|
position: "relative"
|
|
3857
3854
|
};
|
|
3858
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: setNodeRef, style, ...attributes, ...listeners, children });
|
|
3855
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: setNodeRef, "data-marquee-id": id, style, ...attributes, ...listeners, children });
|
|
3859
3856
|
};
|
|
3860
3857
|
var DriveExplorer = (props) => {
|
|
3861
3858
|
const { onItemClick, onItemDoubleClick, mimeFilter, className, selectableFolders = false } = props;
|
|
@@ -3872,6 +3869,8 @@ var DriveExplorer = (props) => {
|
|
|
3872
3869
|
setSortBy,
|
|
3873
3870
|
selectedFileIds,
|
|
3874
3871
|
toggleSelected,
|
|
3872
|
+
setSelectedFiles,
|
|
3873
|
+
selectionMode,
|
|
3875
3874
|
navigateToFolder,
|
|
3876
3875
|
hasMore,
|
|
3877
3876
|
loadMore,
|
|
@@ -3901,6 +3900,13 @@ var DriveExplorer = (props) => {
|
|
|
3901
3900
|
const handleDragStart = (event) => {
|
|
3902
3901
|
setDraggingItemId(event.active.id);
|
|
3903
3902
|
};
|
|
3903
|
+
const moveSelection = async (activeId, targetFolderId) => {
|
|
3904
|
+
const ids = selectedFileIds.includes(activeId) && selectedFileIds.length > 1 ? selectedFileIds : [activeId];
|
|
3905
|
+
if (ids.length === 1) return moveItem(ids[0], targetFolderId);
|
|
3906
|
+
setItems((prev) => prev.filter((i) => !ids.includes(i.id)));
|
|
3907
|
+
setSelectedFiles([]);
|
|
3908
|
+
await callAPI("move", { method: "POST", body: JSON.stringify({ ids, targetFolderId }) });
|
|
3909
|
+
};
|
|
3904
3910
|
const handleDragOver = (event) => {
|
|
3905
3911
|
const { over } = event;
|
|
3906
3912
|
if (!over) {
|
|
@@ -3924,13 +3930,13 @@ var DriveExplorer = (props) => {
|
|
|
3924
3930
|
const overId = over.id;
|
|
3925
3931
|
if (overId.startsWith("path-")) {
|
|
3926
3932
|
const targetFolderId = overId.replace("path-", "");
|
|
3927
|
-
await
|
|
3933
|
+
await moveSelection(active.id, targetFolderId === "root" ? "root" : targetFolderId);
|
|
3928
3934
|
return;
|
|
3929
3935
|
}
|
|
3930
3936
|
const overItem = items.find((i) => i.id === over.id);
|
|
3931
3937
|
const activeItem = items.find((i) => i.id === active.id);
|
|
3932
3938
|
if (overItem?.information.type === "FOLDER" && activeItem) {
|
|
3933
|
-
await
|
|
3939
|
+
await moveSelection(active.id, over.id);
|
|
3934
3940
|
return;
|
|
3935
3941
|
}
|
|
3936
3942
|
setItems((prevItems) => {
|
|
@@ -4034,6 +4040,19 @@ var DriveExplorer = (props) => {
|
|
|
4034
4040
|
} else onItemDoubleClick?.(item);
|
|
4035
4041
|
};
|
|
4036
4042
|
const enableDrag = currentView === "BROWSE";
|
|
4043
|
+
const { onMouseDown: marqueeMouseDown, overlay: marqueeOverlay } = useMarquee((ids) => {
|
|
4044
|
+
let files = items.filter((i) => ids.includes(i.id)).map((i) => ({
|
|
4045
|
+
id: i.id,
|
|
4046
|
+
file: {
|
|
4047
|
+
name: i.name,
|
|
4048
|
+
mime: i.information.type === "FILE" ? i.information.mime : "",
|
|
4049
|
+
size: i.information.type === "FILE" ? i.information.sizeInBytes : 0
|
|
4050
|
+
}
|
|
4051
|
+
}));
|
|
4052
|
+
if (selectionMode.type === "SINGLE") files = files.slice(0, 1);
|
|
4053
|
+
else if (selectionMode.maxFile) files = files.slice(0, selectionMode.maxFile);
|
|
4054
|
+
setSelectedFiles(files);
|
|
4055
|
+
});
|
|
4037
4056
|
const stateContent = (() => {
|
|
4038
4057
|
if (isLoading && items.length === 0) {
|
|
4039
4058
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "nd:flex nd:items-center nd:justify-center nd:py-12 nd:flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "nd:size-6 nd:animate-spin nd:text-muted-foreground" }) });
|
|
@@ -4064,7 +4083,8 @@ var DriveExplorer = (props) => {
|
|
|
4064
4083
|
] }),
|
|
4065
4084
|
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx(DrivePathBar, {}) }),
|
|
4066
4085
|
stateContent || /* @__PURE__ */ jsxRuntime.jsxs(ContextMenu, { children: [
|
|
4067
|
-
/* @__PURE__ */ jsxRuntime.jsx(ContextMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: chunkHQTC3554_cjs.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: [
|
|
4086
|
+
/* @__PURE__ */ jsxRuntime.jsx(ContextMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: chunkHQTC3554_cjs.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: [
|
|
4087
|
+
marqueeOverlay,
|
|
4068
4088
|
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs("div", { className: "nd:space-y-3", children: [
|
|
4069
4089
|
groupBy !== "NONE" && /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "nd:text-sm nd:font-medium nd:text-muted-foreground nd:flex nd:items-center nd:gap-2", children: [
|
|
4070
4090
|
groupName,
|