ultisuite-client/components/drive/richtext/docs-editor-workspace.tsx
R3D347HR4Y 82ca9a27db
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat(drive): refactor document and drawing editors with new metadata handling
- Replaced suite page metadata with drive-specific metadata for document and drawing editors.
- Introduced new `driveEditorPageMetadata` function to manage titles and favicons based on editor type.
- Updated layout components for document and drawing editors to utilize the new metadata structure.
- Enhanced document title handling in various editor components to reflect the current editing context.
- Added new SVG icons for UltiDocs, UltiSheets, UltiSlides, and UltiDraw to improve visual consistency across editors.
- Improved print styles and layout handling for better document rendering in print and PDF formats.
2026-06-15 15:51:09 +02:00

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 min-w-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 min-w-0 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>
)
}