@azure/communication-react 1.5.1-alpha-202305170014 → 1.5.1-alpha-202305190012
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/communication-react.d.ts +4 -2
- package/dist/dist-cjs/communication-react/index.js +802 -568
- 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/calling-stateful-client/src/LocalVideoStreamVideoEffectsSubscriber.js +4 -4
- package/dist/dist-esm/calling-stateful-client/src/LocalVideoStreamVideoEffectsSubscriber.js.map +1 -1
- package/dist/dist-esm/react-components/src/components/CameraButton.js +5 -3
- package/dist/dist-esm/react-components/src/components/CameraButton.js.map +1 -1
- package/dist/dist-esm/react-components/src/components/InputBoxComponent.js +564 -364
- 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-components/src/localization/locales/de-DE/strings.json +7 -2
- package/dist/dist-esm/react-components/src/localization/locales/en-GB/strings.json +7 -2
- package/dist/dist-esm/react-components/src/localization/locales/es-ES/strings.json +7 -2
- package/dist/dist-esm/react-components/src/localization/locales/fr-FR/strings.json +7 -2
- package/dist/dist-esm/react-components/src/localization/locales/it-IT/strings.json +7 -2
- package/dist/dist-esm/react-components/src/localization/locales/ja-JP/strings.json +7 -2
- package/dist/dist-esm/react-components/src/localization/locales/ko-KR/strings.json +7 -2
- package/dist/dist-esm/react-components/src/localization/locales/nl-NL/strings.json +7 -2
- package/dist/dist-esm/react-components/src/localization/locales/pt-BR/strings.json +7 -2
- package/dist/dist-esm/react-components/src/localization/locales/ru-RU/strings.json +7 -2
- package/dist/dist-esm/react-components/src/localization/locales/tr-TR/strings.json +7 -2
- package/dist/dist-esm/react-components/src/localization/locales/zh-CN/strings.json +7 -2
- package/dist/dist-esm/react-components/src/localization/locales/zh-TW/strings.json +7 -2
- package/dist/dist-esm/react-components/src/theming/icons.d.ts +2 -1
- package/dist/dist-esm/react-components/src/theming/icons.js +4 -2
- package/dist/dist-esm/react-components/src/theming/icons.js.map +1 -1
- package/dist/dist-esm/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.js +20 -4
- package/dist/dist-esm/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.js.map +1 -1
- package/dist/dist-esm/react-composites/src/composites/CallComposite/components/LocalDeviceSettings.d.ts +1 -0
- package/dist/dist-esm/react-composites/src/composites/CallComposite/components/LocalDeviceSettings.js +9 -1
- package/dist/dist-esm/react-composites/src/composites/CallComposite/components/LocalDeviceSettings.js.map +1 -1
- package/dist/dist-esm/react-composites/src/composites/CallComposite/pages/ConfigurationPage.js +3 -11
- package/dist/dist-esm/react-composites/src/composites/CallComposite/pages/ConfigurationPage.js.map +1 -1
- package/dist/dist-esm/react-composites/src/composites/CallComposite/styles/CallConfiguration.styles.d.ts +2 -0
- package/dist/dist-esm/react-composites/src/composites/CallComposite/styles/CallConfiguration.styles.js +24 -5
- package/dist/dist-esm/react-composites/src/composites/CallComposite/styles/CallConfiguration.styles.js.map +1 -1
- package/dist/dist-esm/react-composites/src/composites/CallComposite/utils/Utils.d.ts +9 -5
- package/dist/dist-esm/react-composites/src/composites/CallComposite/utils/Utils.js +15 -18
- package/dist/dist-esm/react-composites/src/composites/CallComposite/utils/Utils.js.map +1 -1
- package/dist/dist-esm/react-composites/src/composites/common/icons.d.ts +2 -1
- package/dist/dist-esm/react-composites/src/composites/localization/locales/de-DE/strings.json +5 -0
- package/dist/dist-esm/react-composites/src/composites/localization/locales/en-GB/strings.json +5 -0
- package/dist/dist-esm/react-composites/src/composites/localization/locales/es-ES/strings.json +5 -0
- package/dist/dist-esm/react-composites/src/composites/localization/locales/fr-FR/strings.json +5 -0
- package/dist/dist-esm/react-composites/src/composites/localization/locales/it-IT/strings.json +5 -0
- package/dist/dist-esm/react-composites/src/composites/localization/locales/ja-JP/strings.json +5 -0
- package/dist/dist-esm/react-composites/src/composites/localization/locales/ko-KR/strings.json +5 -0
- package/dist/dist-esm/react-composites/src/composites/localization/locales/nl-NL/strings.json +5 -0
- package/dist/dist-esm/react-composites/src/composites/localization/locales/pt-BR/strings.json +5 -0
- package/dist/dist-esm/react-composites/src/composites/localization/locales/ru-RU/strings.json +5 -0
- package/dist/dist-esm/react-composites/src/composites/localization/locales/tr-TR/strings.json +5 -0
- package/dist/dist-esm/react-composites/src/composites/localization/locales/zh-CN/strings.json +5 -0
- package/dist/dist-esm/react-composites/src/composites/localization/locales/zh-TW/strings.json +5 -0
- package/package.json +10 -10
@@ -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;
|
@@ -185,6 +227,10 @@ export const InputBoxComponent = (props) => {
|
|
185
227
|
/* @conditional-compile-remove(mention) */
|
186
228
|
const debouncedQueryUpdate = useDebouncedCallback((query) => __awaiter(void 0, void 0, void 0, function* () {
|
187
229
|
var _a;
|
230
|
+
if (query === undefined) {
|
231
|
+
updateMentionSuggestions([]);
|
232
|
+
return;
|
233
|
+
}
|
188
234
|
const suggestions = (_a = (yield (mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.onQueryUpdated(query)))) !== null && _a !== void 0 ? _a : [];
|
189
235
|
if (suggestions.length === 0) {
|
190
236
|
setActiveSuggestionIndex(undefined);
|
@@ -201,24 +247,149 @@ export const InputBoxComponent = (props) => {
|
|
201
247
|
};
|
202
248
|
}, [debouncedQueryUpdate]);
|
203
249
|
/* @conditional-compile-remove(mention) */
|
204
|
-
|
205
|
-
|
250
|
+
// Update selections index in mention to navigate by words
|
251
|
+
const updateSelectionIndexesWithMentionIfNeeded = useCallback((event, inputTextValue, selectionStartValue, selectionEndValue, tagsValue) => {
|
252
|
+
var _a, _b, _c;
|
253
|
+
let updatedStartIndex = event.currentTarget.selectionStart;
|
254
|
+
let updatedEndIndex = event.currentTarget.selectionEnd;
|
255
|
+
if (event.currentTarget.selectionStart === event.currentTarget.selectionEnd &&
|
256
|
+
event.currentTarget.selectionStart !== null &&
|
257
|
+
event.currentTarget.selectionStart !== -1) {
|
258
|
+
// just a caret movement/usual typing or deleting
|
259
|
+
const mentionTag = findMentionTagForSelection(tagsValue, event.currentTarget.selectionStart);
|
260
|
+
// don't include boundary cases to show correct selection, otherwise it will show selection at mention boundaries
|
261
|
+
if (mentionTag !== undefined &&
|
262
|
+
mentionTag.plainTextBeginIndex !== undefined &&
|
263
|
+
event.currentTarget.selectionStart > mentionTag.plainTextBeginIndex &&
|
264
|
+
event.currentTarget.selectionStart < ((_a = mentionTag.plainTextEndIndex) !== null && _a !== void 0 ? _a : mentionTag.plainTextBeginIndex)) {
|
265
|
+
// get updated selection index
|
266
|
+
const newSelectionIndex = findNewSelectionIndexForMention({
|
267
|
+
tag: mentionTag,
|
268
|
+
textValue: inputTextValue,
|
269
|
+
currentSelectionIndex: event.currentTarget.selectionStart,
|
270
|
+
previousSelectionIndex: selectionStartValue !== null && selectionStartValue !== void 0 ? selectionStartValue : inputTextValue.length
|
271
|
+
});
|
272
|
+
updatedStartIndex = newSelectionIndex;
|
273
|
+
updatedEndIndex = newSelectionIndex;
|
274
|
+
}
|
275
|
+
}
|
276
|
+
else if (event.currentTarget.selectionStart !== event.currentTarget.selectionEnd) {
|
277
|
+
// Both e.currentTarget.selectionStart !== selectionStartValue and e.currentTarget.selectionEnd !== selectionEndValue can be true when a user selects a text by double click
|
278
|
+
if (event.currentTarget.selectionStart !== null && event.currentTarget.selectionStart !== selectionStartValue) {
|
279
|
+
// the selection start is changed
|
280
|
+
const mentionTag = findMentionTagForSelection(tagsValue, event.currentTarget.selectionStart);
|
281
|
+
// don't include boundary cases to show correct selection, otherwise it will show selection at mention boundaries
|
282
|
+
if (mentionTag !== undefined &&
|
283
|
+
mentionTag.plainTextBeginIndex !== undefined &&
|
284
|
+
event.currentTarget.selectionStart > mentionTag.plainTextBeginIndex &&
|
285
|
+
event.currentTarget.selectionStart < ((_b = mentionTag.plainTextEndIndex) !== null && _b !== void 0 ? _b : mentionTag.plainTextBeginIndex)) {
|
286
|
+
updatedStartIndex = findNewSelectionIndexForMention({
|
287
|
+
tag: mentionTag,
|
288
|
+
textValue: inputTextValue,
|
289
|
+
currentSelectionIndex: event.currentTarget.selectionStart,
|
290
|
+
previousSelectionIndex: selectionStartValue !== null && selectionStartValue !== void 0 ? selectionStartValue : inputTextValue.length
|
291
|
+
});
|
292
|
+
}
|
293
|
+
}
|
294
|
+
if (event.currentTarget.selectionEnd !== null && event.currentTarget.selectionEnd !== selectionEndValue) {
|
295
|
+
// the selection end is changed
|
296
|
+
const mentionTag = findMentionTagForSelection(tagsValue, event.currentTarget.selectionEnd);
|
297
|
+
// don't include boundary cases to show correct selection, otherwise it will show selection at mention boundaries
|
298
|
+
if (mentionTag !== undefined &&
|
299
|
+
mentionTag.plainTextBeginIndex !== undefined &&
|
300
|
+
event.currentTarget.selectionEnd > mentionTag.plainTextBeginIndex &&
|
301
|
+
event.currentTarget.selectionEnd < ((_c = mentionTag.plainTextEndIndex) !== null && _c !== void 0 ? _c : mentionTag.plainTextBeginIndex)) {
|
302
|
+
updatedEndIndex = findNewSelectionIndexForMention({
|
303
|
+
tag: mentionTag,
|
304
|
+
textValue: inputTextValue,
|
305
|
+
currentSelectionIndex: event.currentTarget.selectionEnd,
|
306
|
+
previousSelectionIndex: selectionEndValue !== null && selectionEndValue !== void 0 ? selectionEndValue : inputTextValue.length
|
307
|
+
});
|
308
|
+
}
|
309
|
+
}
|
310
|
+
}
|
311
|
+
// e.currentTarget.selectionDirection should be set to handle shift + arrow keys
|
312
|
+
if (event.currentTarget.selectionDirection === null) {
|
313
|
+
event.currentTarget.setSelectionRange(updatedStartIndex, updatedEndIndex);
|
314
|
+
}
|
315
|
+
else {
|
316
|
+
event.currentTarget.setSelectionRange(updatedStartIndex, updatedEndIndex, event.currentTarget.selectionDirection);
|
317
|
+
}
|
318
|
+
setSelectionStartValue(updatedStartIndex);
|
319
|
+
setSelectionEndValue(updatedEndIndex);
|
320
|
+
}, [setSelectionStartValue, setSelectionEndValue]);
|
321
|
+
/* @conditional-compile-remove(mention) */
|
322
|
+
const handleOnSelect = useCallback((event, inputTextValue, tags, shouldHandleOnMouseDownDuringSelect, selectionStartValue, selectionEndValue) => {
|
323
|
+
var _a, _b, _c;
|
324
|
+
/* @conditional-compile-remove(mention) */
|
325
|
+
if (shouldHandleOnMouseDownDuringSelect && event.currentTarget.selectionStart !== null) {
|
326
|
+
// on select was triggered by mouse down
|
327
|
+
const mentionTag = findMentionTagForSelection(tags, event.currentTarget.selectionStart);
|
328
|
+
if (mentionTag !== undefined && mentionTag.plainTextBeginIndex !== undefined) {
|
329
|
+
// handle mention click
|
330
|
+
if (event.currentTarget.selectionDirection === null) {
|
331
|
+
event.currentTarget.setSelectionRange(mentionTag.plainTextBeginIndex, (_a = mentionTag.plainTextEndIndex) !== null && _a !== void 0 ? _a : mentionTag.plainTextBeginIndex);
|
332
|
+
}
|
333
|
+
else {
|
334
|
+
event.currentTarget.setSelectionRange(mentionTag.plainTextBeginIndex, (_b = mentionTag.plainTextEndIndex) !== null && _b !== void 0 ? _b : mentionTag.plainTextBeginIndex, event.currentTarget.selectionDirection);
|
335
|
+
}
|
336
|
+
setSelectionStartValue(mentionTag.plainTextBeginIndex);
|
337
|
+
setSelectionEndValue((_c = mentionTag.plainTextEndIndex) !== null && _c !== void 0 ? _c : mentionTag.plainTextBeginIndex);
|
338
|
+
}
|
339
|
+
else {
|
340
|
+
setSelectionStartValue(event.currentTarget.selectionStart);
|
341
|
+
setSelectionEndValue(event.currentTarget.selectionEnd);
|
342
|
+
}
|
343
|
+
}
|
344
|
+
else {
|
345
|
+
// selection was changed by keyboard
|
346
|
+
updateSelectionIndexesWithMentionIfNeeded(event, inputTextValue, selectionStartValue, selectionEndValue, tags);
|
347
|
+
}
|
348
|
+
// 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)
|
349
|
+
}, [updateSelectionIndexesWithMentionIfNeeded]);
|
350
|
+
/* @conditional-compile-remove(mention) */
|
351
|
+
const handleOnChange = useCallback((event, tagsValue, htmlTextValue, inputTextValue, currentTriggerStartIndex, previousSelectionStart, previousSelectionEnd, currentSelectionStart, currentSelectionEnd, updatedValue) => __awaiter(void 0, void 0, void 0, function* () {
|
352
|
+
var _b;
|
353
|
+
if (event.currentTarget === null) {
|
354
|
+
return;
|
355
|
+
}
|
356
|
+
// handle backspace change
|
357
|
+
// onSelect is not called for backspace as selection is not changed and local caret index is outdated
|
358
|
+
setCaretIndex(undefined);
|
206
359
|
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 :
|
360
|
+
const triggerText = (_b = mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.trigger) !== null && _b !== void 0 ? _b : DEFAULT_MENTION_TRIGGER;
|
208
361
|
const newTextLength = newValue.length;
|
209
|
-
|
210
|
-
|
211
|
-
|
362
|
+
// updating indexes to set between 0 and text length, otherwise selectionRange won't be set correctly
|
363
|
+
const currentSelectionEndValue = getValidatedIndexInRange({
|
364
|
+
min: 0,
|
365
|
+
max: newTextLength,
|
366
|
+
currentValue: currentSelectionEnd
|
367
|
+
});
|
368
|
+
const currentSelectionStartValue = getValidatedIndexInRange({
|
369
|
+
min: 0,
|
370
|
+
max: newTextLength,
|
371
|
+
currentValue: currentSelectionStart
|
372
|
+
});
|
373
|
+
const previousSelectionStartValue = getValidatedIndexInRange({
|
374
|
+
min: 0,
|
375
|
+
max: inputTextValue.length,
|
376
|
+
currentValue: previousSelectionStart
|
377
|
+
});
|
378
|
+
const previousSelectionEndValue = getValidatedIndexInRange({
|
379
|
+
min: 0,
|
380
|
+
max: inputTextValue.length,
|
381
|
+
currentValue: previousSelectionEnd
|
382
|
+
});
|
212
383
|
// If we are enabled for lookups,
|
213
384
|
if (mentionLookupOptions !== undefined) {
|
214
385
|
// Look at the range of the change for a trigger character
|
215
|
-
const triggerPriorIndex = newValue.lastIndexOf(triggerText,
|
386
|
+
const triggerPriorIndex = newValue.lastIndexOf(triggerText, currentSelectionEndValue - 1);
|
216
387
|
// Update the caret position, if not doing a lookup
|
217
388
|
setCaretPosition(Caret.getRelativePosition(event.currentTarget));
|
218
389
|
if (triggerPriorIndex !== undefined) {
|
219
390
|
// trigger is found
|
220
391
|
const isSpaceBeforeTrigger = newValue.substring(triggerPriorIndex - 1, triggerPriorIndex) === ' ';
|
221
|
-
const wordAtSelection = newValue.substring(triggerPriorIndex,
|
392
|
+
const wordAtSelection = newValue.substring(triggerPriorIndex, currentSelectionEndValue);
|
222
393
|
let tagIndex = currentTriggerStartIndex;
|
223
394
|
if (!isSpaceBeforeTrigger && triggerPriorIndex !== 0) {
|
224
395
|
//no space before the trigger <- continuation of the previous word
|
@@ -227,14 +398,14 @@ export const InputBoxComponent = (props) => {
|
|
227
398
|
}
|
228
399
|
else if (wordAtSelection === triggerText) {
|
229
400
|
// start of the mention
|
230
|
-
tagIndex =
|
401
|
+
tagIndex = currentSelectionEndValue - triggerText.length;
|
231
402
|
if (tagIndex < 0) {
|
232
403
|
tagIndex = 0;
|
233
404
|
}
|
234
405
|
setCurrentTriggerStartIndex(tagIndex);
|
235
406
|
}
|
236
407
|
if (tagIndex === -1) {
|
237
|
-
|
408
|
+
yield debouncedQueryUpdate(undefined);
|
238
409
|
}
|
239
410
|
else {
|
240
411
|
// In the middle of a @mention lookup
|
@@ -249,88 +420,42 @@ export const InputBoxComponent = (props) => {
|
|
249
420
|
}
|
250
421
|
let result = '';
|
251
422
|
if (tagsValue.length === 0) {
|
252
|
-
// no tags in the string
|
423
|
+
// no tags in the string and newValue should be used as a result string
|
253
424
|
result = newValue;
|
254
425
|
}
|
255
426
|
else {
|
256
|
-
// there are tags in the text value
|
257
|
-
|
258
|
-
|
427
|
+
// there are tags in the text value and htmlTextValue is html string
|
428
|
+
// find diff between old and new text
|
429
|
+
const { changeStart, oldChangeEnd, newChangeEnd } = findStringsDiffIndexes({
|
430
|
+
oldText: inputTextValue,
|
431
|
+
newText: newValue,
|
432
|
+
previousSelectionStart: previousSelectionStartValue,
|
433
|
+
previousSelectionEnd: previousSelectionEndValue,
|
434
|
+
currentSelectionStart: currentSelectionStartValue,
|
435
|
+
currentSelectionEnd: currentSelectionEndValue
|
436
|
+
});
|
259
437
|
const change = newValue.substring(changeStart, newChangeEnd);
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
438
|
+
// get updated html string
|
439
|
+
const updatedContent = updateHTML({
|
440
|
+
htmlText: htmlTextValue,
|
441
|
+
oldPlainText: inputTextValue,
|
442
|
+
newPlainText: newValue,
|
443
|
+
tags: tagsValue,
|
444
|
+
startIndex: changeStart,
|
445
|
+
oldPlainTextEndIndex: oldChangeEnd,
|
446
|
+
change,
|
447
|
+
mentionTrigger: triggerText
|
448
|
+
});
|
449
|
+
result = updatedContent.updatedHTML;
|
450
|
+
// update caret index if needed
|
451
|
+
if (updatedContent.updatedSelectionIndex !== null) {
|
452
|
+
setCaretIndex(updatedContent.updatedSelectionIndex);
|
453
|
+
setSelectionEndValue(updatedContent.updatedSelectionIndex);
|
454
|
+
setSelectionStartValue(updatedContent.updatedSelectionIndex);
|
268
455
|
}
|
269
456
|
}
|
270
457
|
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
|
-
};
|
458
|
+
}), [onChange, mentionLookupOptions, setCaretIndex, setCaretPosition, debouncedQueryUpdate]);
|
334
459
|
const getInputFieldTextValue = () => {
|
335
460
|
/* @conditional-compile-remove(mention) */
|
336
461
|
return inputTextValue;
|
@@ -342,61 +467,52 @@ export const InputBoxComponent = (props) => {
|
|
342
467
|
updateMentionSuggestions([]);
|
343
468
|
} })),
|
344
469
|
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) => {
|
470
|
+
// Remove when switching to react 17+, currently needed because of https://legacy.reactjs.org/docs/legacy-event-pooling.html
|
471
|
+
/* @conditional-compile-remove(mention) */
|
472
|
+
// Prevents React from resetting event's properties
|
473
|
+
e.persist();
|
345
474
|
/* @conditional-compile-remove(mention) */
|
346
475
|
setInputTextValue(newValue !== null && newValue !== void 0 ? newValue : '');
|
347
476
|
/* @conditional-compile-remove(mention) */
|
348
|
-
handleOnChange(e, newValue);
|
477
|
+
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
478
|
/* @conditional-compile-remove(mention) */
|
350
479
|
return;
|
351
480
|
onChange(e, newValue);
|
352
481
|
},
|
353
482
|
/* @conditional-compile-remove(mention) */
|
354
483
|
onSelect: (e) => {
|
355
|
-
|
484
|
+
// update selection if needed
|
356
485
|
if (caretIndex !== undefined) {
|
357
|
-
e.currentTarget.setSelectionRange(caretIndex, caretIndex);
|
358
486
|
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);
|
487
|
+
// sometimes setting selectionRage in effect for updating caretIndex doesn't work as expected and onSelect should handle this case
|
488
|
+
if (caretIndex !== e.currentTarget.selectionStart || caretIndex !== e.currentTarget.selectionEnd) {
|
489
|
+
e.currentTarget.setSelectionRange(caretIndex, caretIndex);
|
381
490
|
}
|
491
|
+
return;
|
382
492
|
}
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
setShouldHandleOnMouseDownDuringSelect(false);
|
388
|
-
}, onMouseDown: () => {
|
493
|
+
handleOnSelect(e, inputTextValue, tagsValue, shouldHandleOnMouseDownDuringSelect, selectionStartValue, selectionEndValue);
|
494
|
+
},
|
495
|
+
/* @conditional-compile-remove(mention) */
|
496
|
+
onMouseDown: () => {
|
389
497
|
// as events order is onMouseDown -> onSelect -> onClick
|
390
498
|
// onClick and onMouseDown can't handle clicking on mention event because
|
391
|
-
//
|
392
|
-
//
|
499
|
+
// onMouseDown doesn't have correct selectionRange yet and
|
500
|
+
// onClick already has wrong range as it's called after onSelect that updates the selection range
|
393
501
|
// so we need to handle onMouseDown to prevent onSelect default behavior
|
394
|
-
/* @conditional-compile-remove(mention) */
|
395
502
|
setShouldHandleOnMouseDownDuringSelect(true);
|
396
|
-
},
|
503
|
+
},
|
504
|
+
/* @conditional-compile-remove(mention) */
|
505
|
+
onTouchStart: () => {
|
397
506
|
// see onMouseDown for more details
|
398
|
-
/* @conditional-compile-remove(mention) */
|
399
507
|
setShouldHandleOnMouseDownDuringSelect(true);
|
508
|
+
},
|
509
|
+
/* @conditional-compile-remove(mention) */
|
510
|
+
onBlur: () => {
|
511
|
+
// setup all flags to default values when text field loses focus
|
512
|
+
setShouldHandleOnMouseDownDuringSelect(false);
|
513
|
+
setCaretIndex(undefined);
|
514
|
+
setSelectionStartValue(null);
|
515
|
+
setSelectionEndValue(null);
|
400
516
|
}, autoComplete: "off", onKeyDown: onTextFieldKeyDown, styles: mergedTextFieldStyle, disabled: disabled, errorMessage: errorMessage, onRenderSuffix: onRenderChildren, elementRef: inputBoxRef }))));
|
401
517
|
};
|
402
518
|
/**
|
@@ -425,77 +541,93 @@ export const InputBoxButton = (props) => {
|
|
425
541
|
};
|
426
542
|
/* @conditional-compile-remove(mention) */
|
427
543
|
/**
|
428
|
-
*
|
544
|
+
* Get validated value for index between min and max values. If currentValue is not defined, -1 will be used instead.
|
429
545
|
*
|
430
546
|
* @private
|
547
|
+
* @param props - Props for finding a valid index in range.
|
548
|
+
* @returns Valid index in the range.
|
549
|
+
*/
|
550
|
+
const getValidatedIndexInRange = (props) => {
|
551
|
+
const { min, max, currentValue } = props;
|
552
|
+
let updatedValue = currentValue !== null && currentValue !== void 0 ? currentValue : -1;
|
553
|
+
updatedValue = Math.max(min, updatedValue);
|
554
|
+
updatedValue = Math.min(updatedValue, max);
|
555
|
+
return updatedValue;
|
556
|
+
};
|
557
|
+
/* @conditional-compile-remove(mention) */
|
558
|
+
/**
|
559
|
+
* Find mention tag for selection if exists.
|
560
|
+
*
|
561
|
+
* @private
|
562
|
+
* @param tags - Existing list of tags.
|
563
|
+
* @param selection - Selection index.
|
564
|
+
* @returns Mention tag if exists, otherwise undefined.
|
431
565
|
*/
|
432
566
|
const findMentionTagForSelection = (tags, selection) => {
|
433
567
|
let mentionTag = undefined;
|
434
568
|
for (let i = 0; i < tags.length; i++) {
|
435
569
|
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;
|
570
|
+
const closingTagInfo = getTagClosingTagInfo(tag);
|
571
|
+
if (tag.plainTextBeginIndex !== undefined && tag.plainTextBeginIndex > selection) {
|
572
|
+
// no need to check further as the selection is before the tag
|
573
|
+
break;
|
444
574
|
}
|
445
|
-
if (tag.
|
446
|
-
|
447
|
-
|
448
|
-
|
575
|
+
else if (tag.plainTextBeginIndex !== undefined &&
|
576
|
+
tag.plainTextBeginIndex <= selection &&
|
577
|
+
selection <= closingTagInfo.plainTextEndIndex) {
|
578
|
+
// no need to check if tag doesn't contain selection
|
579
|
+
if (tag.subTags !== undefined && tag.subTags.length !== 0) {
|
580
|
+
const selectedTag = findMentionTagForSelection(tag.subTags, selection);
|
581
|
+
if (selectedTag !== undefined) {
|
582
|
+
mentionTag = selectedTag;
|
583
|
+
break;
|
584
|
+
}
|
585
|
+
}
|
586
|
+
else if (tag.tagType === MSFT_MENTION_TAG) {
|
587
|
+
mentionTag = tag;
|
449
588
|
break;
|
450
589
|
}
|
451
590
|
}
|
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
591
|
}
|
460
592
|
return mentionTag;
|
461
593
|
};
|
462
594
|
/* @conditional-compile-remove(mention) */
|
463
595
|
/**
|
464
|
-
*
|
596
|
+
* Find a new the selection index.
|
465
597
|
*
|
466
598
|
* @private
|
599
|
+
* @param props - Props for finding new selection index for mention.
|
600
|
+
* @returns New selection index if it is inside of a mention tag, otherwise the current selection.
|
467
601
|
*/
|
468
|
-
const findNewSelectionIndexForMention = (
|
602
|
+
const findNewSelectionIndexForMention = (props) => {
|
469
603
|
var _a;
|
470
|
-
|
471
|
-
|
472
|
-
|
604
|
+
const { tag, textValue, currentSelectionIndex, previousSelectionIndex } = props;
|
605
|
+
// check if this is a mention tag and selection should be updated
|
606
|
+
if (tag.tagType !== MSFT_MENTION_TAG ||
|
607
|
+
tag.plainTextBeginIndex === undefined ||
|
608
|
+
currentSelectionIndex === previousSelectionIndex ||
|
473
609
|
tag.plainTextEndIndex === undefined) {
|
474
|
-
return
|
610
|
+
return currentSelectionIndex;
|
475
611
|
}
|
476
612
|
let spaceIndex = 0;
|
477
|
-
if (
|
478
|
-
// the cursor is moved to the left
|
479
|
-
spaceIndex = textValue.lastIndexOf(' ',
|
613
|
+
if (currentSelectionIndex <= previousSelectionIndex) {
|
614
|
+
// the cursor is moved to the left, find the last index before the cursor
|
615
|
+
spaceIndex = textValue.lastIndexOf(' ', currentSelectionIndex !== null && currentSelectionIndex !== void 0 ? currentSelectionIndex : 0);
|
480
616
|
if (spaceIndex === -1) {
|
481
|
-
// no space before the selection
|
617
|
+
// no space before the selection, use the beginning of the tag
|
482
618
|
spaceIndex = tag.plainTextBeginIndex;
|
483
619
|
}
|
484
620
|
}
|
485
621
|
else {
|
486
|
-
// the cursor is moved to the right
|
487
|
-
spaceIndex = textValue.indexOf(' ',
|
622
|
+
// the cursor is moved to the right, find the fist index after the cursor
|
623
|
+
spaceIndex = textValue.indexOf(' ', currentSelectionIndex !== null && currentSelectionIndex !== void 0 ? currentSelectionIndex : 0);
|
488
624
|
if (spaceIndex === -1) {
|
489
|
-
// no space after the selection
|
625
|
+
// no space after the selection, use the end of the tag
|
490
626
|
spaceIndex = (_a = tag.plainTextEndIndex) !== null && _a !== void 0 ? _a : tag.plainTextBeginIndex;
|
491
627
|
}
|
492
628
|
}
|
493
|
-
|
494
|
-
|
495
|
-
}
|
496
|
-
else if (spaceIndex > tag.plainTextEndIndex) {
|
497
|
-
spaceIndex = tag.plainTextEndIndex;
|
498
|
-
}
|
629
|
+
spaceIndex = Math.max(tag.plainTextBeginIndex, spaceIndex);
|
630
|
+
spaceIndex = Math.min(tag.plainTextEndIndex, spaceIndex);
|
499
631
|
return spaceIndex;
|
500
632
|
};
|
501
633
|
/* @conditional-compile-remove(mention) */
|
@@ -503,10 +635,21 @@ const findNewSelectionIndexForMention = (tag, textValue, selection, previousSele
|
|
503
635
|
* Handle mention tag edit and by word deleting
|
504
636
|
*
|
505
637
|
* @private
|
638
|
+
* @param props - Props for mention update HTML function.
|
639
|
+
* @returns Updated texts and indexes.
|
506
640
|
*/
|
507
|
-
const handleMentionTagUpdate = (
|
508
|
-
|
509
|
-
|
641
|
+
const handleMentionTagUpdate = (props) => {
|
642
|
+
const { htmlText, oldPlainText, change, tag, closeTagIdx, closeTagLength, plainTextEndIndex, startIndex, oldPlainTextEndIndex, mentionTagLength } = props;
|
643
|
+
let processedChange = props.processedChange;
|
644
|
+
let lastProcessedHTMLIndex = props.lastProcessedHTMLIndex;
|
645
|
+
if (tag.tagType !== MSFT_MENTION_TAG || tag.plainTextBeginIndex === undefined) {
|
646
|
+
// not a mention tag
|
647
|
+
return {
|
648
|
+
result: '',
|
649
|
+
updatedChange: processedChange,
|
650
|
+
htmlIndex: lastProcessedHTMLIndex,
|
651
|
+
plainTextSelectionEndIndex: null
|
652
|
+
};
|
510
653
|
}
|
511
654
|
let result = '';
|
512
655
|
let plainTextSelectionEndIndex = null;
|
@@ -529,9 +672,11 @@ const handleMentionTagUpdate = (htmlText, oldPlainText, lastProcessedHTMLIndex,
|
|
529
672
|
}
|
530
673
|
isSpaceLengthHandled = true;
|
531
674
|
if (rangeStart === -1 || rangeStart === undefined || rangeStart < tag.plainTextBeginIndex) {
|
675
|
+
// rangeStart should be at least equal to tag.plainTextBeginIndex
|
532
676
|
rangeStart = tag.plainTextBeginIndex;
|
533
677
|
}
|
534
678
|
if (rangeEnd > plainTextEndIndex) {
|
679
|
+
// rangeEnd should be at most equal to plainTextEndIndex
|
535
680
|
rangeEnd = plainTextEndIndex;
|
536
681
|
}
|
537
682
|
if (rangeStart === tag.plainTextBeginIndex && rangeEnd === plainTextEndIndex) {
|
@@ -551,28 +696,61 @@ const handleMentionTagUpdate = (htmlText, oldPlainText, lastProcessedHTMLIndex,
|
|
551
696
|
}
|
552
697
|
endChangeDiff = rangeEnd - tag.plainTextBeginIndex - mentionTagLength;
|
553
698
|
result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length + startChangeDiff);
|
554
|
-
|
699
|
+
if (startIndex < tag.plainTextBeginIndex) {
|
700
|
+
// if the change is before the tag, the selection should start from startIndex (rangeStart will be equal to tag.plainTextBeginIndex)
|
701
|
+
plainTextSelectionEndIndex = startIndex + change.length;
|
702
|
+
}
|
703
|
+
else {
|
704
|
+
// if the change is inside the tag, the selection should start with rangeStart
|
705
|
+
plainTextSelectionEndIndex = rangeStart + processedChange.length;
|
706
|
+
}
|
555
707
|
lastProcessedHTMLIndex = tag.openTagIdx + tag.openTagBody.length + endChangeDiff;
|
556
708
|
// processed change should not be changed as it should be added after the tag
|
557
709
|
}
|
558
|
-
return
|
710
|
+
return { result, updatedChange: processedChange, htmlIndex: lastProcessedHTMLIndex, plainTextSelectionEndIndex };
|
559
711
|
};
|
560
712
|
/* @conditional-compile-remove(mention) */
|
561
713
|
/**
|
562
|
-
*
|
714
|
+
* Get closing tag information if exists otherwise return information as for self closing tag
|
563
715
|
*
|
564
716
|
* @private
|
717
|
+
* @param tag - Tag data.
|
718
|
+
* @returns Closing tag information for the provided tag.
|
565
719
|
*/
|
566
|
-
const
|
567
|
-
let
|
568
|
-
|
569
|
-
|
570
|
-
|
720
|
+
const getTagClosingTagInfo = (tag) => {
|
721
|
+
let plainTextEndIndex = 0;
|
722
|
+
let closeTagIdx = 0;
|
723
|
+
let closeTagLength = 0;
|
724
|
+
if (tag.plainTextEndIndex !== undefined && tag.closeTagIdx !== undefined) {
|
725
|
+
// close tag exists
|
726
|
+
plainTextEndIndex = tag.plainTextEndIndex;
|
727
|
+
closeTagIdx = tag.closeTagIdx;
|
728
|
+
// tag.tagType.length + </>
|
729
|
+
closeTagLength = tag.tagType.length + 3;
|
730
|
+
}
|
731
|
+
else if (tag.plainTextBeginIndex !== undefined) {
|
732
|
+
// no close tag
|
733
|
+
plainTextEndIndex = tag.plainTextBeginIndex;
|
734
|
+
closeTagIdx = tag.openTagIdx + tag.openTagBody.length;
|
735
|
+
closeTagLength = 0;
|
571
736
|
}
|
572
|
-
|
573
|
-
|
574
|
-
|
737
|
+
return { plainTextEndIndex, closeTagIdx, closeTagLength };
|
738
|
+
};
|
739
|
+
/* @conditional-compile-remove(mention) */
|
740
|
+
/**
|
741
|
+
* Go through the text and update it with the changed text
|
742
|
+
*
|
743
|
+
* @private
|
744
|
+
* @param props - Props for update HTML function.
|
745
|
+
* @returns Updated HTML and selection index if the selection index should be set.
|
746
|
+
*/
|
747
|
+
const updateHTML = (props) => {
|
748
|
+
const { htmlText, oldPlainText, newPlainText, tags, startIndex, oldPlainTextEndIndex, change, mentionTrigger } = props;
|
749
|
+
if (tags.length === 0 || (startIndex === 0 && oldPlainTextEndIndex === oldPlainText.length - 1)) {
|
750
|
+
// no tags added yet or the whole text is changed
|
751
|
+
return { updatedHTML: newPlainText, updatedSelectionIndex: null };
|
575
752
|
}
|
753
|
+
let result = '';
|
576
754
|
let lastProcessedHTMLIndex = 0;
|
577
755
|
// the value can be updated with empty string when the change covers more than 1 place (tag + before or after the tag)
|
578
756
|
// in this case change won't be added as part of the tag
|
@@ -583,7 +761,7 @@ const updateHTML = (htmlText, oldPlainText, newPlainText, tags, startIndex, oldP
|
|
583
761
|
let processedChange = change;
|
584
762
|
// end tag plain text index of the last processed tag
|
585
763
|
let lastProcessedPlainTextTagEndIndex = 0;
|
586
|
-
// as some tags/text can be removed fully,
|
764
|
+
// as some tags/text can be removed fully, selection should be updated correctly
|
587
765
|
let changeNewEndIndex = null;
|
588
766
|
for (let i = 0; i < tags.length; i++) {
|
589
767
|
const tag = tags[i];
|
@@ -592,165 +770,225 @@ const updateHTML = (htmlText, oldPlainText, newPlainText, tags, startIndex, oldP
|
|
592
770
|
}
|
593
771
|
// all plain text indexes includes trigger length for the mention that shouldn't be included in
|
594
772
|
// htmlText.substring because html strings don't include the trigger
|
595
|
-
// mentionTagLength will be set only for
|
773
|
+
// mentionTagLength will be set only for mention tag, otherwise should be 0
|
596
774
|
let mentionTagLength = 0;
|
597
775
|
let isMentionTag = false;
|
598
|
-
if (tag.tagType ===
|
776
|
+
if (tag.tagType === MSFT_MENTION_TAG) {
|
599
777
|
mentionTagLength = mentionTrigger.length;
|
600
778
|
isMentionTag = true;
|
601
779
|
}
|
602
|
-
//change start is before the open tag
|
603
780
|
if (startIndex <= tag.plainTextBeginIndex) {
|
781
|
+
// change start is before the open tag
|
604
782
|
// Math.max(lastProcessedPlainTextTagEndIndex, startIndex) is used as startIndex may not be in [[previous tag].plainTextEndIndex - tag.plainTextBeginIndex] range
|
605
783
|
const startChangeDiff = tag.plainTextBeginIndex - Math.max(lastProcessedPlainTextTagEndIndex, startIndex);
|
606
784
|
result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx - startChangeDiff) + processedChange;
|
785
|
+
processedChange = '';
|
607
786
|
if (oldPlainTextEndIndex <= tag.plainTextBeginIndex) {
|
608
787
|
// the whole change is before tag start
|
609
|
-
//
|
788
|
+
// mentionTag length can be ignored here as the change is before the tag
|
610
789
|
const endChangeDiff = tag.plainTextBeginIndex - oldPlainTextEndIndex;
|
611
790
|
lastProcessedHTMLIndex = tag.openTagIdx - endChangeDiff;
|
612
|
-
processedChange = '';
|
613
791
|
// the change is handled; exit
|
614
792
|
break;
|
615
793
|
}
|
616
794
|
else {
|
617
795
|
// change continues in the tag
|
618
796
|
lastProcessedHTMLIndex = tag.openTagIdx;
|
619
|
-
processedChange = '';
|
620
797
|
// proceed to the next check
|
621
798
|
}
|
622
799
|
}
|
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) {
|
800
|
+
const closingTagInfo = getTagClosingTagInfo(tag);
|
801
|
+
if (startIndex <= closingTagInfo.plainTextEndIndex) {
|
640
802
|
// change started before the end tag
|
641
|
-
if (startIndex <= tag.plainTextBeginIndex && oldPlainTextEndIndex === plainTextEndIndex) {
|
803
|
+
if (startIndex <= tag.plainTextBeginIndex && oldPlainTextEndIndex === closingTagInfo.plainTextEndIndex) {
|
642
804
|
// the change is a tag or starts before the tag
|
643
805
|
// tag should be removed, no matter if there are subtags
|
644
806
|
result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx) + processedChange;
|
645
807
|
processedChange = '';
|
646
|
-
lastProcessedHTMLIndex = closeTagIdx + closeTagLength;
|
808
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
|
647
809
|
// the change is handled; exit
|
648
810
|
break;
|
649
811
|
}
|
650
|
-
else if (startIndex >= tag.plainTextBeginIndex && oldPlainTextEndIndex
|
651
|
-
//
|
812
|
+
else if (startIndex >= tag.plainTextBeginIndex && oldPlainTextEndIndex <= closingTagInfo.plainTextEndIndex) {
|
813
|
+
// the change is between the tag
|
652
814
|
if (isMentionTag) {
|
653
815
|
if (change !== '') {
|
654
|
-
|
655
|
-
|
656
|
-
|
816
|
+
if (startIndex !== tag.plainTextBeginIndex && startIndex !== closingTagInfo.plainTextEndIndex) {
|
817
|
+
// mention tag should be deleted when user tries to edit it in the middle
|
818
|
+
result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx) + processedChange;
|
819
|
+
changeNewEndIndex = tag.plainTextBeginIndex + processedChange.length;
|
820
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
|
821
|
+
}
|
822
|
+
else if (startIndex === tag.plainTextBeginIndex) {
|
823
|
+
// non empty change at the beginning of the mention tag to be added before the mention tag
|
824
|
+
result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx) + processedChange;
|
825
|
+
changeNewEndIndex = tag.plainTextBeginIndex + processedChange.length;
|
826
|
+
lastProcessedHTMLIndex = tag.openTagIdx;
|
827
|
+
}
|
828
|
+
else if (startIndex === closingTagInfo.plainTextEndIndex) {
|
829
|
+
// non empty change at the end of the mention tag to be added after the mention tag
|
830
|
+
result +=
|
831
|
+
htmlText.substring(lastProcessedHTMLIndex, closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength) +
|
832
|
+
processedChange;
|
833
|
+
changeNewEndIndex = closingTagInfo.plainTextEndIndex + processedChange.length;
|
834
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
|
835
|
+
}
|
657
836
|
processedChange = '';
|
658
|
-
lastProcessedHTMLIndex = closeTagIdx + closeTagLength;
|
659
837
|
}
|
660
838
|
else {
|
661
|
-
const
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
839
|
+
const updateMentionTagResult = handleMentionTagUpdate({
|
840
|
+
htmlText,
|
841
|
+
oldPlainText,
|
842
|
+
lastProcessedHTMLIndex,
|
843
|
+
processedChange,
|
844
|
+
change,
|
845
|
+
tag,
|
846
|
+
closeTagIdx: closingTagInfo.closeTagIdx,
|
847
|
+
closeTagLength: closingTagInfo.closeTagLength,
|
848
|
+
plainTextEndIndex: closingTagInfo.plainTextEndIndex,
|
849
|
+
startIndex,
|
850
|
+
oldPlainTextEndIndex,
|
851
|
+
mentionTagLength
|
852
|
+
});
|
853
|
+
result += updateMentionTagResult.result;
|
854
|
+
changeNewEndIndex = updateMentionTagResult.plainTextSelectionEndIndex;
|
855
|
+
processedChange = updateMentionTagResult.updatedChange;
|
856
|
+
lastProcessedHTMLIndex = updateMentionTagResult.htmlIndex;
|
666
857
|
}
|
667
|
-
// the change is handled; exit
|
668
|
-
break;
|
669
858
|
}
|
670
|
-
else if (tag.subTags !== undefined && tag.subTags.length !== 0 && tag.content) {
|
859
|
+
else if (tag.subTags !== undefined && tag.subTags.length !== 0 && tag.content !== undefined) {
|
671
860
|
// with subtags
|
672
|
-
// before the tag content
|
673
861
|
const stringBefore = htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length);
|
674
|
-
lastProcessedHTMLIndex = closeTagIdx;
|
675
|
-
const
|
676
|
-
|
677
|
-
|
678
|
-
|
862
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx;
|
863
|
+
const updatedContent = updateHTML({
|
864
|
+
htmlText: tag.content,
|
865
|
+
oldPlainText,
|
866
|
+
newPlainText,
|
867
|
+
tags: tag.subTags,
|
868
|
+
startIndex,
|
869
|
+
oldPlainTextEndIndex,
|
870
|
+
change: processedChange,
|
871
|
+
mentionTrigger
|
872
|
+
});
|
873
|
+
result += stringBefore + updatedContent.updatedHTML;
|
874
|
+
changeNewEndIndex = updatedContent.updatedSelectionIndex;
|
679
875
|
}
|
680
876
|
else {
|
681
877
|
// no subtags
|
682
|
-
const startChangeDiff = startIndex - tag.plainTextBeginIndex
|
683
|
-
const endChangeDiff = oldPlainTextEndIndex - tag.plainTextBeginIndex - mentionTagLength;
|
878
|
+
const startChangeDiff = startIndex - tag.plainTextBeginIndex;
|
684
879
|
result +=
|
685
880
|
htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length + startChangeDiff) +
|
686
881
|
processedChange;
|
687
882
|
processedChange = '';
|
688
|
-
|
689
|
-
|
690
|
-
|
883
|
+
if (oldPlainTextEndIndex < closingTagInfo.plainTextEndIndex) {
|
884
|
+
const endChangeDiff = oldPlainTextEndIndex - tag.plainTextBeginIndex;
|
885
|
+
lastProcessedHTMLIndex = tag.openTagIdx + tag.openTagBody.length + endChangeDiff;
|
886
|
+
}
|
887
|
+
else if (oldPlainTextEndIndex === closingTagInfo.plainTextEndIndex) {
|
888
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx;
|
889
|
+
}
|
691
890
|
}
|
891
|
+
// the change is handled; exit
|
892
|
+
break;
|
692
893
|
}
|
693
|
-
else if (startIndex > tag.plainTextBeginIndex && oldPlainTextEndIndex > plainTextEndIndex) {
|
694
|
-
//the change started in the tag but finishes somewhere further
|
894
|
+
else if (startIndex > tag.plainTextBeginIndex && oldPlainTextEndIndex > closingTagInfo.plainTextEndIndex) {
|
895
|
+
// the change started in the tag but finishes somewhere further
|
695
896
|
const startChangeDiff = startIndex - tag.plainTextBeginIndex - mentionTagLength;
|
696
897
|
if (isMentionTag) {
|
697
|
-
const
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
898
|
+
const updateMentionTagResult = handleMentionTagUpdate({
|
899
|
+
htmlText,
|
900
|
+
oldPlainText,
|
901
|
+
lastProcessedHTMLIndex,
|
902
|
+
processedChange: '',
|
903
|
+
change,
|
904
|
+
tag,
|
905
|
+
closeTagIdx: closingTagInfo.closeTagIdx,
|
906
|
+
closeTagLength: closingTagInfo.closeTagLength,
|
907
|
+
plainTextEndIndex: closingTagInfo.plainTextEndIndex,
|
908
|
+
startIndex,
|
909
|
+
oldPlainTextEndIndex,
|
910
|
+
mentionTagLength
|
911
|
+
});
|
912
|
+
result += updateMentionTagResult.result;
|
913
|
+
lastProcessedHTMLIndex = updateMentionTagResult.htmlIndex;
|
702
914
|
// no need to handle plainTextSelectionEndIndex as the change will be added later
|
703
|
-
// proceed with the next calculations
|
704
915
|
}
|
705
916
|
else if (tag.subTags !== undefined && tag.subTags.length !== 0 && tag.content !== undefined) {
|
706
917
|
// with subtags
|
707
|
-
// before the tag content
|
708
918
|
const stringBefore = htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length);
|
709
|
-
lastProcessedHTMLIndex = closeTagIdx;
|
710
|
-
const
|
711
|
-
|
712
|
-
|
713
|
-
|
919
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx;
|
920
|
+
const updatedContent = updateHTML({
|
921
|
+
htmlText: tag.content,
|
922
|
+
oldPlainText,
|
923
|
+
newPlainText,
|
924
|
+
tags: tag.subTags,
|
925
|
+
startIndex,
|
926
|
+
oldPlainTextEndIndex,
|
927
|
+
change: '',
|
928
|
+
mentionTrigger
|
929
|
+
});
|
930
|
+
result += stringBefore + updatedContent.updatedHTML;
|
714
931
|
}
|
715
932
|
else {
|
716
933
|
// no subtags
|
717
934
|
result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length + startChangeDiff);
|
718
|
-
lastProcessedHTMLIndex = closeTagIdx;
|
719
|
-
// proceed with the next calculations
|
935
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx;
|
720
936
|
}
|
937
|
+
// proceed with the next calculations
|
721
938
|
}
|
722
|
-
else if (startIndex < tag.plainTextBeginIndex && oldPlainTextEndIndex > plainTextEndIndex) {
|
939
|
+
else if (startIndex < tag.plainTextBeginIndex && oldPlainTextEndIndex > closingTagInfo.plainTextEndIndex) {
|
723
940
|
// the change starts before the tag and finishes after it
|
724
941
|
// tag should be removed, no matter if there are subtags
|
725
942
|
// no need to save anything between lastProcessedHTMLIndex and closeTagIdx + closeTagLength
|
726
|
-
lastProcessedHTMLIndex = closeTagIdx + closeTagLength;
|
943
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
|
727
944
|
// proceed with the next calculations
|
728
945
|
}
|
729
|
-
else if (startIndex === tag.plainTextBeginIndex && oldPlainTextEndIndex > plainTextEndIndex) {
|
946
|
+
else if (startIndex === tag.plainTextBeginIndex && oldPlainTextEndIndex > closingTagInfo.plainTextEndIndex) {
|
730
947
|
// the change starts in the tag and finishes after it
|
731
948
|
// tag should be removed, no matter if there are subtags
|
732
949
|
result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx);
|
733
950
|
// processedChange shouldn't be updated as it will be added after the tag
|
734
|
-
lastProcessedHTMLIndex = closeTagIdx + closeTagLength;
|
951
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
|
735
952
|
// proceed with the next calculations
|
736
953
|
}
|
737
|
-
else if (startIndex < tag.plainTextBeginIndex && oldPlainTextEndIndex < plainTextEndIndex) {
|
954
|
+
else if (startIndex < tag.plainTextBeginIndex && oldPlainTextEndIndex < closingTagInfo.plainTextEndIndex) {
|
738
955
|
// the change starts before the tag and ends in a tag
|
739
956
|
if (isMentionTag) {
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
957
|
+
// mention tag
|
958
|
+
const updateMentionTagResult = handleMentionTagUpdate({
|
959
|
+
htmlText,
|
960
|
+
oldPlainText,
|
961
|
+
lastProcessedHTMLIndex,
|
962
|
+
processedChange: '',
|
963
|
+
change,
|
964
|
+
tag,
|
965
|
+
closeTagIdx: closingTagInfo.closeTagIdx,
|
966
|
+
closeTagLength: closingTagInfo.closeTagLength,
|
967
|
+
plainTextEndIndex: closingTagInfo.plainTextEndIndex,
|
968
|
+
startIndex,
|
969
|
+
oldPlainTextEndIndex,
|
970
|
+
mentionTagLength
|
971
|
+
});
|
972
|
+
changeNewEndIndex = updateMentionTagResult.plainTextSelectionEndIndex;
|
973
|
+
result += updateMentionTagResult.result;
|
974
|
+
lastProcessedHTMLIndex = updateMentionTagResult.htmlIndex;
|
745
975
|
}
|
746
976
|
else if (tag.subTags !== undefined && tag.subTags.length !== 0 && tag.content !== undefined) {
|
747
977
|
// with subtags
|
748
|
-
// before the tag content
|
749
978
|
const stringBefore = htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length);
|
750
|
-
lastProcessedHTMLIndex = closeTagIdx;
|
751
|
-
const
|
752
|
-
|
753
|
-
|
979
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx;
|
980
|
+
const updatedContent = updateHTML({
|
981
|
+
htmlText: tag.content,
|
982
|
+
oldPlainText,
|
983
|
+
newPlainText,
|
984
|
+
tags: tag.subTags,
|
985
|
+
startIndex,
|
986
|
+
oldPlainTextEndIndex,
|
987
|
+
change: processedChange,
|
988
|
+
mentionTrigger
|
989
|
+
});
|
990
|
+
processedChange = '';
|
991
|
+
result += stringBefore + updatedContent.updatedHTML;
|
754
992
|
}
|
755
993
|
else {
|
756
994
|
// no subtags
|
@@ -758,123 +996,67 @@ const updateHTML = (htmlText, oldPlainText, newPlainText, tags, startIndex, oldP
|
|
758
996
|
htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length) + processedChange;
|
759
997
|
processedChange = '';
|
760
998
|
// oldPlainTextEndIndex already includes mentionTag length
|
761
|
-
const endChangeDiff = plainTextEndIndex - oldPlainTextEndIndex;
|
999
|
+
const endChangeDiff = closingTagInfo.plainTextEndIndex - oldPlainTextEndIndex;
|
762
1000
|
// as change may be before the end of the tag, we need to add the rest of the tag
|
763
|
-
lastProcessedHTMLIndex = closeTagIdx - endChangeDiff;
|
1001
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx - endChangeDiff;
|
764
1002
|
}
|
765
1003
|
// the change is handled; exit
|
766
1004
|
break;
|
767
1005
|
}
|
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;
|
1006
|
+
lastProcessedPlainTextTagEndIndex = closingTagInfo.plainTextEndIndex;
|
818
1007
|
}
|
819
|
-
if (i === tags.length - 1 && oldPlainTextEndIndex >= plainTextEndIndex) {
|
1008
|
+
if (i === tags.length - 1 && oldPlainTextEndIndex >= closingTagInfo.plainTextEndIndex) {
|
820
1009
|
// the last tag should handle the end of the change if needed
|
821
1010
|
// oldPlainTextEndIndex already includes mentionTag length
|
822
|
-
const endChangeDiff = oldPlainTextEndIndex - plainTextEndIndex;
|
823
|
-
if (startIndex >= plainTextEndIndex) {
|
824
|
-
const startChangeDiff = startIndex - plainTextEndIndex;
|
1011
|
+
const endChangeDiff = oldPlainTextEndIndex - closingTagInfo.plainTextEndIndex;
|
1012
|
+
if (startIndex >= closingTagInfo.plainTextEndIndex) {
|
1013
|
+
const startChangeDiff = startIndex - closingTagInfo.plainTextEndIndex;
|
825
1014
|
result +=
|
826
|
-
htmlText.substring(lastProcessedHTMLIndex, closeTagIdx + closeTagLength + startChangeDiff) + processedChange;
|
1015
|
+
htmlText.substring(lastProcessedHTMLIndex, closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength + startChangeDiff) + processedChange;
|
827
1016
|
}
|
828
1017
|
else {
|
829
|
-
result +=
|
1018
|
+
result +=
|
1019
|
+
htmlText.substring(lastProcessedHTMLIndex, closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength) +
|
1020
|
+
processedChange;
|
830
1021
|
}
|
831
1022
|
processedChange = '';
|
832
|
-
lastProcessedHTMLIndex = closeTagIdx + closeTagLength + endChangeDiff;
|
1023
|
+
lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength + endChangeDiff;
|
833
1024
|
// the change is handled; exit
|
834
1025
|
// break is not required here as this is the last element but added for consistency
|
835
1026
|
break;
|
836
1027
|
}
|
837
1028
|
}
|
838
1029
|
if (lastProcessedHTMLIndex < htmlText.length) {
|
1030
|
+
// add the rest of the html string
|
839
1031
|
result += htmlText.substring(lastProcessedHTMLIndex);
|
840
1032
|
}
|
841
|
-
return
|
1033
|
+
return { updatedHTML: result, updatedSelectionIndex: changeNewEndIndex };
|
842
1034
|
};
|
843
1035
|
/* @conditional-compile-remove(mention) */
|
844
1036
|
/**
|
845
1037
|
* Given the oldText and newText, find the start index, old end index and new end index for the changes
|
846
1038
|
*
|
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
1039
|
* @private
|
1040
|
+
* @param props - Props for finding stings diff indexes function.
|
1041
|
+
* @returns Indexes for change start and ends in new and old texts. The old and new end indexes are exclusive.
|
852
1042
|
*/
|
853
|
-
const findStringsDiffIndexes = (
|
854
|
-
|
1043
|
+
const findStringsDiffIndexes = (props) => {
|
1044
|
+
const { oldText, newText, previousSelectionStart, previousSelectionEnd, currentSelectionStart, currentSelectionEnd } = props;
|
855
1045
|
const newTextLength = newText.length;
|
856
1046
|
const oldTextLength = oldText.length;
|
857
|
-
let changeStart = 0;
|
1047
|
+
// let changeStart = 0;
|
858
1048
|
let newChangeEnd = newTextLength;
|
859
1049
|
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
|
-
}
|
1050
|
+
const previousSelectionStartValue = previousSelectionStart > -1 ? previousSelectionStart : oldTextLength;
|
1051
|
+
const previousSelectionEndValue = previousSelectionEnd > -1 ? previousSelectionEnd : oldTextLength;
|
1052
|
+
const currentSelectionStartValue = currentSelectionStart > -1 ? currentSelectionStart : newTextLength;
|
1053
|
+
const currentSelectionEndValue = currentSelectionEnd > -1 ? currentSelectionEnd : newTextLength;
|
1054
|
+
const changeStart = Math.min(previousSelectionStartValue, previousSelectionEndValue, currentSelectionStartValue, currentSelectionEndValue, newTextLength, oldTextLength);
|
873
1055
|
if (oldTextLength < newTextLength) {
|
874
1056
|
//insert or replacement
|
875
1057
|
if (oldTextLength === changeStart) {
|
876
1058
|
// when change was at the end of string
|
877
|
-
//
|
1059
|
+
// change is found
|
878
1060
|
newChangeEnd = newTextLength;
|
879
1061
|
oldChangeEnd = oldTextLength;
|
880
1062
|
}
|
@@ -883,7 +1065,7 @@ const findStringsDiffIndexes = (oldText, newText, selectionEnd // should be a va
|
|
883
1065
|
newChangeEnd = newTextLength - i - 1;
|
884
1066
|
oldChangeEnd = oldTextLength - i - 1;
|
885
1067
|
if (newText[newChangeEnd] !== oldText[oldChangeEnd]) {
|
886
|
-
//
|
1068
|
+
// change is found
|
887
1069
|
break;
|
888
1070
|
}
|
889
1071
|
}
|
@@ -896,7 +1078,7 @@ const findStringsDiffIndexes = (oldText, newText, selectionEnd // should be a va
|
|
896
1078
|
//deletion or replacement
|
897
1079
|
if (newTextLength === changeStart) {
|
898
1080
|
// when change was at the end of string
|
899
|
-
//
|
1081
|
+
// change is found
|
900
1082
|
newChangeEnd = newTextLength;
|
901
1083
|
oldChangeEnd = oldTextLength;
|
902
1084
|
}
|
@@ -905,7 +1087,7 @@ const findStringsDiffIndexes = (oldText, newText, selectionEnd // should be a va
|
|
905
1087
|
newChangeEnd = newTextLength - i - 1;
|
906
1088
|
oldChangeEnd = oldTextLength - i - 1;
|
907
1089
|
if (newText[newChangeEnd] !== oldText[oldChangeEnd]) {
|
908
|
-
//
|
1090
|
+
// change is found
|
909
1091
|
break;
|
910
1092
|
}
|
911
1093
|
}
|
@@ -915,12 +1097,12 @@ const findStringsDiffIndexes = (oldText, newText, selectionEnd // should be a va
|
|
915
1097
|
}
|
916
1098
|
}
|
917
1099
|
else {
|
918
|
-
//replacement
|
1100
|
+
// replacement
|
919
1101
|
for (let i = 1; i < oldTextLength && oldTextLength - i >= changeStart; i++) {
|
920
1102
|
newChangeEnd = newTextLength - i - 1;
|
921
1103
|
oldChangeEnd = oldTextLength - i - 1;
|
922
1104
|
if (newText[newChangeEnd] !== oldText[oldChangeEnd]) {
|
923
|
-
//
|
1105
|
+
// change is found
|
924
1106
|
break;
|
925
1107
|
}
|
926
1108
|
}
|
@@ -935,13 +1117,30 @@ const findStringsDiffIndexes = (oldText, newText, selectionEnd // should be a va
|
|
935
1117
|
return { changeStart, oldChangeEnd, newChangeEnd };
|
936
1118
|
};
|
937
1119
|
/* @conditional-compile-remove(mention) */
|
1120
|
+
/**
|
1121
|
+
* Get the html string for the mention suggestion.
|
1122
|
+
*
|
1123
|
+
* @private
|
1124
|
+
* @param suggestion - The mention suggestion.
|
1125
|
+
* @param localeStrings - The locale strings.
|
1126
|
+
* @returns The html string for the mention suggestion.
|
1127
|
+
*/
|
938
1128
|
const htmlStringForMentionSuggestion = (suggestion, localeStrings) => {
|
939
1129
|
const idHTML = ' id ="' + suggestion.id + '"';
|
940
1130
|
const displayTextHTML = ' displayText ="' + suggestion.displayText + '"';
|
941
1131
|
const displayText = getDisplayNameForMentionSuggestion(suggestion, localeStrings);
|
942
|
-
return '<
|
1132
|
+
return '<' + MSFT_MENTION_TAG + idHTML + displayTextHTML + '>' + displayText + '</' + MSFT_MENTION_TAG + '>';
|
943
1133
|
};
|
944
1134
|
/* @conditional-compile-remove(mention) */
|
1135
|
+
/**
|
1136
|
+
* Get display name for the mention suggestion.
|
1137
|
+
*
|
1138
|
+
* @private
|
1139
|
+
*
|
1140
|
+
* @param suggestion - The mention suggestion.
|
1141
|
+
* @param localeStrings - The locale strings.
|
1142
|
+
* @returns The display name for the mention suggestion or display name placeholder if display name is empty.
|
1143
|
+
*/
|
945
1144
|
const getDisplayNameForMentionSuggestion = (suggestion, localeStrings) => {
|
946
1145
|
const displayNamePlaceholder = localeStrings.participantItem.displayNamePlaceholder;
|
947
1146
|
return suggestion.displayText !== '' ? suggestion.displayText : displayNamePlaceholder !== null && displayNamePlaceholder !== void 0 ? displayNamePlaceholder : '';
|
@@ -949,8 +1148,9 @@ const getDisplayNameForMentionSuggestion = (suggestion, localeStrings) => {
|
|
949
1148
|
/* @conditional-compile-remove(mention) */
|
950
1149
|
/**
|
951
1150
|
* Parse the text and return the tags and the plain text in one go
|
1151
|
+
* @private
|
952
1152
|
* @param text - The text to parse for HTML tags
|
953
|
-
* @param trigger The trigger to show for the
|
1153
|
+
* @param trigger The trigger to show for the mention tag in plain text
|
954
1154
|
*
|
955
1155
|
* @returns An array of tags and the plain text representation
|
956
1156
|
*/
|
@@ -994,7 +1194,7 @@ const textToTagParser = (text, trigger) => {
|
|
994
1194
|
// Tag startIdx is absolute to the text. This is updated later to be relative to the parent tag
|
995
1195
|
currentOpenTag.content = text.substring(currentOpenTag.openTagIdx + currentOpenTag.openTagBody.length, foundHtmlTag.startIdx);
|
996
1196
|
// Insert the plain text pieces for the sub tags
|
997
|
-
if (currentOpenTag.tagType ===
|
1197
|
+
if (currentOpenTag.tagType === MSFT_MENTION_TAG) {
|
998
1198
|
plainTextRepresentation =
|
999
1199
|
plainTextRepresentation.slice(0, currentOpenTag.plainTextBeginIndex) +
|
1000
1200
|
trigger +
|
@@ -1024,7 +1224,7 @@ const textToTagParser = (text, trigger) => {
|
|
1024
1224
|
// Update parsing index; move past the end of the close tag
|
1025
1225
|
parseIndex = foundHtmlTag.startIdx + foundHtmlTag.content.length;
|
1026
1226
|
} // While parseIndex < text.length loop
|
1027
|
-
return
|
1227
|
+
return { tags, plainText: plainTextRepresentation };
|
1028
1228
|
};
|
1029
1229
|
/* @conditional-compile-remove(mention) */
|
1030
1230
|
const parseOpenTag = (tag, startIdx) => {
|