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