ultisuite-client/components/drive/drive-search-suggestions.tsx
R3D347HR4Y 6ec95262af Add OnlyOffice integration and update project configurations
- 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.
2026-06-07 15:49:21 +02:00

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>
)
}