import React, { useCallback, useRef, useState } from 'react' import ReactMarkdown from 'react-markdown' import EmojiPickerButton from '../comments/EmojiPickerButton' function ToolbarButton({ title, onClick, children, className = '' }) { return ( ) } export default function UploadDescriptionEditor({ id, value, onChange, placeholder, error, rows = 8 }) { const [tab, setTab] = useState('write') const textareaRef = useRef(null) const focusTextarea = useCallback(() => { requestAnimationFrame(() => { textareaRef.current?.focus() }) }, []) const wrapSelection = useCallback((before, after, placeholderText = 'text') => { const textarea = textareaRef.current if (!textarea) return const current = String(value || '') const start = textarea.selectionStart const end = textarea.selectionEnd const selected = current.slice(start, end) const replacement = `${before}${selected || placeholderText}${after}` const next = current.slice(0, start) + replacement + current.slice(end) onChange?.(next) requestAnimationFrame(() => { textarea.focus() if (selected) { textarea.selectionStart = start + replacement.length textarea.selectionEnd = start + replacement.length } else { textarea.selectionStart = start + before.length textarea.selectionEnd = start + before.length + placeholderText.length } }) }, [onChange, value]) const prefixLines = useCallback((prefix) => { const textarea = textareaRef.current if (!textarea) return const current = String(value || '') const start = textarea.selectionStart const end = textarea.selectionEnd const selected = current.slice(start, end) const fallback = prefix.endsWith('. ') ? `${prefix}item` : `${prefix}item` const source = selected || fallback const nextBlock = source.split('\n').map((line) => `${prefix}${line}`).join('\n') const next = current.slice(0, start) + nextBlock + current.slice(end) onChange?.(next) requestAnimationFrame(() => { textarea.focus() textarea.selectionStart = start textarea.selectionEnd = start + nextBlock.length }) }, [onChange, value]) const insertLink = useCallback(() => { const textarea = textareaRef.current if (!textarea) return const current = String(value || '') const start = textarea.selectionStart const end = textarea.selectionEnd const selected = current.slice(start, end) const replacement = selected && /^https?:\/\//i.test(selected) ? `[link](${selected})` : `[link](https://)` const next = current.slice(0, start) + replacement + current.slice(end) onChange?.(next) requestAnimationFrame(() => { textarea.focus() if (selected && /^https?:\/\//i.test(selected)) { textarea.selectionStart = start + 1 textarea.selectionEnd = start + 5 } else { const urlStart = start + replacement.indexOf('https://') textarea.selectionStart = urlStart textarea.selectionEnd = urlStart + 'https://'.length } }) }, [onChange, value]) const insertAtCursor = useCallback((text) => { const textarea = textareaRef.current if (!textarea) { onChange?.(`${String(value || '')}${text}`) return } const current = String(value || '') const start = textarea.selectionStart ?? current.length const end = textarea.selectionEnd ?? current.length const next = current.slice(0, start) + text + current.slice(end) onChange?.(next) requestAnimationFrame(() => { textarea.focus() textarea.selectionStart = start + text.length textarea.selectionEnd = start + text.length }) }, [onChange, value]) const handleKeyDown = useCallback((event) => { const withModifier = event.ctrlKey || event.metaKey if (!withModifier) return switch (event.key.toLowerCase()) { case 'b': event.preventDefault() wrapSelection('**', '**') break case 'i': event.preventDefault() wrapSelection('*', '*') break case 'k': event.preventDefault() insertLink() break case 'e': event.preventDefault() wrapSelection('`', '`') break default: break } }, [insertLink, wrapSelection]) const previewValue = String(value || '').trim() return (
Safe formatting only
{tab === 'write' && ( <>
wrapSelection('**', '**')}>B wrapSelection('*', '*')}>I wrapSelection('`', '`')}>{''} Link