ultisuite-client/components/drive/breadcrumb-nav.tsx
R3D347HR4Y ad1370ea7e
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat: enhance configuration and add new demo layouts
- Introduced turbopack alias for canvas in next.config.mjs.
- Updated package.json scripts for development and branding tasks.
- Added new dependencies for Tiptap extensions.
- Implemented new demo layouts for agenda, contacts, drive, and mail applications.
- Enhanced globals.css for improved theming and splash screen animations.
- Added OAuth callback handling for drive mounts.
- Updated layout components to integrate new demo shells and improve structure.
2026-06-12 19:10:24 +02:00

139 lines
4.9 KiB
TypeScript

"use client"
import { Fragment, useState } from "react"
import Link from "next/link"
import { ChevronRight } from "lucide-react"
import { BreadcrumbFolderMenu } from "@/components/drive/breadcrumb-folder-menu"
import type { DriveView } from "@/lib/drive/drive-url"
import { useDriveRouteRoot } from "@/lib/drive/drive-route-context"
import { buildDriveFolderHref, driveRouteBase, folderPathFromSegments } from "@/lib/drive/drive-url"
import { displayFileName } from "@/lib/drive/display-file-name"
import { cn } from "@/lib/utils"
/** xs/sm: single line, intermediate crumbs clamp + ellipsis. */
const MOBILE_INTERMEDIATE_CRUMB_CLASS =
"max-md:line-clamp-1 max-md:min-w-0 max-md:shrink max-md:overflow-hidden max-md:break-all max-md:[overflow-wrap:anywhere]"
/** Shared line box so mixed font sizes stay vertically centered in the chrome row. */
const CRUMB_LINE_CLASS = "leading-6 md:leading-7"
export function BreadcrumbNav({
view,
segments,
rootId,
writable = true,
}: {
view: Extract<DriveView, "files" | "shared" | "org" | "mount">
segments: string[]
rootId?: string | null
/** When false, double-click rename is disabled (e.g. read-only share). */
writable?: boolean
}) {
const [renameOpen, setRenameOpen] = useState(false)
const routeRoot = useDriveRouteRoot()
const base = driveRouteBase(routeRoot)
const rootLabel =
view === "shared"
? "Partagés avec moi"
: view === "org"
? "Dossier d'organisation"
: view === "mount"
? "Volume monté"
: "Mon Drive"
const rootHref =
view === "shared"
? `${base}/shared`
: view === "org" && rootId
? `${base}/org/${encodeURIComponent(rootId)}`
: view === "mount" && rootId
? `${base}/mounts/${encodeURIComponent(rootId)}`
: base
const folderPath = folderPathFromSegments(segments)
const canRenameCurrent = writable && segments.length > 0
const crumbs = [{ label: rootLabel, href: rootHref, segments: [] as string[] }]
for (let i = 0; i < segments.length; i++) {
const slice = segments.slice(0, i + 1)
crumbs.push({
label: displayFileName(segments[i]),
href: buildDriveFolderHref(view, slice, rootId ?? undefined, routeRoot),
segments: slice,
})
}
return (
<>
<nav className="flex min-w-0 flex-nowrap items-center gap-1 overflow-hidden text-[#5f6368] md:gap-2 dark:text-muted-foreground">
{crumbs.map((c, i) => {
const isLast = i === crumbs.length - 1
const isRenamableFolder = isLast && canRenameCurrent
return (
<Fragment key={c.href}>
{i > 0 && (
<ChevronRight
className="size-4 shrink-0 opacity-60 md:size-5"
aria-hidden
/>
)}
{isLast ? (
<span className="inline-flex min-w-0 max-w-full shrink items-center gap-0.5">
<span
className={cn(
"min-w-0 max-w-full shrink truncate text-base font-normal tracking-tight text-[#202124] md:text-[1.375rem] dark:text-foreground",
CRUMB_LINE_CLASS,
isRenamableFolder &&
"cursor-text rounded-sm px-0.5 hover:bg-mail-nav-hover"
)}
title={
isRenamableFolder
? `${c.label} — double-clic pour renommer`
: c.label
}
onDoubleClick={
isRenamableFolder
? (e) => {
e.preventDefault()
setRenameOpen(true)
}
: undefined
}
>
{c.label}
</span>
{segments.length > 0 ? (
<BreadcrumbFolderMenu
view={view}
segments={segments}
folderPath={folderPath}
writable={writable}
allowShare={view === "files"}
renameOpen={renameOpen}
onRenameOpenChange={setRenameOpen}
/>
) : null}
</span>
) : (
<Link
href={c.href}
title={c.label}
className={cn(
"inline-flex shrink-0 cursor-pointer text-sm hover:text-[#202124] md:text-[1.125rem] dark:hover:text-foreground",
CRUMB_LINE_CLASS,
MOBILE_INTERMEDIATE_CRUMB_CLASS,
i === 0 ? "max-md:max-w-[5.5rem]" : "max-md:max-w-[4.5rem]"
)}
>
{c.label}
</Link>
)}
</Fragment>
)
})}
<span className="sr-only">{folderPath}</span>
</nav>
</>
)
}