242 lines
8.0 KiB
TypeScript
242 lines
8.0 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useRef, useState, type ReactNode } from "react"
|
|
import type { Editor } from "@tiptap/react"
|
|
import { DocsPageView } from "@/components/drive/richtext/docs-page-view"
|
|
import {
|
|
DocsRulerToolbarRow,
|
|
DocsRulersLeftRail,
|
|
} from "@/components/drive/richtext/docs-rulers-chrome"
|
|
import type { DocPageLayout } from "@/lib/drive/doc-page-setup"
|
|
import { DOCS_VERTICAL_RULER_WIDTH_PX } from "@/lib/drive/docs-page-layout-constants"
|
|
import { docsZoomToScale } from "@/lib/drive/docs-ruler-scale"
|
|
import { useDocsRulerSync } from "@/lib/drive/use-docs-ruler-sync"
|
|
import { useDocsRulerMarginDrag } from "@/components/drive/richtext/use-docs-ruler-margin-drag"
|
|
import { DocsRulerMarginDragTooltip } from "@/components/drive/richtext/docs-ruler-markers"
|
|
import { DocsGraphicFloatingToolbar } from "@/components/drive/richtext/docs-graphic-floating-toolbar"
|
|
import { DocsTableFloatingToolbar } from "@/components/drive/richtext/docs-table-floating-toolbar"
|
|
import {
|
|
DOCS_GRAPHIC_DRAW_EVENT,
|
|
DocsGraphicDrawModal,
|
|
} from "@/components/drive/richtext/docs-graphic-draw-modal"
|
|
import {
|
|
DOCS_GRAPHIC_OPTIONS_EVENT,
|
|
DOCS_GRAPHIC_OPTIONS_SIDEBAR_WIDTH_PX,
|
|
DocsGraphicOptionsSidebar,
|
|
} from "@/components/drive/richtext/docs-graphic-options-sidebar"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
export function DocsEditorWorkspace({
|
|
editor,
|
|
pageLayout,
|
|
zoom,
|
|
editable,
|
|
showLayout,
|
|
showRuler,
|
|
showNonPrintableChars,
|
|
editorMode,
|
|
outlineExpanded,
|
|
onToggleOutline,
|
|
onPageCountChange,
|
|
onCurrentPageChange,
|
|
toolbar,
|
|
toolbarShellClassName,
|
|
onRegionContentChange,
|
|
onPageSetupChange,
|
|
onRegionEditorChange,
|
|
onPageStackReady,
|
|
}: {
|
|
editor: Editor
|
|
pageLayout: DocPageLayout
|
|
zoom: number
|
|
editable: boolean
|
|
showLayout: boolean
|
|
showRuler: boolean
|
|
showNonPrintableChars: boolean
|
|
editorMode: "edit" | "suggest" | "view"
|
|
outlineExpanded?: boolean
|
|
onToggleOutline?: () => void
|
|
onPageCountChange?: (count: number) => void
|
|
onCurrentPageChange?: (page: number) => void
|
|
toolbar?: ReactNode
|
|
toolbarShellClassName?: string
|
|
onRegionContentChange?: (
|
|
region: "header" | "footer",
|
|
content: Record<string, unknown>,
|
|
meta: { pageIndex: number; contentHeightPx: number }
|
|
) => void
|
|
onPageSetupChange?: (
|
|
patch: Partial<import("@/lib/drive/doc-page-setup").DocPageSetup>,
|
|
options?: { immediate?: boolean }
|
|
) => void
|
|
onRegionEditorChange?: (editor: import("@tiptap/react").Editor | null) => void
|
|
onPageStackReady?: (getPageStack: () => HTMLElement | null) => void
|
|
}) {
|
|
const canvasRef = useRef<HTMLDivElement>(null)
|
|
const rulerTrackRef = useRef<HTMLDivElement>(null)
|
|
const [pageCount, setPageCount] = useState(1)
|
|
const [narrowViewport, setNarrowViewport] = useState(false)
|
|
const [graphicOptionsOpen, setGraphicOptionsOpen] = useState(false)
|
|
const [graphicOptionsSection, setGraphicOptionsSection] =
|
|
useState<import("@/lib/drive/docs-graphic-ui").DocsGraphicOptionsSection | null>(null)
|
|
const [graphicDrawOpen, setGraphicDrawOpen] = useState(false)
|
|
|
|
useEffect(() => {
|
|
const onOpenOptions = (event: Event) => {
|
|
const detail = (event as CustomEvent<{ section?: import("@/lib/drive/docs-graphic-ui").DocsGraphicOptionsSection }>).detail
|
|
setGraphicOptionsSection(detail?.section ?? null)
|
|
setGraphicOptionsOpen(true)
|
|
}
|
|
const onOpenDraw = () => setGraphicDrawOpen(true)
|
|
window.addEventListener(DOCS_GRAPHIC_OPTIONS_EVENT, onOpenOptions)
|
|
window.addEventListener(DOCS_GRAPHIC_DRAW_EVENT, onOpenDraw)
|
|
return () => {
|
|
window.removeEventListener(DOCS_GRAPHIC_OPTIONS_EVENT, onOpenOptions)
|
|
window.removeEventListener(DOCS_GRAPHIC_DRAW_EVENT, onOpenDraw)
|
|
}
|
|
}, [])
|
|
|
|
const rulersVisible = showLayout && showRuler
|
|
const scale = docsZoomToScale(zoom)
|
|
const showToolbarShell = Boolean(toolbar) || rulersVisible
|
|
const marginsEditable = editable && editorMode !== "view"
|
|
const graphicOptionsSidebarOpen =
|
|
graphicOptionsOpen && editable && editorMode !== "view"
|
|
const graphicOptionsSidebarInset = graphicOptionsSidebarOpen
|
|
? DOCS_GRAPHIC_OPTIONS_SIDEBAR_WIDTH_PX
|
|
: 0
|
|
|
|
const {
|
|
pageLayoutWithMargins,
|
|
beginMarginDrag,
|
|
moveMarginDrag,
|
|
endMarginDrag,
|
|
dragTooltip,
|
|
} = useDocsRulerMarginDrag({
|
|
pageLayout,
|
|
editable: marginsEditable,
|
|
onPageSetupChange,
|
|
})
|
|
|
|
const rulerSync = useDocsRulerSync({
|
|
canvasRef,
|
|
rulerTrackRef,
|
|
editor,
|
|
pageLayout: pageLayoutWithMargins,
|
|
zoom,
|
|
pageCount,
|
|
narrowViewport,
|
|
})
|
|
|
|
useEffect(() => {
|
|
onCurrentPageChange?.(rulerSync.currentPage + 1)
|
|
}, [onCurrentPageChange, rulerSync.currentPage])
|
|
|
|
useEffect(() => {
|
|
onPageStackReady?.(
|
|
() => canvasRef.current?.querySelector<HTMLElement>("[data-docs-page-stack]") ?? null
|
|
)
|
|
}, [onPageStackReady, pageCount, showLayout, zoom])
|
|
|
|
return (
|
|
<div className="docs-editor-workspace flex min-h-0 flex-1 flex-col">
|
|
<DocsRulerMarginDragTooltip tooltip={dragTooltip} />
|
|
<DocsGraphicFloatingToolbar
|
|
editor={editor}
|
|
canvasRef={canvasRef}
|
|
disabled={!editable || editorMode === "view"}
|
|
/>
|
|
<DocsTableFloatingToolbar
|
|
editor={editor}
|
|
canvasRef={canvasRef}
|
|
disabled={!editable || editorMode === "view"}
|
|
/>
|
|
<DocsGraphicDrawModal
|
|
editor={editor}
|
|
open={graphicDrawOpen && editable && editorMode !== "view"}
|
|
onClose={() => setGraphicDrawOpen(false)}
|
|
/>
|
|
{showToolbarShell ? (
|
|
<div
|
|
className={cn(
|
|
"docs-toolbar-shell shrink-0",
|
|
toolbarShellClassName
|
|
)}
|
|
>
|
|
{toolbar}
|
|
{rulersVisible ? (
|
|
<DocsRulerToolbarRow
|
|
pageLayout={pageLayoutWithMargins}
|
|
scale={scale}
|
|
rulerSync={rulerSync}
|
|
rulerTrackRef={rulerTrackRef}
|
|
contentInsetRight={graphicOptionsSidebarInset}
|
|
outlineExpanded={outlineExpanded}
|
|
onToggleOutline={onToggleOutline}
|
|
editable={marginsEditable}
|
|
onMarginDragStart={beginMarginDrag}
|
|
onMarginDrag={moveMarginDrag}
|
|
onMarginDragEnd={endMarginDrag}
|
|
/>
|
|
) : null}
|
|
</div>
|
|
) : null}
|
|
|
|
<div className="relative min-h-0 flex-1 overflow-hidden">
|
|
{rulersVisible ? (
|
|
<DocsRulersLeftRail
|
|
pageLayout={pageLayoutWithMargins}
|
|
scale={scale}
|
|
rulerSync={rulerSync}
|
|
editable={marginsEditable}
|
|
onMarginDragStart={beginMarginDrag}
|
|
onMarginDrag={moveMarginDrag}
|
|
onMarginDragEnd={endMarginDrag}
|
|
/>
|
|
) : null}
|
|
|
|
<div
|
|
className="flex h-full min-h-0 flex-row"
|
|
style={
|
|
rulersVisible
|
|
? { paddingLeft: DOCS_VERTICAL_RULER_WIDTH_PX }
|
|
: undefined
|
|
}
|
|
>
|
|
<div className="flex h-full min-h-0 min-w-0 flex-1 flex-col">
|
|
<DocsPageView
|
|
editor={editor}
|
|
pageLayout={pageLayoutWithMargins}
|
|
zoom={zoom}
|
|
editable={editable}
|
|
showLayout={showLayout}
|
|
showRuler={false}
|
|
showNonPrintableChars={showNonPrintableChars}
|
|
editorMode={editorMode}
|
|
canvasRef={canvasRef}
|
|
onPageCountChange={(count) => {
|
|
setPageCount(count)
|
|
onPageCountChange?.(count)
|
|
}}
|
|
onNarrowViewportChange={setNarrowViewport}
|
|
onRegionContentChange={onRegionContentChange}
|
|
onPageSetupChange={onPageSetupChange}
|
|
onRegionEditorChange={onRegionEditorChange}
|
|
/>
|
|
</div>
|
|
<DocsGraphicOptionsSidebar
|
|
editor={editor}
|
|
pageLayout={pageLayoutWithMargins}
|
|
open={graphicOptionsSidebarOpen}
|
|
focusSection={graphicOptionsSection}
|
|
onClose={() => {
|
|
setGraphicOptionsOpen(false)
|
|
setGraphicOptionsSection(null)
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|