200 lines
6.3 KiB
TypeScript
200 lines
6.3 KiB
TypeScript
"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<DocPageSetup>) => void
|
|
}) {
|
|
const imageInputRef = useRef<HTMLInputElement>(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<DocsInsertMenuActions>(
|
|
() => ({
|
|
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 = (
|
|
<>
|
|
<DocsDriveImagePickerDialog
|
|
open={driveImagePickerOpen}
|
|
onOpenChange={setDriveImagePickerOpen}
|
|
onPickImage={handlePickDriveImage}
|
|
/>
|
|
<DocsDriveDrawPickerDialog
|
|
open={driveDrawPickerOpen}
|
|
onOpenChange={setDriveDrawPickerOpen}
|
|
onPickDraw={handlePickDriveDraw}
|
|
/>
|
|
<DocsPageNumbersDialog
|
|
open={pageNumbersOpen}
|
|
onOpenChange={setPageNumbersOpen}
|
|
settings={pageSetup?.pageNumbers}
|
|
onApply={(pageNumbers) => onPageSetupPatch?.({ pageNumbers })}
|
|
/>
|
|
<input
|
|
ref={imageInputRef}
|
|
type="file"
|
|
accept="image/*"
|
|
className="hidden"
|
|
onChange={(event) => {
|
|
const file = event.target.files?.[0]
|
|
if (file) insertImageFile(file)
|
|
event.target.value = ""
|
|
}}
|
|
/>
|
|
</>
|
|
)
|
|
|
|
return {
|
|
actions,
|
|
dialogs,
|
|
disabled,
|
|
pageElementsEnabled: Boolean(onPageSetupPatch),
|
|
}
|
|
}
|