140 lines
4.4 KiB
TypeScript
140 lines
4.4 KiB
TypeScript
"use client"
|
|
|
|
import { memo, useRef } from "react"
|
|
import type { DocPageLayout } from "@/lib/drive/doc-page-setup"
|
|
import { DOCS_HORIZONTAL_RULER_HEIGHT_PX } from "@/lib/drive/docs-page-layout-constants"
|
|
import { docsPageLengthToScreen } from "@/lib/drive/docs-ruler-scale"
|
|
import type { DocsParagraphIndents } from "@/lib/drive/use-docs-ruler-sync"
|
|
import { buildHorizontalRulerTicks } from "@/lib/drive/docs-ruler-math"
|
|
import type { DocsRulerMarginSide } from "@/lib/drive/docs-ruler-margin-math"
|
|
import {
|
|
DocsRulerDraggableHandle,
|
|
DocsRulerFirstLineMarker,
|
|
DocsRulerTriangleMarker,
|
|
useRulerPointerDrag,
|
|
} from "@/components/drive/richtext/docs-ruler-markers"
|
|
|
|
function DocsHorizontalRulerInner({
|
|
pageLayout,
|
|
scale,
|
|
indents,
|
|
editable,
|
|
onMarginDragStart,
|
|
onMarginDrag,
|
|
onMarginDragEnd,
|
|
}: {
|
|
pageLayout: DocPageLayout
|
|
scale: number
|
|
indents: DocsParagraphIndents
|
|
editable?: boolean
|
|
onMarginDragStart?: (side: DocsRulerMarginSide) => void
|
|
onMarginDrag?: (side: DocsRulerMarginSide, pagePx: number, clientX: number, clientY: number) => void
|
|
onMarginDragEnd?: () => void
|
|
}) {
|
|
const rulerRef = useRef<HTMLDivElement>(null)
|
|
const pageWidth = pageLayout.widthPx
|
|
const margins = pageLayout.marginsPx
|
|
const scaledWidth = docsPageLengthToScreen(pageWidth, scale)
|
|
const ticks = buildHorizontalRulerTicks(pageWidth, pageLayout.format.id)
|
|
const s = (px: number) => docsPageLengthToScreen(px, scale)
|
|
|
|
const leftDrag = useRulerPointerDrag({
|
|
rulerRef,
|
|
axis: "horizontal",
|
|
disabled: !editable,
|
|
onDrag: (pagePx, clientX, clientY) => onMarginDrag?.("left", pagePx, clientX, clientY),
|
|
onDragEnd: () => onMarginDragEnd?.(),
|
|
})
|
|
|
|
const rightDrag = useRulerPointerDrag({
|
|
rulerRef,
|
|
axis: "horizontal",
|
|
disabled: !editable,
|
|
onDrag: (pagePx, clientX, clientY) => onMarginDrag?.("right", 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="horizontal"
|
|
data-docs-ruler-scale={scale}
|
|
className="docs-horizontal-ruler relative overflow-visible bg-transparent"
|
|
style={{
|
|
width: scaledWidth,
|
|
height: DOCS_HORIZONTAL_RULER_HEIGHT_PX,
|
|
}}
|
|
>
|
|
<div
|
|
className="absolute top-0 h-full bg-transparent"
|
|
style={{ left: 0, width: s(margins.left) }}
|
|
/>
|
|
<div
|
|
className="absolute top-0 h-full bg-transparent"
|
|
style={{ left: s(pageWidth - margins.right), width: s(margins.right) }}
|
|
/>
|
|
|
|
{ticks.map((tick, index) => (
|
|
<div
|
|
key={`${tick.pos}-${index}`}
|
|
className="pointer-events-none absolute bottom-0 w-px bg-[#80868b] dark:bg-muted-foreground/70"
|
|
style={{
|
|
left: s(tick.pos),
|
|
height: tick.major ? 10 : 5,
|
|
}}
|
|
/>
|
|
))}
|
|
|
|
{ticks
|
|
.filter((tick) => tick.major && tick.label != null)
|
|
.map((tick) => (
|
|
<span
|
|
key={`label-${tick.pos}`}
|
|
className="pointer-events-none absolute top-[2px] -translate-x-1/2 text-[9px] leading-none text-[#5f6368] dark:text-muted-foreground"
|
|
style={{ left: s(tick.pos) }}
|
|
>
|
|
{tick.label}
|
|
</span>
|
|
))}
|
|
|
|
<DocsRulerDraggableHandle
|
|
style={{ left: s(margins.left) }}
|
|
axis="horizontal"
|
|
disabled={!editable}
|
|
ariaLabel="Marge gauche"
|
|
onPointerDown={wrapMarginDown("left", leftDrag.onPointerDown)}
|
|
>
|
|
<DocsRulerTriangleMarker left={0} className="relative" />
|
|
</DocsRulerDraggableHandle>
|
|
|
|
<DocsRulerDraggableHandle
|
|
style={{ left: s(pageWidth - margins.right) }}
|
|
axis="horizontal"
|
|
disabled={!editable}
|
|
ariaLabel="Marge droite"
|
|
onPointerDown={wrapMarginDown("right", rightDrag.onPointerDown)}
|
|
>
|
|
<DocsRulerTriangleMarker left={0} className="relative" />
|
|
</DocsRulerDraggableHandle>
|
|
|
|
<DocsRulerTriangleMarker
|
|
left={s(indents.leftPx)}
|
|
className="pointer-events-none absolute bottom-0 -translate-x-1/2"
|
|
/>
|
|
|
|
{Math.abs(indents.firstLinePx - indents.leftPx) > 1 ? (
|
|
<DocsRulerFirstLineMarker left={s(indents.firstLinePx)} />
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export const DocsHorizontalRuler = memo(DocsHorizontalRulerInner)
|