"use client" import { useCallback, useMemo, useRef, useState, type ReactNode } from "react" import type { Editor } from "@tiptap/react" import type { DocsInsertMenuActions } from "@/components/drive/richtext/docs-insert-menu" import { DocsDriveDrawPickerDialog } from "@/components/drive/richtext/docs-drive-draw-picker-dialog" import { DocsDriveImagePickerDialog } from "@/components/drive/richtext/docs-drive-image-picker-dialog" import { DocsPageNumbersDialog } from "@/components/drive/richtext/docs-header-footer-dialogs" import { openDocsGraphicDrawModal } from "@/lib/drive/docs-graphic-draw-bridge" import { importExcalidrawFromDriveFile } from "@/lib/drive/docs-graphic-draw-import" import { openDocsLinkPopover } from "@/lib/drive/docs-link-bridge" import { startDocsRegionEdit } from "@/lib/drive/docs-page-elements-bridge" import type { DocPageSetup } from "@/lib/drive/doc-page-setup" import { buildImageInsertGraphicAttrs, buildInsertGraphicAttrs, } from "@/lib/drive/extensions/docs-graphic" import type { DriveFileInfo } from "@/lib/api/types" export function useDocsInsertMenu({ editor, disabled, pageSetup, onPageSetupPatch, }: { editor: Editor | null disabled?: boolean pageSetup?: DocPageSetup | null onPageSetupPatch?: (patch: Partial) => void }) { const imageInputRef = useRef(null) const [driveImagePickerOpen, setDriveImagePickerOpen] = useState(false) const [driveDrawPickerOpen, setDriveDrawPickerOpen] = useState(false) const [pageNumbersOpen, setPageNumbersOpen] = useState(false) const insertImageSrc = useCallback( (src: string, options?: { alt?: string }) => { if (!editor) return void buildImageInsertGraphicAttrs({ src, alt: options?.alt ?? "", wrap: "square", placement: "block", }).then((attrs) => { editor.chain().focus().insertDocsGraphic(attrs).run() }) }, [editor] ) const insertImageFile = useCallback( (file: File) => { const reader = new FileReader() reader.onload = () => { const src = reader.result as string insertImageSrc(src, { alt: file.name }) } reader.readAsDataURL(file) }, [insertImageSrc] ) const applyPageNumbers = useCallback( (placement: "header" | "footer") => { onPageSetupPatch?.({ pageNumbers: { enabled: true, placement, align: "right", startAt: pageSetup?.pageNumbers?.startAt ?? 1, showOnFirstPage: pageSetup?.pageNumbers?.showOnFirstPage ?? true, }, }) startDocsRegionEdit(placement) }, [onPageSetupPatch, pageSetup?.pageNumbers] ) const actions = useMemo( () => ({ onInsertImageFromComputer: () => imageInputRef.current?.click(), onInsertImageFromDrive: () => setDriveImagePickerOpen(true), onInsertNewDraw: () => { if (!editor) return editor .chain() .focus() .insertDocsGraphic( buildInsertGraphicAttrs("draw", { width: 320, height: 240, drawDriveFileId: null }) ) .run() openDocsGraphicDrawModal() }, onInsertDrawFromDrive: () => setDriveDrawPickerOpen(true), onInsertLink: () => openDocsLinkPopover(), onInsertHorizontalRule: () => { editor?.chain().focus().insertContent({ type: "horizontalRule" }).run() }, onInsertTable: (rows, cols) => { editor ?.chain() .focus() .insertTable({ rows, cols, withHeaderRow: false }) .run() }, onInsertHeader: () => startDocsRegionEdit("header"), onInsertFooter: () => startDocsRegionEdit("footer"), onInsertWatermark: () => { const text = window.prompt("Texte du filigrane", pageSetup?.pageBackground?.watermark?.text ?? "") if (text == null || !text.trim()) return onPageSetupPatch?.({ pageBackground: { ...pageSetup?.pageBackground, watermark: { kind: "text", text: text.trim(), color: pageSetup?.pageBackground?.watermark?.color ?? "#b4b4b4", opacity: pageSetup?.pageBackground?.watermark?.opacity ?? 0.35, rotationDeg: pageSetup?.pageBackground?.watermark?.rotationDeg ?? -35, }, }, }) }, onInsertPageNumbersHeader: () => applyPageNumbers("header"), onInsertPageNumbersFooter: () => applyPageNumbers("footer"), onOpenPageNumbersOptions: () => setPageNumbersOpen(true), }), [applyPageNumbers, editor, onPageSetupPatch, pageSetup?.pageBackground] ) const handlePickDriveImage = useCallback( async (src: string, file: DriveFileInfo) => { insertImageSrc(src, { alt: file.name }) }, [insertImageSrc] ) const handlePickDriveDraw = useCallback( async (file: DriveFileInfo) => { if (!editor) return const imported = await importExcalidrawFromDriveFile(file) editor .chain() .focus() .insertDocsGraphic( buildInsertGraphicAttrs("draw", { drawScene: imported.drawScene, src: imported.src, width: imported.width, height: imported.height, drawDriveFileId: imported.drawDriveFileId, alt: imported.alt, lockAspectRatio: true, }) ) .run() }, [editor] ) const dialogs: ReactNode = ( <> onPageSetupPatch?.({ pageNumbers })} /> { const file = event.target.files?.[0] if (file) insertImageFile(file) event.target.value = "" }} /> ) return { actions, dialogs, disabled, pageElementsEnabled: Boolean(onPageSetupPatch), } }