Skip to content

Commit 30baac0

Browse files
committed
fix: at mention was not working properly
1 parent ff20b25 commit 30baac0

File tree

1 file changed

+76
-43
lines changed

1 file changed

+76
-43
lines changed

webview-ui/src/components/LexicalTextArea.tsx

Lines changed: 76 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { LexicalComposer } from "@lexical/react/LexicalComposer"
1111
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin"
1212
import { ContentEditable } from "@lexical/react/LexicalContentEditable"
1313
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin"
14-
import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin"
1514
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"
1615
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary"
1716
import {
@@ -24,8 +23,6 @@ import {
2423
CLEAR_HISTORY_COMMAND,
2524
COMMAND_PRIORITY_EDITOR,
2625
createCommand,
27-
EditorState,
28-
LexicalEditor,
2926
LexicalNode,
3027
LexicalCommand,
3128
TextNode,
@@ -197,31 +194,38 @@ const LexicalTextArea = forwardRef<LexicalTextAreaHandle, LexicalTextAreaProps>(
197194
}
198195
}, [value, editor])
199196

200-
// --- OnChange Handler ---
201-
const handleOnChange = (editorState: EditorState, currentEditor: LexicalEditor, tags: Set<string>) => {
202-
if (isFirstRender.current) return
197+
// --- Event Handlers ---
198+
const handleInternalFocus = (event: React.FocusEvent<HTMLDivElement>) => {
199+
setIsFocused(true)
200+
onFocus(event)
201+
}
203202

204-
editorState.read(() => {
205-
const plainText = $getRoot().getTextContent()
203+
const handleInternalBlur = (event: React.FocusEvent<HTMLDivElement>) => {
204+
setTimeout(() => {
205+
setIsFocused(false)
206+
onBlur(event)
207+
}, 100)
208+
}
206209

207-
if (plainText !== value) {
208-
onChange(plainText)
209-
}
210+
const handleKeyDown = async (event: React.KeyboardEvent<HTMLDivElement>) => {
211+
// Context Menu Logic - Check before Lexical update
212+
let showMenu = false
213+
let menuType: "mention" | "command" | null = null
214+
let query = ""
210215

211-
// Context Menu Logic
216+
editor.getEditorState().read(() => {
212217
const selection = $getSelection()
213-
let showMenu = false
214-
let menuType: "mention" | "command" | null = null
215-
let query = ""
216218

217219
if ($isRangeSelection(selection) && selection.isCollapsed()) {
218220
const anchor = selection.anchor
219221
const anchorNode = anchor.getNode()
220222
const anchorOffset = anchor.offset
221223

222-
if (anchorNode instanceof TextNode) {
224+
let textBeforeCursor = ""
225+
226+
if ($isRangeSelection(selection) && selection.isCollapsed() && anchorNode instanceof TextNode) {
223227
const textContent = anchorNode.getTextContent()
224-
const textBeforeCursor = textContent.substring(0, anchorOffset)
228+
textBeforeCursor = textContent.substring(0, anchorOffset)
225229

226230
const root = $getRoot()
227231
const firstParagraph = root.getFirstChild()
@@ -235,9 +239,25 @@ const LexicalTextArea = forwardRef<LexicalTextAreaHandle, LexicalTextAreaProps>(
235239
query = textBeforeCursor.substring(1)
236240
}
237241
}
242+
}
238243

239-
// handles mention
240-
if (!showMenu) {
244+
// handles mention
245+
if (!showMenu) {
246+
// Check if the pressed key is '@'
247+
if (event.key === "@") {
248+
// Check if '@' is at the start or preceded by whitespace in the current text before cursor
249+
const charBeforeCursor =
250+
textBeforeCursor.length > 0 ? textBeforeCursor[textBeforeCursor.length - 1] : null
251+
if (
252+
textBeforeCursor.length === 0 ||
253+
(charBeforeCursor !== null && /\s/.test(charBeforeCursor))
254+
) {
255+
showMenu = true
256+
menuType = "mention"
257+
query = "" // Initial query is empty after typing '@'
258+
}
259+
} else {
260+
// For other keys, check if we are currently in a mention query
241261
const mentionMatch = textBeforeCursor.match(/@([^\s@]*)$/)
242262
if (mentionMatch) {
243263
const charBeforeMention = textBeforeCursor[mentionMatch.index! - 1]
@@ -250,32 +270,46 @@ const LexicalTextArea = forwardRef<LexicalTextAreaHandle, LexicalTextAreaProps>(
250270
}
251271
}
252272
}
273+
})
274+
275+
// If Enter is pressed and a menu is expected, prevent default behavior
276+
if (event.key === "Enter" && showMenu) {
277+
event.preventDefault()
278+
event.stopPropagation()
279+
// Do not return here, allow the rest of the handler to update menu state
280+
}
281+
282+
console.log({ showMenu, menuType })
283+
284+
// Call context menu handlers immediately
285+
onShowContextMenu(showMenu, menuType)
286+
if (menuType === "mention") {
287+
onMentionQueryChange(query)
288+
onCommandQueryChange("")
289+
} else if (menuType === "command") {
290+
onCommandQueryChange(query)
291+
onMentionQueryChange("")
292+
} else {
293+
onCommandQueryChange("")
294+
onMentionQueryChange("")
295+
}
296+
297+
// Continue with Lexical update for text content changes
298+
editor.update(() => {
299+
if (isFirstRender.current) {
300+
isFirstRender.current = false
301+
return
302+
}
253303

254-
onShowContextMenu(showMenu, menuType)
255-
if (menuType === "mention") {
256-
onMentionQueryChange(query)
257-
onCommandQueryChange("")
258-
} else if (menuType === "command") {
259-
onCommandQueryChange(query)
260-
onMentionQueryChange("")
261-
} else {
262-
onCommandQueryChange("")
263-
onMentionQueryChange("")
304+
const plainText = $getRoot().getTextContent()
305+
306+
// if the text content is not equal to the value, update the value
307+
if (plainText !== value) {
308+
onChange(plainText)
264309
}
265310
})
266-
}
267311

268-
// --- Event Handlers ---
269-
const handleInternalFocus = (event: React.FocusEvent<HTMLDivElement>) => {
270-
setIsFocused(true)
271-
onFocus(event)
272-
}
273-
274-
const handleInternalBlur = (event: React.FocusEvent<HTMLDivElement>) => {
275-
setTimeout(() => {
276-
setIsFocused(false)
277-
onBlur(event)
278-
}, 100)
312+
onKeyDown(event)
279313
}
280314

281315
// --- Drop/Drag Handlers ---
@@ -603,9 +637,9 @@ const LexicalTextArea = forwardRef<LexicalTextAreaHandle, LexicalTextAreaProps>(
603637
outline: "none", // Remove default browser outline
604638
tabSize: 4, // Standard tab size
605639
}}
606-
onKeyDown={onKeyDown}
607640
onFocus={handleInternalFocus}
608641
onBlur={handleInternalBlur}
642+
onKeyDown={handleKeyDown}
609643
/>
610644
}
611645
placeholder={
@@ -628,7 +662,6 @@ const LexicalTextArea = forwardRef<LexicalTextAreaHandle, LexicalTextAreaProps>(
628662
}
629663
ErrorBoundary={LexicalErrorBoundary} // Basic error boundary
630664
/>
631-
<OnChangePlugin onChange={handleOnChange} ignoreSelectionChange={true} />
632665
<HistoryPlugin />
633666
<AutosizePlugin contentEditableRef={contentEditableRef} onHeightChange={onHeightChange} />
634667
</div>

0 commit comments

Comments
 (0)