153 lines
5.0 KiB
TypeScript
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),
|
|
}
|
|
},
|
|
})
|