"use client" import { useCallback, useMemo, useRef, useState } from "react" import type { DocPageLayout, DocPageSetup } from "@/lib/drive/doc-page-setup" import { pxToMm } from "@/lib/drive/doc-page-setup" import { clampPageMarginPx, mergeMarginPreview, type DocsPageMarginsPx, type DocsRulerMarginSide, } from "@/lib/drive/docs-ruler-margin-math" import { formatMarginDistanceLabel } from "@/lib/drive/docs-ruler-units" import type { DocsRulerDragTooltipState } from "@/components/drive/richtext/docs-ruler-markers" function applyMarginDragPx( side: DocsRulerMarginSide, rawValuePx: number, base: DocsPageMarginsPx, preview: Partial | null, pageWidth: number, pageHeight: number ): { nextPreview: Partial; clamped: number } { const current = mergeMarginPreview(base, preview) let nextValue = rawValuePx if (side === "right") { nextValue = pageWidth - rawValuePx } else if (side === "bottom") { nextValue = pageHeight - rawValuePx } const clamped = clampPageMarginPx( side, nextValue, current, pageWidth, pageHeight ) return { nextPreview: { ...(preview ?? {}), [side]: clamped }, clamped, } } export function useDocsRulerMarginDrag({ pageLayout, editable, onPageSetupChange, }: { pageLayout: DocPageLayout editable: boolean onPageSetupChange?: ( patch: Partial, options?: { immediate?: boolean } ) => void }) { const [previewPx, setPreviewPx] = useState | null>(null) const [dragTooltip, setDragTooltip] = useState(null) const dragBaseRef = useRef(pageLayout.marginsPx) /** Sync preview during drag — window pointer events don't flush React state in time. */ const previewRef = useRef | null>(null) const pageLayoutRef = useRef(pageLayout) pageLayoutRef.current = pageLayout const marginsPx = useMemo( () => mergeMarginPreview(pageLayout.marginsPx, previewPx), [pageLayout.marginsPx, previewPx] ) const pageLayoutWithMargins = useMemo( (): DocPageLayout => ({ ...pageLayout, marginsPx, }), [pageLayout, marginsPx] ) const beginMarginDrag = useCallback( (_side: DocsRulerMarginSide) => { if (!editable) return const layout = pageLayoutRef.current dragBaseRef.current = mergeMarginPreview(layout.marginsPx, previewRef.current) previewRef.current = null }, [editable] ) const moveMarginDrag = useCallback( (side: DocsRulerMarginSide, rawValuePx: number, clientX: number, clientY: number) => { if (!editable) return const layout = pageLayoutRef.current const { nextPreview, clamped } = applyMarginDragPx( side, rawValuePx, dragBaseRef.current, previewRef.current, layout.widthPx, layout.heightPx ) previewRef.current = nextPreview setPreviewPx(nextPreview) setDragTooltip({ label: formatMarginDistanceLabel(clamped, layout.format.id), x: clientX, y: clientY, }) }, [editable] ) const endMarginDrag = useCallback(() => { setDragTooltip(null) const preview = previewRef.current previewRef.current = null if (!editable || !preview) { setPreviewPx(null) return } const layout = pageLayoutRef.current const merged = mergeMarginPreview(layout.marginsPx, preview) setPreviewPx(null) onPageSetupChange?.( { marginsMm: { top: pxToMm(merged.top), right: pxToMm(merged.right), bottom: pxToMm(merged.bottom), left: pxToMm(merged.left), }, }, { immediate: true } ) }, [editable, onPageSetupChange]) const cancelMarginDrag = useCallback(() => { previewRef.current = null setPreviewPx(null) setDragTooltip(null) }, []) return { marginsPx, pageLayoutWithMargins, marginDragActive: previewPx != null, dragTooltip, beginMarginDrag, moveMarginDrag, endMarginDrag, cancelMarginDrag, } }