ultisuite-client/lib/drive/extensions/docs-paragraph-spacing.ts
R3D347HR4Y 303b2b1074
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
wow
2026-06-11 01:22:40 +02:00

153 lines
5.0 KiB
TypeScript

import { Extension } from "@tiptap/core"
import {
buildParagraphSpacingStyle,
DOCS_DEFAULT_PARAGRAPH_SPACING,
parseLineHeightAttr,
parseSpacingPt,
readParagraphSpacingAttrs,
} from "@/lib/drive/docs-line-spacing"
import {
setDocsCustomSpacing,
setDocsLineHeight,
toggleDocsKeepLinesTogether,
toggleDocsKeepWithNext,
toggleDocsPageBreakBefore,
toggleDocsPreventWidowOrphan,
toggleDocsSpaceAfter,
toggleDocsSpaceBefore,
} from "@/lib/drive/docs-line-spacing-actions"
declare module "@tiptap/core" {
interface Commands<ReturnType> {
docsParagraphSpacing: {
setDocsLineHeight: (lineHeight: number) => ReturnType
setDocsCustomSpacing: (input: {
lineHeight: number | null
spaceBeforePt: number
spaceAfterPt: number
}) => ReturnType
toggleDocsSpaceBefore: () => ReturnType
toggleDocsSpaceAfter: () => ReturnType
toggleDocsKeepWithNext: () => ReturnType
toggleDocsKeepLinesTogether: () => ReturnType
toggleDocsPreventWidowOrphan: () => ReturnType
toggleDocsPageBreakBefore: () => ReturnType
}
}
}
function parseBoolAttr(raw: unknown): boolean {
return raw === true || raw === "true" || raw === "1"
}
function spacingRenderHtml(attributes: Record<string, unknown>) {
const spacing = readParagraphSpacingAttrs({ attrs: attributes })
const style = buildParagraphSpacingStyle(spacing)
const html: Record<string, string> = {}
if (spacing.lineHeight != null) html["data-line-height"] = String(spacing.lineHeight)
if (spacing.spaceBeforePt > 0) html["data-space-before-pt"] = String(spacing.spaceBeforePt)
if (spacing.spaceAfterPt > 0) html["data-space-after-pt"] = String(spacing.spaceAfterPt)
if (spacing.keepWithNext) html["data-keep-with-next"] = "true"
if (spacing.keepLinesTogether) html["data-keep-lines-together"] = "true"
if (spacing.preventWidowOrphan) html["data-prevent-widow-orphan"] = "true"
if (spacing.pageBreakBefore) html["data-page-break-before"] = "true"
if (style) html.style = style
if (spacing.pageBreakBefore) {
html.class = "docs-paragraph-spacing--page-break-before"
}
return html
}
export const DocsParagraphSpacing = Extension.create({
name: "docsParagraphSpacing",
addGlobalAttributes() {
return [
{
types: ["paragraph", "heading"],
attributes: {
lineHeight: {
default: DOCS_DEFAULT_PARAGRAPH_SPACING.lineHeight,
parseHTML: (element) => {
const raw = element.getAttribute("data-line-height")
if (raw != null) return parseLineHeightAttr(raw)
const style = (element as HTMLElement).style?.lineHeight
if (!style) return null
const numeric = Number.parseFloat(style)
return Number.isFinite(numeric) ? numeric : null
},
renderHTML: (attributes) => spacingRenderHtml(attributes),
},
spaceBeforePt: {
default: 0,
parseHTML: (element) => parseSpacingPt(element.getAttribute("data-space-before-pt")),
renderHTML: () => ({}),
},
spaceAfterPt: {
default: 0,
parseHTML: (element) => parseSpacingPt(element.getAttribute("data-space-after-pt")),
renderHTML: () => ({}),
},
keepWithNext: {
default: false,
parseHTML: (element) => parseBoolAttr(element.getAttribute("data-keep-with-next")),
renderHTML: () => ({}),
},
keepLinesTogether: {
default: false,
parseHTML: (element) => parseBoolAttr(element.getAttribute("data-keep-lines-together")),
renderHTML: () => ({}),
},
preventWidowOrphan: {
default: false,
parseHTML: (element) => parseBoolAttr(element.getAttribute("data-prevent-widow-orphan")),
renderHTML: () => ({}),
},
pageBreakBefore: {
default: false,
parseHTML: (element) => parseBoolAttr(element.getAttribute("data-page-break-before")),
renderHTML: () => ({}),
},
},
},
]
},
addCommands() {
return {
setDocsLineHeight:
(lineHeight: number) =>
({ editor }) =>
setDocsLineHeight(editor, lineHeight),
setDocsCustomSpacing:
(input) =>
({ editor }) =>
setDocsCustomSpacing(editor, input),
toggleDocsSpaceBefore:
() =>
({ editor }) =>
toggleDocsSpaceBefore(editor),
toggleDocsSpaceAfter:
() =>
({ editor }) =>
toggleDocsSpaceAfter(editor),
toggleDocsKeepWithNext:
() =>
({ editor }) =>
toggleDocsKeepWithNext(editor),
toggleDocsKeepLinesTogether:
() =>
({ editor }) =>
toggleDocsKeepLinesTogether(editor),
toggleDocsPreventWidowOrphan:
() =>
({ editor }) =>
toggleDocsPreventWidowOrphan(editor),
toggleDocsPageBreakBefore:
() =>
({ editor }) =>
toggleDocsPageBreakBefore(editor),
}
},
})