82 lines
3.0 KiB
TypeScript
82 lines
3.0 KiB
TypeScript
/** Default line height for Normal text (matches built-in paragraph styles). */
|
|
export const DOCS_DEFAULT_LINE_HEIGHT = 1.15
|
|
|
|
/** Quick spacing added by « Insérer un espacement avant/après » (Google Docs ≈ 12 pt). */
|
|
export const DOCS_QUICK_PARAGRAPH_SPACE_PT = 12
|
|
|
|
export const DOCS_LINE_HEIGHT_PRESETS = [
|
|
{ id: "1", value: 1, label: "Simple" },
|
|
{ id: "1.15", value: 1.15, label: "1,15" },
|
|
{ id: "1.5", value: 1.5, label: "1,5" },
|
|
{ id: "2", value: 2, label: "Double" },
|
|
] as const
|
|
|
|
export type DocsLineHeightPresetId = (typeof DOCS_LINE_HEIGHT_PRESETS)[number]["id"]
|
|
|
|
export type DocsParagraphSpacingAttrs = {
|
|
lineHeight: number | null
|
|
spaceBeforePt: number
|
|
spaceAfterPt: number
|
|
keepWithNext: boolean
|
|
keepLinesTogether: boolean
|
|
preventWidowOrphan: boolean
|
|
pageBreakBefore: boolean
|
|
}
|
|
|
|
export const DOCS_DEFAULT_PARAGRAPH_SPACING: DocsParagraphSpacingAttrs = {
|
|
lineHeight: null,
|
|
spaceBeforePt: 0,
|
|
spaceAfterPt: 0,
|
|
keepWithNext: false,
|
|
keepLinesTogether: false,
|
|
preventWidowOrphan: false,
|
|
pageBreakBefore: false,
|
|
}
|
|
|
|
export function lineHeightPresetId(value: number | null | undefined): DocsLineHeightPresetId | "custom" | null {
|
|
if (value == null) return null
|
|
for (const preset of DOCS_LINE_HEIGHT_PRESETS) {
|
|
if (Math.abs(preset.value - value) < 0.001) return preset.id
|
|
}
|
|
return "custom"
|
|
}
|
|
|
|
export function parseLineHeightAttr(raw: unknown): number | null {
|
|
if (raw == null || raw === "") return null
|
|
const value = typeof raw === "number" ? raw : Number.parseFloat(String(raw))
|
|
return Number.isFinite(value) && value > 0 ? value : null
|
|
}
|
|
|
|
export function parseSpacingPt(raw: unknown): number {
|
|
if (raw == null || raw === "") return 0
|
|
const value = typeof raw === "number" ? raw : Number.parseFloat(String(raw))
|
|
return Number.isFinite(value) && value > 0 ? Math.round(value * 100) / 100 : 0
|
|
}
|
|
|
|
export function readParagraphSpacingAttrs(node: ProseMirrorNodeLike): DocsParagraphSpacingAttrs {
|
|
const attrs = node.attrs as Record<string, unknown>
|
|
return {
|
|
lineHeight: parseLineHeightAttr(attrs.lineHeight),
|
|
spaceBeforePt: parseSpacingPt(attrs.spaceBeforePt),
|
|
spaceAfterPt: parseSpacingPt(attrs.spaceAfterPt),
|
|
keepWithNext: Boolean(attrs.keepWithNext),
|
|
keepLinesTogether: Boolean(attrs.keepLinesTogether),
|
|
preventWidowOrphan: Boolean(attrs.preventWidowOrphan),
|
|
pageBreakBefore: Boolean(attrs.pageBreakBefore),
|
|
}
|
|
}
|
|
|
|
type ProseMirrorNodeLike = { attrs: Record<string, unknown> }
|
|
|
|
export function buildParagraphSpacingStyle(attrs: DocsParagraphSpacingAttrs): string {
|
|
const parts: string[] = []
|
|
if (attrs.lineHeight != null) parts.push(`line-height: ${attrs.lineHeight}`)
|
|
if (attrs.spaceBeforePt > 0) parts.push(`margin-top: ${attrs.spaceBeforePt}pt`)
|
|
if (attrs.spaceAfterPt > 0) parts.push(`margin-bottom: ${attrs.spaceAfterPt}pt`)
|
|
if (attrs.keepWithNext) parts.push("break-after: avoid")
|
|
if (attrs.keepLinesTogether) parts.push("break-inside: avoid")
|
|
if (attrs.preventWidowOrphan) parts.push("orphans: 2; widows: 2")
|
|
if (attrs.pageBreakBefore) parts.push("break-before: page")
|
|
return parts.join("; ")
|
|
}
|