- Updated .env.example to include configuration for OnlyOffice Document Server. - Modified the workspace configuration to remove the drive-suite path. - Adjusted TypeScript environment imports for consistency. - Enhanced Next.js configuration to disable canvas in Webpack. - Updated package.json to include new dependencies for OnlyOffice and PDF.js. - Added global styles for OnlyOffice theme integration in the CSS. - Created new layout and page components for the Drive feature, including public sharing and editing functionalities. - Updated metadata handling across various layouts to reflect the new app structure.
172 lines
5.1 KiB
TypeScript
172 lines
5.1 KiB
TypeScript
"use client"
|
|
|
|
import { useRouter } from "next/navigation"
|
|
import { FolderOpen, Loader2 } from "lucide-react"
|
|
import type { DriveFileInfo } from "@/lib/api/types"
|
|
import { DriveFileTypeIcon } from "@/lib/drive/drive-file-icon"
|
|
import { Button } from "@/components/ui/button"
|
|
import {
|
|
type DriveSearchScope,
|
|
DRIVE_SEARCH_SCOPES,
|
|
driveSearchScopeShortLabel,
|
|
itemLocationLabel,
|
|
itemParentFolderPath,
|
|
} from "@/lib/drive/drive-search"
|
|
import { driveFolderHref } from "@/lib/drive/drive-sidebar-tree"
|
|
import { displayFileName } from "@/lib/drive/display-file-name"
|
|
import { MAIL_SEARCH_SUGGESTIONS_DROPDOWN_CLASS } from "@/lib/mail-chrome-classes"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
function ScopePicker({
|
|
scope,
|
|
onScopeChange,
|
|
showFolderScope,
|
|
}: {
|
|
scope: DriveSearchScope
|
|
onScopeChange: (scope: DriveSearchScope) => void
|
|
showFolderScope: boolean
|
|
}) {
|
|
const options = DRIVE_SEARCH_SCOPES.filter((s) => s !== "folder" || showFolderScope)
|
|
return (
|
|
<div className="flex flex-wrap gap-1.5 px-3 pb-2 pt-1">
|
|
{options.map((option) => (
|
|
<button
|
|
key={option}
|
|
type="button"
|
|
onMouseDown={(e) => e.preventDefault()}
|
|
onClick={() => onScopeChange(option)}
|
|
className={cn(
|
|
"rounded-full px-3 py-1 text-xs font-medium transition-colors",
|
|
scope === option
|
|
? "bg-mail-active text-[#1967d2] dark:text-[#8ab4f8]"
|
|
: "bg-mail-surface-muted text-mail-text-muted hover:bg-mail-nav-hover"
|
|
)}
|
|
>
|
|
{driveSearchScopeShortLabel(option)}
|
|
</button>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function SuggestionRow({
|
|
item,
|
|
scope,
|
|
onPick,
|
|
}: {
|
|
item: DriveFileInfo
|
|
scope: DriveSearchScope
|
|
onPick: (item: DriveFileInfo) => void
|
|
}) {
|
|
const router = useRouter()
|
|
const view = scope === "shared" ? "shared" : "files"
|
|
const parentPath = itemParentFolderPath(item.path, item.type)
|
|
const parentHref = driveFolderHref(view, parentPath)
|
|
const location = itemLocationLabel(item.path, item.type)
|
|
|
|
return (
|
|
<div className="group flex min-w-0 items-center gap-2 rounded-lg px-2 py-1.5 hover:bg-mail-nav-hover">
|
|
<button
|
|
type="button"
|
|
className="flex min-w-0 flex-1 items-center gap-3 text-left"
|
|
onMouseDown={(e) => e.preventDefault()}
|
|
onClick={() => onPick(item)}
|
|
>
|
|
<DriveFileTypeIcon file={item} className="size-5 shrink-0" />
|
|
<span className="min-w-0 flex-1">
|
|
<span className="block truncate text-sm text-[#202124] dark:text-foreground">
|
|
{displayFileName(item.name)}
|
|
</span>
|
|
<span className="block truncate text-xs text-[#5f6368] dark:text-muted-foreground">
|
|
{location}
|
|
</span>
|
|
</span>
|
|
</button>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon"
|
|
className="size-8 shrink-0 text-[#5f6368] opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100 dark:text-muted-foreground"
|
|
aria-label="Ouvrir le dossier parent"
|
|
onMouseDown={(e) => e.preventDefault()}
|
|
onClick={() => router.push(parentHref)}
|
|
>
|
|
<FolderOpen className="size-4" />
|
|
</Button>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function DriveSearchSuggestionsPanel({
|
|
query,
|
|
scope,
|
|
onScopeChange,
|
|
showFolderScope,
|
|
suggestions,
|
|
loading,
|
|
onPickItem,
|
|
onSubmitSearch,
|
|
className,
|
|
}: {
|
|
query: string
|
|
scope: DriveSearchScope
|
|
onScopeChange: (scope: DriveSearchScope) => void
|
|
showFolderScope: boolean
|
|
suggestions: DriveFileInfo[]
|
|
loading: boolean
|
|
onPickItem: (item: DriveFileInfo) => void
|
|
onSubmitSearch: () => void
|
|
className?: string
|
|
}) {
|
|
const trimmed = query.trim()
|
|
if (trimmed.length < 2) return null
|
|
|
|
return (
|
|
<div
|
|
data-drive-search-suggestions
|
|
className={cn(
|
|
MAIL_SEARCH_SUGGESTIONS_DROPDOWN_CLASS,
|
|
"top-[calc(100%+6px)] rounded-2xl",
|
|
className
|
|
)}
|
|
>
|
|
<ScopePicker
|
|
scope={scope}
|
|
onScopeChange={onScopeChange}
|
|
showFolderScope={showFolderScope}
|
|
/>
|
|
<div className="border-t border-mail-border-subtle px-1 py-1">
|
|
{loading ? (
|
|
<div className="flex items-center gap-2 px-3 py-4 text-sm text-[#5f6368] dark:text-muted-foreground">
|
|
<Loader2 className="size-4 animate-spin" />
|
|
Recherche…
|
|
</div>
|
|
) : suggestions.length === 0 ? (
|
|
<p className="px-3 py-4 text-sm text-[#5f6368] dark:text-muted-foreground">
|
|
Aucune suggestion pour « {trimmed} »
|
|
</p>
|
|
) : (
|
|
<>
|
|
{suggestions.map((item) => (
|
|
<SuggestionRow
|
|
key={item.path}
|
|
item={item}
|
|
scope={scope}
|
|
onPick={onPickItem}
|
|
/>
|
|
))}
|
|
<button
|
|
type="button"
|
|
className="mt-0.5 w-full rounded-lg px-3 py-2 text-left text-sm font-medium text-[#1967d2] hover:bg-mail-nav-hover dark:text-[#8ab4f8]"
|
|
onMouseDown={(e) => e.preventDefault()}
|
|
onClick={onSubmitSearch}
|
|
>
|
|
Tous les résultats pour « {trimmed} »
|
|
</button>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|