Files
drive/apps/drive-web/src/directories/directory-path-breadcrumb.tsx

123 lines
2.9 KiB
TypeScript

import { Link } from "@tanstack/react-router"
import type { PrimitiveAtom } from "jotai"
import { atom } from "jotai"
import { Fragment } from "react"
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip"
import type { FileDragInfo } from "@/files/use-file-drop"
import { useFileDrop } from "@/files/use-file-drop"
import { cn } from "@/lib/utils"
import type { DirectoryInfoWithPath } from "@/vfs/vfs"
import type { PathSegment } from "../lib/path"
/**
* This is a placeholder file drag info atom that always stores null and is never mutated.
*/
const nullFileDragInfoAtom = atom<FileDragInfo | null>(null)
export function DirectoryPathBreadcrumb({
directory,
rootLabel,
directoryUrlFn,
fileDragInfoAtom = nullFileDragInfoAtom,
}: {
directory: DirectoryInfoWithPath
rootLabel: string
directoryUrlFn: (directoryId: string) => string
fileDragInfoAtom?: PrimitiveAtom<FileDragInfo | null>
}) {
if (directory.path.length === 1) {
return (
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbPage>{rootLabel}</BreadcrumbPage>
</BreadcrumbItem>
<BreadcrumbSeparator />
</BreadcrumbList>
</Breadcrumb>
)
}
const breadcrumbItems: React.ReactNode[] = [
<FilePathBreadcrumbItem
key={directory.path[0]!.id}
segment={directory.path[0]!}
label={rootLabel}
directoryUrlFn={directoryUrlFn}
fileDragInfoAtom={fileDragInfoAtom}
/>,
]
for (let i = 1; i < directory.path.length - 1; i++) {
breadcrumbItems.push(
<Fragment key={directory.path[i]!.id}>
<BreadcrumbSeparator />
<FilePathBreadcrumbItem
segment={directory.path[i]!}
directoryUrlFn={directoryUrlFn}
fileDragInfoAtom={fileDragInfoAtom}
/>
</Fragment>,
)
}
return (
<Breadcrumb>
<BreadcrumbList>
{breadcrumbItems}
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>{directory.name}</BreadcrumbPage>{" "}
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
)
}
function FilePathBreadcrumbItem({
segment,
label,
directoryUrlFn,
fileDragInfoAtom,
}: {
segment: PathSegment
label?: string
directoryUrlFn: (directoryId: string) => string
fileDragInfoAtom: PrimitiveAtom<FileDragInfo | null>
}) {
const { isDraggedOver, dropHandlers } = useFileDrop({
destDir: segment.id,
dragInfoAtom: fileDragInfoAtom,
enabled: true,
})
const dirName = label || segment.name
return (
<Tooltip open={isDraggedOver}>
<TooltipTrigger asChild>
<BreadcrumbItem
className={cn({ "bg-muted": isDraggedOver })}
{...dropHandlers}
>
<BreadcrumbLink asChild>
<Link to={directoryUrlFn(segment.id)}>{dirName}</Link>
</BreadcrumbLink>
</BreadcrumbItem>
</TooltipTrigger>
<TooltipContent>Move to {dirName}</TooltipContent>
</Tooltip>
)
}