"use client"
import type { ReactNode } from "react"
import { toast } from "sonner"
import {
CheckSquare,
Copy,
Download,
ExternalLink,
FolderInput,
Link2,
Pencil,
Star,
Trash2,
Undo2,
} from "lucide-react"
import { Icon } from "@iconify/react"
import {
ContextMenuItem,
} from "@/components/ui/context-menu"
import {
DropdownMenuItem,
} from "@/components/ui/dropdown-menu"
import type { useDriveMutations } from "@/lib/api/hooks/use-drive-queries"
import type { DriveFileInfo } from "@/lib/api/types"
import {
guardDriveMenuPointer,
stopDriveMenuEvent,
} from "@/lib/drive/drive-menu-guard"
import { cn } from "@/lib/utils"
import { driveTrashItemKey } from "@/lib/drive/drive-trash"
import { mountCloudSuiteBrand } from "@/lib/drive/cloud-native-open"
export const DRIVE_MENU_ITEM_CLASS =
"gap-3 py-2 text-[#3c4043] focus:text-[#3c4043] dark:text-[#e8eaed] dark:focus:text-[#e8eaed] [&_svg]:text-[#3c4043] dark:[&_svg]:text-[#e8eaed]"
export const DRIVE_MENU_ITEM_DESTRUCTIVE_CLASS =
"gap-3 py-2 text-destructive focus:text-destructive [&_svg]:text-destructive"
const SHEET_ACTION_CLASS =
"flex w-full items-center gap-3 px-4 py-3 text-left text-sm text-[#3c4043] transition-colors hover:bg-accent active:bg-accent/80 dark:text-[#e8eaed] [&_svg]:text-[#3c4043] dark:[&_svg]:text-[#e8eaed]"
export function canShareDriveItem(item: DriveFileInfo) {
return item.type === "file" || item.type === "directory"
}
function DriveMenuItemIcon({ children }: { children: ReactNode }) {
return (
{children}
)
}
type Mutations = ReturnType
type DriveFileMenuActionsProps = {
variant: "dropdown" | "context" | "sheet"
targets: DriveFileInfo[]
isTrash?: boolean
allowShare?: boolean
/** When false, hide rename / move / copy / delete. */
writable?: boolean
/** When true, hide the "Ouvrir" action (e.g. current folder in breadcrumb). */
hideOpen?: boolean
/** When false, hide favorite actions (public share). */
hideFavorite?: boolean
onOpen: () => void
onClose?: () => void
setSharePath: (path: string | null, itemType?: "file" | "directory" | null) => void
mutations: Mutations
onRenameRequest: () => void
onMoveRequest?: () => void
onCopyRequest?: () => void
onDownloadRequest?: () => void
onQuickLinkRequest?: () => void
onEnterSelectionMode?: () => void
}
export function DriveFileMenuActions({
variant,
targets,
isTrash,
allowShare = true,
writable = true,
hideOpen = false,
hideFavorite = false,
onOpen,
onClose,
setSharePath,
mutations,
onRenameRequest,
onMoveRequest,
onCopyRequest,
onDownloadRequest,
onQuickLinkRequest,
onEnterSelectionMode,
}: DriveFileMenuActionsProps) {
const single = targets.length === 1 ? targets[0]! : null
const multi = targets.length > 1
const mountSuiteBrand = single ? mountCloudSuiteBrand(single) : null
const runAsync = async (fn: () => Promise, ok: string, err: string) => {
onClose?.()
try {
await fn()
toast.success(ok)
} catch {
toast.error(err)
}
}
const runMenuAction = (
fn: () => void,
asyncFn?: () => Promise,
ok?: string,
err?: string
) => {
guardDriveMenuPointer()
onClose?.()
if (asyncFn && ok && err) {
void runAsync(asyncFn, ok, err)
return
}
fn()
}
const selectAction = (fn: () => void) => () => {
guardDriveMenuPointer()
onClose?.()
window.setTimeout(fn, 0)
}
const favoriteLabel = targets.every((f) => f.is_favorite)
? "Retirer des favoris"
: "Ajouter aux favoris"
const favoriteAsync = async () => {
for (const f of targets) {
await mutations.favorite.mutateAsync({
path: f.path,
favorite: !f.is_favorite,
})
}
}
const deleteAsync = async () => {
for (const f of targets) {
await mutations.deleteFile.mutateAsync(f.path)
}
}
const restoreAsync = async () => {
for (const f of targets) {
await mutations.restore.mutateAsync(driveTrashItemKey(f))
}
}
const deleteTrashAsync = async () => {
for (const f of targets) {
await mutations.deleteTrash.mutateAsync(driveTrashItemKey(f))
}
}
const actions: Array<{
key: string
label: string
icon: ReactNode
destructive?: boolean
visible: boolean
onSelect: () => void
}> = [
{
key: "open",
label: mountSuiteBrand?.label ?? "Ouvrir",
icon: mountSuiteBrand ? (
) : (
),
visible: !hideOpen && !multi && Boolean(single),
onSelect: () => runMenuAction(() => window.setTimeout(() => onOpen(), 0)),
},
{
key: "select",
label: "Sélectionner",
icon: ,
visible: variant === "sheet" && Boolean(!isTrash && single && onEnterSelectionMode),
onSelect: () =>
runMenuAction(() => {
window.setTimeout(() => onEnterSelectionMode?.(), 0)
}),
},
{
key: "share",
label: "Partager",
icon: ,
visible: Boolean(!isTrash && allowShare && !multi && single && canShareDriveItem(single)),
onSelect: () => runMenuAction(() => setSharePath(single!.path, single!.type)),
},
{
key: "copy",
label: multi ? `Copier vers (${targets.length})` : "Copier vers",
icon: ,
visible: Boolean(writable && !isTrash && onCopyRequest),
onSelect: () =>
runMenuAction(() => {
window.setTimeout(() => onCopyRequest?.(), 0)
}),
},
{
key: "move",
label: multi ? `Déplacer vers (${targets.length})` : "Déplacer vers",
icon: ,
visible: Boolean(writable && !isTrash && onMoveRequest),
onSelect: () =>
runMenuAction(() => {
window.setTimeout(() => onMoveRequest?.(), 0)
}),
},
{
key: "download",
label: "Télécharger",
icon: ,
visible: Boolean(!isTrash && onDownloadRequest),
onSelect: () =>
runMenuAction(() => {
window.setTimeout(() => onDownloadRequest?.(), 0)
}),
},
{
key: "quick-link",
label: "Obtenir le lien",
icon: ,
visible: Boolean(!isTrash && onQuickLinkRequest && single),
onSelect: () =>
runMenuAction(() => {
window.setTimeout(() => onQuickLinkRequest?.(), 0)
}),
},
{
key: "favorite",
label: favoriteLabel,
icon: ,
visible: !isTrash && !hideFavorite,
onSelect: () =>
runMenuAction(
() => {},
favoriteAsync,
favoriteLabel === "Retirer des favoris" ? "Retiré des favoris" : "Ajouté aux favoris",
"Impossible de modifier les favoris"
),
},
{
key: "restore",
label: `Restaurer${multi ? ` (${targets.length})` : ""}`,
icon: ,
visible: Boolean(isTrash),
onSelect: () =>
runMenuAction(
() => {},
restoreAsync,
"Élément(s) restauré(s)",
"Impossible de restaurer"
),
},
{
key: "delete-trash",
label: `Supprimer définitivement${multi ? ` (${targets.length})` : ""}`,
icon: ,
destructive: true,
visible: Boolean(isTrash && writable),
onSelect: () =>
runMenuAction(
() => {},
deleteTrashAsync,
"Élément(s) supprimé(s) définitivement",
"Impossible de supprimer définitivement"
),
},
{
key: "rename",
label: "Renommer",
icon: ,
visible: Boolean(writable && !isTrash && single && !multi),
onSelect: () =>
runMenuAction(() => {
window.setTimeout(() => onRenameRequest(), 0)
}),
},
{
key: "delete",
label: `Supprimer${multi ? ` (${targets.length})` : ""}`,
icon: ,
destructive: true,
visible: writable && !isTrash,
onSelect: () =>
runMenuAction(() => {}, deleteAsync, "Supprimé", "Impossible de supprimer"),
},
]
if (variant === "sheet") {
return (
<>
{actions
.filter((action) => action.visible)
.map((action) => (
))}
>
)
}
if (variant === "context") {
return (
<>
{actions
.filter((action) => action.visible)
.map((action) => (
stopDriveMenuEvent(e)}
onSelect={selectAction(action.onSelect)}
>
{action.icon}
{action.label}
))}
>
)
}
return (
<>
{actions
.filter((action) => action.visible)
.map((action) => (
stopDriveMenuEvent(e)}
onSelect={() => action.onSelect()}
>
{action.icon}
{action.label}
))}
>
)
}