@muhgholy/next-drive 4.23.23 → 4.23.24

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.
@@ -1530,9 +1530,16 @@ var DriveDndContext = createContext({
1530
1530
  var useDriveDnd = () => useContext(DriveDndContext);
1531
1531
  var DriveDndProvider = (props) => {
1532
1532
  const { children } = props;
1533
- const { items, setItems, sortBy, setSortBy, moveItem, callAPI, currentView } = useDrive();
1533
+ const { items, setItems, sortBy, setSortBy, moveItem, callAPI, currentView, selectedFileIds, setSelectedFiles } = useDrive();
1534
1534
  const [dragOverFolderId, setDragOverFolderId] = useState(null);
1535
1535
  const [draggingItemId, setDraggingItemId] = useState(null);
1536
+ const moveSelection = useCallback(async (activeId, targetFolderId) => {
1537
+ const ids = selectedFileIds.includes(activeId) && selectedFileIds.length > 1 ? selectedFileIds : [activeId];
1538
+ if (ids.length === 1) return moveItem(ids[0], targetFolderId);
1539
+ setItems((prev) => prev.filter((i) => !ids.includes(i.id)));
1540
+ setSelectedFiles([]);
1541
+ await callAPI("move", { method: "POST", body: JSON.stringify({ ids, targetFolderId }) });
1542
+ }, [selectedFileIds, moveItem, setItems, setSelectedFiles, callAPI]);
1536
1543
  const sensors = useSensors(
1537
1544
  useSensor(PointerSensor, { activationConstraint: { distance: 8 } }),
1538
1545
  useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
@@ -1567,12 +1574,12 @@ var DriveDndProvider = (props) => {
1567
1574
  if (overId.startsWith("path-")) {
1568
1575
  const pathId = overId.slice(5);
1569
1576
  const targetFolderId = pathId === "root" || pathId === "null" ? "root" : pathId;
1570
- await moveItem(active.id, targetFolderId);
1577
+ await moveSelection(active.id, targetFolderId);
1571
1578
  return;
1572
1579
  }
1573
1580
  const overItem = items.find((i) => i.id === over.id);
1574
1581
  if (overItem?.information.type === "FOLDER") {
1575
- await moveItem(active.id, over.id);
1582
+ await moveSelection(active.id, over.id);
1576
1583
  return;
1577
1584
  }
1578
1585
  const oldIndex = items.findIndex((i) => i.id === active.id);
@@ -1582,7 +1589,7 @@ var DriveDndProvider = (props) => {
1582
1589
  setItems(reordered);
1583
1590
  if (sortBy.field !== "order") setSortBy({ field: "order", order: 1 });
1584
1591
  await callAPI("reorder", { method: "POST", body: JSON.stringify({ ids: reordered.map((i) => i.id) }) });
1585
- }, [items, setItems, sortBy, setSortBy, moveItem, callAPI]);
1592
+ }, [items, setItems, sortBy, setSortBy, moveSelection, callAPI]);
1586
1593
  const enableDnd = currentView === "BROWSE";
1587
1594
  return /* @__PURE__ */ jsx(DriveDndContext.Provider, { value: { dragOverFolderId, draggingItemId }, children: enableDnd ? /* @__PURE__ */ jsx(
1588
1595
  DndContext,
@@ -1948,6 +1955,49 @@ var DriveItemRename = (props) => {
1948
1955
  }
1949
1956
  ) });
1950
1957
  };
1958
+ var useMarquee = (onSelectIds) => {
1959
+ const [box, setBox] = useState(null);
1960
+ const onMouseDown = (e) => {
1961
+ if (e.button !== 0 || e.target.closest("[data-marquee-id]")) return;
1962
+ const container = e.currentTarget;
1963
+ const start = { x: e.clientX, y: e.clientY };
1964
+ let moved = false;
1965
+ const handleMove = (ev) => {
1966
+ if (!moved && Math.abs(ev.clientX - start.x) < 4 && Math.abs(ev.clientY - start.y) < 4) return;
1967
+ moved = true;
1968
+ const left = Math.min(start.x, ev.clientX);
1969
+ const top = Math.min(start.y, ev.clientY);
1970
+ const right = Math.max(start.x, ev.clientX);
1971
+ const bottom = Math.max(start.y, ev.clientY);
1972
+ const cRect = container.getBoundingClientRect();
1973
+ setBox({
1974
+ left: left - cRect.left + container.scrollLeft,
1975
+ top: top - cRect.top + container.scrollTop,
1976
+ width: right - left,
1977
+ height: bottom - top
1978
+ });
1979
+ const ids = [];
1980
+ container.querySelectorAll("[data-marquee-id]").forEach((el) => {
1981
+ const r = el.getBoundingClientRect();
1982
+ if (r.left < right && r.right > left && r.top < bottom && r.bottom > top) ids.push(el.dataset.marqueeId);
1983
+ });
1984
+ onSelectIds(ids);
1985
+ };
1986
+ const handleUp = () => {
1987
+ window.removeEventListener("mousemove", handleMove);
1988
+ window.removeEventListener("mouseup", handleUp);
1989
+ if (!moved) onSelectIds([]);
1990
+ setBox(null);
1991
+ };
1992
+ window.addEventListener("mousemove", handleMove);
1993
+ window.addEventListener("mouseup", handleUp);
1994
+ };
1995
+ const overlay = box ? React3__default.createElement("div", {
1996
+ className: "nd:absolute nd:z-50 nd:rounded-sm nd:border nd:border-primary nd:bg-primary/10 nd:pointer-events-none",
1997
+ style: { left: box.left, top: box.top, width: box.width, height: box.height }
1998
+ }) : null;
1999
+ return { onMouseDown, overlay };
2000
+ };
1951
2001
  var SortableItem = (props) => {
1952
2002
  const { id, children, disabled, isDragOverTarget } = props;
1953
2003
  const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id, disabled });
@@ -1958,7 +2008,7 @@ var SortableItem = (props) => {
1958
2008
  zIndex: isDragging ? 50 : "auto",
1959
2009
  position: "relative"
1960
2010
  };
1961
- return /* @__PURE__ */ jsx("div", { ref: setNodeRef, style, ...attributes, ...listeners, children });
2011
+ return /* @__PURE__ */ jsx("div", { ref: setNodeRef, "data-marquee-id": id, style, ...attributes, ...listeners, children });
1962
2012
  };
1963
2013
  var FileItem = (props) => {
1964
2014
  const { item, isSelected, isDragOver, onSelect, onDoubleClick, onRename, onDelete, onRestore } = props;
@@ -2045,6 +2095,8 @@ var DriveFileGrid = (props) => {
2045
2095
  sortBy,
2046
2096
  selectedFileIds,
2047
2097
  toggleSelected,
2098
+ setSelectedFiles,
2099
+ selectionMode,
2048
2100
  navigateToFolder,
2049
2101
  hasMore,
2050
2102
  loadMore,
@@ -2056,6 +2108,19 @@ var DriveFileGrid = (props) => {
2056
2108
  restoreItem
2057
2109
  } = useDrive();
2058
2110
  const { dragOverFolderId } = useDriveDnd();
2111
+ const { onMouseDown: marqueeMouseDown, overlay: marqueeOverlay } = useMarquee((ids) => {
2112
+ let files = items.filter((i) => ids.includes(i.id)).map((i) => ({
2113
+ id: i.id,
2114
+ file: {
2115
+ name: i.name,
2116
+ mime: i.information.type === "FILE" ? i.information.mime : "",
2117
+ size: i.information.type === "FILE" ? i.information.sizeInBytes : 0
2118
+ }
2119
+ }));
2120
+ if (selectionMode.type === "SINGLE") files = files.slice(0, 1);
2121
+ else if (selectionMode.maxFile) files = files.slice(0, selectionMode.maxFile);
2122
+ setSelectedFiles(files);
2123
+ });
2059
2124
  const [dialogs, setDialogs] = React3__default.useState({ newFolder: false, rename: false, delete: false });
2060
2125
  const [itemToDelete, setItemToDelete] = React3__default.useState(null);
2061
2126
  const [itemToRename, setItemToRename] = React3__default.useState(null);
@@ -2178,7 +2243,8 @@ var DriveFileGrid = (props) => {
2178
2243
  indicatorClassName: "nd:bg-primary"
2179
2244
  }
2180
2245
  ) }),
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: [
2246
+ /* @__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: [
2247
+ marqueeOverlay,
2182
2248
  /* @__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
2249
  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
2250
  groupName,
@@ -3829,7 +3895,7 @@ var SortableItem2 = (props) => {
3829
3895
  zIndex: isDragging ? 50 : "auto",
3830
3896
  position: "relative"
3831
3897
  };
3832
- return /* @__PURE__ */ jsx("div", { ref: setNodeRef, style, ...attributes, ...listeners, children });
3898
+ return /* @__PURE__ */ jsx("div", { ref: setNodeRef, "data-marquee-id": id, style, ...attributes, ...listeners, children });
3833
3899
  };
3834
3900
  var DriveExplorer = (props) => {
3835
3901
  const { onItemClick, onItemDoubleClick, mimeFilter, className, selectableFolders = false } = props;
@@ -3846,6 +3912,8 @@ var DriveExplorer = (props) => {
3846
3912
  setSortBy,
3847
3913
  selectedFileIds,
3848
3914
  toggleSelected,
3915
+ setSelectedFiles,
3916
+ selectionMode,
3849
3917
  navigateToFolder,
3850
3918
  hasMore,
3851
3919
  loadMore,
@@ -3875,6 +3943,13 @@ var DriveExplorer = (props) => {
3875
3943
  const handleDragStart = (event) => {
3876
3944
  setDraggingItemId(event.active.id);
3877
3945
  };
3946
+ const moveSelection = async (activeId, targetFolderId) => {
3947
+ const ids = selectedFileIds.includes(activeId) && selectedFileIds.length > 1 ? selectedFileIds : [activeId];
3948
+ if (ids.length === 1) return moveItem(ids[0], targetFolderId);
3949
+ setItems((prev) => prev.filter((i) => !ids.includes(i.id)));
3950
+ setSelectedFiles([]);
3951
+ await callAPI("move", { method: "POST", body: JSON.stringify({ ids, targetFolderId }) });
3952
+ };
3878
3953
  const handleDragOver = (event) => {
3879
3954
  const { over } = event;
3880
3955
  if (!over) {
@@ -3898,13 +3973,13 @@ var DriveExplorer = (props) => {
3898
3973
  const overId = over.id;
3899
3974
  if (overId.startsWith("path-")) {
3900
3975
  const targetFolderId = overId.replace("path-", "");
3901
- await moveItem(active.id, targetFolderId === "root" ? "root" : targetFolderId);
3976
+ await moveSelection(active.id, targetFolderId === "root" ? "root" : targetFolderId);
3902
3977
  return;
3903
3978
  }
3904
3979
  const overItem = items.find((i) => i.id === over.id);
3905
3980
  const activeItem = items.find((i) => i.id === active.id);
3906
3981
  if (overItem?.information.type === "FOLDER" && activeItem) {
3907
- await moveItem(active.id, over.id);
3982
+ await moveSelection(active.id, over.id);
3908
3983
  return;
3909
3984
  }
3910
3985
  setItems((prevItems) => {
@@ -4008,6 +4083,19 @@ var DriveExplorer = (props) => {
4008
4083
  } else onItemDoubleClick?.(item);
4009
4084
  };
4010
4085
  const enableDrag = currentView === "BROWSE";
4086
+ const { onMouseDown: marqueeMouseDown, overlay: marqueeOverlay } = useMarquee((ids) => {
4087
+ let files = items.filter((i) => ids.includes(i.id)).map((i) => ({
4088
+ id: i.id,
4089
+ file: {
4090
+ name: i.name,
4091
+ mime: i.information.type === "FILE" ? i.information.mime : "",
4092
+ size: i.information.type === "FILE" ? i.information.sizeInBytes : 0
4093
+ }
4094
+ }));
4095
+ if (selectionMode.type === "SINGLE") files = files.slice(0, 1);
4096
+ else if (selectionMode.maxFile) files = files.slice(0, selectionMode.maxFile);
4097
+ setSelectedFiles(files);
4098
+ });
4011
4099
  const stateContent = (() => {
4012
4100
  if (isLoading && items.length === 0) {
4013
4101
  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 +4126,8 @@ var DriveExplorer = (props) => {
4038
4126
  ] }),
4039
4127
  /* @__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
4128
  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: [
4129
+ /* @__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: [
4130
+ marqueeOverlay,
4042
4131
  /* @__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
4132
  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
4133
  groupName,