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

239 lines
8.8 KiB
TypeScript

"use client"
import { useEffect, useMemo, useState } from "react"
import type { Editor } from "@tiptap/react"
import type {
DocsFormatMenuActions,
DocsFormatMenuState,
} from "@/components/drive/richtext/docs-format-menu"
import { applyDocsParagraphStyleById, readActiveParagraphStyleId } from "@/lib/drive/docs-paragraph-style-apply"
import {
docsClearTableCellBorders,
docsDefaultTableBorder,
docsSetTableAlignment,
docsSetTableCellBackground,
docsSetTableCellBordersAll,
docsSetTableRowHeight,
docsTableCanMerge,
docsTableCanSplit,
} from "@/lib/drive/docs-table-actions"
import {
readDocsCustomSpacingDraft,
readDocsParagraphSpacingState,
setDocsCustomSpacing,
} from "@/lib/drive/docs-line-spacing-actions"
import { DOCS_DEFAULT_PARAGRAPH_SPACING } from "@/lib/drive/docs-line-spacing"
import {
readDocsListState,
} from "@/lib/drive/docs-list-actions"
export function useDocsFormatMenu({
editor,
disabled,
onPageSetup,
}: {
editor: Editor | null
disabled?: boolean
onPageSetup?: () => void
}) {
const [revision, setRevision] = useState(0)
useEffect(() => {
if (!editor || disabled) return
const refresh = () => setRevision((value) => value + 1)
editor.on("transaction", refresh)
editor.on("selectionUpdate", refresh)
return () => {
editor.off("transaction", refresh)
editor.off("selectionUpdate", refresh)
}
}, [disabled, editor])
const state = useMemo<DocsFormatMenuState>(() => {
if (!editor) {
return {
isBold: false,
isItalic: false,
isUnderline: false,
isStrike: false,
alignLeft: true,
alignCenter: false,
alignRight: false,
alignJustify: false,
isBulletList: false,
isOrderedList: false,
styleId: "normal",
tableSelected: false,
tableCanMerge: false,
tableCanSplit: false,
graphicSelected: false,
imageSelected: false,
lineHeightPresetId: "1.15",
hasSpaceBefore: false,
hasSpaceAfter: false,
keepWithNext: false,
keepLinesTogether: false,
preventWidowOrphan: false,
pageBreakBefore: false,
customSpacingDraft: { ...DOCS_DEFAULT_PARAGRAPH_SPACING },
isTaskList: false,
bulletStyleId: null,
orderedStyleId: null,
checklistStyleId: null,
orderedListStart: 1,
}
}
let styleId = readActiveParagraphStyleId(editor)
const spacing = readDocsParagraphSpacingState(editor)
const listState = readDocsListState(editor)
return {
isBold: editor.isActive("bold"),
isItalic: editor.isActive("italic"),
isUnderline: editor.isActive("underline"),
isStrike: editor.isActive("strike"),
alignLeft: editor.isActive({ textAlign: "left" }),
alignCenter: editor.isActive({ textAlign: "center" }),
alignRight: editor.isActive({ textAlign: "right" }),
alignJustify: editor.isActive({ textAlign: "justify" }),
isBulletList: editor.isActive("bulletList"),
isOrderedList: editor.isActive("orderedList"),
styleId,
tableSelected: editor.isActive("table"),
tableCanMerge: docsTableCanMerge(editor),
tableCanSplit: docsTableCanSplit(editor),
graphicSelected:
editor.isActive("docsGraphic") || editor.isActive("docsInlineGraphic"),
imageSelected: editor.isActive("image"),
lineHeightPresetId: spacing.lineHeightPresetId,
hasSpaceBefore: spacing.hasSpaceBefore,
hasSpaceAfter: spacing.hasSpaceAfter,
keepWithNext: spacing.keepWithNext,
keepLinesTogether: spacing.keepLinesTogether,
preventWidowOrphan: spacing.preventWidowOrphan,
pageBreakBefore: spacing.pageBreakBefore,
customSpacingDraft: readDocsCustomSpacingDraft(editor),
isTaskList: listState.isTaskList,
bulletStyleId: listState.bulletStyleId,
orderedStyleId: listState.orderedStyleId,
checklistStyleId: listState.checklistStyleId,
orderedListStart: listState.orderedStart,
}
}, [editor, revision])
const actions = useMemo<DocsFormatMenuActions>(
() => ({
onBold: () => editor?.chain().focus().toggleMark("bold").run(),
onItalic: () => editor?.chain().focus().toggleMark("italic").run(),
onUnderline: () => editor?.chain().focus().toggleUnderline().run(),
onStrike: () => editor?.chain().focus().toggleMark("strike").run(),
onAlignLeft: () => editor?.chain().focus().setTextAlign("left").run(),
onAlignCenter: () => editor?.chain().focus().setTextAlign("center").run(),
onAlignRight: () => editor?.chain().focus().setTextAlign("right").run(),
onAlignJustify: () => editor?.chain().focus().setTextAlign("justify").run(),
onIncreaseIndent: () => {
if (!editor) return
editor.chain().focus().increaseDocsIndent().run()
},
onDecreaseIndent: () => {
if (!editor) return
editor.chain().focus().decreaseDocsIndent().run()
},
onToggleBulletList: () => editor?.chain().focus().toggleBulletList().run(),
onToggleOrderedList: () => editor?.chain().focus().toggleOrderedList().run(),
onApplyStyle: (styleId) => {
if (!editor) return
applyDocsParagraphStyleById(editor, styleId)
},
onClearFormatting: () =>
editor?.chain().focus().unsetAllMarks().clearNodes().run(),
onPageSetup,
onTableAddRowBefore: () => editor?.chain().focus().addRowBefore().run(),
onTableAddRowAfter: () => editor?.chain().focus().addRowAfter().run(),
onTableAddColumnBefore: () => editor?.chain().focus().addColumnBefore().run(),
onTableAddColumnAfter: () => editor?.chain().focus().addColumnAfter().run(),
onTableDeleteRow: () => editor?.chain().focus().deleteRow().run(),
onTableDeleteColumn: () => editor?.chain().focus().deleteColumn().run(),
onTableDelete: () => editor?.chain().focus().deleteTable().run(),
onTableMergeCells: () => editor?.chain().focus().mergeCells().run(),
onTableSplitCell: () => editor?.chain().focus().splitCell().run(),
onTableToggleHeaderRow: () => editor?.chain().focus().toggleHeaderRow().run(),
onTableToggleHeaderColumn: () => editor?.chain().focus().toggleHeaderColumn().run(),
onTableSetCellBackground: (color) => docsSetTableCellBackground(editor, color),
onTableAlignLeft: () => docsSetTableAlignment(editor, "left"),
onTableAlignCenter: () => docsSetTableAlignment(editor, "center"),
onTableAlignRight: () => docsSetTableAlignment(editor, "right"),
onTableFixStructure: () => editor?.chain().focus().fixTables().run(),
onTableSetCellBorders: (color) =>
docsSetTableCellBordersAll(editor, docsDefaultTableBorder(color)),
onTableClearCellBorders: () => docsClearTableCellBorders(editor),
onTableSetRowHeight: (height) => docsSetTableRowHeight(editor, height),
onSetLineHeight: (lineHeight) => {
if (!editor) return
editor.chain().focus().setDocsLineHeight(lineHeight).run()
},
onToggleSpaceBefore: () => {
if (!editor) return
editor.chain().focus().toggleDocsSpaceBefore().run()
},
onToggleSpaceAfter: () => {
if (!editor) return
editor.chain().focus().toggleDocsSpaceAfter().run()
},
onApplyCustomSpacing: (input) => {
if (!editor) return
setDocsCustomSpacing(editor, input)
},
onToggleKeepWithNext: () => {
if (!editor) return
editor.chain().focus().toggleDocsKeepWithNext().run()
},
onToggleKeepLinesTogether: () => {
if (!editor) return
editor.chain().focus().toggleDocsKeepLinesTogether().run()
},
onTogglePreventWidowOrphan: () => {
if (!editor) return
editor.chain().focus().toggleDocsPreventWidowOrphan().run()
},
onTogglePageBreakBefore: () => {
if (!editor) return
editor.chain().focus().toggleDocsPageBreakBefore().run()
},
onApplyBulletStyle: (styleId) => {
if (!editor) return
editor.chain().focus().applyDocsBulletStyle(styleId).run()
},
onApplyOrderedStyle: (styleId) => {
if (!editor) return
editor.chain().focus().applyDocsOrderedStyle(styleId).run()
},
onApplyChecklistStyle: (styleId) => {
if (!editor) return
editor.chain().focus().applyDocsChecklistStyle(styleId).run()
},
onRestartOrderedList: () => {
if (!editor) return
editor.chain().focus().restartDocsOrderedList().run()
},
onContinueOrderedList: () => {
if (!editor) return
editor.chain().focus().continueDocsOrderedList().run()
},
onSetOrderedListStart: (start) => {
if (!editor) return
editor.chain().focus().setDocsOrderedListStart(start).run()
},
}),
[editor, onPageSetup]
)
return {
actions,
state,
disabled,
}
}