diff --git a/src/components/Common/Editor/editorPlugins.tsx b/src/components/Common/Editor/editorPlugins.tsx index d0d43b4..a2d6cba 100644 --- a/src/components/Common/Editor/editorPlugins.tsx +++ b/src/components/Common/Editor/editorPlugins.tsx @@ -32,7 +32,7 @@ export const ProcessCodeBlocks = (content: string): string => { while ((match = codeBlockRegex.exec(content)) !== null) { const beforeCode = content.slice(lastIndex, match.index); - result += beforeCode.replace(/(<[^>]+>)/gi, '\\$1').replace(/^---$/gm, '\\---').replace(/<>/gm, '\\<>'); + result += beforeCode.replace(/(<[^>]+>)/gi, '\\$1').replace(/^[-*_]{3,}$/gm, '\\$&').replace(/<>/gm, '\\<>'); const [_, language, code] = match; if (!language || !(language in codeBlockLanguages)) { @@ -45,7 +45,7 @@ export const ProcessCodeBlocks = (content: string): string => { } const afterLastCode = content.slice(lastIndex); - result += afterLastCode.replace(/(<[^>]+>)/gi, '\\$1').replace(/^---$/gm, '\\---').replace(/<>/gm, '\\<>'); + result += afterLastCode.replace(/(<[^>]+>)/gi, '\\$1').replace(/^[-*_]{3,}$/gm, '\\$&').replace(/<>/gm, '\\<>'); return result; }; diff --git a/src/components/Common/Editor/index.tsx b/src/components/Common/Editor/index.tsx index 20837d3..c51cf03 100644 --- a/src/components/Common/Editor/index.tsx +++ b/src/components/Common/Editor/index.tsx @@ -25,7 +25,7 @@ import { AttachmentsRender } from '../AttachmentRender'; import { ShowCamera } from '../CameraDialog'; import { ShowAudioDialog } from '../AudioDialog'; -import { showTagSelectPop } from '../PopoverFloat/tagSelectPop'; +import { IsTagSelectVisible, showTagSelectPop } from '../PopoverFloat/tagSelectPop'; import { showAiWriteSuggestions } from '../PopoverFloat/aiWritePop'; import { AiStore } from '@/store/aiStore'; import { Icon } from '@iconify/react'; @@ -65,6 +65,27 @@ export const HandleFileType = (originFiles: Attachment[]): FileType[] => { return res } +export const getEditorElements = () => { + const editorElements = document.querySelectorAll('._contentEditable_uazmk_379') as NodeListOf + return editorElements +} + + +export const handleEditorKeyEvents = () => { + const editorElements = getEditorElements() + editorElements.forEach(element => { + element.addEventListener('keydown', (e) => { + const isTagSelectVisible = IsTagSelectVisible() + if (e.key === 'Enter' && isTagSelectVisible) { + e.preventDefault() + return false + } + }, true) + }) +} + + + const Editor = observer(({ content, onChange, onSend, isSendLoading, bottomSlot, originFiles, mode }: IProps) => { content = ProcessCodeBlocks(content) @@ -89,6 +110,7 @@ const Editor = observer(({ content, onChange, onSend, isSendLoading, bottomSlot, files: [] as FileType[], lastRange: null as Range | null, lastRangeText: '', + lastSelection: null as Selection | null, updateSendStatus() { if (store.files?.length == 0 && mdxEditorRef.current?.getMarkdown() == '') { return setCanSend(false) @@ -103,6 +125,7 @@ const Editor = observer(({ content, onChange, onSend, isSendLoading, bottomSlot, replaceMarkdownTag(text: string, forceFocus = false) { if (mdxEditorRef.current) { if (store.lastRange) { + console.log('replaceMarkdownTag', store.lastRangeText) const currentTextBeforeRange = store.lastRangeText.replace(/ /g, " ") ?? '' const currentText = mdxEditorRef.current!.getMarkdown().replace(/\\/g, '').replace(/ /g, " ") const tag = currentTextBeforeRange.replace(helper.regex.isEndsWithHashTag, "#" + text + ' ') @@ -141,30 +164,32 @@ const Editor = observer(({ content, onChange, onSend, isSendLoading, bottomSlot, focus(force = false) { console.log(mdxEditorRef.current) if (force && store.lastRange) { - onChange?.(mdxEditorRef.current!.getMarkdown()) - const editorElement = document.querySelector('._contentEditable_uazmk_379') as HTMLElement - if (editorElement) { - requestAnimationFrame(() => { - const range = document.createRange() - const selection = window.getSelection() - const walker = document.createTreeWalker( - editorElement, - NodeFilter.SHOW_TEXT, - null - ) - let lastNode: any = null - while (walker.nextNode()) { - lastNode = walker.currentNode - } - if (lastNode) { - range.setStart(lastNode, lastNode?.length) - range.setEnd(lastNode, lastNode?.length) - selection?.removeAllRanges() - selection?.addRange(range) - editorElement.focus() - } + const editorElements = getEditorElements() + if (editorElements.length > 0) { + editorElements.forEach(editorElement => { + requestAnimationFrame(() => { + const range = document.createRange() + const selection = window.getSelection() + const walker = document.createTreeWalker( + editorElement, + NodeFilter.SHOW_TEXT, + null + ) + let lastNode: any = null + while (walker.nextNode()) { + lastNode = walker.currentNode + } + if (lastNode) { + range.setStart(lastNode, lastNode?.length) + range.setEnd(lastNode, lastNode?.length) + selection?.removeAllRanges() + selection?.addRange(range) + editorElement.focus() + } + }) }) } + onChange?.(mdxEditorRef.current!.getMarkdown()) } else { mdxEditorRef.current!.focus(() => { onChange?.(mdxEditorRef.current!.getMarkdown()) @@ -231,16 +256,19 @@ const Editor = observer(({ content, onChange, onSend, isSendLoading, bottomSlot, handlePopTag() { const selection = window.getSelection(); if (selection!.rangeCount > 0) { - let lastRange = selection!.getRangeAt(0); - store.lastRange = lastRange - store.lastRangeText = lastRange.endContainer.textContent?.slice(0, lastRange.endOffset) ?? '' + if (!IsTagSelectVisible()) { + let lastRange = selection!.getRangeAt(0); + store.lastRange = lastRange + store.lastRangeText = lastRange.endContainer.textContent?.slice(0, lastRange.endOffset) ?? '' + store.lastSelection = selection + } const hasHashTagRegex = /#[^\s#]+/g const endsWithBankRegex = /\s$/g - const currentText = store.lastRange.startContainer.textContent?.slice(0, store.lastRange.endOffset) ?? '' + const currentText = store.lastRange?.startContainer.textContent?.slice(0, store.lastRange?.endOffset) ?? '' const isEndsWithBank = endsWithBankRegex.test(currentText) const isEndsWithHashTag = helper.regex.isEndsWithHashTag.test(currentText) if (currentText == '' || !isEndsWithHashTag) { - setTimeout(() => eventBus.emit('tagselect:hidden')) + setTimeout(() => eventBus.emit('tagselect:hidden')) return } if (isEndsWithHashTag && currentText != '' && !isEndsWithBank) { @@ -300,6 +328,7 @@ const Editor = observer(({ content, onChange, onSend, isSendLoading, bottomSlot, eventBus.on('editor:deleteLastChar', store.deleteLastChar) eventBus.on('editor:focus', store.focus) eventBus.on('editor:setMarkdownLoading', store.setMarkdownLoading) + handleEditorKeyEvents() return () => { eventBus.off('editor:replace', store.replaceMarkdownTag) eventBus.off('editor:clear', store.clearMarkdown) @@ -333,13 +362,6 @@ const Editor = observer(({ content, onChange, onSend, isSendLoading, bottomSlot, shadow='none' {...getRootProps()} className={`p-2 relative border-2 border-border transition-all ${isDragAccept ? 'border-2 border-green-500 border-dashed transition-all' : ''}`}>
{ - // if (e.key === 'Enter') { - // if (isPc) { - // e.preventDefault(); - // } - // } - // }} onKeyUp={async event => { event.preventDefault(); if (event.key === 'Enter' && event.ctrlKey) { diff --git a/src/components/Common/PopoverFloat/index.tsx b/src/components/Common/PopoverFloat/index.tsx index 4330b17..8fdc30a 100644 --- a/src/components/Common/PopoverFloat/index.tsx +++ b/src/components/Common/PopoverFloat/index.tsx @@ -25,7 +25,7 @@ const PopoverFloat = observer(({ }: PopoverFloatProps) => { const isPc = useMediaQuery('(min-width: 768px)') const popRef: any = useRef(null) - + const getPosition = () => { if (!anchorRect || typeof window === 'undefined') return { top: 0, left: 0 } let top = anchorRect.bottom || anchorRect.top @@ -35,7 +35,7 @@ const PopoverFloat = observer(({ const popWidth = popRef.current?.clientWidth ?? maxWidth const viewportHeight = window.innerHeight const viewportWidth = window.innerWidth - + if (top + popHeight > viewportHeight) { top = Math.max(10, anchorRect.top - popHeight) } else { @@ -55,19 +55,20 @@ const PopoverFloat = observer(({ useEffect(() => { if (!show || !closeOnClickOutside) return - + const handleClickOutside = (event: MouseEvent) => { if (popRef.current && !popRef.current.contains(event.target as Node)) { onHide() } } - + document.addEventListener('mousedown', handleClickOutside) return () => document.removeEventListener('mousedown', handleClickOutside) }, [show, onHide, closeOnClickOutside]) return ( - {children} diff --git a/src/components/Common/PopoverFloat/tagSelectPop.tsx b/src/components/Common/PopoverFloat/tagSelectPop.tsx index b6fa121..7cb0ec0 100644 --- a/src/components/Common/PopoverFloat/tagSelectPop.tsx +++ b/src/components/Common/PopoverFloat/tagSelectPop.tsx @@ -8,6 +8,12 @@ import { RootStore } from '@/store'; import { BlinkoStore } from '@/store/blinkoStore'; import { DialogStore } from '@/store/module/Dialog'; +export const IsTagSelectVisible = () => { + const tagSelectPopup = document.getElementById('tag-select-popup') + const isTagSelectVisible = tagSelectPopup && window.getComputedStyle(tagSelectPopup).opacity !== '0' + return isTagSelectVisible +} + export const showTagSelectPop = (text: string = '') => { setTimeout(() => { const selection = window.getSelection(); @@ -57,20 +63,19 @@ const TagSelect = observer(() => { switch (e.key) { case 'ArrowDown': e.preventDefault() - e.stopPropagation() store.selectedIndex = (store.selectedIndex + 1) % store.tagList.length break case 'ArrowUp': e.preventDefault() - e.stopPropagation() store.selectedIndex = (store.selectedIndex - 1 + store.tagList.length) % store.tagList.length break case 'Enter': - e.preventDefault() e.stopPropagation() + e.preventDefault() const selectedTag = store.tagList[store.selectedIndex] if (selectedTag) { store.hidden() + console.log('selectedTag', selectedTag) eventBus.emit('editor:replace', selectedTag, true) } break