ultisuite-client/lib/drive/docs-page-metrics.ts
R3D347HR4Y 580f843e4c feat(drive): enhance paragraph style and font family selection in rich text editor
- Updated class names for paragraph style and font family select triggers to improve styling consistency.
- Added new CSS rules for hover and focus states on select triggers, enhancing user interaction feedback.
- Implemented logic to drop trailing empty pages in document metrics calculations, optimizing page count accuracy.
- Enhanced tests to verify new page count behavior when content fits within existing layouts.
2026-06-15 18:02:52 +02:00

99 lines
3.0 KiB
TypeScript

import type { DocPageSetup } from "./doc-page-setup.ts"
import { DOCS_PAGE_GAP_PX } from "./docs-page-layout-constants.ts"
import { mmToPx } from "./doc-page-setup.ts"
export type DocsPageMetrics = {
pageWidth: number
pageHeight: number
margins: { top: number; right: number; bottom: number; left: number }
headerMarginPx: number
footerMarginPx: number
bodyAreaHeight: number
interPageSpacer: number
}
export function computePageMetrics(pageLayout: {
widthPx: number
heightPx: number
marginsPx: { top: number; right: number; bottom: number; left: number }
headerMarginMm?: number
footerMarginMm?: number
effectiveMarginsPx?: { top: number; right: number; bottom: number; left: number }
}): DocsPageMetrics {
const margins = pageLayout.effectiveMarginsPx ?? pageLayout.marginsPx
const headerMarginPx =
pageLayout.headerMarginMm != null
? mmToPx(pageLayout.headerMarginMm)
: pageLayout.marginsPx.top
const footerMarginPx =
pageLayout.footerMarginMm != null
? mmToPx(pageLayout.footerMarginMm)
: pageLayout.marginsPx.bottom
const bodyAreaHeight = pageLayout.heightPx - margins.top - margins.bottom
const interPageSpacer =
margins.bottom + DOCS_PAGE_GAP_PX + margins.top
return {
pageWidth: pageLayout.widthPx,
pageHeight: pageLayout.heightPx,
margins,
headerMarginPx,
footerMarginPx,
bodyAreaHeight: Math.max(1, bodyAreaHeight),
interPageSpacer,
}
}
/** Page count from prose content height (excludes outer surface padding). */
export function computePageCount(contentHeight: number, metrics: DocsPageMetrics): number {
if (contentHeight <= 0) return 1
const { bodyAreaHeight, interPageSpacer } = metrics
let remaining = contentHeight
let pages = 1
while (remaining > bodyAreaHeight) {
remaining -= bodyAreaHeight
if (remaining <= 0) break
remaining -= interPageSpacer
pages += 1
}
// Drop a trailing page that would be entirely empty (simulated height overshoot).
while (pages > 1 && contentHeight <= computeProseMinHeight(pages - 1, metrics)) {
pages -= 1
}
return pages
}
export function computeStackHeight(pageCount: number, pageHeight: number): number {
return pageCount * pageHeight + Math.max(0, pageCount - 1) * DOCS_PAGE_GAP_PX
}
/** Minimum prose height to align with visual page stack including inter-page spacers. */
export function computeProseMinHeight(pageCount: number, metrics: DocsPageMetrics): number {
const { bodyAreaHeight, interPageSpacer } = metrics
return (
pageCount * bodyAreaHeight + Math.max(0, pageCount - 1) * interPageSpacer
)
}
export function emptyRegionDoc() {
return { type: "doc", content: [{ type: "paragraph" }] }
}
export function pageSetupFromMetrics(
setup: DocPageSetup | null | undefined,
patch: Partial<DocPageSetup>
): DocPageSetup {
return { ...(setup ?? defaultMinimalSetup()), ...patch }
}
function defaultMinimalSetup(): DocPageSetup {
return {
widthMm: 210,
heightMm: 297,
marginsMm: { top: 25.4, right: 25.4, bottom: 25.4, left: 25.4 },
}
}