ultisuite-client/lib/drive/use-docs-toolbar-state.ts
2026-06-09 17:06:20 +02:00

153 lines
4.7 KiB
TypeScript

"use client"
import { useEditorState } from "@tiptap/react"
import type { Editor } from "@tiptap/react"
import {
canStepFontSizePx,
readFontSizeToolbarState,
type FontSizeToolbarState,
} from "@/lib/drive/docs-font-size"
import {
readFontFamilyToolbarState,
type FontFamilyToolbarState,
} from "@/lib/drive/docs-font-family"
export const DOCS_TOOLBAR_DEFAULT_TEXT_COLOR = "#000000"
function activeTextStyle(editor: Editor): string {
if (editor.isActive("heading", { level: 1 })) return "heading1"
if (editor.isActive("heading", { level: 2 })) return "heading2"
if (editor.isActive("heading", { level: 3 })) return "heading3"
if (editor.isActive("heading", { level: 4 })) return "heading4"
return "paragraph"
}
export type DocsToolbarEditorState = {
canUndo: boolean
canRedo: boolean
styleId: string
fontFamilyState: FontFamilyToolbarState
fontSizeState: FontSizeToolbarState
canStepFontSizeDown: boolean
canStepFontSizeUp: boolean
textColor: string
highlightColor: string | null
isBold: boolean
isItalic: boolean
isUnderline: boolean
isLink: boolean
alignLeft: boolean
alignCenter: boolean
alignRight: boolean
alignJustify: boolean
isBulletList: boolean
isOrderedList: boolean
}
function readHighlightColor(editor: Editor): string | null {
const { storedMarks } = editor.state
if (storedMarks) {
const mark = storedMarks.find((m) => m.type.name === "highlight")
const stored = mark?.attrs.color as string | undefined
if (stored) return stored
}
if (!editor.isActive("highlight")) return null
const color = editor.getAttributes("highlight").color as string | undefined
return color ?? null
}
function selectDocsToolbarState({ editor }: { editor: Editor }): DocsToolbarEditorState {
const highlightColor = readHighlightColor(editor)
return {
canUndo: editor.can().chain().focus().undo().run(),
canRedo: editor.can().chain().focus().redo().run(),
styleId: activeTextStyle(editor),
fontFamilyState: readFontFamilyToolbarState(editor),
fontSizeState: readFontSizeToolbarState(editor),
canStepFontSizeDown: canStepFontSizePx(editor, -1),
canStepFontSizeUp: canStepFontSizePx(editor, 1),
textColor:
(editor.getAttributes("textStyle").color as string | undefined) ||
DOCS_TOOLBAR_DEFAULT_TEXT_COLOR,
highlightColor,
isBold: editor.isActive("bold"),
isItalic: editor.isActive("italic"),
isUnderline: editor.isActive("underline"),
isLink: editor.isActive("link"),
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"),
}
}
function fontFamilyStateEqual(a: FontFamilyToolbarState, b: FontFamilyToolbarState): boolean {
if (a.kind !== b.kind) return false
if (a.kind === "single" && b.kind === "single") return a.name === b.name
return true
}
function fontSizeStateEqual(a: FontSizeToolbarState, b: FontSizeToolbarState): boolean {
if (a.kind !== b.kind) return false
if (a.kind === "single" && b.kind === "single") return a.size === b.size
return true
}
function toolbarStateEqual(
a: DocsToolbarEditorState,
b: DocsToolbarEditorState | null
): boolean {
if (!b) return false
if (!fontFamilyStateEqual(a.fontFamilyState, b.fontFamilyState)) return false
if (!fontSizeStateEqual(a.fontSizeState, b.fontSizeState)) return false
return (
a.canUndo === b.canUndo &&
a.canRedo === b.canRedo &&
a.styleId === b.styleId &&
a.canStepFontSizeDown === b.canStepFontSizeDown &&
a.canStepFontSizeUp === b.canStepFontSizeUp &&
a.textColor === b.textColor &&
a.highlightColor === b.highlightColor &&
a.isBold === b.isBold &&
a.isItalic === b.isItalic &&
a.isUnderline === b.isUnderline &&
a.isLink === b.isLink &&
a.alignLeft === b.alignLeft &&
a.alignCenter === b.alignCenter &&
a.alignRight === b.alignRight &&
a.alignJustify === b.alignJustify &&
a.isBulletList === b.isBulletList &&
a.isOrderedList === b.isOrderedList
)
}
function selectDocsToolbarStateOptional({
editor,
}: {
editor: Editor | null
}): DocsToolbarEditorState | null {
if (!editor) return null
return selectDocsToolbarState({ editor })
}
function toolbarStateEqualOptional(
a: DocsToolbarEditorState | null,
b: DocsToolbarEditorState | null
): boolean {
if (a == null || b == null) return a === b
return toolbarStateEqual(a, b)
}
export function useDocsToolbarState(editor: Editor | null): DocsToolbarEditorState | null {
return useEditorState({
editor,
selector: selectDocsToolbarStateOptional,
equalityFn: toolbarStateEqualOptional,
})
}