diff --git a/src/ts/component/block/text.tsx b/src/ts/component/block/text.tsx index bc04b6418a..08bd4fbb2f 100644 --- a/src/ts/component/block/text.tsx +++ b/src/ts/component/block/text.tsx @@ -52,6 +52,7 @@ const BlockText = observer(class BlockText extends React.Component { this.onCopy = this.onCopy.bind(this); this.onSelectIcon = this.onSelectIcon.bind(this); this.onUploadIcon = this.onUploadIcon.bind(this); + this.onCompositionEnd = this.onCompositionEnd.bind(this); }; render () { @@ -207,7 +208,7 @@ const BlockText = observer(class BlockText extends React.Component { onMouseUp={this.onMouseUp} onInput={this.onInput} onDragStart={e => e.preventDefault()} - onCompositionEnd={this.onKeyUp} + onCompositionEnd={this.onCompositionEnd} /> ); @@ -253,55 +254,51 @@ const BlockText = observer(class BlockText extends React.Component { this._isMounted = false; }; - setValue (v: string) { + setValue (v: string, restoreRange?: I.TextRange) { const { rootId, block, renderLinks, renderObjects, renderMentions, renderEmoji } = this.props; const fields = block.fields || {}; - let text = String(v || ''); if (text === '\n') { text = ''; - }; - + } this.text = text; - let html = text; + // Only apply unicode replacements if not composing IME if (block.isTextCode()) { const lang = U.Prism.aliasMap[fields.lang] || 'plain';fields.lang; const grammar = Prism.languages[lang] || {}; - html = Prism.highlight(html, grammar, lang); this.refLang?.setValue(lang); - } else { + } else if (!keyboard.isComposition) { const parsed = Mark.fromUnicode(html, this.marks); - html = parsed.text; this.marks = parsed.marks; - html = Mark.toHtml(html, this.marks); - }; - + } else { + html = Mark.toHtml(html, this.marks); + } html = html.replace(/\n/g, '
'); - if (this.refEditable) { this.refEditable.setValue(html); - }; - + // Restore cursor position if provided + if (restoreRange) { + this.refEditable.setRange(restoreRange); + } + } if (!block.isTextCode() && (html != text) && this.marks.length) { if (this.frame) { raf.cancel(this.frame); - }; - + } this.frame = raf(() => { renderMentions(rootId, this.node, this.marks, () => this.getValue()); renderObjects(rootId, this.node, this.marks, () => this.getValue(), this.props); renderLinks(rootId, this.node, this.marks, () => this.getValue(), this.props); renderEmoji(this.node); }); - }; - + } if (block.isTextTitle() || block.isTextDescription()) { this.placeholderCheck(); - }; + } }; renderLatex () { @@ -1236,6 +1233,14 @@ const BlockText = observer(class BlockText extends React.Component { }; }; + onCompositionEnd = (e: any, value: string, range: I.TextRange) => { + // Use provided value and range if available, fallback to current + const v = value !== undefined ? value : this.getValue(); + const r = range !== undefined ? range : this.getRange(); + + this.setValue(v, r); + }; + }); export default BlockText; diff --git a/src/ts/component/form/editable.tsx b/src/ts/component/form/editable.tsx index 0bb9061628..c7bf30520f 100644 --- a/src/ts/component/form/editable.tsx +++ b/src/ts/component/form/editable.tsx @@ -23,7 +23,7 @@ interface Props { onInput?: (e: any) => void; onDragStart?: (e: any) => void; onCompositionStart?: (e: any) => void; - onCompositionEnd?: (e: any) => void; + onCompositionEnd?: (e: any, value: string, range: I.TextRange) => void; }; interface EditableRefProps { @@ -200,7 +200,7 @@ const Editable = forwardRef(({ keyboard.setComposition(false); if (onCompositionEnd) { - onCompositionEnd(e); + onCompositionEnd(e, getTextValue(), getRangeHandler()); }; };