'use client' import { ClipboardEventHandler, FC, FocusEvent, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react' import { withController } from '@baseapp-frontend/utils' import { type MDXEditorMethods } from '@mdxeditor/editor' import { FormControl, FormHelperText } from '@mui/material' import { ForwardRefEditor } from './ForwardRefEditor' import { MENTION_MARKDOWN_PROTOCOL } from './InitializedMDXEditor/plugins/mentions/constants' import { stripMentionLinksFromMarkdown } from './InitializedMDXEditor/plugins/mentions/utils' import { EditorContainer as DefaultEditorContainer, StyledInputLabel } from './styled' import { MarkdownEditorFieldProps } from './types' const MarkdownEditorField: FC = ({ value, onChange, onKeyDown, onPaste, placeholder, toolbarConfig, showDiffSourceToggle, showUndoRedo, minHeight, maxHeight = 300, hasBorder = true, label, labelBackgroundColor, helperText, showHelperText = true, error, Toolbar, ToolbarProps, EditorContainer = DefaultEditorContainer, EditorContainerProps, inputRef, mentions, ...props }) => { const editorRef = useRef(null) const containerRef = useRef(null) const [focused, setFocused] = useState(false) const filled = Boolean(value && value.trim()) const isMentionsDisabled = mentions?.disabled === true const effectiveMentions = isMentionsDisabled ? undefined : mentions const sanitizedValue = useMemo( () => (isMentionsDisabled ? stripMentionLinksFromMarkdown(value ?? '') : (value ?? '')), [value, isMentionsDisabled], ) const handlePaste = useCallback>( (event) => { if (isMentionsDisabled) { const text = event.clipboardData?.getData('text/plain') ?? '' if (text.includes(MENTION_MARKDOWN_PROTOCOL)) { event.preventDefault() editorRef.current?.insertMarkdown(stripMentionLinksFromMarkdown(text)) return } } onPaste?.(event) }, [isMentionsDisabled, onPaste], ) // Expose a minimal HTMLInputElement-compatible proxy so consumers that hold // an HTMLInputElement ref (e.g. SocialInput → SocialTextField → here) can // still call .focus(), read .value, and invoke .setSelectionRange() — the // textarea idiom used by MessageUpdate/CommentUpdate to "focus with caret // at end". Reads delegate to MDXEditorMethods; caret positioning is // absorbed into focus() via defaultSelection: 'rootEnd'. useImperativeHandle( inputRef, () => ({ get value() { return editorRef.current?.getMarkdown() ?? '' }, setSelectionRange: () => {}, focus: (options?: FocusOptions) => { editorRef.current?.focus(undefined, { preventScroll: options?.preventScroll, defaultSelection: 'rootEnd', }) }, }) as unknown as HTMLInputElement, [], ) const handleFocus = useCallback(() => { setFocused(true) }, []) const handleBlur = useCallback((e: FocusEvent) => { if (containerRef.current && !containerRef.current.contains(e.relatedTarget as Node)) { setFocused(false) } }, []) useEffect(() => { const currentMarkdown = editorRef?.current?.getMarkdown()?.trim() const incomingValue = sanitizedValue.trim() if (currentMarkdown !== incomingValue) { editorRef?.current?.setMarkdown(sanitizedValue) } }, [sanitizedValue]) const editorContent = ( { onKeyDown?.(e) }} onPaste={handlePaste} onChange={(content: string) => { onChange?.(content) }} ref={editorRef} /> ) if (label) { return ( {label} {editorContent} {showHelperText && helperText && ( {helperText} )} ) } return (
{editorContent} {showHelperText && helperText && {helperText}}
) } export default withController(MarkdownEditorField)