@azure/communication-react 1.5.1-alpha-202305160013 → 1.5.1-alpha-202305180013
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dist-cjs/communication-react/index.js +595 -373
- package/dist/dist-cjs/communication-react/index.js.map +1 -1
- package/dist/dist-esm/acs-ui-common/src/telemetryVersion.js +1 -1
- package/dist/dist-esm/acs-ui-common/src/telemetryVersion.js.map +1 -1
- package/dist/dist-esm/react-components/src/components/CameraButton.js +4 -2
- package/dist/dist-esm/react-components/src/components/CameraButton.js.map +1 -1
- package/dist/dist-esm/react-components/src/components/InputBoxComponent.js +559 -363
- package/dist/dist-esm/react-components/src/components/InputBoxComponent.js.map +1 -1
- package/dist/dist-esm/react-components/src/components/VideoEffects/VideoEffectsItem.js +1 -1
- package/dist/dist-esm/react-components/src/components/VideoEffects/VideoEffectsItem.js.map +1 -1
- package/dist/dist-esm/react-composites/src/composites/CallComposite/components/CallArrangement.js +12 -2
- package/dist/dist-esm/react-composites/src/composites/CallComposite/components/CallArrangement.js.map +1 -1
- package/dist/dist-esm/react-composites/src/composites/CallComposite/pages/ConfigurationPage.js +1 -1
- package/dist/dist-esm/react-composites/src/composites/CallComposite/pages/ConfigurationPage.js.map +1 -1
- package/dist/dist-esm/react-composites/src/composites/CallWithChatComposite/CallWithChatComposite.js +12 -1
- package/dist/dist-esm/react-composites/src/composites/CallWithChatComposite/CallWithChatComposite.js.map +1 -1
- package/dist/dist-esm/react-composites/src/composites/ChatComposite/adapter/AzureCommunicationChatAdapter.js +4 -0
- package/dist/dist-esm/react-composites/src/composites/ChatComposite/adapter/AzureCommunicationChatAdapter.js.map +1 -1
- package/dist/dist-esm/react-composites/src/composites/common/ControlBar/CommonCallControlBar.js +3 -1
- package/dist/dist-esm/react-composites/src/composites/common/ControlBar/CommonCallControlBar.js.map +1 -1
- package/dist/dist-esm/react-composites/src/composites/common/Drawer/MoreDrawer.js +1 -1
- package/dist/dist-esm/react-composites/src/composites/common/Drawer/MoreDrawer.js.map +1 -1
- package/package.json +8 -8
@@ -25,14 +25,16 @@ import { _MentionPopover } from './MentionPopover';
|
|
25
25
|
/* @conditional-compile-remove(mention) */
|
26
26
|
import { useDebouncedCallback } from 'use-debounce';
|
27
27
|
/* @conditional-compile-remove(mention) */
|
28
|
-
const
|
28
|
+
const DEFAULT_MENTION_TRIGGER = '@';
|
29
|
+
/* @conditional-compile-remove(mention) */
|
30
|
+
const MSFT_MENTION_TAG = 'msft-mention';
|
29
31
|
/**
|
30
32
|
* @private
|
31
33
|
*/
|
32
34
|
export const InputBoxComponent = (props) => {
|
33
|
-
const { styles, id, 'data-ui-id': dataUiId, textValue, onChange, textFieldRef, placeholderText, onKeyDown, onEnterKeyDown, supportNewline, inputClassName, errorMessage, disabled,
|
35
|
+
const { styles, id, 'data-ui-id': dataUiId, textValue, onChange, textFieldRef, placeholderText, onKeyDown, onEnterKeyDown, supportNewline, inputClassName, errorMessage, disabled,
|
34
36
|
/* @conditional-compile-remove(mention) */
|
35
|
-
mentionLookupOptions } = props;
|
37
|
+
mentionLookupOptions, children } = props;
|
36
38
|
const inputBoxRef = useRef(null);
|
37
39
|
/* @conditional-compile-remove(mention) */
|
38
40
|
// Current suggestion list, provided by the callback
|
@@ -48,10 +50,13 @@ export const InputBoxComponent = (props) => {
|
|
48
50
|
/* @conditional-compile-remove(mention) */
|
49
51
|
const [tagsValue, setTagsValue] = useState([]);
|
50
52
|
/* @conditional-compile-remove(mention) */
|
53
|
+
// Index of the previous selection start in the text field
|
51
54
|
const [selectionStartValue, setSelectionStartValue] = useState(null);
|
52
55
|
/* @conditional-compile-remove(mention) */
|
56
|
+
// Index of the previous selection end in the text field
|
53
57
|
const [selectionEndValue, setSelectionEndValue] = useState(null);
|
54
58
|
/* @conditional-compile-remove(mention) */
|
59
|
+
// Boolean value to check if onMouseDown event should be handled during select as selection range for onMouseDown event is not updated yet and the selection range for mouse click/taps will be updated in onSelect event if needed.
|
55
60
|
const [shouldHandleOnMouseDownDuringSelect, setShouldHandleOnMouseDownDuringSelect] = useState(true);
|
56
61
|
/* @conditional-compile-remove(mention) */
|
57
62
|
// Caret position in the text field
|
@@ -62,24 +67,38 @@ export const InputBoxComponent = (props) => {
|
|
62
67
|
/* @conditional-compile-remove(mention) */
|
63
68
|
const localeStrings = useLocale().strings;
|
64
69
|
/* @conditional-compile-remove(mention) */
|
70
|
+
// Set mention suggestions
|
65
71
|
const updateMentionSuggestions = useCallback((suggestions) => {
|
66
|
-
var _a;
|
67
72
|
setMentionSuggestions(suggestions);
|
68
|
-
|
69
|
-
(_a = textFieldRef === null || textFieldRef === void 0 ? void 0 : textFieldRef.current) === null || _a === void 0 ? void 0 : _a.setSelectionEnd(caretIndex);
|
70
|
-
}
|
71
|
-
}, [textFieldRef, caretIndex]);
|
73
|
+
}, [setMentionSuggestions]);
|
72
74
|
/* @conditional-compile-remove(mention) */
|
73
75
|
// Parse the text and get the plain text version to display in the input box
|
74
76
|
useEffect(() => {
|
75
|
-
const trigger = (mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.trigger) ||
|
76
|
-
const
|
77
|
-
setInputTextValue(plainText);
|
78
|
-
setTagsValue(tags);
|
77
|
+
const trigger = (mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.trigger) || DEFAULT_MENTION_TRIGGER;
|
78
|
+
const parsedHTMLData = textToTagParser(textValue, trigger);
|
79
|
+
setInputTextValue(parsedHTMLData.plainText);
|
80
|
+
setTagsValue(parsedHTMLData.tags);
|
79
81
|
updateMentionSuggestions([]);
|
80
82
|
}, [textValue, mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.trigger, updateMentionSuggestions]);
|
81
83
|
const mergedRootStyle = mergeStyles(inputBoxWrapperStyle, styles === null || styles === void 0 ? void 0 : styles.root);
|
82
84
|
const mergedTextFiledStyle = mergeStyles(inputBoxStyle, inputClassName, props.inlineChildren ? {} : inputBoxNewLineSpaceAffordance);
|
85
|
+
/* @conditional-compile-remove(mention) */
|
86
|
+
useEffect(() => {
|
87
|
+
var _a;
|
88
|
+
// effect for caret index update
|
89
|
+
if (caretIndex === undefined || textFieldRef === undefined || (textFieldRef === null || textFieldRef === void 0 ? void 0 : textFieldRef.current) === undefined) {
|
90
|
+
return;
|
91
|
+
}
|
92
|
+
// get validated caret index between 0 and inputTextValue.length otherwise caret will be set to incorrect index
|
93
|
+
const updatedCaretIndex = getValidatedIndexInRange({
|
94
|
+
min: 0,
|
95
|
+
max: inputTextValue.length,
|
96
|
+
currentValue: caretIndex
|
97
|
+
});
|
98
|
+
(_a = textFieldRef === null || textFieldRef === void 0 ? void 0 : textFieldRef.current) === null || _a === void 0 ? void 0 : _a.setSelectionRange(updatedCaretIndex, updatedCaretIndex);
|
99
|
+
setSelectionStartValue(updatedCaretIndex);
|
100
|
+
setSelectionEndValue(updatedCaretIndex);
|
101
|
+
}, [caretIndex, inputTextValue.length, textFieldRef, setSelectionStartValue, setSelectionEndValue]);
|
83
102
|
const mergedTextContainerStyle = mergeStyles(textContainerStyle, styles === null || styles === void 0 ? void 0 : styles.textFieldContainer);
|
84
103
|
const mergedTextFieldStyle = concatStyleSets(textFieldStyle, {
|
85
104
|
fieldGroup: styles === null || styles === void 0 ? void 0 : styles.textField,
|
@@ -94,7 +113,7 @@ export const InputBoxComponent = (props) => {
|
|
94
113
|
const mergedChildrenStyle = mergeStyles(props.inlineChildren ? {} : newLineButtonsContainerStyle);
|
95
114
|
/* @conditional-compile-remove(mention) */
|
96
115
|
const onSuggestionSelected = useCallback((suggestion) => {
|
97
|
-
var _a, _b;
|
116
|
+
var _a, _b, _c;
|
98
117
|
let selectionEnd = ((_a = textFieldRef === null || textFieldRef === void 0 ? void 0 : textFieldRef.current) === null || _a === void 0 ? void 0 : _a.selectionEnd) || -1;
|
99
118
|
if (selectionEnd < 0) {
|
100
119
|
selectionEnd = 0;
|
@@ -106,16 +125,30 @@ export const InputBoxComponent = (props) => {
|
|
106
125
|
const mention = htmlStringForMentionSuggestion(suggestion, localeStrings);
|
107
126
|
// update plain text with the mention html text
|
108
127
|
const newPlainText = inputTextValue.substring(0, currentTriggerStartIndex) + mention + inputTextValue.substring(selectionEnd);
|
109
|
-
const triggerText = (_b = mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.trigger) !== null && _b !== void 0 ? _b :
|
128
|
+
const triggerText = (_b = mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.trigger) !== null && _b !== void 0 ? _b : DEFAULT_MENTION_TRIGGER;
|
110
129
|
// update html text with updated plain text
|
111
|
-
const
|
130
|
+
const updatedContent = updateHTML({
|
131
|
+
htmlText: textValue,
|
132
|
+
oldPlainText,
|
133
|
+
newPlainText,
|
134
|
+
tags: tagsValue,
|
135
|
+
startIndex: currentTriggerStartIndex,
|
136
|
+
oldPlainTextEndIndex: selectionEnd,
|
137
|
+
change: mention,
|
138
|
+
mentionTrigger: triggerText
|
139
|
+
});
|
112
140
|
const displayName = getDisplayNameForMentionSuggestion(suggestion, localeStrings);
|
113
|
-
|
114
|
-
|
141
|
+
const newCaretIndex = currentTriggerStartIndex + displayName.length + triggerText.length;
|
142
|
+
// move the caret in the text field to the end of the mention plain text
|
143
|
+
setCaretIndex(newCaretIndex);
|
144
|
+
setSelectionEndValue(newCaretIndex);
|
145
|
+
setSelectionStartValue(newCaretIndex);
|
115
146
|
setCurrentTriggerStartIndex(-1);
|
116
147
|
updateMentionSuggestions([]);
|
148
|
+
// set focus back to text field
|
149
|
+
(_c = textFieldRef === null || textFieldRef === void 0 ? void 0 : textFieldRef.current) === null || _c === void 0 ? void 0 : _c.focus();
|
117
150
|
setActiveSuggestionIndex(undefined);
|
118
|
-
onChange && onChange(undefined, updatedHTML);
|
151
|
+
onChange && onChange(undefined, updatedContent.updatedHTML);
|
119
152
|
}, [
|
120
153
|
textFieldRef,
|
121
154
|
inputTextValue,
|
@@ -130,6 +163,15 @@ export const InputBoxComponent = (props) => {
|
|
130
163
|
localeStrings
|
131
164
|
]);
|
132
165
|
const onTextFieldKeyDown = useCallback((ev) => {
|
166
|
+
/* @conditional-compile-remove(mention) */
|
167
|
+
// caretIndex should be set to undefined when the user is typing
|
168
|
+
setCaretIndex(undefined);
|
169
|
+
// shouldHandleOnMouseDownDuringSelect should be set to false after the last mouse down event.
|
170
|
+
// it shouldn't be updated in onMouseUp
|
171
|
+
// as onMouseUp can be triggered before or after onSelect event
|
172
|
+
// because its order depends on mouse events not selection.
|
173
|
+
/* @conditional-compile-remove(mention) */
|
174
|
+
setShouldHandleOnMouseDownDuringSelect(false);
|
133
175
|
// Uses KeyCode 229 and which code 229 to determine if the press of the enter key is from a composition session or not (Safari only)
|
134
176
|
if (ev.nativeEvent.isComposing || ev.nativeEvent.keyCode === 229 || ev.nativeEvent.which === 229) {
|
135
177
|
return;
|
@@ -201,24 +243,149 @@ export const InputBoxComponent = (props) => {
|
|
201
243
|
};
|
202
244
|
}, [debouncedQueryUpdate]);
|
203
245
|
/* @conditional-compile-remove(mention) */
|
204
|
-
|
205
|
-
|
246
|
+
// Update selections index in mention to navigate by words
|
247
|
+
const updateSelectionIndexesWithMentionIfNeeded = useCallback((event, inputTextValue, selectionStartValue, selectionEndValue, tagsValue) => {
|
248
|
+
var _a, _b, _c;
|
249
|
+
let updatedStartIndex = event.currentTarget.selectionStart;
|
250
|
+
let updatedEndIndex = event.currentTarget.selectionEnd;
|
251
|
+
if (event.currentTarget.selectionStart === event.currentTarget.selectionEnd &&
|
252
|
+
event.currentTarget.selectionStart !== null &&
|
253
|
+
event.currentTarget.selectionStart !== -1) {
|
254
|
+
// just a caret movement/usual typing or deleting
|
255
|
+
const mentionTag = findMentionTagForSelection(tagsValue, event.currentTarget.selectionStart);
|
256
|
+
// don't include boundary cases to show correct selection, otherwise it will show selection at mention boundaries
|
257
|
+
if (mentionTag !== undefined &&
|
258
|
+
mentionTag.plainTextBeginIndex !== undefined &&
|
259
|
+
event.currentTarget.selectionStart > mentionTag.plainTextBeginIndex &&
|
260
|
+
event.currentTarget.selectionStart < ((_a = mentionTag.plainTextEndIndex) !== null && _a !== void 0 ? _a : mentionTag.plainTextBeginIndex)) {
|
261
|
+
// get updated selection index
|
262
|
+
const newSelectionIndex = findNewSelectionIndexForMention({
|
263
|
+
tag: mentionTag,
|
264
|
+
textValue: inputTextValue,
|
265
|
+
currentSelectionIndex: event.currentTarget.selectionStart,
|
266
|
+
previousSelectionIndex: selectionStartValue !== null && selectionStartValue !== void 0 ? selectionStartValue : inputTextValue.length
|
267
|
+
});
|
268
|
+
updatedStartIndex = newSelectionIndex;
|
269
|
+
updatedEndIndex = newSelectionIndex;
|
270
|
+
}
|
271
|
+
}
|
272
|
+
else if (event.currentTarget.selectionStart !== event.currentTarget.selectionEnd) {
|
273
|
+
// Both e.currentTarget.selectionStart !== selectionStartValue and e.currentTarget.selectionEnd !== selectionEndValue can be true when a user selects a text by double click
|
274
|
+
if (event.currentTarget.selectionStart !== null && event.currentTarget.selectionStart !== selectionStartValue) {
|
275
|
+
// the selection start is changed
|
276
|
+
const mentionTag = findMentionTagForSelection(tagsValue, event.currentTarget.selectionStart);
|
277
|
+
// don't include boundary cases to show correct selection, otherwise it will show selection at mention boundaries
|
278
|
+
if (mentionTag !== undefined &&
|
279
|
+
mentionTag.plainTextBeginIndex !== undefined &&
|
280
|
+
event.currentTarget.selectionStart > mentionTag.plainTextBeginIndex &&
|
281
|
+
event.currentTarget.selectionStart < ((_b = mentionTag.plainTextEndIndex) !== null && _b !== void 0 ? _b : mentionTag.plainTextBeginIndex)) {
|
282
|
+
updatedStartIndex = findNewSelectionIndexForMention({
|
283
|
+
tag: mentionTag,
|
284
|
+
textValue: inputTextValue,
|
285
|
+
currentSelectionIndex: event.currentTarget.selectionStart,
|
286
|
+
previousSelectionIndex: selectionStartValue !== null && selectionStartValue !== void 0 ? selectionStartValue : inputTextValue.length
|
287
|
+
});
|
288
|
+
}
|
289
|
+
}
|
290
|
+
if (event.currentTarget.selectionEnd !== null && event.currentTarget.selectionEnd !== selectionEndValue) {
|
291
|
+
// the selection end is changed
|
292
|
+
const mentionTag = findMentionTagForSelection(tagsValue, event.currentTarget.selectionEnd);
|
293
|
+
// don't include boundary cases to show correct selection, otherwise it will show selection at mention boundaries
|
294
|
+
if (mentionTag !== undefined &&
|
295
|
+
mentionTag.plainTextBeginIndex !== undefined &&
|
296
|
+
event.currentTarget.selectionEnd > mentionTag.plainTextBeginIndex &&
|
297
|
+
event.currentTarget.selectionEnd < ((_c = mentionTag.plainTextEndIndex) !== null && _c !== void 0 ? _c : mentionTag.plainTextBeginIndex)) {
|
298
|
+
updatedEndIndex = findNewSelectionIndexForMention({
|
299
|
+
tag: mentionTag,
|
300
|
+
textValue: inputTextValue,
|
301
|
+
currentSelectionIndex: event.currentTarget.selectionEnd,
|
302
|
+
previousSelectionIndex: selectionEndValue !== null && selectionEndValue !== void 0 ? selectionEndValue : inputTextValue.length
|
303
|
+
});
|
304
|
+
}
|
305
|
+
}
|
306
|
+
}
|
307
|
+
// e.currentTarget.selectionDirection should be set to handle shift + arrow keys
|
308
|
+
if (event.currentTarget.selectionDirection === null) {
|
309
|
+
event.currentTarget.setSelectionRange(updatedStartIndex, updatedEndIndex);
|
310
|
+
}
|
311
|
+
else {
|
312
|
+
event.currentTarget.setSelectionRange(updatedStartIndex, updatedEndIndex, event.currentTarget.selectionDirection);
|
313
|
+
}
|
314
|
+
setSelectionStartValue(updatedStartIndex);
|
315
|
+
setSelectionEndValue(updatedEndIndex);
|
316
|
+
}, [setSelectionStartValue, setSelectionEndValue]);
|
317
|
+
/* @conditional-compile-remove(mention) */
|
318
|
+
const handleOnSelect = useCallback((event, inputTextValue, tags, shouldHandleOnMouseDownDuringSelect, selectionStartValue, selectionEndValue) => {
|
319
|
+
var _a, _b, _c;
|
320
|
+
/* @conditional-compile-remove(mention) */
|
321
|
+
if (shouldHandleOnMouseDownDuringSelect && event.currentTarget.selectionStart !== null) {
|
322
|
+
// on select was triggered by mouse down
|
323
|
+
const mentionTag = findMentionTagForSelection(tags, event.currentTarget.selectionStart);
|
324
|
+
if (mentionTag !== undefined && mentionTag.plainTextBeginIndex !== undefined) {
|
325
|
+
// handle mention click
|
326
|
+
if (event.currentTarget.selectionDirection === null) {
|
327
|
+
event.currentTarget.setSelectionRange(mentionTag.plainTextBeginIndex, (_a = mentionTag.plainTextEndIndex) !== null && _a !== void 0 ? _a : mentionTag.plainTextBeginIndex);
|
328
|
+
}
|
329
|
+
else {
|
330
|
+
event.currentTarget.setSelectionRange(mentionTag.plainTextBeginIndex, (_b = mentionTag.plainTextEndIndex) !== null && _b !== void 0 ? _b : mentionTag.plainTextBeginIndex, event.currentTarget.selectionDirection);
|
331
|
+
}
|
332
|
+
setSelectionStartValue(mentionTag.plainTextBeginIndex);
|
333
|
+
setSelectionEndValue((_c = mentionTag.plainTextEndIndex) !== null && _c !== void 0 ? _c : mentionTag.plainTextBeginIndex);
|
334
|
+
}
|
335
|
+
else {
|
336
|
+
setSelectionStartValue(event.currentTarget.selectionStart);
|
337
|
+
setSelectionEndValue(event.currentTarget.selectionEnd);
|
338
|
+
}
|
339
|
+
}
|
340
|
+
else {
|
341
|
+
// selection was changed by keyboard
|
342
|
+
updateSelectionIndexesWithMentionIfNeeded(event, inputTextValue, selectionStartValue, selectionEndValue, tags);
|
343
|
+
}
|
344
|
+
// don't set setShouldHandleOnMouseDownDuringSelect(false) here as setSelectionRange could trigger additional calls of onSelect event and they may not be handled correctly (because of setSelectionRange calls or rerender)
|
345
|
+
}, [updateSelectionIndexesWithMentionIfNeeded]);
|
346
|
+
/* @conditional-compile-remove(mention) */
|
347
|
+
const handleOnChange = useCallback((event, tagsValue, htmlTextValue, inputTextValue, currentTriggerStartIndex, previousSelectionStart, previousSelectionEnd, currentSelectionStart, currentSelectionEnd, updatedValue) => __awaiter(void 0, void 0, void 0, function* () {
|
348
|
+
var _b;
|
349
|
+
if (event.currentTarget === null) {
|
350
|
+
return;
|
351
|
+
}
|
352
|
+
// handle backspace change
|
353
|
+
// onSelect is not called for backspace as selection is not changed and local caret index is outdated
|
354
|
+
setCaretIndex(undefined);
|
206
355
|
const newValue = updatedValue !== null && updatedValue !== void 0 ? updatedValue : '';
|
207
|
-
const triggerText = (_b = mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.trigger) !== null && _b !== void 0 ? _b :
|
356
|
+
const triggerText = (_b = mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.trigger) !== null && _b !== void 0 ? _b : DEFAULT_MENTION_TRIGGER;
|
208
357
|
const newTextLength = newValue.length;
|
209
|
-
|
210
|
-
|
211
|
-
|
358
|
+
// updating indexes to set between 0 and text length, otherwise selectionRange won't be set correctly
|
359
|
+
const currentSelectionEndValue = getValidatedIndexInRange({
|
360
|
+
min: 0,
|
361
|
+
max: newTextLength,
|
362
|
+
currentValue: currentSelectionEnd
|
363
|
+
});
|
364
|
+
const currentSelectionStartValue = getValidatedIndexInRange({
|
365
|
+
min: 0,
|
366
|
+
max: newTextLength,
|
367
|
+
currentValue: currentSelectionStart
|
368
|
+
});
|
369
|
+
const previousSelectionStartValue = getValidatedIndexInRange({
|
370
|
+
min: 0,
|
371
|
+
max: inputTextValue.length,
|
372
|
+
currentValue: previousSelectionStart
|
373
|
+
});
|
374
|
+
const previousSelectionEndValue = getValidatedIndexInRange({
|
375
|
+
min: 0,
|
376
|
+
max: inputTextValue.length,
|
377
|
+
currentValue: previousSelectionEnd
|
378
|
+
});
|
212
379
|
// If we are enabled for lookups,
|
213
380
|
if (mentionLookupOptions !== undefined) {
|
214
381
|
// Look at the range of the change for a trigger character
|
215
|
-
const triggerPriorIndex = newValue.lastIndexOf(triggerText,
|
382
|
+
const triggerPriorIndex = newValue.lastIndexOf(triggerText, currentSelectionEndValue - 1);
|
216
383
|
// Update the caret position, if not doing a lookup
|
217
384
|
setCaretPosition(Caret.getRelativePosition(event.currentTarget));
|
218
385
|
if (triggerPriorIndex !== undefined) {
|
219
386
|
// trigger is found
|
220
387
|
const isSpaceBeforeTrigger = newValue.substring(triggerPriorIndex - 1, triggerPriorIndex) === ' ';
|
221
|
-
const wordAtSelection = newValue.substring(triggerPriorIndex,
|
388
|
+
const wordAtSelection = newValue.substring(triggerPriorIndex, currentSelectionEndValue);
|
222
389
|
let tagIndex = currentTriggerStartIndex;
|
223
390
|
if (!isSpaceBeforeTrigger && triggerPriorIndex !== 0) {
|
224
391
|
//no space before the trigger <- continuation of the previous word
|
@@ -227,7 +394,7 @@ export const InputBoxComponent = (props) => {
|
|
227
394
|
}
|
228
395
|
else if (wordAtSelection === triggerText) {
|
229
396
|
// start of the mention
|
230
|
-
tagIndex =
|
397
|
+
tagIndex = currentSelectionEndValue - triggerText.length;
|
231
398
|
if (tagIndex < 0) {
|
232
399
|
tagIndex = 0;
|
233
400
|
}
|
@@ -249,88 +416,42 @@ export const InputBoxComponent = (props) => {
|
|
249
416
|
}
|
250
417
|
let result = '';
|
251
418
|
if (tagsValue.length === 0) {
|
252
|
-
// no tags in the string
|
419
|
+
// no tags in the string and newValue should be used as a result string
|
253
420
|
result = newValue;
|
254
421
|
}
|
255
422
|
else {
|
256
|
-
// there are tags in the text value
|
257
|
-
|
258
|
-
|
423
|
+
// there are tags in the text value and htmlTextValue is html string
|
424
|
+
// find diff between old and new text
|
425
|
+
const { changeStart, oldChangeEnd, newChangeEnd } = findStringsDiffIndexes({
|
426
|
+
oldText: inputTextValue,
|
427
|
+
newText: newValue,
|
428
|
+
previousSelectionStart: previousSelectionStartValue,
|
429
|
+
previousSelectionEnd: previousSelectionEndValue,
|
430
|
+
currentSelectionStart: currentSelectionStartValue,
|
431
|
+
currentSelectionEnd: currentSelectionEndValue
|
432
|
+
});
|
259
433
|
const change = newValue.substring(changeStart, newChangeEnd);
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
434
|
+
// get updated html string
|
435
|
+
const updatedContent = updateHTML({
|
436
|
+
htmlText: htmlTextValue,
|
437
|
+
oldPlainText: inputTextValue,
|
438
|
+
newPlainText: newValue,
|
439
|
+
tags: tagsValue,
|
440
|
+
startIndex: changeStart,
|
441
|
+
oldPlainTextEndIndex: oldChangeEnd,
|
442
|
+
change,
|
443
|
+
mentionTrigger: triggerText
|
444
|
+
});
|
445
|
+
result = updatedContent.updatedHTML;
|
446
|
+
// update caret index if needed
|
447
|
+
if (updatedContent.updatedSelectionIndex !== null) {
|
448
|
+
setCaretIndex(updatedContent.updatedSelectionIndex);
|
449
|
+
setSelectionEndValue(updatedContent.updatedSelectionIndex);
|
450
|
+
setSelectionStartValue(updatedContent.updatedSelectionIndex);
|
268
451
|
}
|
269
452
|
}
|
270
453
|
onChange && onChange(event, result);
|
271
|
-
}), [
|
272
|
-
onChange,
|
273
|
-
mentionLookupOptions,
|
274
|
-
tagsValue,
|
275
|
-
textValue,
|
276
|
-
inputTextValue,
|
277
|
-
currentTriggerStartIndex,
|
278
|
-
setCaretIndex,
|
279
|
-
setCaretPosition,
|
280
|
-
updateMentionSuggestions,
|
281
|
-
debouncedQueryUpdate,
|
282
|
-
textFieldRef
|
283
|
-
]);
|
284
|
-
/* @conditional-compile-remove(mention) */
|
285
|
-
const updateSelectionIndexesWithMentionIfNeeded = (event) => {
|
286
|
-
var _a;
|
287
|
-
let updatedStartIndex = event.currentTarget.selectionStart;
|
288
|
-
let updatedEndIndex = event.currentTarget.selectionEnd;
|
289
|
-
if (event.currentTarget.selectionStart === event.currentTarget.selectionEnd &&
|
290
|
-
event.currentTarget.selectionStart !== null &&
|
291
|
-
event.currentTarget.selectionStart !== -1) {
|
292
|
-
const mentionTag = findMentionTagForSelection(tagsValue, event.currentTarget.selectionStart);
|
293
|
-
if (mentionTag !== undefined && mentionTag.plainTextBeginIndex !== undefined) {
|
294
|
-
if (selectionStartValue === null) {
|
295
|
-
updatedStartIndex = mentionTag.plainTextBeginIndex;
|
296
|
-
updatedEndIndex = (_a = mentionTag.plainTextEndIndex) !== null && _a !== void 0 ? _a : mentionTag.plainTextBeginIndex;
|
297
|
-
}
|
298
|
-
else {
|
299
|
-
const newSelectionIndex = findNewSelectionIndexForMention(mentionTag, inputTextValue, event.currentTarget.selectionStart, selectionStartValue);
|
300
|
-
updatedStartIndex = newSelectionIndex;
|
301
|
-
updatedEndIndex = newSelectionIndex;
|
302
|
-
}
|
303
|
-
}
|
304
|
-
}
|
305
|
-
else if (event.currentTarget.selectionStart !== event.currentTarget.selectionEnd) {
|
306
|
-
// Both e.currentTarget.selectionStart !== selectionStartValue and e.currentTarget.selectionEnd !== selectionEndValue can be true when a user selects a text by double click
|
307
|
-
if (event.currentTarget.selectionStart !== null && event.currentTarget.selectionStart !== selectionStartValue) {
|
308
|
-
// the selection start is changed
|
309
|
-
const mentionTag = findMentionTagForSelection(tagsValue, event.currentTarget.selectionStart);
|
310
|
-
if (mentionTag !== undefined && mentionTag.plainTextBeginIndex !== undefined) {
|
311
|
-
//TODO: here it takes -1 when it shouldn't, update selectionstart and end with mouse move and or touch move
|
312
|
-
updatedStartIndex = findNewSelectionIndexForMention(mentionTag, inputTextValue, event.currentTarget.selectionStart, selectionStartValue !== null && selectionStartValue !== void 0 ? selectionStartValue : -1);
|
313
|
-
}
|
314
|
-
}
|
315
|
-
if (event.currentTarget.selectionEnd !== null && event.currentTarget.selectionEnd !== selectionEndValue) {
|
316
|
-
// the selection end is changed
|
317
|
-
const mentionTag = findMentionTagForSelection(tagsValue, event.currentTarget.selectionEnd);
|
318
|
-
if (mentionTag !== undefined && mentionTag.plainTextBeginIndex !== undefined) {
|
319
|
-
//TODO: here it takes -1 when it shouldn't, update selectionstart and end with mouse move and or touch move
|
320
|
-
updatedEndIndex = findNewSelectionIndexForMention(mentionTag, inputTextValue, event.currentTarget.selectionEnd, selectionEndValue !== null && selectionEndValue !== void 0 ? selectionEndValue : -1);
|
321
|
-
}
|
322
|
-
}
|
323
|
-
}
|
324
|
-
// e.currentTarget.selectionDirection should be set to handle shift + arrow keys
|
325
|
-
if (event.currentTarget.selectionDirection === null) {
|
326
|
-
event.currentTarget.setSelectionRange(updatedStartIndex, updatedEndIndex);
|
327
|
-
}
|
328
|
-
else {
|
329
|
-
event.currentTarget.setSelectionRange(updatedStartIndex, updatedEndIndex, event.currentTarget.selectionDirection);
|
330
|
-
}
|
331
|
-
setSelectionStartValue(updatedStartIndex);
|
332
|
-
setSelectionEndValue(updatedEndIndex);
|
333
|
-
};
|
454
|
+
}), [onChange, mentionLookupOptions, setCaretIndex, setCaretPosition, updateMentionSuggestions, debouncedQueryUpdate]);
|
334
455
|
const getInputFieldTextValue = () => {
|
335
456
|
/* @conditional-compile-remove(mention) */
|
336
457
|
return inputTextValue;
|
@@ -342,61 +463,52 @@ export const InputBoxComponent = (props) => {
|
|
342
463
|
updateMentionSuggestions([]);
|
343
464
|
} })),
|
344
465
|
React.createElement(TextField, { autoFocus: props.autoFocus === 'sendBoxTextField', "data-ui-id": dataUiId, multiline: true, autoAdjustHeight: true, multiple: false, resizable: false, componentRef: textFieldRef, id: id, inputClassName: mergedTextFiledStyle, placeholder: placeholderText, value: getInputFieldTextValue(), onChange: (e, newValue) => {
|
466
|
+
// Remove when switching to react 17+, currently needed because of https://legacy.reactjs.org/docs/legacy-event-pooling.html
|
467
|
+
/* @conditional-compile-remove(mention) */
|
468
|
+
// Prevents React from resetting event's properties
|
469
|
+
e.persist();
|
345
470
|
/* @conditional-compile-remove(mention) */
|
346
471
|
setInputTextValue(newValue !== null && newValue !== void 0 ? newValue : '');
|
347
472
|
/* @conditional-compile-remove(mention) */
|
348
|
-
handleOnChange(e, newValue);
|
473
|
+
handleOnChange(e, tagsValue, textValue, inputTextValue, currentTriggerStartIndex, selectionStartValue === null ? undefined : selectionStartValue, selectionEndValue === null ? undefined : selectionEndValue, e.currentTarget.selectionStart === null ? undefined : e.currentTarget.selectionStart, e.currentTarget.selectionEnd === null ? undefined : e.currentTarget.selectionEnd, newValue);
|
349
474
|
/* @conditional-compile-remove(mention) */
|
350
475
|
return;
|
351
476
|
onChange(e, newValue);
|
352
477
|
},
|
353
478
|
/* @conditional-compile-remove(mention) */
|
354
479
|
onSelect: (e) => {
|
355
|
-
|
480
|
+
// update selection if needed
|
356
481
|
if (caretIndex !== undefined) {
|
357
|
-
e.currentTarget.setSelectionRange(caretIndex, caretIndex);
|
358
482
|
setCaretIndex(undefined);
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
/* @conditional-compile-remove(mention) */
|
363
|
-
if (shouldHandleOnMouseDownDuringSelect &&
|
364
|
-
e.currentTarget.selectionStart !== null &&
|
365
|
-
e.currentTarget.selectionStart === e.currentTarget.selectionEnd) {
|
366
|
-
// handle mention click
|
367
|
-
const mentionTag = findMentionTagForSelection(tagsValue, e.currentTarget.selectionStart);
|
368
|
-
if (mentionTag !== undefined && mentionTag.plainTextBeginIndex !== undefined) {
|
369
|
-
if (e.currentTarget.selectionDirection === null) {
|
370
|
-
e.currentTarget.setSelectionRange(mentionTag.plainTextBeginIndex, (_a = mentionTag.plainTextEndIndex) !== null && _a !== void 0 ? _a : mentionTag.plainTextBeginIndex);
|
371
|
-
}
|
372
|
-
else {
|
373
|
-
e.currentTarget.setSelectionRange(mentionTag.plainTextBeginIndex, (_b = mentionTag.plainTextEndIndex) !== null && _b !== void 0 ? _b : mentionTag.plainTextBeginIndex, e.currentTarget.selectionDirection);
|
374
|
-
}
|
375
|
-
setSelectionStartValue(mentionTag.plainTextBeginIndex);
|
376
|
-
setSelectionEndValue((_c = mentionTag.plainTextEndIndex) !== null && _c !== void 0 ? _c : mentionTag.plainTextBeginIndex);
|
377
|
-
}
|
378
|
-
else {
|
379
|
-
setSelectionStartValue(e.currentTarget.selectionStart);
|
380
|
-
setSelectionEndValue(e.currentTarget.selectionEnd);
|
483
|
+
// sometimes setting selectionRage in effect for updating caretIndex doesn't work as expected and onSelect should handle this case
|
484
|
+
if (caretIndex !== e.currentTarget.selectionStart || caretIndex !== e.currentTarget.selectionEnd) {
|
485
|
+
e.currentTarget.setSelectionRange(caretIndex, caretIndex);
|
381
486
|
}
|
487
|
+
return;
|
382
488
|
}
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
setShouldHandleOnMouseDownDuringSelect(false);
|
388
|
-
}, onMouseDown: () => {
|
489
|
+
handleOnSelect(e, inputTextValue, tagsValue, shouldHandleOnMouseDownDuringSelect, selectionStartValue, selectionEndValue);
|
490
|
+
},
|
491
|
+
/* @conditional-compile-remove(mention) */
|
492
|
+
onMouseDown: () => {
|
389
493
|
// as events order is onMouseDown -> onSelect -> onClick
|
390
494
|
// onClick and onMouseDown can't handle clicking on mention event because
|
391
|
-
//
|
392
|
-
//
|
495
|
+
// onMouseDown doesn't have correct selectionRange yet and
|
496
|
+
// onClick already has wrong range as it's called after onSelect that updates the selection range
|
393
497
|
// so we need to handle onMouseDown to prevent onSelect default behavior
|
394
|
-
/* @conditional-compile-remove(mention) */
|
395
498
|
setShouldHandleOnMouseDownDuringSelect(true);
|
396
|
-
},
|
499
|
+
},
|
500
|
+
/* @conditional-compile-remove(mention) */
|
501
|
+
onTouchStart: () => {
|
397
502
|
// see onMouseDown for more details
|
398
|
-
/* @conditional-compile-remove(mention) */
|
399
503
|
setShouldHandleOnMouseDownDuringSelect(true);
|
504
|
+
},
|
505
|
+
/* @conditional-compile-remove(mention) */
|
506
|
+
onBlur: () => {
|
507
|
+
// setup all flags to default values when text field loses focus
|
508
|
+
setShouldHandleOnMouseDownDuringSelect(false);
|
509
|
+
setCaretIndex(undefined);
|
510
|
+
setSelectionStartValue(null);
|
511
|
+
setSelectionEndValue(null);
|
400
512
|
}, autoComplete: "off", onKeyDown: onTextFieldKeyDown, styles: mergedTextFieldStyle, disabled: disabled, errorMessage: errorMessage, onRenderSuffix: onRenderChildren, elementRef: inputBoxRef }))));
|
401
513
|
};
|
402
514
|
/**
|
@@ -425,77 +537,93 @@ export const InputBoxButton = (props) => {
|
|
425
537
|
};
|
426
538
|
/* @conditional-compile-remove(mention) */
|
427
539
|
/**
|
428
|
-
*
|
540
|
+
* Get validated value for index between min and max values. If currentValue is not defined, -1 will be used instead.
|
429
541
|
*
|
430
542
|
* @private
|
543
|
+
* @param props - Props for finding a valid index in range.
|
544
|
+
* @returns Valid index in the range.
|
545
|
+
*/
|
546
|
+
const getValidatedIndexInRange = (props) => {
|
547
|
+
const { min, max, currentValue } = props;
|
548
|
+
let updatedValue = currentValue !== null && currentValue !== void 0 ? currentValue : -1;
|
549
|
+
updatedValue = Math.max(min, updatedValue);
|
550
|
+
updatedValue = Math.min(updatedValue, max);
|
551
|
+
return updatedValue;
|
552
|
+
};
|
553
|
+
/* @conditional-compile-remove(mention) */
|
554
|
+
/**
|
555
|
+
* Find mention tag for selection if exists.
|
556
|
+
*
|
557
|
+
* @private
|
558
|
+
* @param tags - Existing list of tags.
|
559
|
+
* @param selection - Selection index.
|
560
|
+
* @returns Mention tag if exists, otherwise undefined.
|
431
561
|
*/
|
432
562
|
const findMentionTagForSelection = (tags, selection) => {
|
433
563
|
let mentionTag = undefined;
|
434
564
|
for (let i = 0; i < tags.length; i++) {
|
435
565
|
const tag = tags[i];
|
436
|
-
|
437
|
-
if (tag.
|
438
|
-
//
|
439
|
-
|
440
|
-
}
|
441
|
-
else if (tag.plainTextBeginIndex !== undefined) {
|
442
|
-
//no close tag
|
443
|
-
plainTextEndIndex = tag.plainTextBeginIndex;
|
566
|
+
const closingTagInfo = getTagClosingTagInfo(tag);
|
567
|
+
if (tag.plainTextBeginIndex !== undefined && tag.plainTextBeginIndex > selection) {
|
568
|
+
// no need to check further as the selection is before the tag
|
569
|
+
break;
|
444
570
|
}
|
445
|
-
if (tag.
|
446
|
-
|
447
|
-
|
448
|
-
|
571
|
+
else if (tag.plainTextBeginIndex !== undefined &&
|
572
|
+
tag.plainTextBeginIndex <= selection &&
|
573
|
+
selection <= closingTagInfo.plainTextEndIndex) {
|
574
|
+
// no need to check if tag doesn't contain selection
|
575
|
+
if (tag.subTags !== undefined && tag.subTags.length !== 0) {
|
576
|
+
const selectedTag = findMentionTagForSelection(tag.subTags, selection);
|
577
|
+
if (selectedTag !== undefined) {
|
578
|
+
mentionTag = selectedTag;
|
579
|
+
break;
|
580
|
+
}
|
581
|
+
}
|
582
|
+
else if (tag.tagType === MSFT_MENTION_TAG) {
|
583
|
+
mentionTag = tag;
|
449
584
|
break;
|
450
585
|
}
|
451
586
|
}
|
452
|
-
else if (tag.tagType === 'msft-mention' &&
|
453
|
-
tag.plainTextBeginIndex !== undefined &&
|
454
|
-
tag.plainTextBeginIndex < selection &&
|
455
|
-
selection < plainTextEndIndex) {
|
456
|
-
mentionTag = tag;
|
457
|
-
break;
|
458
|
-
}
|
459
587
|
}
|
460
588
|
return mentionTag;
|
461
589
|
};
|
462
590
|
/* @conditional-compile-remove(mention) */
|
463
591
|
/**
|
464
|
-
*
|
592
|
+
* Find a new the selection index.
|
465
593
|
*
|
466
594
|
* @private
|
595
|
+
* @param props - Props for finding new selection index for mention.
|
596
|
+
* @returns New selection index if it is inside of a mention tag, otherwise the current selection.
|
467
597
|
*/
|
468
|
-
const findNewSelectionIndexForMention = (
|
598
|
+
const findNewSelectionIndexForMention = (props) => {
|
469
599
|
var _a;
|
470
|
-
|
471
|
-
|
472
|
-
|
600
|
+
const { tag, textValue, currentSelectionIndex, previousSelectionIndex } = props;
|
601
|
+
// check if this is a mention tag and selection should be updated
|
602
|
+
if (tag.tagType !== MSFT_MENTION_TAG ||
|
603
|
+
tag.plainTextBeginIndex === undefined ||
|
604
|
+
currentSelectionIndex === previousSelectionIndex ||
|
473
605
|
tag.plainTextEndIndex === undefined) {
|
474
|
-
return
|
606
|
+
return currentSelectionIndex;
|
475
607
|
}
|
476
608
|
let spaceIndex = 0;
|
477
|
-
if (
|
478
|
-
// the cursor is moved to the left
|
479
|
-
spaceIndex = textValue.lastIndexOf(' ',
|
609
|
+
if (currentSelectionIndex <= previousSelectionIndex) {
|
610
|
+
// the cursor is moved to the left, find the last index before the cursor
|
611
|
+
spaceIndex = textValue.lastIndexOf(' ', currentSelectionIndex !== null && currentSelectionIndex !== void 0 ? currentSelectionIndex : 0);
|
480
612
|
if (spaceIndex === -1) {
|
481
|
-
// no space before the selection
|
613
|
+
// no space before the selection, use the beginning of the tag
|
482
614
|
spaceIndex = tag.plainTextBeginIndex;
|
483
615
|
}
|
484
616
|
}
|
485
617
|
else {
|
486
|
-
// the cursor is moved to the right
|
487
|
-
spaceIndex = textValue.indexOf(' ',
|
618
|
+
// the cursor is moved to the right, find the fist index after the cursor
|
619
|
+
spaceIndex = textValue.indexOf(' ', currentSelectionIndex !== null && currentSelectionIndex !== void 0 ? currentSelectionIndex : 0);
|
488
620
|
if (spaceIndex === -1) {
|
489
|
-
// no space after the selection
|
621
|
+
// no space after the selection, use the end of the tag
|
490
622
|
spaceIndex = (_a = tag.plainTextEndIndex) !== null && _a !== void 0 ? _a : tag.plainTextBeginIndex;
|
491
623
|
}
|
492
624
|
}
|
493
|
-
|
494
|
-
|
495
|
-
}
|
496
|
-
else if (spaceIndex > tag.plainTextEndIndex) {
|
497
|
-
spaceIndex = tag.plainTextEndIndex;
|
498
|
-
}
|
625
|
+
spaceIndex = Math.max(tag.plainTextBeginIndex, spaceIndex);
|
626
|
+
spaceIndex = Math.min(tag.plainTextEndIndex, spaceIndex);
|
499
627
|
return spaceIndex;
|
500
628
|
};
|
501
629
|
/* @conditional-compile-remove(mention) */
|
@@ -503,10 +631,21 @@ const findNewSelectionIndexForMention = (tag, textValue, selection, previousSele
|
|
503
631
|
* Handle mention tag edit and by word deleting
|
504
632
|
*
|
505
633
|
* @private
|
634
|
+
* @param props - Props for mention update HTML function.
|
635
|
+
* @returns Updated texts and indexes.
|
506
636
|
*/
|
507
|
-
const handleMentionTagUpdate = (
|
508
|
-
|
509
|
-
|
637
|
+
const handleMentionTagUpdate = (props) => {
|
638
|
+
const { htmlText, oldPlainText, change, tag, closeTagIdx, closeTagLength, plainTextEndIndex, startIndex, oldPlainTextEndIndex, mentionTagLength } = props;
|
639
|
+
let processedChange = props.processedChange;
|
640
|
+
let lastProcessedHTMLIndex = props.lastProcessedHTMLIndex;
|
641
|
+
if (tag.tagType !== MSFT_MENTION_TAG || tag.plainTextBeginIndex === undefined) {
|
642
|
+
// not a mention tag
|
643
|
+
return {
|
644
|
+
result: '',
|
645
|
+
updatedChange: processedChange,
|
646
|
+
htmlIndex: lastProcessedHTMLIndex,
|
647
|
+
plainTextSelectionEndIndex: null
|
648
|
+
};
|
510
649
|
}
|
511
650
|
let result = '';
|
512
651
|
let plainTextSelectionEndIndex = null;
|
@@ -529,9 +668,11 @@ const handleMentionTagUpdate = (htmlText, oldPlainText, lastProcessedHTMLIndex,
|
|
529
668
|
}
|
530
669
|
isSpaceLengthHandled = true;
|
531
670
|
if (rangeStart === -1 || rangeStart === undefined || rangeStart < tag.plainTextBeginIndex) {
|
671
|
+
// rangeStart should be at least equal to tag.plainTextBeginIndex
|
532
672
|
rangeStart = tag.plainTextBeginIndex;
|
533
673
|
}
|
534
674
|
if (rangeEnd > plainTextEndIndex) {
|
675
|
+
// rangeEnd should be at most equal to plainTextEndIndex
|
535
676
|
rangeEnd = plainTextEndIndex;
|
536
677
|
}
|
537
678
|
if (rangeStart === tag.plainTextBeginIndex && rangeEnd === plainTextEndIndex) {
|
@@ -551,28 +692,61 @@ const handleMentionTagUpdate = (htmlText, oldPlainText, lastProcessedHTMLIndex,
|
|
551
692
|
}
|
552
693
|
endChangeDiff = rangeEnd - tag.plainTextBeginIndex - mentionTagLength;
|
553
694
|
result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length + startChangeDiff);
|
554
|
-
|
695
|
+
if (startIndex < tag.plainTextBeginIndex) {
|
696
|
+
// if the change is before the tag, the selection should start from startIndex (rangeStart will be equal to tag.plainTextBeginIndex)
|
697
|
+
plainTextSelectionEndIndex = startIndex + change.length;
|
698
|
+
}
|
699
|
+
else {
|
700
|
+
// if the change is inside the tag, the selection should start with rangeStart
|
701
|
+
plainTextSelectionEndIndex = rangeStart + processedChange.length;
|
702
|
+
}
|
555
703
|
lastProcessedHTMLIndex = tag.openTagIdx + tag.openTagBody.length + endChangeDiff;
|
556
704
|
// processed change should not be changed as it should be added after the tag
|
557
705
|
}
|
558
|
-
return
|
706
|
+
return { result, updatedChange: processedChange, htmlIndex: lastProcessedHTMLIndex, plainTextSelectionEndIndex };
|
559
707
|
};
|
560
708
|
/* @conditional-compile-remove(mention) */
|
561
709
|
/**
|
562
|
-
*
|
710
|
+
* Get closing tag information if exists otherwise return information as for self closing tag
|
563
711
|
*
|
564
712
|
* @private
|
713
|
+
* @param tag - Tag data.
|
714
|
+
* @returns Closing tag information for the provided tag.
|
565
715
|
*/
|
566
|
-
const
|
567
|
-
let
|
568
|
-
|
569
|
-
|
570
|
-
|
716
|
+
const getTagClosingTagInfo = (tag) => {
|
717
|
+
let plainTextEndIndex = 0;
|
718
|
+
let closeTagIdx = 0;
|
719
|
+
let closeTagLength = 0;
|
720
|
+
if (tag.plainTextEndIndex !== undefined && tag.closeTagIdx !== undefined) {
|
721
|
+
// close tag exists
|
722
|
+
plainTextEndIndex = tag.plainTextEndIndex;
|
723
|
+
closeTagIdx = tag.closeTagIdx;
|
724
|
+
// tag.tagType.length + </>
|
725
|
+
closeTagLength = tag.tagType.length + 3;
|
726
|
+
}
|
727
|
+
else if (tag.plainTextBeginIndex !== undefined) {
|
728
|
+
// no close tag
|
729
|
+
plainTextEndIndex = tag.plainTextBeginIndex;
|
730
|
+
closeTagIdx = tag.openTagIdx + tag.openTagBody.length;
|
731
|
+
closeTagLength = 0;
|
571
732
|
}
|
572
|
-
|
573
|
-
|
574
|
-
|
733
|
+
return { plainTextEndIndex, closeTagIdx, closeTagLength };
|
734
|
+
};
|
735
|
+
/* @conditional-compile-remove(mention) */
|
736
|
+
/**
|
737
|
+
* Go through the text and update it with the changed text
|
738
|
+
*
|
739
|
+
* @private
|
740
|
+
* @param props - Props for update HTML function.
|
741
|
+
* @returns Updated HTML and selection index if the selection index should be set.
|
742
|
+
*/
|
743
|
+
const updateHTML = (props) => {
|
744
|
+
const { htmlText, oldPlainText, newPlainText, tags, startIndex, oldPlainTextEndIndex, change, mentionTrigger } = props;
|
745
|
+
if (tags.length === 0 || (startIndex === 0 && oldPlainTextEndIndex === oldPlainText.length - 1)) {
|
746
|
+
// no tags added yet or the whole text is changed
|
747
|
+
return { updatedHTML: newPlainText, updatedSelectionIndex: null };
|
575
748
|
}
|
749
|
+
let result = '';
|
576
750
|
let lastProcessedHTMLIndex = 0;
|
577
751
|
// the value can be updated with empty string when the change covers more than 1 place (tag + before or after the tag)
|
578
752
|
// in this case change won't be added as part of the tag
|
@@ -583,7 +757,7 @@ const updateHTML = (htmlText, oldPlainText, newPlainText, tags, startIndex, oldP
|
|
583
757
|
let processedChange = change;
|
584
758
|
// end tag plain text index of the last processed tag
|
585
759
|
let lastProcessedPlainTextTagEndIndex = 0;
|
586
|
-
// as some tags/text can be removed fully,
|
760
|
+
// as some tags/text can be removed fully, selection should be updated correctly
|
587
761
|
let changeNewEndIndex = null;
|
588
762
|
for (let i = 0; i < tags.length; i++) {
|
589
763
|
const tag = tags[i];
|
@@ -592,165 +766,225 @@ const updateHTML = (htmlText, oldPlainText, newPlainText, tags, startIndex, oldP
|
|
592
766
|
}
|
593
767
|
// all plain text indexes includes trigger length for the mention that shouldn't be included in
|
594
768
|
// htmlText.substring because html strings don't include the trigger
|
595
|
-
// mentionTagLength will be set only for
|
769
|
+
// mentionTagLength will be set only for mention tag, otherwise should be 0
|
596
770
|
let mentionTagLength = 0;
|
597
771
|
let isMentionTag = false;
|
598
|
-
if (tag.tagType ===
|
772
|
+
if (tag.tagType === MSFT_MENTION_TAG) {
|
599
773
|
mentionTagLength = mentionTrigger.length;
|
600
774
|
isMentionTag = true;
|
601
775
|
}
|
602
|
-
//change start is before the open tag
|
603
776
|
if (startIndex <= tag.plainTextBeginIndex) {
|
777
|
+
// change start is before the open tag
|
604
778
|
// Math.max(lastProcessedPlainTextTagEndIndex, startIndex) is used as startIndex may not be in [[previous tag].plainTextEndIndex - tag.plainTextBeginIndex] range
|
605
779
|
const startChangeDiff = tag.plainTextBeginIndex - Math.max(lastProcessedPlainTextTagEndIndex, startIndex);
|
606
780
|
result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx - startChangeDiff) + processedChange;
|
781
|
+
processedChange = '';
|
607
782
|
if (oldPlainTextEndIndex <= tag.plainTextBeginIndex) {
|
608
783
|
// the whole change is before tag start
|
609
|
-
//
|
784
|
+
// mentionTag length can be ignored here as the change is before the tag
|
610
785
|
const endChangeDiff = tag.plainTextBeginIndex - oldPlainTextEndIndex;
|
611
786
|
lastProcessedHTMLIndex = tag.openTagIdx - endChangeDiff;
|
612
|
-
processedChange = '';
|
613
787
|
// the change is handled; exit
|
614
788
|
break;
|
615
789
|
}
|
616
790
|
else {
|
617
791
|
// change continues in the tag
|
618
792
|
lastProcessedHTMLIndex = tag.openTagIdx;
|
619
|
-
processedChange = '';
|
620
793
|
// proceed to the next check
|
621
794
|
}
|
622
795
|
}
|
623
|
-
|
624
|
-
|
625
|
-
let closeTagLength = 0;
|
626
|
-
if (tag.plainTextEndIndex !== undefined && tag.closeTagIdx !== undefined) {
|
627
|
-
// close tag exists
|
628
|
-
plainTextEndIndex = tag.plainTextEndIndex;
|
629
|
-
closeTagIdx = tag.closeTagIdx;
|
630
|
-
// tag.tagType.length + </>
|
631
|
-
closeTagLength = tag.tagType.length + 3;
|
632
|
-
}
|
633
|
-
else {
|
634
|
-
// no close tag
|
635
|
-
plainTextEndIndex = tag.plainTextBeginIndex;
|
636
|
-
closeTagIdx = tag.openTagIdx + tag.openTagBody.length;
|
637
|
-
closeTagLength = 0;
|
638
|
-
}
|
639
|
-
if (startIndex < plainTextEndIndex) {
|
796
|
+
const closingTagInfo = getTagClosingTagInfo(tag);
|
797
|
+
if (startIndex <= closingTagInfo.plainTextEndIndex) {
|
640
798
|
// change started before the end tag
|
641
|
-
if (startIndex <= tag.plainTextBeginIndex && oldPlainTextEndIndex === plainTextEndIndex) {
|
799
|
+
if (startIndex <= tag.plainTextBeginIndex && oldPlainTextEndIndex === closingTagInfo.plainTextEndIndex) {
|
642
800
|
// the change is a tag or starts before the tag
|
643
801
|
// tag should be removed, no matter if there are subtags
|
644
802
|
result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx) + processedChange;
|
645
803
|
processedChange = '';
|
646
|
-
lastProcessedHTMLIndex = closeTagIdx + closeTagLength;
|
804
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
|
647
805
|
// the change is handled; exit
|
648
806
|
break;
|
649
807
|
}
|
650
|
-
else if (startIndex >= tag.plainTextBeginIndex && oldPlainTextEndIndex
|
651
|
-
//
|
808
|
+
else if (startIndex >= tag.plainTextBeginIndex && oldPlainTextEndIndex <= closingTagInfo.plainTextEndIndex) {
|
809
|
+
// the change is between the tag
|
652
810
|
if (isMentionTag) {
|
653
811
|
if (change !== '') {
|
654
|
-
|
655
|
-
|
656
|
-
|
812
|
+
if (startIndex !== tag.plainTextBeginIndex && startIndex !== closingTagInfo.plainTextEndIndex) {
|
813
|
+
// mention tag should be deleted when user tries to edit it in the middle
|
814
|
+
result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx) + processedChange;
|
815
|
+
changeNewEndIndex = tag.plainTextBeginIndex + processedChange.length;
|
816
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
|
817
|
+
}
|
818
|
+
else if (startIndex === tag.plainTextBeginIndex) {
|
819
|
+
// non empty change at the beginning of the mention tag to be added before the mention tag
|
820
|
+
result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx) + processedChange;
|
821
|
+
changeNewEndIndex = tag.plainTextBeginIndex + processedChange.length;
|
822
|
+
lastProcessedHTMLIndex = tag.openTagIdx;
|
823
|
+
}
|
824
|
+
else if (startIndex === closingTagInfo.plainTextEndIndex) {
|
825
|
+
// non empty change at the end of the mention tag to be added after the mention tag
|
826
|
+
result +=
|
827
|
+
htmlText.substring(lastProcessedHTMLIndex, closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength) +
|
828
|
+
processedChange;
|
829
|
+
changeNewEndIndex = closingTagInfo.plainTextEndIndex + processedChange.length;
|
830
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
|
831
|
+
}
|
657
832
|
processedChange = '';
|
658
|
-
lastProcessedHTMLIndex = closeTagIdx + closeTagLength;
|
659
833
|
}
|
660
834
|
else {
|
661
|
-
const
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
835
|
+
const updateMentionTagResult = handleMentionTagUpdate({
|
836
|
+
htmlText,
|
837
|
+
oldPlainText,
|
838
|
+
lastProcessedHTMLIndex,
|
839
|
+
processedChange,
|
840
|
+
change,
|
841
|
+
tag,
|
842
|
+
closeTagIdx: closingTagInfo.closeTagIdx,
|
843
|
+
closeTagLength: closingTagInfo.closeTagLength,
|
844
|
+
plainTextEndIndex: closingTagInfo.plainTextEndIndex,
|
845
|
+
startIndex,
|
846
|
+
oldPlainTextEndIndex,
|
847
|
+
mentionTagLength
|
848
|
+
});
|
849
|
+
result += updateMentionTagResult.result;
|
850
|
+
changeNewEndIndex = updateMentionTagResult.plainTextSelectionEndIndex;
|
851
|
+
processedChange = updateMentionTagResult.updatedChange;
|
852
|
+
lastProcessedHTMLIndex = updateMentionTagResult.htmlIndex;
|
666
853
|
}
|
667
|
-
// the change is handled; exit
|
668
|
-
break;
|
669
854
|
}
|
670
|
-
else if (tag.subTags !== undefined && tag.subTags.length !== 0 && tag.content) {
|
855
|
+
else if (tag.subTags !== undefined && tag.subTags.length !== 0 && tag.content !== undefined) {
|
671
856
|
// with subtags
|
672
|
-
// before the tag content
|
673
857
|
const stringBefore = htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length);
|
674
|
-
lastProcessedHTMLIndex = closeTagIdx;
|
675
|
-
const
|
676
|
-
|
677
|
-
|
678
|
-
|
858
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx;
|
859
|
+
const updatedContent = updateHTML({
|
860
|
+
htmlText: tag.content,
|
861
|
+
oldPlainText,
|
862
|
+
newPlainText,
|
863
|
+
tags: tag.subTags,
|
864
|
+
startIndex,
|
865
|
+
oldPlainTextEndIndex,
|
866
|
+
change: processedChange,
|
867
|
+
mentionTrigger
|
868
|
+
});
|
869
|
+
result += stringBefore + updatedContent.updatedHTML;
|
870
|
+
changeNewEndIndex = updatedContent.updatedSelectionIndex;
|
679
871
|
}
|
680
872
|
else {
|
681
873
|
// no subtags
|
682
|
-
const startChangeDiff = startIndex - tag.plainTextBeginIndex
|
683
|
-
const endChangeDiff = oldPlainTextEndIndex - tag.plainTextBeginIndex - mentionTagLength;
|
874
|
+
const startChangeDiff = startIndex - tag.plainTextBeginIndex;
|
684
875
|
result +=
|
685
876
|
htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length + startChangeDiff) +
|
686
877
|
processedChange;
|
687
878
|
processedChange = '';
|
688
|
-
|
689
|
-
|
690
|
-
|
879
|
+
if (oldPlainTextEndIndex < closingTagInfo.plainTextEndIndex) {
|
880
|
+
const endChangeDiff = oldPlainTextEndIndex - tag.plainTextBeginIndex;
|
881
|
+
lastProcessedHTMLIndex = tag.openTagIdx + tag.openTagBody.length + endChangeDiff;
|
882
|
+
}
|
883
|
+
else if (oldPlainTextEndIndex === closingTagInfo.plainTextEndIndex) {
|
884
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx;
|
885
|
+
}
|
691
886
|
}
|
887
|
+
// the change is handled; exit
|
888
|
+
break;
|
692
889
|
}
|
693
|
-
else if (startIndex > tag.plainTextBeginIndex && oldPlainTextEndIndex > plainTextEndIndex) {
|
694
|
-
//the change started in the tag but finishes somewhere further
|
890
|
+
else if (startIndex > tag.plainTextBeginIndex && oldPlainTextEndIndex > closingTagInfo.plainTextEndIndex) {
|
891
|
+
// the change started in the tag but finishes somewhere further
|
695
892
|
const startChangeDiff = startIndex - tag.plainTextBeginIndex - mentionTagLength;
|
696
893
|
if (isMentionTag) {
|
697
|
-
const
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
894
|
+
const updateMentionTagResult = handleMentionTagUpdate({
|
895
|
+
htmlText,
|
896
|
+
oldPlainText,
|
897
|
+
lastProcessedHTMLIndex,
|
898
|
+
processedChange: '',
|
899
|
+
change,
|
900
|
+
tag,
|
901
|
+
closeTagIdx: closingTagInfo.closeTagIdx,
|
902
|
+
closeTagLength: closingTagInfo.closeTagLength,
|
903
|
+
plainTextEndIndex: closingTagInfo.plainTextEndIndex,
|
904
|
+
startIndex,
|
905
|
+
oldPlainTextEndIndex,
|
906
|
+
mentionTagLength
|
907
|
+
});
|
908
|
+
result += updateMentionTagResult.result;
|
909
|
+
lastProcessedHTMLIndex = updateMentionTagResult.htmlIndex;
|
702
910
|
// no need to handle plainTextSelectionEndIndex as the change will be added later
|
703
|
-
// proceed with the next calculations
|
704
911
|
}
|
705
912
|
else if (tag.subTags !== undefined && tag.subTags.length !== 0 && tag.content !== undefined) {
|
706
913
|
// with subtags
|
707
|
-
// before the tag content
|
708
914
|
const stringBefore = htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length);
|
709
|
-
lastProcessedHTMLIndex = closeTagIdx;
|
710
|
-
const
|
711
|
-
|
712
|
-
|
713
|
-
|
915
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx;
|
916
|
+
const updatedContent = updateHTML({
|
917
|
+
htmlText: tag.content,
|
918
|
+
oldPlainText,
|
919
|
+
newPlainText,
|
920
|
+
tags: tag.subTags,
|
921
|
+
startIndex,
|
922
|
+
oldPlainTextEndIndex,
|
923
|
+
change: '',
|
924
|
+
mentionTrigger
|
925
|
+
});
|
926
|
+
result += stringBefore + updatedContent.updatedHTML;
|
714
927
|
}
|
715
928
|
else {
|
716
929
|
// no subtags
|
717
930
|
result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length + startChangeDiff);
|
718
|
-
lastProcessedHTMLIndex = closeTagIdx;
|
719
|
-
// proceed with the next calculations
|
931
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx;
|
720
932
|
}
|
933
|
+
// proceed with the next calculations
|
721
934
|
}
|
722
|
-
else if (startIndex < tag.plainTextBeginIndex && oldPlainTextEndIndex > plainTextEndIndex) {
|
935
|
+
else if (startIndex < tag.plainTextBeginIndex && oldPlainTextEndIndex > closingTagInfo.plainTextEndIndex) {
|
723
936
|
// the change starts before the tag and finishes after it
|
724
937
|
// tag should be removed, no matter if there are subtags
|
725
938
|
// no need to save anything between lastProcessedHTMLIndex and closeTagIdx + closeTagLength
|
726
|
-
lastProcessedHTMLIndex = closeTagIdx + closeTagLength;
|
939
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
|
727
940
|
// proceed with the next calculations
|
728
941
|
}
|
729
|
-
else if (startIndex === tag.plainTextBeginIndex && oldPlainTextEndIndex > plainTextEndIndex) {
|
942
|
+
else if (startIndex === tag.plainTextBeginIndex && oldPlainTextEndIndex > closingTagInfo.plainTextEndIndex) {
|
730
943
|
// the change starts in the tag and finishes after it
|
731
944
|
// tag should be removed, no matter if there are subtags
|
732
945
|
result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx);
|
733
946
|
// processedChange shouldn't be updated as it will be added after the tag
|
734
|
-
lastProcessedHTMLIndex = closeTagIdx + closeTagLength;
|
947
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
|
735
948
|
// proceed with the next calculations
|
736
949
|
}
|
737
|
-
else if (startIndex < tag.plainTextBeginIndex && oldPlainTextEndIndex < plainTextEndIndex) {
|
950
|
+
else if (startIndex < tag.plainTextBeginIndex && oldPlainTextEndIndex < closingTagInfo.plainTextEndIndex) {
|
738
951
|
// the change starts before the tag and ends in a tag
|
739
952
|
if (isMentionTag) {
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
953
|
+
// mention tag
|
954
|
+
const updateMentionTagResult = handleMentionTagUpdate({
|
955
|
+
htmlText,
|
956
|
+
oldPlainText,
|
957
|
+
lastProcessedHTMLIndex,
|
958
|
+
processedChange: '',
|
959
|
+
change,
|
960
|
+
tag,
|
961
|
+
closeTagIdx: closingTagInfo.closeTagIdx,
|
962
|
+
closeTagLength: closingTagInfo.closeTagLength,
|
963
|
+
plainTextEndIndex: closingTagInfo.plainTextEndIndex,
|
964
|
+
startIndex,
|
965
|
+
oldPlainTextEndIndex,
|
966
|
+
mentionTagLength
|
967
|
+
});
|
968
|
+
changeNewEndIndex = updateMentionTagResult.plainTextSelectionEndIndex;
|
969
|
+
result += updateMentionTagResult.result;
|
970
|
+
lastProcessedHTMLIndex = updateMentionTagResult.htmlIndex;
|
745
971
|
}
|
746
972
|
else if (tag.subTags !== undefined && tag.subTags.length !== 0 && tag.content !== undefined) {
|
747
973
|
// with subtags
|
748
|
-
// before the tag content
|
749
974
|
const stringBefore = htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length);
|
750
|
-
lastProcessedHTMLIndex = closeTagIdx;
|
751
|
-
const
|
752
|
-
|
753
|
-
|
975
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx;
|
976
|
+
const updatedContent = updateHTML({
|
977
|
+
htmlText: tag.content,
|
978
|
+
oldPlainText,
|
979
|
+
newPlainText,
|
980
|
+
tags: tag.subTags,
|
981
|
+
startIndex,
|
982
|
+
oldPlainTextEndIndex,
|
983
|
+
change: processedChange,
|
984
|
+
mentionTrigger
|
985
|
+
});
|
986
|
+
processedChange = '';
|
987
|
+
result += stringBefore + updatedContent.updatedHTML;
|
754
988
|
}
|
755
989
|
else {
|
756
990
|
// no subtags
|
@@ -758,123 +992,67 @@ const updateHTML = (htmlText, oldPlainText, newPlainText, tags, startIndex, oldP
|
|
758
992
|
htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length) + processedChange;
|
759
993
|
processedChange = '';
|
760
994
|
// oldPlainTextEndIndex already includes mentionTag length
|
761
|
-
const endChangeDiff = plainTextEndIndex - oldPlainTextEndIndex;
|
995
|
+
const endChangeDiff = closingTagInfo.plainTextEndIndex - oldPlainTextEndIndex;
|
762
996
|
// as change may be before the end of the tag, we need to add the rest of the tag
|
763
|
-
lastProcessedHTMLIndex = closeTagIdx - endChangeDiff;
|
997
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx - endChangeDiff;
|
764
998
|
}
|
765
999
|
// the change is handled; exit
|
766
1000
|
break;
|
767
1001
|
}
|
768
|
-
|
769
|
-
// the change starts in the tag and ends at the end of a tag
|
770
|
-
if (isMentionTag) {
|
771
|
-
if (change !== '' && startIndex === plainTextEndIndex) {
|
772
|
-
// non empty change at the end of the mention tag to be added after the mention tag
|
773
|
-
result += htmlText.substring(lastProcessedHTMLIndex, closeTagIdx + closeTagLength) + processedChange;
|
774
|
-
changeNewEndIndex = plainTextEndIndex + processedChange.length;
|
775
|
-
processedChange = '';
|
776
|
-
lastProcessedHTMLIndex = closeTagIdx + closeTagLength;
|
777
|
-
}
|
778
|
-
else if (change !== '') {
|
779
|
-
// mention tag should be deleted when user tries to edit it
|
780
|
-
result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx) + processedChange;
|
781
|
-
changeNewEndIndex = tag.plainTextBeginIndex + processedChange.length;
|
782
|
-
processedChange = '';
|
783
|
-
lastProcessedHTMLIndex = closeTagIdx + closeTagLength;
|
784
|
-
}
|
785
|
-
else {
|
786
|
-
const [resultValue, updatedChange, htmlIndex, plainTextSelectionEndIndex] = handleMentionTagUpdate(htmlText, oldPlainText, lastProcessedHTMLIndex, processedChange, tag, closeTagIdx, closeTagLength, plainTextEndIndex, startIndex, oldPlainTextEndIndex, mentionTagLength);
|
787
|
-
result += resultValue;
|
788
|
-
processedChange = updatedChange;
|
789
|
-
lastProcessedHTMLIndex = htmlIndex;
|
790
|
-
changeNewEndIndex = plainTextSelectionEndIndex;
|
791
|
-
}
|
792
|
-
// the change is handled; exit
|
793
|
-
break;
|
794
|
-
}
|
795
|
-
else if (tag.subTags !== undefined && tag.subTags.length !== 0 && tag.content !== undefined) {
|
796
|
-
// with subtags
|
797
|
-
// before the tag content
|
798
|
-
const stringBefore = htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length);
|
799
|
-
lastProcessedHTMLIndex = closeTagIdx;
|
800
|
-
const [content, updatedChangeNewEndIndex] = updateHTML(tag.content, oldPlainText, newPlainText, tag.subTags, startIndex - mentionTagLength, oldPlainTextEndIndex - mentionTagLength, processedChange, mentionTrigger);
|
801
|
-
result += stringBefore + content;
|
802
|
-
changeNewEndIndex = updatedChangeNewEndIndex;
|
803
|
-
break;
|
804
|
-
}
|
805
|
-
else {
|
806
|
-
// no subtags
|
807
|
-
const startChangeDiff = startIndex - tag.plainTextBeginIndex - mentionTagLength;
|
808
|
-
result +=
|
809
|
-
htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length + startChangeDiff) +
|
810
|
-
processedChange;
|
811
|
-
processedChange = '';
|
812
|
-
lastProcessedHTMLIndex = closeTagIdx;
|
813
|
-
// the change is handled; exit
|
814
|
-
break;
|
815
|
-
}
|
816
|
-
}
|
817
|
-
lastProcessedPlainTextTagEndIndex = plainTextEndIndex;
|
1002
|
+
lastProcessedPlainTextTagEndIndex = closingTagInfo.plainTextEndIndex;
|
818
1003
|
}
|
819
|
-
if (i === tags.length - 1 && oldPlainTextEndIndex >= plainTextEndIndex) {
|
1004
|
+
if (i === tags.length - 1 && oldPlainTextEndIndex >= closingTagInfo.plainTextEndIndex) {
|
820
1005
|
// the last tag should handle the end of the change if needed
|
821
1006
|
// oldPlainTextEndIndex already includes mentionTag length
|
822
|
-
const endChangeDiff = oldPlainTextEndIndex - plainTextEndIndex;
|
823
|
-
if (startIndex >= plainTextEndIndex) {
|
824
|
-
const startChangeDiff = startIndex - plainTextEndIndex;
|
1007
|
+
const endChangeDiff = oldPlainTextEndIndex - closingTagInfo.plainTextEndIndex;
|
1008
|
+
if (startIndex >= closingTagInfo.plainTextEndIndex) {
|
1009
|
+
const startChangeDiff = startIndex - closingTagInfo.plainTextEndIndex;
|
825
1010
|
result +=
|
826
|
-
htmlText.substring(lastProcessedHTMLIndex, closeTagIdx + closeTagLength + startChangeDiff) + processedChange;
|
1011
|
+
htmlText.substring(lastProcessedHTMLIndex, closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength + startChangeDiff) + processedChange;
|
827
1012
|
}
|
828
1013
|
else {
|
829
|
-
result +=
|
1014
|
+
result +=
|
1015
|
+
htmlText.substring(lastProcessedHTMLIndex, closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength) +
|
1016
|
+
processedChange;
|
830
1017
|
}
|
831
1018
|
processedChange = '';
|
832
|
-
lastProcessedHTMLIndex = closeTagIdx + closeTagLength + endChangeDiff;
|
1019
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength + endChangeDiff;
|
833
1020
|
// the change is handled; exit
|
834
1021
|
// break is not required here as this is the last element but added for consistency
|
835
1022
|
break;
|
836
1023
|
}
|
837
1024
|
}
|
838
1025
|
if (lastProcessedHTMLIndex < htmlText.length) {
|
1026
|
+
// add the rest of the html string
|
839
1027
|
result += htmlText.substring(lastProcessedHTMLIndex);
|
840
1028
|
}
|
841
|
-
return
|
1029
|
+
return { updatedHTML: result, updatedSelectionIndex: changeNewEndIndex };
|
842
1030
|
};
|
843
1031
|
/* @conditional-compile-remove(mention) */
|
844
1032
|
/**
|
845
1033
|
* Given the oldText and newText, find the start index, old end index and new end index for the changes
|
846
1034
|
*
|
847
|
-
* @param oldText - the old text
|
848
|
-
* @param newText - the new text
|
849
|
-
* @param selectionEnd - the end of the selection
|
850
|
-
* @returns change start index, old end index and new end index. The old and new end indexes are exclusive.
|
851
1035
|
* @private
|
1036
|
+
* @param props - Props for finding stings diff indexes function.
|
1037
|
+
* @returns Indexes for change start and ends in new and old texts. The old and new end indexes are exclusive.
|
852
1038
|
*/
|
853
|
-
const findStringsDiffIndexes = (
|
854
|
-
|
1039
|
+
const findStringsDiffIndexes = (props) => {
|
1040
|
+
const { oldText, newText, previousSelectionStart, previousSelectionEnd, currentSelectionStart, currentSelectionEnd } = props;
|
855
1041
|
const newTextLength = newText.length;
|
856
1042
|
const oldTextLength = oldText.length;
|
857
|
-
let changeStart = 0;
|
1043
|
+
// let changeStart = 0;
|
858
1044
|
let newChangeEnd = newTextLength;
|
859
1045
|
let oldChangeEnd = oldTextLength;
|
860
|
-
const
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
break;
|
866
|
-
}
|
867
|
-
else if (i === length - 1 && newText[i] === oldText[i]) {
|
868
|
-
// the symbol is added at the end of inputTextValue
|
869
|
-
changeStart = length;
|
870
|
-
break;
|
871
|
-
}
|
872
|
-
}
|
1046
|
+
const previousSelectionStartValue = previousSelectionStart > -1 ? previousSelectionStart : oldTextLength;
|
1047
|
+
const previousSelectionEndValue = previousSelectionEnd > -1 ? previousSelectionEnd : oldTextLength;
|
1048
|
+
const currentSelectionStartValue = currentSelectionStart > -1 ? currentSelectionStart : newTextLength;
|
1049
|
+
const currentSelectionEndValue = currentSelectionEnd > -1 ? currentSelectionEnd : newTextLength;
|
1050
|
+
const changeStart = Math.min(previousSelectionStartValue, previousSelectionEndValue, currentSelectionStartValue, currentSelectionEndValue, newTextLength, oldTextLength);
|
873
1051
|
if (oldTextLength < newTextLength) {
|
874
1052
|
//insert or replacement
|
875
1053
|
if (oldTextLength === changeStart) {
|
876
1054
|
// when change was at the end of string
|
877
|
-
//
|
1055
|
+
// change is found
|
878
1056
|
newChangeEnd = newTextLength;
|
879
1057
|
oldChangeEnd = oldTextLength;
|
880
1058
|
}
|
@@ -883,7 +1061,7 @@ const findStringsDiffIndexes = (oldText, newText, selectionEnd // should be a va
|
|
883
1061
|
newChangeEnd = newTextLength - i - 1;
|
884
1062
|
oldChangeEnd = oldTextLength - i - 1;
|
885
1063
|
if (newText[newChangeEnd] !== oldText[oldChangeEnd]) {
|
886
|
-
//
|
1064
|
+
// change is found
|
887
1065
|
break;
|
888
1066
|
}
|
889
1067
|
}
|
@@ -896,7 +1074,7 @@ const findStringsDiffIndexes = (oldText, newText, selectionEnd // should be a va
|
|
896
1074
|
//deletion or replacement
|
897
1075
|
if (newTextLength === changeStart) {
|
898
1076
|
// when change was at the end of string
|
899
|
-
//
|
1077
|
+
// change is found
|
900
1078
|
newChangeEnd = newTextLength;
|
901
1079
|
oldChangeEnd = oldTextLength;
|
902
1080
|
}
|
@@ -905,7 +1083,7 @@ const findStringsDiffIndexes = (oldText, newText, selectionEnd // should be a va
|
|
905
1083
|
newChangeEnd = newTextLength - i - 1;
|
906
1084
|
oldChangeEnd = oldTextLength - i - 1;
|
907
1085
|
if (newText[newChangeEnd] !== oldText[oldChangeEnd]) {
|
908
|
-
//
|
1086
|
+
// change is found
|
909
1087
|
break;
|
910
1088
|
}
|
911
1089
|
}
|
@@ -915,12 +1093,12 @@ const findStringsDiffIndexes = (oldText, newText, selectionEnd // should be a va
|
|
915
1093
|
}
|
916
1094
|
}
|
917
1095
|
else {
|
918
|
-
//replacement
|
1096
|
+
// replacement
|
919
1097
|
for (let i = 1; i < oldTextLength && oldTextLength - i >= changeStart; i++) {
|
920
1098
|
newChangeEnd = newTextLength - i - 1;
|
921
1099
|
oldChangeEnd = oldTextLength - i - 1;
|
922
1100
|
if (newText[newChangeEnd] !== oldText[oldChangeEnd]) {
|
923
|
-
//
|
1101
|
+
// change is found
|
924
1102
|
break;
|
925
1103
|
}
|
926
1104
|
}
|
@@ -935,13 +1113,30 @@ const findStringsDiffIndexes = (oldText, newText, selectionEnd // should be a va
|
|
935
1113
|
return { changeStart, oldChangeEnd, newChangeEnd };
|
936
1114
|
};
|
937
1115
|
/* @conditional-compile-remove(mention) */
|
1116
|
+
/**
|
1117
|
+
* Get the html string for the mention suggestion.
|
1118
|
+
*
|
1119
|
+
* @private
|
1120
|
+
* @param suggestion - The mention suggestion.
|
1121
|
+
* @param localeStrings - The locale strings.
|
1122
|
+
* @returns The html string for the mention suggestion.
|
1123
|
+
*/
|
938
1124
|
const htmlStringForMentionSuggestion = (suggestion, localeStrings) => {
|
939
1125
|
const idHTML = ' id ="' + suggestion.id + '"';
|
940
1126
|
const displayTextHTML = ' displayText ="' + suggestion.displayText + '"';
|
941
1127
|
const displayText = getDisplayNameForMentionSuggestion(suggestion, localeStrings);
|
942
|
-
return '<
|
1128
|
+
return '<' + MSFT_MENTION_TAG + idHTML + displayTextHTML + '>' + displayText + '</' + MSFT_MENTION_TAG + '>';
|
943
1129
|
};
|
944
1130
|
/* @conditional-compile-remove(mention) */
|
1131
|
+
/**
|
1132
|
+
* Get display name for the mention suggestion.
|
1133
|
+
*
|
1134
|
+
* @private
|
1135
|
+
*
|
1136
|
+
* @param suggestion - The mention suggestion.
|
1137
|
+
* @param localeStrings - The locale strings.
|
1138
|
+
* @returns The display name for the mention suggestion or display name placeholder if display name is empty.
|
1139
|
+
*/
|
945
1140
|
const getDisplayNameForMentionSuggestion = (suggestion, localeStrings) => {
|
946
1141
|
const displayNamePlaceholder = localeStrings.participantItem.displayNamePlaceholder;
|
947
1142
|
return suggestion.displayText !== '' ? suggestion.displayText : displayNamePlaceholder !== null && displayNamePlaceholder !== void 0 ? displayNamePlaceholder : '';
|
@@ -949,8 +1144,9 @@ const getDisplayNameForMentionSuggestion = (suggestion, localeStrings) => {
|
|
949
1144
|
/* @conditional-compile-remove(mention) */
|
950
1145
|
/**
|
951
1146
|
* Parse the text and return the tags and the plain text in one go
|
1147
|
+
* @private
|
952
1148
|
* @param text - The text to parse for HTML tags
|
953
|
-
* @param trigger The trigger to show for the
|
1149
|
+
* @param trigger The trigger to show for the mention tag in plain text
|
954
1150
|
*
|
955
1151
|
* @returns An array of tags and the plain text representation
|
956
1152
|
*/
|
@@ -994,7 +1190,7 @@ const textToTagParser = (text, trigger) => {
|
|
994
1190
|
// Tag startIdx is absolute to the text. This is updated later to be relative to the parent tag
|
995
1191
|
currentOpenTag.content = text.substring(currentOpenTag.openTagIdx + currentOpenTag.openTagBody.length, foundHtmlTag.startIdx);
|
996
1192
|
// Insert the plain text pieces for the sub tags
|
997
|
-
if (currentOpenTag.tagType ===
|
1193
|
+
if (currentOpenTag.tagType === MSFT_MENTION_TAG) {
|
998
1194
|
plainTextRepresentation =
|
999
1195
|
plainTextRepresentation.slice(0, currentOpenTag.plainTextBeginIndex) +
|
1000
1196
|
trigger +
|
@@ -1024,7 +1220,7 @@ const textToTagParser = (text, trigger) => {
|
|
1024
1220
|
// Update parsing index; move past the end of the close tag
|
1025
1221
|
parseIndex = foundHtmlTag.startIdx + foundHtmlTag.content.length;
|
1026
1222
|
} // While parseIndex < text.length loop
|
1027
|
-
return
|
1223
|
+
return { tags, plainText: plainTextRepresentation };
|
1028
1224
|
};
|
1029
1225
|
/* @conditional-compile-remove(mention) */
|
1030
1226
|
const parseOpenTag = (tag, startIdx) => {
|