"use client" import { memo, useEffect, useRef, useState } from "react" import { EditorContent, type Editor } from "@tiptap/react" import type { DocPageLayout } from "@/lib/drive/doc-page-setup" import { cn } from "@/lib/utils" import { focusEditorAtPointer } from "@/lib/drive/focus-editor-at-pointer" const PAGE_GAP_PX = 12 /** Actual block layout height — ignores CSS min-height on ProseMirror (page stack). */ function measureProseContentHeight(prose: HTMLElement): number { if (prose.childElementCount === 0) { return 0 } let maxBottom = 0 for (const child of prose.children) { const el = child as HTMLElement maxBottom = Math.max(maxBottom, el.offsetTop + el.offsetHeight) } return maxBottom } function DocsPageViewInner({ editor, pageLayout, zoom, editable, onPageCountChange, }: { editor: Editor pageLayout: DocPageLayout zoom: number editable: boolean onPageCountChange?: (count: number) => void }) { const pageWidth = pageLayout.widthPx const pageHeight = pageLayout.heightPx const margins = pageLayout.marginsPx const canvasRef = useRef(null) const contentRef = useRef(null) const [pageCount, setPageCount] = useState(1) const [narrowViewport, setNarrowViewport] = useState(false) const onPageCountChangeRef = useRef(onPageCountChange) onPageCountChangeRef.current = onPageCountChange const scale = zoom / 100 const scaledWidth = pageWidth * scale useEffect(() => { const canvas = canvasRef.current if (!canvas) return const syncViewport = () => { setNarrowViewport(canvas.clientWidth < scaledWidth) } syncViewport() const ro = new ResizeObserver(syncViewport) ro.observe(canvas) return () => ro.disconnect() }, [scaledWidth]) useEffect(() => { const surface = contentRef.current if (!surface) return let rafId = 0 const measure = () => { const prose = surface.querySelector(".ProseMirror") as HTMLElement | null if (!prose) return const contentHeight = measureProseContentHeight(prose) const paddedHeight = margins.top + margins.bottom + contentHeight const count = Math.max(1, Math.ceil(paddedHeight / pageHeight)) setPageCount((prev) => (prev === count ? prev : count)) } const scheduleMeasure = () => { if (rafId) cancelAnimationFrame(rafId) rafId = requestAnimationFrame(measure) } scheduleMeasure() const prose = surface.querySelector(".ProseMirror") as HTMLElement | null const ro = prose ? new ResizeObserver(scheduleMeasure) : null if (prose && ro) ro.observe(prose) const onTransaction = () => scheduleMeasure() editor.on("transaction", onTransaction) return () => { if (rafId) cancelAnimationFrame(rafId) ro?.disconnect() editor.off("transaction", onTransaction) } }, [margins.bottom, margins.top, pageHeight, editor]) useEffect(() => { onPageCountChangeRef.current?.(pageCount) }, [pageCount]) const stackHeight = pageCount * pageHeight + (pageCount - 1) * PAGE_GAP_PX const innerMinHeight = Math.max(pageHeight - margins.top - margins.bottom, stackHeight - margins.top - margins.bottom) const scaledHeight = stackHeight * scale const verticalPadding = narrowViewport ? 32 : 64 const textAreaBorderCss = pageLayout.textAreaBorderCss const sheetBorderCss = pageLayout.sheetBorderCss const pageBackground = pageLayout.pageColor const backgroundLayers = pageLayout.pageBackgroundLayers const renderPageBackground = (index: number) => ( <> {backgroundLayers?.gradientCss ? (
) : null} {backgroundLayers?.fillImageStyle ? (
) : null} {backgroundLayers?.watermarkStyle ? (
{backgroundLayers.watermarkStyle.imageSrc ? ( ) : ( {backgroundLayers.watermarkStyle.text} )}
) : null} ) return (
{Array.from({ length: pageCount }, (_, index) => (
{renderPageBackground(index)}
))} {textAreaBorderCss ? Array.from({ length: pageCount }, (_, index) => (
)) : null}
{ if (!editable) return const target = event.target as HTMLElement if (target.closest(".ProseMirror")) return event.preventDefault() focusEditorAtPointer(editor, event.clientX, event.clientY) }} >
) } export const DocsPageView = memo(DocsPageViewInner) export function DocsStatusBar({ pageLayout, pageCount, className, }: { pageLayout: DocPageLayout pageCount: number className?: string }) { return (
Page 1 sur {pageCount} {pageLayout.format.label} ({pageLayout.format.widthMm} × {pageLayout.format.heightMm} mm)
) }