SPB Git forge

spb/spb-cloud

Public
10commits 1branches 0releases
3.2 MBsize
maindefault branch
2 h agolast push
JavaScript 56.7% TypeScript 42.6%
3.8 KB · 113 lines typescript
Raw Blame History
1/**2 * Glisser-déposer interne SPB Cloud (fichiers ↔ dossiers) + événements de synchro.3 */4import toast from "react-hot-toast";56export const DND_FILES = "application/x-spb-files";7export const DND_FOLDER = "application/x-spb-folder";8export const FOLDERS_CHANGED_EVENT = "spb:folders-changed";910/** Cible de dépôt : "root" ou id de dossier. */11export type DropTarget = string;1213export function notifyFoldersChanged() {14  if (typeof window !== "undefined") window.dispatchEvent(new Event(FOLDERS_CHANGED_EVENT));15}1617export function hasInternalDrag(dt: DataTransfer | null): boolean {18  if (!dt) return false;19  const types = Array.from(dt.types || []);20  return types.includes(DND_FILES) || types.includes(DND_FOLDER);21}2223export function hasNativeFiles(dt: DataTransfer | null): boolean {24  if (!dt) return false;25  const types = Array.from(dt.types || []);26  return types.includes("Files") && !hasInternalDrag(dt);27}2829export function readFileIds(dt: DataTransfer | null): string[] {30  try {31    const raw = dt?.getData(DND_FILES);32    if (!raw) return [];33    const arr = JSON.parse(raw);34    return Array.isArray(arr) ? arr.filter((x) => typeof x === "string") : [];35  } catch {36    return [];37  }38}3940export function readFolderId(dt: DataTransfer | null): string | null {41  try {42    const raw = dt?.getData(DND_FOLDER);43    return raw || null;44  } catch {45    return null;46  }47}4849/** Image fantôme personnalisée « N fichiers ». */50export function setDragGhost(e: React.DragEvent, label: string) {51  try {52    const ghost = document.createElement("div");53    ghost.textContent = label;54    ghost.style.cssText =55      "position:fixed;top:-1000px;left:-1000px;padding:8px 14px;border-radius:12px;font:600 13px system-ui,sans-serif;color:#fff;" +56      "background:linear-gradient(135deg,rgb(var(--accent-500)),rgb(var(--accent-700)));box-shadow:0 8px 24px rgb(0 0 0/.25);white-space:nowrap;";57    document.body.appendChild(ghost);58    e.dataTransfer.setDragImage(ghost, 16, 16);59    setTimeout(() => ghost.remove(), 0);60  } catch {}61}6263/** Déplace des fichiers vers un dossier ("root" accepté). Retourne le nombre déplacé. */64export async function moveFiles(ids: string[], target: DropTarget): Promise<number> {65  const results = await Promise.all(66    ids.map((id) =>67      fetch(`/api/files/${id}`, {68        method: "PATCH",69        headers: { "Content-Type": "application/json" },70        body: JSON.stringify({ folderId: target }),71      }).then((r) => r.ok).catch(() => false)72    )73  );74  const ok = results.filter(Boolean).length;75  if (ok === ids.length) toast.success(ok > 1 ? `${ok} fichiers déplacés` : "Fichier déplacé");76  else if (ok > 0) toast.error(`${ids.length - ok} fichier(s) non déplacé(s)`);77  else toast.error("Déplacement impossible");78  return ok;79}8081/** Déplace un dossier (anti-cycle géré côté serveur). */82export async function moveFolder(id: string, target: DropTarget): Promise<boolean> {83  if (id === target) return false;84  try {85    const res = await fetch(`/api/folders/${id}`, {86      method: "PATCH",87      headers: { "Content-Type": "application/json" },88      body: JSON.stringify({ parentId: target }),89    });90    if (res.ok) {91      toast.success("Dossier déplacé");92      notifyFoldersChanged();93      return true;94    }95    const d = await res.json().catch(() => ({}));96    toast.error(d.error || "Déplacement impossible");97  } catch {98    toast.error("Erreur réseau");99  }100  return false;101}102103/**104 * Gère un dépôt interne sur une cible. Retourne true si quelque chose a été déplacé.105 */106export async function handleInternalDrop(dt: DataTransfer | null, target: DropTarget): Promise<boolean> {107  const folderId = readFolderId(dt);108  if (folderId) return moveFolder(folderId, target);109  const ids = readFileIds(dt);110  if (ids.length) return (await moveFiles(ids, target)) > 0;111  return false;112}113