"use client" import { useCallback, useEffect, useRef, useState } from "react" import type { Editor } from "@tiptap/react" import { Link2, Loader2, Search } from "lucide-react" import { Button } from "@/components/ui/button" import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" import type { DriveFileInfo } from "@/lib/api/types" import { useDriveSearchSuggestions } from "@/lib/api/hooks/use-drive-queries" import { DriveFileTypeIcon } from "@/lib/drive/drive-file-icon" import { itemLocationLabel } from "@/lib/drive/drive-search" import { DOCS_LINK_POPOVER_EVENT } from "@/lib/drive/docs-link-bridge" import { normalizeDocsLinkHref, resolveDriveItemLinkHref } from "@/lib/drive/docs-link-href" import { displayFileName } from "@/lib/drive/display-file-name" import { useDebouncedValue } from "@/lib/hooks/use-debounced-value" import { cn } from "@/lib/utils" type SavedSelection = { from: number; to: number } function DocsLinkToolbarBtn({ active, disabled, onPrepareOpen, }: { active?: boolean disabled?: boolean onPrepareOpen: () => void }) { return ( ) } function DriveFileSuggestionRow({ item, disabled, onPick, }: { item: DriveFileInfo disabled?: boolean onPick: (item: DriveFileInfo) => void }) { return ( ) } export function DocsLinkPopover({ editor, disabled, active, }: { editor: Editor disabled?: boolean active?: boolean }) { const [open, setOpen] = useState(false) const [linkUrl, setLinkUrl] = useState("") const [driveQuery, setDriveQuery] = useState("") const [resolvingDriveFile, setResolvingDriveFile] = useState(false) const urlInputRef = useRef(null) const selectionRef = useRef(null) const debouncedDriveQuery = useDebouncedValue(driveQuery, 250) const { data: driveResults, isFetching: driveLoading } = useDriveSearchSuggestions( debouncedDriveQuery, "all", "/", open ) const driveSuggestions = driveResults?.files ?? [] const hasTextSelection = useCallback(() => { const sel = selectionRef.current return Boolean(sel && sel.from !== sel.to) }, []) const applyLink = useCallback( (rawUrl?: string) => { const url = normalizeDocsLinkHref(rawUrl ?? linkUrl) const sel = selectionRef.current let chain = editor.chain().focus() if (sel && sel.from !== sel.to) { chain = chain.setTextSelection({ from: sel.from, to: sel.to }) } else if (editor.isActive("link")) { chain = chain.extendMarkRange("link") } else { return } if (!url) { chain.unsetLink().run() } else { chain.setLink({ href: url }).run() } setOpen(false) setLinkUrl("") setDriveQuery("") selectionRef.current = null }, [editor, linkUrl] ) const prepareOpen = useCallback(() => { const { from, to } = editor.state.selection selectionRef.current = { from, to } const prev = editor.getAttributes("link").href as string | undefined setLinkUrl(prev ?? "") setDriveQuery("") }, [editor]) const pickDriveFile = useCallback( async (file: DriveFileInfo) => { setResolvingDriveFile(true) try { const href = await resolveDriveItemLinkHref(file) setLinkUrl(href) applyLink(href) } catch { window.alert("Impossible de créer le lien vers ce fichier.") } finally { setResolvingDriveFile(false) } }, [applyLink] ) useEffect(() => { if (!open) return const id = window.requestAnimationFrame(() => { urlInputRef.current?.focus() urlInputRef.current?.select() }) return () => window.cancelAnimationFrame(id) }, [open]) useEffect(() => { const onOpenRequest = () => { prepareOpen() setOpen(true) } window.addEventListener(DOCS_LINK_POPOVER_EVENT, onOpenRequest) return () => window.removeEventListener(DOCS_LINK_POPOVER_EVENT, onOpenRequest) }, [prepareOpen]) const canApply = hasTextSelection() || editor.isActive("link") const trimmedDriveQuery = driveQuery.trim() return ( { setOpen(next) if (!next) { setLinkUrl("") setDriveQuery("") selectionRef.current = null } }} >
setLinkUrl(event.target.value)} onKeyDown={(event) => { if (event.key === "Enter") { event.preventDefault() applyLink() } }} />
{!canApply ? (

Sélectionnez du texte pour créer un hyperlien.

) : null}
setDriveQuery(event.target.value)} />
{resolvingDriveFile ? (
Création du lien…
) : trimmedDriveQuery.length < 2 ? (

Saisissez au moins 2 caractères pour rechercher dans votre Drive.

) : driveLoading ? (
Recherche…
) : driveSuggestions.length === 0 ? (

Aucun fichier pour « {trimmedDriveQuery} »

) : ( driveSuggestions.map((item) => ( )) )}
{editor.isActive("link") ? (
) : null}
) }