'use client' import React, { useCallback, useMemo, useState } from 'react' import { createEditor, Descendant, Editor, Transforms, BaseEditor, Element, Text } from 'slate' import { Slate, Editable, withReact, RenderElementProps, RenderLeafProps, ReactEditor } from 'slate-react' import { withHistory, HistoryEditor } from 'slate-history' import { useDocumentStore } from '@/stores/document-store' import { useDesignStore } from '@/stores/design-store' import { FileText, Bold, Italic, Type, List, ListOrdered } from 'lucide-react' import { withMentions, getMentionSearch, insertMention } from './mention-plugin' import MentionSuggestions from './MentionSuggestions' import MentionElementComponent from './MentionElement' // Define custom types for Slate interface CustomElement { type?: string children: CustomText[] nodeId?: string nodeType?: string } interface CustomText { text: string bold?: boolean italic?: boolean underline?: boolean } declare module 'slate' { interface CustomTypes { Editor: BaseEditor & ReactEditor & HistoryEditor Element: CustomElement Text: CustomText } } const CustomElement = ({ attributes, children, element }: RenderElementProps) => { const customElement = element as CustomElement switch (customElement.type) { case 'mention': return case 'heading-one': return

{children}

case 'heading-two': return

{children}

case 'heading-three': return

{children}

case 'bulleted-list': return case 'numbered-list': return
    {children}
case 'list-item': return
  • {children}
  • case 'paragraph': default: return

    {children}

    } } const CustomLeaf = ({ attributes, children, leaf }: RenderLeafProps) => { const customLeaf = leaf as CustomText if (customLeaf.bold) { children = {children} } if (customLeaf.italic) { children = {children} } if (customLeaf.underline) { children = {children} } return {children} } const DocumentEditor: React.FC = () => { const { getCurrentDocument, updateCurrentDocumentContent } = useDocumentStore() const { currentDesign } = useDesignStore() const editor = useMemo(() => withMentions(withHistory(withReact(createEditor()))), []) const currentDoc = getCurrentDocument() const [mentionSearch, setMentionSearch] = useState<{ search: string } | null>(null) // Reset editor when document changes to prevent invalid path errors React.useEffect(() => { if (editor) { // Clear history and reset editor state when document changes editor.history = { undos: [], redos: [] } editor.selection = null } }, [currentDoc.id, editor]) const renderElement = useCallback((props: RenderElementProps) => , []) const renderLeaf = useCallback((props: RenderLeafProps) => , []) const handleChange = useCallback((newValue: Descendant[]) => { updateCurrentDocumentContent(newValue) // Check for mention triggers const mentionSearchResult = getMentionSearch(editor) setMentionSearch(mentionSearchResult ? { search: mentionSearchResult.search } : null) }, [updateCurrentDocumentContent, editor]) const isMarkActive = (format: string) => { const marks = Editor.marks(editor) return marks ? (marks as any)[format] === true : false } const isBlockActive = (format: string) => { const { selection } = editor if (!selection) return false const [match] = Array.from( Editor.nodes(editor, { at: Editor.unhangRange(editor, selection), match: n => !Editor.isEditor(n) && Element.isElement(n) && (n as CustomElement).type === format, }) ) return !!match } const toggleMark = (format: string) => { const isActive = isMarkActive(format) if (isActive) { Editor.removeMark(editor, format) } else { Editor.addMark(editor, format, true) } } const toggleBlock = (format: string) => { const isActive = isBlockActive(format) const isList = ['numbered-list', 'bulleted-list'].includes(format) Transforms.unwrapNodes(editor, { match: n => !Editor.isEditor(n) && Element.isElement(n) && ['numbered-list', 'bulleted-list'].includes((n as CustomElement).type as string), split: true, }) let newProperties: Partial if (isActive) { newProperties = { type: 'paragraph' } } else if (isList) { newProperties = { type: 'list-item' } } else { newProperties = { type: format } } Transforms.setNodes(editor, newProperties) if (!isActive && isList) { const block = { type: format, children: [] } as CustomElement Transforms.wrapNodes(editor, block) } } const handleMentionSelect = useCallback((suggestion: { id: string; type: string; label: string }) => { insertMention(editor, suggestion.id, suggestion.type, suggestion.label) setMentionSearch(null) }, [editor]) const handleMentionClose = useCallback(() => setMentionSearch(null), []) if (!currentDesign) { return (
    No design selected
    ) } return (
    {/* Toolbar */}
    {/* Editor */}
    {/* Mention Suggestions */} {mentionSearch && ( )}
    ) } export default DocumentEditor