Skip to content

Commit 9163b79

Browse files
[Chat] Fix for inconsistencies in cursor position during mention editing (#3242)
1 parent c2f1ad4 commit 9163b79

4 files changed

Lines changed: 57 additions & 18 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "none",
3+
"comment": "Fixed cursor position during mention editing",
4+
"packageName": "@azure/communication-react",
5+
"email": "98852890+vhuseinova-msft@users.noreply.github.com",
6+
"dependentChangeType": "none"
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "none",
3+
"comment": "Fixed cursor position during mention editing",
4+
"packageName": "@azure/communication-react",
5+
"email": "98852890+vhuseinova-msft@users.noreply.github.com",
6+
"dependentChangeType": "none"
7+
}

packages/react-components/src/components/TextFieldWithMention/TextFieldWithMention.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,8 @@ export const TextFieldWithMention = (props: TextFieldWithMentionProps): JSX.Elem
627627

628628
// Adjust the selection range based on a mouse / touch interaction
629629
const handleOnInteractionStarted = useCallback(() => {
630+
// reset caret index as a new selection is started or cursor position will be changed
631+
setCaretIndex(undefined);
630632
setInteractionStartSelection(undefined);
631633
setShouldHandleMoveEvent(true);
632634
setShouldHandleOnMouseDownDuringSelect(true);
@@ -686,6 +688,17 @@ export const TextFieldWithMention = (props: TextFieldWithMentionProps): JSX.Elem
686688
});
687689
}}
688690
onSelect={(e) => {
691+
// update selection if needed
692+
if (caretIndex !== undefined) {
693+
// sometimes setting selectionRage in effect for updating caretIndex doesn't work as expected and
694+
// onSelect still returns outdated value for cursor position
695+
// e.g. when user select some text and a first name in a mention then delete or type something else
696+
if (caretIndex !== e.currentTarget.selectionStart || caretIndex !== e.currentTarget.selectionEnd) {
697+
e.currentTarget.setSelectionRange(caretIndex, caretIndex);
698+
}
699+
setCaretIndex(undefined);
700+
return;
701+
}
689702
handleOnSelect({
690703
event: e,
691704
inputTextValue,

packages/react-components/src/components/TextFieldWithMention/mentionTagUtils.ts

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -455,25 +455,36 @@ export const updateHTML = (props: UpdateHTMLProps): { updatedHTML: string; updat
455455
break;
456456
} else if (startIndex > tag.plainTextBeginIndex && oldPlainTextEndIndex > closingTagInfo.plainTextEndIndex) {
457457
// the change started in the tag but finishes somewhere further
458-
const startChangeDiff = startIndex - tag.plainTextBeginIndex - mentionTagLength;
459458
if (isMentionTag) {
460-
const updateMentionTagResult = handleMentionTagUpdate({
461-
htmlText,
462-
oldPlainText,
463-
lastProcessedHTMLIndex,
464-
processedChange: '',
465-
change,
466-
tag,
467-
closeTagIdx: closingTagInfo.closeTagIdx,
468-
closeTagLength: closingTagInfo.closeTagLength,
469-
plainTextEndIndex: closingTagInfo.plainTextEndIndex,
470-
startIndex,
471-
oldPlainTextEndIndex,
472-
mentionTagLength
473-
});
474-
result += updateMentionTagResult.result;
475-
lastProcessedHTMLIndex = updateMentionTagResult.htmlIndex;
476-
// no need to handle plainTextSelectionEndIndex as the change will be added later
459+
if (startIndex === closingTagInfo.plainTextEndIndex) {
460+
// the change should be handled out of mention tag
461+
// as startIndex === closingTagInfo.plainTextEndIndex and oldPlainTextEndIndex > closingTagInfo.plainTextEndIndex
462+
result += htmlText.substring(
463+
lastProcessedHTMLIndex,
464+
closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength
465+
);
466+
// no need to handle plainTextSelectionEndIndex as the change will be added later
467+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
468+
} else {
469+
// part of the mention tag was changed/deleted
470+
const updateMentionTagResult = handleMentionTagUpdate({
471+
htmlText,
472+
oldPlainText,
473+
lastProcessedHTMLIndex,
474+
processedChange: '',
475+
change,
476+
tag,
477+
closeTagIdx: closingTagInfo.closeTagIdx,
478+
closeTagLength: closingTagInfo.closeTagLength,
479+
plainTextEndIndex: closingTagInfo.plainTextEndIndex,
480+
startIndex,
481+
oldPlainTextEndIndex,
482+
mentionTagLength
483+
});
484+
result += updateMentionTagResult.result;
485+
lastProcessedHTMLIndex = updateMentionTagResult.htmlIndex;
486+
// no need to handle plainTextSelectionEndIndex as the change will be added later
487+
}
477488
} else if (tag.subTags !== undefined && tag.subTags.length !== 0 && tag.content !== undefined) {
478489
// with subtags
479490
const stringBefore = htmlText.substring(lastProcessedHTMLIndex, tag.openTagIndex + tag.openTagBody.length);
@@ -490,6 +501,7 @@ export const updateHTML = (props: UpdateHTMLProps): { updatedHTML: string; updat
490501
result += stringBefore + updatedContent.updatedHTML;
491502
} else {
492503
// no subtags
504+
const startChangeDiff = startIndex - tag.plainTextBeginIndex - mentionTagLength;
493505
result += htmlText.substring(
494506
lastProcessedHTMLIndex,
495507
tag.openTagIndex + tag.openTagBody.length + startChangeDiff

0 commit comments

Comments
 (0)