'use client' import type { Editor } from '@tiptap/react' import { useTiptapEditor } from '@admin/hooks/content-editor/use-tiptap-editor' import { isMarkInSchema } from '@admin/utils/editor/is-mark-in-schema' import { isNodeTypeSelected } from '@admin/utils/editor/is-node-type-selected' import { BoldIcon, Code2Icon, ItalicIcon, StrikethroughIcon as StrikeIcon, UnderlineIcon } from 'lucide-react' import { useCallback, useEffect, useState } from 'react' export type Mark = 'bold' | 'italic' | 'strike' | 'code' | 'underline' export interface UseMarkConfig { editor?: Editor | null type: Mark hideWhenUnavailable?: boolean onToggled?: () => void } export const markIcons = { bold: BoldIcon, italic: ItalicIcon, underline: UnderlineIcon, strike: StrikeIcon, code: Code2Icon } export const MARK_SHORTCUT_KEYS: Record = { bold: 'mod+b', italic: 'mod+i', underline: 'mod+u', strike: 'mod+shift+s', code: 'mod+e' } export function canToggleMark(editor: Editor | null, type: Mark): boolean { if (!editor || !editor.isEditable) return false if (!isMarkInSchema(type, editor) || isNodeTypeSelected(editor, ['image'])) return false return editor.can().toggleMark(type) } export function isMarkActive(editor: Editor | null, type: Mark): boolean { if (!editor || !editor.isEditable) return false return editor.isActive(type) } export function toggleMark(editor: Editor | null, type: Mark): boolean { if (!editor || !editor.isEditable) return false if (!canToggleMark(editor, type)) return false return editor.chain().focus().toggleMark(type).run() } export function shouldShowButton(props: { editor: Editor | null type: Mark hideWhenUnavailable: boolean }): boolean { const { editor, type, hideWhenUnavailable } = props if (!editor || !editor.isEditable) return false if (!hideWhenUnavailable) { return true } if (!isMarkInSchema(type, editor)) return false if (!editor.isActive('code')) { return canToggleMark(editor, type) } return true } export function getFormattedMarkName(type: Mark): string { return type.charAt(0).toUpperCase() + type.slice(1) } export function useMark(config: UseMarkConfig) { const { editor: providedEditor, type, hideWhenUnavailable = false, onToggled } = config const { editor } = useTiptapEditor(providedEditor) const [isVisible, setIsVisible] = useState(true) const canToggle = canToggleMark(editor, type) const isActive = isMarkActive(editor, type) useEffect(() => { if (!editor) return const handleSelectionUpdate = () => { setIsVisible(shouldShowButton({ editor, type, hideWhenUnavailable })) } handleSelectionUpdate() editor.on('selectionUpdate', handleSelectionUpdate) return () => { editor.off('selectionUpdate', handleSelectionUpdate) } }, [editor, type, hideWhenUnavailable]) const handleMark = useCallback(() => { if (!editor) return false const success = toggleMark(editor, type) if (success) { onToggled?.() } return success }, [editor, type, onToggled]) return { isVisible, isActive, handleMark, canToggle, label: getFormattedMarkName(type), shortcutKeys: MARK_SHORTCUT_KEYS[type], Icon: markIcons[type] } }