ultisuite-client/components/drive/richtext/docs-vertical-ruler.tsx
R3D347HR4Y 2a7c153748
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
wrap page
2026-06-10 12:48:27 +02:00

131 lines
4.2 KiB
TypeScript

"use client"
import { memo, useRef } from "react"
import type { DocPageLayout } from "@/lib/drive/doc-page-setup"
import { DOCS_VERTICAL_RULER_WIDTH_PX } from "@/lib/drive/docs-page-layout-constants"
import { docsPageLengthToScreen } from "@/lib/drive/docs-ruler-scale"
import { buildVerticalRulerTicks } from "@/lib/drive/docs-ruler-math"
import type { DocsRulerMarginSide } from "@/lib/drive/docs-ruler-margin-math"
import {
DocsRulerDownTriangleMarker,
DocsRulerDraggableHandle,
DocsRulerUpTriangleMarker,
useRulerPointerDrag,
} from "@/components/drive/richtext/docs-ruler-markers"
function DocsVerticalRulerInner({
pageLayout,
scale,
editable,
onMarginDragStart,
onMarginDrag,
onMarginDragEnd,
}: {
pageLayout: DocPageLayout
scale: number
editable?: boolean
onMarginDragStart?: (side: DocsRulerMarginSide) => void
onMarginDrag?: (side: DocsRulerMarginSide, pagePx: number, clientX: number, clientY: number) => void
onMarginDragEnd?: () => void
}) {
const rulerRef = useRef<HTMLDivElement>(null)
const pageHeight = pageLayout.heightPx
const margins = pageLayout.marginsPx
const scaledHeight = docsPageLengthToScreen(pageHeight, scale)
const ticks = buildVerticalRulerTicks(pageHeight, margins.top, pageLayout.format.id)
const s = (px: number) => docsPageLengthToScreen(px, scale)
const topDrag = useRulerPointerDrag({
rulerRef,
axis: "vertical",
disabled: !editable,
onDrag: (pagePx, clientX, clientY) => onMarginDrag?.("top", pagePx, clientX, clientY),
onDragEnd: () => onMarginDragEnd?.(),
})
const bottomDrag = useRulerPointerDrag({
rulerRef,
axis: "vertical",
disabled: !editable,
onDrag: (pagePx, clientX, clientY) => onMarginDrag?.("bottom", pagePx, clientX, clientY),
onDragEnd: () => onMarginDragEnd?.(),
})
const wrapMarginDown =
(side: DocsRulerMarginSide, handler: (e: React.PointerEvent<HTMLDivElement>) => void) =>
(event: React.PointerEvent<HTMLDivElement>) => {
onMarginDragStart?.(side)
handler(event)
}
return (
<div
ref={rulerRef}
data-docs-ruler="vertical"
data-docs-ruler-scale={scale}
className="docs-vertical-ruler relative overflow-visible border-r border-[#dadce0] bg-white dark:border-border dark:bg-background"
style={{
width: DOCS_VERTICAL_RULER_WIDTH_PX,
height: scaledHeight,
}}
>
<div
className="pointer-events-none absolute left-0 right-0 bg-[#f1f3f4] dark:bg-muted/60"
style={{ top: 0, height: s(margins.top) }}
/>
<div
className="pointer-events-none absolute left-0 right-0 bg-[#f1f3f4] dark:bg-muted/60"
style={{
top: s(pageHeight - margins.bottom),
height: s(margins.bottom),
}}
/>
{ticks.map((tick, index) => (
<div
key={`${tick.pos}-${index}`}
className="pointer-events-none absolute right-0 h-px bg-[#80868b] dark:bg-muted-foreground/70"
style={{
top: s(tick.pos),
width: tick.major ? 10 : 5,
}}
/>
))}
{ticks
.filter((tick) => tick.major && tick.label != null)
.map((tick) => (
<span
key={`vlabel-${tick.pos}`}
className="pointer-events-none absolute right-[11px] -translate-y-1/2 text-[9px] leading-none text-[#5f6368] dark:text-muted-foreground"
style={{ top: s(tick.pos) }}
>
{tick.label}
</span>
))}
<DocsRulerDraggableHandle
style={{ top: s(margins.top) }}
axis="vertical"
disabled={!editable}
ariaLabel="Marge haute"
onPointerDown={wrapMarginDown("top", topDrag.onPointerDown)}
>
<DocsRulerUpTriangleMarker top={0} />
</DocsRulerDraggableHandle>
<DocsRulerDraggableHandle
style={{ top: s(pageHeight - margins.bottom) }}
axis="vertical"
disabled={!editable}
ariaLabel="Marge basse"
onPointerDown={wrapMarginDown("bottom", bottomDrag.onPointerDown)}
>
<DocsRulerDownTriangleMarker top={0} />
</DocsRulerDraggableHandle>
</div>
)
}
export const DocsVerticalRuler = memo(DocsVerticalRulerInner)