diff --git a/src/app/components/editor/autocomplete/AutocompleteMenu.tsx b/src/app/components/editor/autocomplete/AutocompleteMenu.tsx index 938c27dcd4..84acf2e213 100644 --- a/src/app/components/editor/autocomplete/AutocompleteMenu.tsx +++ b/src/app/components/editor/autocomplete/AutocompleteMenu.tsx @@ -7,7 +7,7 @@ import { Header, Menu, Scroll, config } from 'folds'; import { preventScrollWithArrowKey, stopPropagation } from '$utils/keyboard'; import { useAlive } from '$hooks/useAlive'; import type { Editor } from 'slate'; -import { ReactEditor } from 'slate-react'; +import { focusEditor } from '$components/editor/utils'; import * as css from './AutocompleteMenu.css'; import { BaseAutocompleteMenu } from './BaseAutocompleteMenu'; @@ -33,7 +33,7 @@ export function AutocompleteMenu({ } }; const [isActive, setIsActive] = useState(true); - useEffect(() => ReactEditor.focus(editor), [editor, isActive]); + useEffect(() => focusEditor(editor), [editor, isActive]); function handleInput(evt: KeyboardEvent) { if (!evt) return; if ( diff --git a/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx b/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx index 2b4776a2d8..a630127957 100644 --- a/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx +++ b/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx @@ -1,7 +1,6 @@ import type { KeyboardEvent as ReactKeyboardEvent } from 'react'; import { useEffect, useMemo } from 'react'; import type { Editor } from 'slate'; -import { ReactEditor } from 'slate-react'; import { Box, MenuItem, Text, toRem } from 'folds'; import type { Room } from '$types/matrix-sdk'; @@ -93,7 +92,6 @@ export function EmoticonAutocomplete({ const emoticonEl = createEmoticonElement(key, shortcode); replaceWithElement(editor, query.range, emoticonEl); moveCursor(editor, true); - ReactEditor.focus(editor); requestClose(); }); diff --git a/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx b/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx index dae9469651..ee125eff3d 100644 --- a/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx +++ b/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx @@ -1,7 +1,6 @@ import type { KeyboardEvent as ReactKeyboardEvent } from 'react'; import { useCallback, useEffect, useMemo } from 'react'; import type { Editor } from 'slate'; -import { ReactEditor } from 'slate-react'; import { Avatar, MenuItem, Text } from 'folds'; import { Hash, sizedIcon } from '$components/icons/phosphor'; import type { MatrixClient } from '$types/matrix-sdk'; @@ -121,7 +120,6 @@ export function RoomMentionAutocomplete({ ); replaceWithElement(editor, query.range, mentionEl); moveCursor(editor, true); - ReactEditor.focus(editor); requestClose(); }; diff --git a/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx b/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx index 4afd29495e..d378fa45e3 100644 --- a/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx +++ b/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx @@ -1,7 +1,6 @@ import type { KeyboardEvent as ReactKeyboardEvent } from 'react'; import { useEffect } from 'react'; import type { Editor } from 'slate'; -import { ReactEditor } from 'slate-react'; import { Avatar, MenuItem, Text } from 'folds'; import { userFallbackIcon } from '$components/icons/phosphor'; import type { MatrixClient, Room, RoomMember } from '$types/matrix-sdk'; @@ -123,7 +122,6 @@ export function UserMentionAutocomplete({ ); replaceWithElement(editor, query.range, mentionEl); moveCursor(editor, true); - ReactEditor.focus(editor); requestClose(); }; diff --git a/src/app/components/editor/utils.ts b/src/app/components/editor/utils.ts index a50420acfa..0d1b935a64 100644 --- a/src/app/components/editor/utils.ts +++ b/src/app/components/editor/utils.ts @@ -1,5 +1,6 @@ import type { BasePoint, BaseRange } from 'slate'; import { Editor, Element, Point, Range, Text, Transforms } from 'slate'; +import { ReactEditor } from 'slate-react'; import type { Room } from '$types/matrix-sdk'; import type { Nicknames } from '$state/nicknames'; import { getMxIdLocalPart, isUserId } from '$utils/matrix'; @@ -157,6 +158,16 @@ export const moveCursor = (editor: Editor, withSpace?: boolean) => { Transforms.collapse(editor, { edge: 'end' }); }; +export const focusEditor = (editor: Editor) => { + requestAnimationFrame(() => { + try { + ReactEditor.focus(editor); + } catch { + // Slate DOM may not reflect the latest selection yet. + } + }); +}; + interface PointUntilCharOptions { match: (char: string) => boolean; reverse?: boolean; diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index f4d8e9e532..82a9a094b6 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -60,6 +60,7 @@ import { getLinks, MarkdownFormattingToolbarBottom, MarkdownFormattingToolbarToggle, + focusEditor, replaceWithElement, BlockType, } from '$components/editor'; @@ -752,8 +753,12 @@ export const RoomInput = forwardRef( }; const handleCloseAutocomplete = useCallback(() => { - setAutocompleteQuery(undefined); - ReactEditor.focus(editor); + setAutocompleteQuery((prev) => { + if (prev !== undefined) { + focusEditor(editor); + } + return undefined; + }); }, [editor]); const handleQuickReact = useCallback(