@azure/communication-react 1.5.1-alpha-202306030013 → 1.5.1-alpha-202306070014

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.
Files changed (58) hide show
  1. package/dist/communication-react.d.ts +2 -0
  2. package/dist/dist-cjs/communication-react/index.js +1534 -1519
  3. package/dist/dist-cjs/communication-react/index.js.map +1 -1
  4. package/dist/dist-esm/acs-ui-common/src/telemetryVersion.js +1 -1
  5. package/dist/dist-esm/acs-ui-common/src/telemetryVersion.js.map +1 -1
  6. package/dist/dist-esm/react-components/src/components/ChatMessage/ChatMessageComponentAsMessageBubble.js +5 -10
  7. package/dist/dist-esm/react-components/src/components/ChatMessage/ChatMessageComponentAsMessageBubble.js.map +1 -1
  8. package/dist/dist-esm/react-components/src/components/FileCard.js +5 -5
  9. package/dist/dist-esm/react-components/src/components/FileCard.js.map +1 -1
  10. package/dist/dist-esm/react-components/src/components/FileCardGroup.d.ts +1 -0
  11. package/dist/dist-esm/react-components/src/components/FileCardGroup.js +2 -2
  12. package/dist/dist-esm/react-components/src/components/FileCardGroup.js.map +1 -1
  13. package/dist/dist-esm/react-components/src/components/FileDownloadCards.d.ts +1 -0
  14. package/dist/dist-esm/react-components/src/components/FileDownloadCards.js +17 -7
  15. package/dist/dist-esm/react-components/src/components/FileDownloadCards.js.map +1 -1
  16. package/dist/dist-esm/react-components/src/components/FileUploadCards.js +1 -3
  17. package/dist/dist-esm/react-components/src/components/FileUploadCards.js.map +1 -1
  18. package/dist/dist-esm/react-components/src/components/InputBoxComponent.js +40 -1357
  19. package/dist/dist-esm/react-components/src/components/InputBoxComponent.js.map +1 -1
  20. package/dist/dist-esm/react-components/src/components/MessageThread.d.ts +2 -0
  21. package/dist/dist-esm/react-components/src/components/MessageThread.js.map +1 -1
  22. package/dist/dist-esm/react-components/src/components/TextFieldWithMention/TextFieldWithMention.d.ts +41 -0
  23. package/dist/dist-esm/react-components/src/components/TextFieldWithMention/TextFieldWithMention.js +554 -0
  24. package/dist/dist-esm/react-components/src/components/TextFieldWithMention/TextFieldWithMention.js.map +1 -0
  25. package/dist/dist-esm/react-components/src/components/TextFieldWithMention/mentionTagUtils.d.ts +167 -0
  26. package/dist/dist-esm/react-components/src/components/TextFieldWithMention/mentionTagUtils.js +780 -0
  27. package/dist/dist-esm/react-components/src/components/TextFieldWithMention/mentionTagUtils.js.map +1 -0
  28. package/dist/dist-esm/react-components/src/components/utils.d.ts +14 -0
  29. package/dist/dist-esm/react-components/src/components/utils.js +22 -0
  30. package/dist/dist-esm/react-components/src/components/utils.js.map +1 -1
  31. package/dist/dist-esm/react-components/src/localization/locales/de-DE/strings.json +2 -1
  32. package/dist/dist-esm/react-components/src/localization/locales/en-GB/strings.json +2 -1
  33. package/dist/dist-esm/react-components/src/localization/locales/en-US/strings.json +2 -1
  34. package/dist/dist-esm/react-components/src/localization/locales/es-ES/strings.json +2 -1
  35. package/dist/dist-esm/react-components/src/localization/locales/fr-FR/strings.json +2 -1
  36. package/dist/dist-esm/react-components/src/localization/locales/it-IT/strings.json +2 -1
  37. package/dist/dist-esm/react-components/src/localization/locales/ja-JP/strings.json +2 -1
  38. package/dist/dist-esm/react-components/src/localization/locales/ko-KR/strings.json +2 -1
  39. package/dist/dist-esm/react-components/src/localization/locales/nl-NL/strings.json +2 -1
  40. package/dist/dist-esm/react-components/src/localization/locales/pt-BR/strings.json +2 -1
  41. package/dist/dist-esm/react-components/src/localization/locales/ru-RU/strings.json +2 -1
  42. package/dist/dist-esm/react-components/src/localization/locales/tr-TR/strings.json +2 -1
  43. package/dist/dist-esm/react-components/src/localization/locales/zh-CN/strings.json +2 -1
  44. package/dist/dist-esm/react-components/src/localization/locales/zh-TW/strings.json +2 -1
  45. package/dist/dist-esm/react-composites/src/composites/localization/locales/de-DE/strings.json +2 -1
  46. package/dist/dist-esm/react-composites/src/composites/localization/locales/en-GB/strings.json +2 -1
  47. package/dist/dist-esm/react-composites/src/composites/localization/locales/es-ES/strings.json +2 -1
  48. package/dist/dist-esm/react-composites/src/composites/localization/locales/fr-FR/strings.json +2 -1
  49. package/dist/dist-esm/react-composites/src/composites/localization/locales/it-IT/strings.json +2 -1
  50. package/dist/dist-esm/react-composites/src/composites/localization/locales/ja-JP/strings.json +2 -1
  51. package/dist/dist-esm/react-composites/src/composites/localization/locales/ko-KR/strings.json +2 -1
  52. package/dist/dist-esm/react-composites/src/composites/localization/locales/nl-NL/strings.json +2 -1
  53. package/dist/dist-esm/react-composites/src/composites/localization/locales/pt-BR/strings.json +2 -1
  54. package/dist/dist-esm/react-composites/src/composites/localization/locales/ru-RU/strings.json +2 -1
  55. package/dist/dist-esm/react-composites/src/composites/localization/locales/tr-TR/strings.json +2 -1
  56. package/dist/dist-esm/react-composites/src/composites/localization/locales/zh-CN/strings.json +2 -1
  57. package/dist/dist-esm/react-composites/src/composites/localization/locales/zh-TW/strings.json +2 -1
  58. package/package.json +8 -8
@@ -1,114 +1,20 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT license.
3
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
- return new (P || (P = Promise))(function (resolve, reject) {
6
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
- step((generator = generator.apply(thisArg, _arguments || [])).next());
10
- });
11
- };
12
- import React, { useState, useCallback, useRef } from 'react';
13
- /* @conditional-compile-remove(mention) */
14
- import { useEffect, useMemo } from 'react';
15
- /* @conditional-compile-remove(mention) */
16
- import { useLocale } from '../localization';
17
- /* @conditional-compile-remove(mention) */
18
- import { Announcer } from './Announcer';
3
+ import React, { useState, useCallback } from 'react';
19
4
  import { Stack, TextField, mergeStyles, concatStyleSets, IconButton, TooltipHost } from '@fluentui/react';
5
+ import { isEnterKeyEventFromCompositionSession } from './utils';
20
6
  import { inputBoxStyle, inputBoxWrapperStyle, inputButtonStyle, textFieldStyle, textContainerStyle, newLineButtonsContainerStyle, inputBoxNewLineSpaceAffordance, inputButtonTooltipStyle, iconWrapperStyle } from './styles/InputBoxComponent.style';
21
- /* @conditional-compile-remove(mention) */
22
- import { Caret } from 'textarea-caret-ts';
23
7
  import { isDarkThemed } from '../theming/themeUtils';
24
8
  import { useTheme } from '../theming';
25
9
  /* @conditional-compile-remove(mention) */
26
- import { _MentionPopover } from './MentionPopover';
27
- /* @conditional-compile-remove(mention) */
28
- import { useDebouncedCallback } from 'use-debounce';
29
- /* @conditional-compile-remove(mention) */
30
- const DEFAULT_MENTION_TRIGGER = '@';
31
- /* @conditional-compile-remove(mention) */
32
- const MSFT_MENTION_TAG = 'msft-mention';
10
+ import { TextFieldWithMention } from './TextFieldWithMention/TextFieldWithMention';
33
11
  /**
34
12
  * @private
35
13
  */
36
14
  export const InputBoxComponent = (props) => {
37
- const { styles, id, 'data-ui-id': dataUiId, textValue, onChange, textFieldRef, placeholderText, onKeyDown, onEnterKeyDown, supportNewline, inputClassName, errorMessage, disabled,
38
- /* @conditional-compile-remove(mention) */
39
- mentionLookupOptions, children } = props;
40
- const inputBoxRef = useRef(null);
41
- /* @conditional-compile-remove(mention) */
42
- // Current suggestion list, provided by the callback
43
- const [mentionSuggestions, setMentionSuggestions] = useState([]);
44
- /* @conditional-compile-remove(mention) */
45
- // Current suggestion list, provided by the callback
46
- const [activeSuggestionIndex, setActiveSuggestionIndex] = useState(undefined);
47
- /* @conditional-compile-remove(mention) */
48
- // Index of the current trigger character in the text field
49
- const [currentTriggerStartIndex, setCurrentTriggerStartIndex] = useState(-1);
50
- /* @conditional-compile-remove(mention) */
51
- const [inputTextValue, setInputTextValue] = useState('');
52
- /* @conditional-compile-remove(mention) */
53
- const [tagsValue, setTagsValue] = useState([]);
54
- /* @conditional-compile-remove(mention) */
55
- // Index of the previous selection start in the text field
56
- const [selectionStartValue, setSelectionStartValue] = useState(null);
57
- /* @conditional-compile-remove(mention) */
58
- // Index of the previous selection end in the text field
59
- const [selectionEndValue, setSelectionEndValue] = useState(null);
60
- /* @conditional-compile-remove(mention) */
61
- // Boolean value to check if onMouseDown event should be handled during select as selection range
62
- // for onMouseDown event is not updated yet and the selection range for mouse click/taps will be
63
- // updated in onSelect event if needed.
64
- const [shouldHandleOnMouseDownDuringSelect, setShouldHandleOnMouseDownDuringSelect] = useState(true);
65
- /* @conditional-compile-remove(mention) */
66
- // Point of start of touch/mouse selection
67
- const [interactionStartPoint, setInteractionStartPoint] = useState();
68
- /* @conditional-compile-remove(mention) */
69
- // Target selection from mouse movement
70
- const [targetSelection, setTargetSelection] = useState();
71
- /* @conditional-compile-remove(mention) */
72
- // Caret position in the text field
73
- const [caretPosition, setCaretPosition] = useState(undefined);
74
- /* @conditional-compile-remove(mention) */
75
- // Index of where the caret is in the text field
76
- const [caretIndex, setCaretIndex] = useState(undefined);
77
- /* @conditional-compile-remove(mention) */
78
- const localeStrings = useLocale().strings;
79
- /* @conditional-compile-remove(mention) */
80
- // Set mention suggestions
81
- const updateMentionSuggestions = useCallback((suggestions) => {
82
- setMentionSuggestions(suggestions);
83
- }, [setMentionSuggestions]);
84
- /* @conditional-compile-remove(mention) */
85
- // Parse the text and get the plain text version to display in the input box
86
- useEffect(() => {
87
- const trigger = (mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.trigger) || DEFAULT_MENTION_TRIGGER;
88
- const parsedHTMLData = textToTagParser(textValue, trigger);
89
- setInputTextValue(parsedHTMLData.plainText);
90
- setTagsValue(parsedHTMLData.tags);
91
- updateMentionSuggestions([]);
92
- }, [textValue, mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.trigger, updateMentionSuggestions]);
15
+ const { styles, id, 'data-ui-id': dataUiId, textValue, onChange, textFieldRef, placeholderText, onKeyDown, onEnterKeyDown, supportNewline, inputClassName, errorMessage, disabled, children } = props;
93
16
  const mergedRootStyle = mergeStyles(inputBoxWrapperStyle, styles === null || styles === void 0 ? void 0 : styles.root);
94
17
  const mergedInputFieldStyle = mergeStyles(inputBoxStyle, inputClassName, props.inlineChildren ? {} : inputBoxNewLineSpaceAffordance);
95
- /* @conditional-compile-remove(mention) */
96
- useEffect(() => {
97
- var _a;
98
- // effect for caret index update
99
- if (caretIndex === undefined || textFieldRef === undefined || (textFieldRef === null || textFieldRef === void 0 ? void 0 : textFieldRef.current) === undefined) {
100
- return;
101
- }
102
- // get validated caret index between 0 and inputTextValue.length otherwise caret will be set to incorrect index
103
- const updatedCaretIndex = getValidatedIndexInRange({
104
- min: 0,
105
- max: inputTextValue.length,
106
- currentValue: caretIndex
107
- });
108
- (_a = textFieldRef === null || textFieldRef === void 0 ? void 0 : textFieldRef.current) === null || _a === void 0 ? void 0 : _a.setSelectionRange(updatedCaretIndex, updatedCaretIndex);
109
- setSelectionStartValue(updatedCaretIndex);
110
- setSelectionEndValue(updatedCaretIndex);
111
- }, [caretIndex, inputTextValue.length, textFieldRef, setSelectionStartValue, setSelectionEndValue]);
112
18
  const mergedTextContainerStyle = mergeStyles(textContainerStyle, styles === null || styles === void 0 ? void 0 : styles.textFieldContainer);
113
19
  const mergedTextFieldStyle = concatStyleSets(textFieldStyle, {
114
20
  fieldGroup: styles === null || styles === void 0 ? void 0 : styles.textField,
@@ -121,494 +27,56 @@ export const InputBoxComponent = (props) => {
121
27
  }
122
28
  });
123
29
  const mergedChildrenStyle = mergeStyles(props.inlineChildren ? {} : newLineButtonsContainerStyle);
124
- /* @conditional-compile-remove(mention) */
125
- const onSuggestionSelected = useCallback((suggestion) => {
126
- var _a, _b, _c;
127
- let selectionEnd = ((_a = textFieldRef === null || textFieldRef === void 0 ? void 0 : textFieldRef.current) === null || _a === void 0 ? void 0 : _a.selectionEnd) || -1;
128
- if (selectionEnd < 0) {
129
- selectionEnd = 0;
130
- }
131
- else if (selectionEnd > inputTextValue.length) {
132
- selectionEnd = inputTextValue.length;
133
- }
134
- const oldPlainText = inputTextValue;
135
- const mention = htmlStringForMentionSuggestion(suggestion, localeStrings);
136
- // update plain text with the mention html text
137
- const newPlainText = inputTextValue.substring(0, currentTriggerStartIndex) + mention + inputTextValue.substring(selectionEnd);
138
- const triggerText = (_b = mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.trigger) !== null && _b !== void 0 ? _b : DEFAULT_MENTION_TRIGGER;
139
- // update html text with updated plain text
140
- const updatedContent = updateHTML({
141
- htmlText: textValue,
142
- oldPlainText,
143
- newPlainText,
144
- tags: tagsValue,
145
- startIndex: currentTriggerStartIndex,
146
- oldPlainTextEndIndex: selectionEnd,
147
- change: mention,
148
- mentionTrigger: triggerText
149
- });
150
- const displayName = getDisplayNameForMentionSuggestion(suggestion, localeStrings);
151
- const newCaretIndex = currentTriggerStartIndex + displayName.length + triggerText.length;
152
- // move the caret in the text field to the end of the mention plain text
153
- setCaretIndex(newCaretIndex);
154
- setSelectionEndValue(newCaretIndex);
155
- setSelectionStartValue(newCaretIndex);
156
- setCurrentTriggerStartIndex(-1);
157
- updateMentionSuggestions([]);
158
- // set focus back to text field
159
- (_c = textFieldRef === null || textFieldRef === void 0 ? void 0 : textFieldRef.current) === null || _c === void 0 ? void 0 : _c.focus();
160
- setActiveSuggestionIndex(undefined);
161
- onChange && onChange(undefined, updatedContent.updatedHTML);
162
- }, [
163
- textFieldRef,
164
- inputTextValue,
165
- currentTriggerStartIndex,
166
- mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.trigger,
167
- onChange,
168
- textValue,
169
- tagsValue,
170
- /* @conditional-compile-remove(mention) */
171
- updateMentionSuggestions,
172
- /* @conditional-compile-remove(mention) */
173
- localeStrings
174
- ]);
175
30
  const onTextFieldKeyDown = useCallback((ev) => {
176
- /* @conditional-compile-remove(mention) */
177
- // caretIndex should be set to undefined when the user is typing
178
- setCaretIndex(undefined);
179
- // shouldHandleOnMouseDownDuringSelect should be set to false after the last mouse down event.
180
- // it shouldn't be updated in onMouseUp
181
- // as onMouseUp can be triggered before or after onSelect event
182
- // because its order depends on mouse events not selection.
183
- /* @conditional-compile-remove(mention) */
184
- setShouldHandleOnMouseDownDuringSelect(false);
185
- // 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)
186
- if (ev.nativeEvent.isComposing || ev.nativeEvent.keyCode === 229 || ev.nativeEvent.which === 229) {
31
+ if (isEnterKeyEventFromCompositionSession(ev)) {
187
32
  return;
188
33
  }
189
- if (ev.key === 'ArrowUp') {
190
- /* @conditional-compile-remove(mention) */
191
- if (mentionSuggestions.length > 0) {
192
- ev.preventDefault();
193
- const newActiveIndex = activeSuggestionIndex === undefined
194
- ? mentionSuggestions.length - 1
195
- : Math.max(activeSuggestionIndex - 1, 0);
196
- setActiveSuggestionIndex(newActiveIndex);
197
- }
198
- }
199
- else if (ev.key === 'ArrowDown') {
200
- /* @conditional-compile-remove(mention) */
201
- if (mentionSuggestions.length > 0) {
202
- ev.preventDefault();
203
- const newActiveIndex = activeSuggestionIndex === undefined
204
- ? 0
205
- : Math.min(activeSuggestionIndex + 1, mentionSuggestions.length - 1);
206
- setActiveSuggestionIndex(newActiveIndex);
207
- }
208
- }
209
- else if (ev.key === 'Escape') {
210
- /* @conditional-compile-remove(mention) */
211
- if (mentionSuggestions.length > 0) {
212
- updateMentionSuggestions([]);
213
- }
214
- }
215
34
  if (ev.key === 'Enter' && (ev.shiftKey === false || !supportNewline)) {
216
35
  ev.preventDefault();
217
- // If we are looking up a mention, select the focused suggestion
218
- /* @conditional-compile-remove(mention) */
219
- if (mentionSuggestions.length > 0 && activeSuggestionIndex !== undefined) {
220
- const selectedMention = mentionSuggestions[activeSuggestionIndex];
221
- if (selectedMention) {
222
- onSuggestionSelected(selectedMention);
223
- return;
224
- }
225
- }
226
36
  onEnterKeyDown && onEnterKeyDown();
227
37
  }
228
38
  onKeyDown && onKeyDown(ev);
229
- }, [
230
- onEnterKeyDown,
231
- onKeyDown,
232
- supportNewline,
233
- /* @conditional-compile-remove(mention) */
234
- mentionSuggestions,
235
- /* @conditional-compile-remove(mention) */
236
- activeSuggestionIndex,
237
- /* @conditional-compile-remove(mention) */
238
- onSuggestionSelected,
239
- /* @conditional-compile-remove(mention) */
240
- updateMentionSuggestions
241
- ]);
39
+ }, [onEnterKeyDown, onKeyDown, supportNewline]);
242
40
  const onRenderChildren = () => {
243
41
  return (React.createElement(Stack, { horizontal: true, className: mergedChildrenStyle }, children));
244
42
  };
245
- /* @conditional-compile-remove(mention) */
246
- const debouncedQueryUpdate = useDebouncedCallback((query) => __awaiter(void 0, void 0, void 0, function* () {
247
- var _a;
248
- const suggestions = (_a = (yield (mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.onQueryUpdated(query)))) !== null && _a !== void 0 ? _a : [];
249
- if (suggestions.length === 0) {
250
- setActiveSuggestionIndex(undefined);
251
- }
252
- else if (activeSuggestionIndex === undefined) {
253
- // Set the active to the first, if it's not already set
254
- setActiveSuggestionIndex(0);
255
- }
256
- updateMentionSuggestions(suggestions);
257
- }), 500);
258
- /* @conditional-compile-remove(mention) */
259
- // Update selections index in mention to navigate by words
260
- const updateSelectionIndexesWithMentionIfNeeded = useCallback((event, inputTextValue, selectionStartValue, selectionEndValue, tagsValue) => {
261
- var _a, _b, _c;
262
- let updatedStartIndex = event.currentTarget.selectionStart;
263
- let updatedEndIndex = event.currentTarget.selectionEnd;
264
- if (event.currentTarget.selectionStart === event.currentTarget.selectionEnd &&
265
- event.currentTarget.selectionStart !== null &&
266
- event.currentTarget.selectionStart !== -1) {
267
- // just a caret movement/usual typing or deleting
268
- const mentionTag = findMentionTagForSelection(tagsValue, event.currentTarget.selectionStart);
269
- // don't include boundary cases to show correct selection, otherwise it will show selection at mention boundaries
270
- if (mentionTag !== undefined &&
271
- mentionTag.plainTextBeginIndex !== undefined &&
272
- event.currentTarget.selectionStart > mentionTag.plainTextBeginIndex &&
273
- event.currentTarget.selectionStart < ((_a = mentionTag.plainTextEndIndex) !== null && _a !== void 0 ? _a : mentionTag.plainTextBeginIndex)) {
274
- // get updated selection index
275
- const newSelectionIndex = findNewSelectionIndexForMention({
276
- tag: mentionTag,
277
- textValue: inputTextValue,
278
- currentSelectionIndex: event.currentTarget.selectionStart,
279
- previousSelectionIndex: selectionStartValue !== null && selectionStartValue !== void 0 ? selectionStartValue : inputTextValue.length
280
- });
281
- updatedStartIndex = newSelectionIndex;
282
- updatedEndIndex = newSelectionIndex;
283
- }
284
- }
285
- else if (event.currentTarget.selectionStart !== event.currentTarget.selectionEnd) {
286
- // Both e.currentTarget.selectionStart !== selectionStartValue and e.currentTarget.selectionEnd !== selectionEndValue can be true when a user selects a text by double click
287
- if (event.currentTarget.selectionStart !== null && event.currentTarget.selectionStart !== selectionStartValue) {
288
- // the selection start is changed
289
- const mentionTag = findMentionTagForSelection(tagsValue, event.currentTarget.selectionStart);
290
- // don't include boundary cases to show correct selection, otherwise it will show selection at mention boundaries
291
- if (mentionTag !== undefined &&
292
- mentionTag.plainTextBeginIndex !== undefined &&
293
- event.currentTarget.selectionStart > mentionTag.plainTextBeginIndex &&
294
- event.currentTarget.selectionStart < ((_b = mentionTag.plainTextEndIndex) !== null && _b !== void 0 ? _b : mentionTag.plainTextBeginIndex)) {
295
- updatedStartIndex = findNewSelectionIndexForMention({
296
- tag: mentionTag,
297
- textValue: inputTextValue,
298
- currentSelectionIndex: event.currentTarget.selectionStart,
299
- previousSelectionIndex: selectionStartValue !== null && selectionStartValue !== void 0 ? selectionStartValue : inputTextValue.length
300
- });
301
- }
302
- }
303
- if (event.currentTarget.selectionEnd !== null && event.currentTarget.selectionEnd !== selectionEndValue) {
304
- // the selection end is changed
305
- const mentionTag = findMentionTagForSelection(tagsValue, event.currentTarget.selectionEnd);
306
- // don't include boundary cases to show correct selection, otherwise it will show selection at mention boundaries
307
- if (mentionTag !== undefined &&
308
- mentionTag.plainTextBeginIndex !== undefined &&
309
- event.currentTarget.selectionEnd > mentionTag.plainTextBeginIndex &&
310
- event.currentTarget.selectionEnd < ((_c = mentionTag.plainTextEndIndex) !== null && _c !== void 0 ? _c : mentionTag.plainTextBeginIndex)) {
311
- updatedEndIndex = findNewSelectionIndexForMention({
312
- tag: mentionTag,
313
- textValue: inputTextValue,
314
- currentSelectionIndex: event.currentTarget.selectionEnd,
315
- previousSelectionIndex: selectionEndValue !== null && selectionEndValue !== void 0 ? selectionEndValue : inputTextValue.length
316
- });
317
- }
318
- }
319
- }
320
- // e.currentTarget.selectionDirection should be set to handle shift + arrow keys
321
- if (event.currentTarget.selectionDirection === null) {
322
- event.currentTarget.setSelectionRange(updatedStartIndex, updatedEndIndex);
323
- }
324
- else {
325
- event.currentTarget.setSelectionRange(updatedStartIndex, updatedEndIndex, event.currentTarget.selectionDirection);
326
- }
327
- setSelectionStartValue(updatedStartIndex);
328
- setSelectionEndValue(updatedEndIndex);
329
- }, [setSelectionStartValue, setSelectionEndValue]);
330
- /* @conditional-compile-remove(mention) */
331
- const handleOnSelect = useCallback((event, inputTextValue, tags, shouldHandleOnMouseDownDuringSelect, selectionStartValue, selectionEndValue) => {
332
- if (shouldHandleOnMouseDownDuringSelect) {
333
- if (targetSelection !== undefined) {
334
- setSelectionStartValue(targetSelection.start);
335
- setSelectionEndValue(targetSelection.end);
336
- event.currentTarget.setSelectionRange(targetSelection.start, targetSelection.end);
337
- setTargetSelection(undefined);
338
- }
339
- else if (event.currentTarget.selectionStart !== null) {
340
- // on select was triggered by mouse down/up with no movement
341
- const mentionTag = findMentionTagForSelection(tags, event.currentTarget.selectionStart);
342
- if (mentionTag !== undefined && mentionTag.plainTextBeginIndex !== undefined) {
343
- // handle mention click
344
- // Get range of word that was clicked on
345
- const selectionRange = rangeOfWordInSelection({
346
- textInput: inputTextValue,
347
- selectionStart: event.currentTarget.selectionStart,
348
- selectionEnd: event.currentTarget.selectionEnd,
349
- tag: mentionTag
350
- });
351
- if (event.currentTarget.selectionDirection === null) {
352
- event.currentTarget.setSelectionRange(selectionRange.start, selectionRange.end);
353
- }
354
- else {
355
- event.currentTarget.setSelectionRange(selectionRange.start, selectionRange.end, event.currentTarget.selectionDirection);
356
- }
357
- setSelectionStartValue(selectionRange.start);
358
- setSelectionEndValue(selectionRange.end);
359
- }
360
- else {
361
- setSelectionStartValue(event.currentTarget.selectionStart);
362
- setSelectionEndValue(event.currentTarget.selectionEnd);
363
- }
364
- }
365
- }
366
- else {
367
- // selection was changed by keyboard
368
- updateSelectionIndexesWithMentionIfNeeded(event, inputTextValue, selectionStartValue, selectionEndValue, tags);
369
- }
370
- // don't set setShouldHandleOnMouseDownDuringSelect(false) here as setSelectionRange
371
- // could trigger additional calls of onSelect event and they may not be handled correctly
372
- // (because of setSelectionRange calls or rerender)
373
- }, [
374
- updateSelectionIndexesWithMentionIfNeeded,
375
- targetSelection,
376
- setTargetSelection,
377
- setSelectionStartValue,
378
- setSelectionEndValue
379
- ]);
380
- /* @conditional-compile-remove(mention) */
381
- const handleOnChange = useCallback((event, tagsValue, htmlTextValue, inputTextValue, currentTriggerStartIndex, previousSelectionStart, previousSelectionEnd, currentSelectionStart, currentSelectionEnd, updatedValue) => __awaiter(void 0, void 0, void 0, function* () {
382
- var _b;
383
- debouncedQueryUpdate.cancel();
384
- if (event.currentTarget === null) {
385
- return;
386
- }
387
- // handle backspace change
388
- // onSelect is not called for backspace as selection is not changed and local caret index is outdated
389
- setCaretIndex(undefined);
390
- const newValue = updatedValue !== null && updatedValue !== void 0 ? updatedValue : '';
391
- const triggerText = (_b = mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.trigger) !== null && _b !== void 0 ? _b : DEFAULT_MENTION_TRIGGER;
392
- const newTextLength = newValue.length;
393
- // updating indexes to set between 0 and text length, otherwise selectionRange won't be set correctly
394
- const currentSelectionEndValue = getValidatedIndexInRange({
395
- min: 0,
396
- max: newTextLength,
397
- currentValue: currentSelectionEnd
398
- });
399
- const currentSelectionStartValue = getValidatedIndexInRange({
400
- min: 0,
401
- max: newTextLength,
402
- currentValue: currentSelectionStart
403
- });
404
- const previousSelectionStartValue = getValidatedIndexInRange({
405
- min: 0,
406
- max: inputTextValue.length,
407
- currentValue: previousSelectionStart
408
- });
409
- const previousSelectionEndValue = getValidatedIndexInRange({
410
- min: 0,
411
- max: inputTextValue.length,
412
- currentValue: previousSelectionEnd
413
- });
414
- // If we are enabled for lookups,
415
- if (mentionLookupOptions !== undefined) {
416
- // Look at the range of the change for a trigger character
417
- const triggerPriorIndex = newValue.lastIndexOf(triggerText, currentSelectionEndValue - 1);
418
- // Update the caret position, if not doing a lookup
419
- setCaretPosition(Caret.getRelativePosition(event.currentTarget));
420
- if (triggerPriorIndex !== undefined) {
421
- // trigger is found
422
- const isSpaceBeforeTrigger = newValue.substring(triggerPriorIndex - 1, triggerPriorIndex) === ' ';
423
- const wordAtSelection = newValue.substring(triggerPriorIndex, currentSelectionEndValue);
424
- let tagIndex = currentTriggerStartIndex;
425
- if (!isSpaceBeforeTrigger && triggerPriorIndex !== 0) {
426
- //no space before the trigger <- continuation of the previous word
427
- tagIndex = -1;
428
- setCurrentTriggerStartIndex(tagIndex);
429
- }
430
- else if (wordAtSelection === triggerText) {
431
- // start of the mention
432
- tagIndex = currentSelectionEndValue - triggerText.length;
433
- if (tagIndex < 0) {
434
- tagIndex = 0;
435
- }
436
- setCurrentTriggerStartIndex(tagIndex);
437
- }
438
- if (tagIndex === -1) {
439
- updateMentionSuggestions([]);
440
- }
441
- else {
442
- // In the middle of a @mention lookup
443
- if (tagIndex > -1) {
444
- const query = wordAtSelection.substring(triggerText.length, wordAtSelection.length);
445
- if (query !== undefined) {
446
- yield debouncedQueryUpdate(query);
447
- }
448
- }
449
- }
450
- }
451
- }
452
- let result = '';
453
- if (tagsValue.length === 0) {
454
- // no tags in the string and newValue should be used as a result string
455
- result = newValue;
456
- }
457
- else {
458
- // there are tags in the text value and htmlTextValue is html string
459
- // find diff between old and new text
460
- const { changeStart, oldChangeEnd, newChangeEnd } = findStringsDiffIndexes({
461
- oldText: inputTextValue,
462
- newText: newValue,
463
- previousSelectionStart: previousSelectionStartValue,
464
- previousSelectionEnd: previousSelectionEndValue,
465
- currentSelectionStart: currentSelectionStartValue,
466
- currentSelectionEnd: currentSelectionEndValue
467
- });
468
- const change = newValue.substring(changeStart, newChangeEnd);
469
- // get updated html string
470
- const updatedContent = updateHTML({
471
- htmlText: htmlTextValue,
472
- oldPlainText: inputTextValue,
473
- newPlainText: newValue,
474
- tags: tagsValue,
475
- startIndex: changeStart,
476
- oldPlainTextEndIndex: oldChangeEnd,
477
- change,
478
- mentionTrigger: triggerText
479
- });
480
- result = updatedContent.updatedHTML;
481
- // update caret index if needed
482
- if (updatedContent.updatedSelectionIndex !== null) {
483
- setCaretIndex(updatedContent.updatedSelectionIndex);
484
- setSelectionEndValue(updatedContent.updatedSelectionIndex);
485
- setSelectionStartValue(updatedContent.updatedSelectionIndex);
486
- }
487
- }
488
- onChange && onChange(event, result);
489
- }), [onChange, mentionLookupOptions, setCaretIndex, setCaretPosition, updateMentionSuggestions, debouncedQueryUpdate]);
490
- const getInputFieldTextValue = () => {
43
+ const renderTextField = () => {
44
+ const textFieldProps = {
45
+ autoFocus: props.autoFocus === 'sendBoxTextField',
46
+ multiline: true,
47
+ autoAdjustHeight: true,
48
+ multiple: false,
49
+ resizable: false,
50
+ componentRef: textFieldRef,
51
+ id,
52
+ inputClassName: mergedInputFieldStyle,
53
+ placeholder: placeholderText,
54
+ autoComplete: 'off',
55
+ styles: mergedTextFieldStyle,
56
+ disabled,
57
+ errorMessage,
58
+ onRenderSuffix: onRenderChildren
59
+ };
491
60
  /* @conditional-compile-remove(mention) */
492
- return inputTextValue;
493
- return textValue;
494
- };
495
- /* @conditional-compile-remove(mention) */
496
- // Adjust the selection range based on a mouse / touch interaction
497
- const handleOnMove = useCallback((event) => {
498
- var _a;
499
- let targetStart = event.currentTarget.selectionStart;
500
- let targetEnd = event.currentTarget.selectionEnd;
501
- // Should we do anything?
502
- if (interactionStartPoint !== undefined &&
503
- // And did selection change?
504
- targetStart !== null &&
505
- (targetStart !== (targetSelection === null || targetSelection === void 0 ? void 0 : targetSelection.start) || targetEnd !== (targetSelection === null || targetSelection === void 0 ? void 0 : targetSelection.end))) {
506
- const direction = event.clientX > interactionStartPoint.x ? 'forward' : 'backward';
507
- const mentionTag = findMentionTagForSelection(tagsValue, direction === 'backward'
508
- ? event.currentTarget.selectionStart
509
- : (_a = event.currentTarget.selectionEnd) !== null && _a !== void 0 ? _a : event.currentTarget.selectionStart);
510
- let updateCurrentTarget = false;
511
- if (mentionTag !== undefined && mentionTag.plainTextBeginIndex !== undefined) {
512
- targetStart = Math.min(mentionTag.plainTextBeginIndex, targetStart);
513
- if (mentionTag.plainTextEndIndex !== undefined && targetEnd !== null) {
514
- targetEnd = Math.max(mentionTag.plainTextEndIndex, targetEnd);
515
- }
516
- updateCurrentTarget = true;
517
- setShouldHandleOnMouseDownDuringSelect(false);
518
- }
519
- // Update selection range
520
- setTargetSelection({ start: targetStart, end: targetEnd });
521
- if (updateCurrentTarget) {
522
- // Only set the control, if the values are updated
523
- event.currentTarget.setSelectionRange(targetStart, targetEnd, direction);
524
- }
525
- }
526
- }, [setTargetSelection, targetSelection, setShouldHandleOnMouseDownDuringSelect, interactionStartPoint, tagsValue]);
527
- /* @conditional-compile-remove(mention) */
528
- const announcerText = useMemo(() => {
529
- if (activeSuggestionIndex === undefined) {
530
- return undefined;
61
+ const textFieldWithMentionProps = {
62
+ textFieldProps: textFieldProps,
63
+ dataUiId: dataUiId,
64
+ textValue: textValue,
65
+ onChange: onChange,
66
+ onKeyDown: onKeyDown,
67
+ onEnterKeyDown: onEnterKeyDown,
68
+ textFieldRef: textFieldRef,
69
+ supportNewline: supportNewline,
70
+ mentionLookupOptions: props.mentionLookupOptions
71
+ };
72
+ /* @conditional-compile-remove(mention) */
73
+ if (props.mentionLookupOptions) {
74
+ return React.createElement(TextFieldWithMention, Object.assign({}, textFieldWithMentionProps));
531
75
  }
532
- const currentMention = mentionSuggestions[activeSuggestionIndex !== null && activeSuggestionIndex !== void 0 ? activeSuggestionIndex : 0];
533
- return (currentMention === null || currentMention === void 0 ? void 0 : currentMention.displayText.length) > 0
534
- ? currentMention === null || currentMention === void 0 ? void 0 : currentMention.displayText
535
- : localeStrings.participantItem.displayNamePlaceholder;
536
- }, [activeSuggestionIndex, mentionSuggestions, localeStrings.participantItem.displayNamePlaceholder]);
76
+ return (React.createElement(TextField, Object.assign({}, textFieldProps, { "data-ui-id": dataUiId, value: textValue, onChange: onChange, onKeyDown: onTextFieldKeyDown })));
77
+ };
537
78
  return (React.createElement(Stack, { className: mergedRootStyle },
538
- React.createElement("div", { className: mergedTextContainerStyle },
539
- /* @conditional-compile-remove(mention) */ mentionSuggestions.length > 0 && (React.createElement(_MentionPopover, { suggestions: mentionSuggestions, activeSuggestionIndex: activeSuggestionIndex, target: inputBoxRef, targetPositionOffset: caretPosition, onRenderSuggestionItem: mentionLookupOptions === null || mentionLookupOptions === void 0 ? void 0 : mentionLookupOptions.onRenderSuggestionItem, onSuggestionSelected: onSuggestionSelected, onDismiss: () => {
540
- updateMentionSuggestions([]);
541
- } })),
542
- /* @conditional-compile-remove(mention) */ announcerText !== undefined && (React.createElement(Announcer, { announcementString: announcerText, ariaLive: 'polite' })),
543
- React.createElement(TextField, { autoFocus: props.autoFocus === 'sendBoxTextField', "data-ui-id": dataUiId, multiline: true, autoAdjustHeight: true, multiple: false, resizable: false, componentRef: textFieldRef, id: id, inputClassName: mergedInputFieldStyle, placeholder: placeholderText, value: getInputFieldTextValue(), onChange: (e, newValue) => {
544
- // Remove when switching to react 17+, currently needed because of https://legacy.reactjs.org/docs/legacy-event-pooling.html
545
- /* @conditional-compile-remove(mention) */
546
- // Prevents React from resetting event's properties
547
- e.persist();
548
- /* @conditional-compile-remove(mention) */
549
- setInputTextValue(newValue !== null && newValue !== void 0 ? newValue : '');
550
- /* @conditional-compile-remove(mention) */
551
- 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);
552
- /* @conditional-compile-remove(mention) */
553
- return;
554
- onChange(e, newValue);
555
- },
556
- /* @conditional-compile-remove(mention) */
557
- onSelect: (e) => {
558
- // update selection if needed
559
- if (caretIndex !== undefined) {
560
- setCaretIndex(undefined);
561
- // sometimes setting selectionRage in effect for updating caretIndex doesn't work as expected and onSelect should handle this case
562
- if (caretIndex !== e.currentTarget.selectionStart || caretIndex !== e.currentTarget.selectionEnd) {
563
- e.currentTarget.setSelectionRange(caretIndex, caretIndex);
564
- }
565
- return;
566
- }
567
- handleOnSelect(e, inputTextValue, tagsValue, shouldHandleOnMouseDownDuringSelect, selectionStartValue, selectionEndValue);
568
- },
569
- /* @conditional-compile-remove(mention) */
570
- onMouseDown: (e) => {
571
- setInteractionStartPoint({ x: e.clientX, y: e.clientY });
572
- // as events order is onMouseDown -> onMouseMove -> onMouseUp -> onSelect -> onClick
573
- // onClick and onMouseDown can't handle clicking on mention event because
574
- // onMouseDown doesn't have correct selectionRange yet and
575
- // onClick already has wrong range as it's called after onSelect that updates the selection range
576
- // so we need to handle onMouseDown to prevent onSelect default behavior
577
- setShouldHandleOnMouseDownDuringSelect(true);
578
- },
579
- /* @conditional-compile-remove(mention) */
580
- onMouseMove: handleOnMove,
581
- /* @conditional-compile-remove(mention) */
582
- onMouseUp: () => {
583
- setInteractionStartPoint(undefined);
584
- },
585
- /* @conditional-compile-remove(mention) */
586
- onTouchStart: (e) => {
587
- setInteractionStartPoint({
588
- x: e.targetTouches.item(0).clientX,
589
- y: e.targetTouches.item(0).clientY
590
- });
591
- // see onMouseDown for more details
592
- setShouldHandleOnMouseDownDuringSelect(true);
593
- },
594
- /* @conditional-compile-remove(mention) */
595
- onTouchMove: handleOnMove,
596
- /* @conditional-compile-remove(mention) */
597
- onTouchEnd: () => {
598
- setInteractionStartPoint(undefined);
599
- },
600
- /* @conditional-compile-remove(mention) */
601
- onBlur: () => {
602
- // setup all flags to default values when text field loses focus
603
- setShouldHandleOnMouseDownDuringSelect(false);
604
- setCaretIndex(undefined);
605
- setSelectionStartValue(null);
606
- setSelectionEndValue(null);
607
- // Dismiss the suggestions on blur, after enough time to select by mouse if needed
608
- setTimeout(() => {
609
- setMentionSuggestions([]);
610
- }, 200);
611
- }, autoComplete: "off", onKeyDown: onTextFieldKeyDown, styles: mergedTextFieldStyle, disabled: disabled, errorMessage: errorMessage, onRenderSuffix: onRenderChildren, elementRef: inputBoxRef }))));
79
+ React.createElement("div", { className: mergedTextContainerStyle }, renderTextField())));
612
80
  };
613
81
  /**
614
82
  * @private
@@ -634,789 +102,4 @@ export const InputBoxButton = (props) => {
634
102
  // VoiceOver fix: Avoid icon from stealing focus when IconButton is double-tapped to send message by wrapping with Stack with pointerEvents style to none
635
103
  onRenderIcon: () => React.createElement(Stack, { className: iconWrapperStyle }, onRenderIcon(isHover)) })));
636
104
  };
637
- /* @conditional-compile-remove(mention) */
638
- /**
639
- * Get validated value for index between min and max values. If currentValue is not defined, -1 will be used instead.
640
- *
641
- * @private
642
- * @param props - Props for finding a valid index in range.
643
- * @returns Valid index in the range.
644
- */
645
- const getValidatedIndexInRange = (props) => {
646
- const { min, max, currentValue } = props;
647
- let updatedValue = currentValue !== null && currentValue !== void 0 ? currentValue : -1;
648
- updatedValue = Math.max(min, updatedValue);
649
- updatedValue = Math.min(updatedValue, max);
650
- return updatedValue;
651
- };
652
- /* @conditional-compile-remove(mention) */
653
- /**
654
- * Find mention tag for selection if exists.
655
- *
656
- * @private
657
- * @param tags - Existing list of tags.
658
- * @param selection - Selection index.
659
- * @returns Mention tag if exists, otherwise undefined.
660
- */
661
- const findMentionTagForSelection = (tags, selection) => {
662
- let mentionTag = undefined;
663
- for (let i = 0; i < tags.length; i++) {
664
- const tag = tags[i];
665
- const closingTagInfo = getTagClosingTagInfo(tag);
666
- if (tag.plainTextBeginIndex !== undefined && tag.plainTextBeginIndex > selection) {
667
- // no need to check further as the selection is before the tag
668
- break;
669
- }
670
- else if (tag.plainTextBeginIndex !== undefined &&
671
- tag.plainTextBeginIndex <= selection &&
672
- selection <= closingTagInfo.plainTextEndIndex) {
673
- // no need to check if tag doesn't contain selection
674
- if (tag.subTags !== undefined && tag.subTags.length !== 0) {
675
- const selectedTag = findMentionTagForSelection(tag.subTags, selection);
676
- if (selectedTag !== undefined) {
677
- mentionTag = selectedTag;
678
- break;
679
- }
680
- }
681
- else if (tag.tagType === MSFT_MENTION_TAG) {
682
- mentionTag = tag;
683
- break;
684
- }
685
- }
686
- }
687
- return mentionTag;
688
- };
689
- /* @conditional-compile-remove(mention) */
690
- const rangeOfWordInSelection = ({ textInput, selectionStart, selectionEnd, tag }) => {
691
- var _a;
692
- if (tag.plainTextBeginIndex === undefined) {
693
- return { start: selectionStart, end: selectionEnd === null ? selectionStart : selectionEnd };
694
- }
695
- // Look at start word index and optionally end word index.
696
- // Select combination of the two and return the range.
697
- let start = selectionStart;
698
- let end = selectionEnd === null ? selectionStart : selectionEnd;
699
- const firstWordStartIndex = textInput.lastIndexOf(' ', selectionStart);
700
- if (firstWordStartIndex === tag.plainTextBeginIndex) {
701
- start = firstWordStartIndex;
702
- }
703
- else {
704
- start = Math.max(firstWordStartIndex + 1, tag.plainTextBeginIndex);
705
- }
706
- const firstWordEndIndex = textInput.indexOf(' ', selectionStart);
707
- end = Math.max(firstWordEndIndex + 1, (_a = tag.plainTextEndIndex) !== null && _a !== void 0 ? _a : firstWordEndIndex + 1);
708
- if (selectionEnd !== null && tag.plainTextEndIndex !== undefined) {
709
- const lastWordEndIndex = textInput.indexOf(' ', selectionEnd);
710
- end = Math.max(lastWordEndIndex > -1 ? lastWordEndIndex : tag.plainTextEndIndex, selectionEnd);
711
- }
712
- return { start, end };
713
- };
714
- /* @conditional-compile-remove(mention) */
715
- /**
716
- * Find a new the selection index.
717
- *
718
- * @private
719
- * @param props - Props for finding new selection index for mention.
720
- * @returns New selection index if it is inside of a mention tag, otherwise the current selection.
721
- */
722
- const findNewSelectionIndexForMention = (props) => {
723
- var _a;
724
- const { tag, textValue, currentSelectionIndex, previousSelectionIndex } = props;
725
- // check if this is a mention tag and selection should be updated
726
- if (tag.tagType !== MSFT_MENTION_TAG ||
727
- tag.plainTextBeginIndex === undefined ||
728
- currentSelectionIndex === previousSelectionIndex ||
729
- tag.plainTextEndIndex === undefined) {
730
- return currentSelectionIndex;
731
- }
732
- let spaceIndex = 0;
733
- if (currentSelectionIndex <= previousSelectionIndex) {
734
- // the cursor is moved to the left, find the last index before the cursor
735
- spaceIndex = textValue.lastIndexOf(' ', currentSelectionIndex !== null && currentSelectionIndex !== void 0 ? currentSelectionIndex : 0);
736
- if (spaceIndex === -1) {
737
- // no space before the selection, use the beginning of the tag
738
- spaceIndex = tag.plainTextBeginIndex;
739
- }
740
- }
741
- else {
742
- // the cursor is moved to the right, find the fist index after the cursor
743
- spaceIndex = textValue.indexOf(' ', currentSelectionIndex !== null && currentSelectionIndex !== void 0 ? currentSelectionIndex : 0);
744
- if (spaceIndex === -1) {
745
- // no space after the selection, use the end of the tag
746
- spaceIndex = (_a = tag.plainTextEndIndex) !== null && _a !== void 0 ? _a : tag.plainTextBeginIndex;
747
- }
748
- }
749
- spaceIndex = Math.max(tag.plainTextBeginIndex, spaceIndex);
750
- spaceIndex = Math.min(tag.plainTextEndIndex, spaceIndex);
751
- return spaceIndex;
752
- };
753
- /* @conditional-compile-remove(mention) */
754
- /**
755
- * Handle mention tag edit and by word deleting
756
- *
757
- * @private
758
- * @param props - Props for mention update HTML function.
759
- * @returns Updated texts and indexes.
760
- */
761
- const handleMentionTagUpdate = (props) => {
762
- const { htmlText, oldPlainText, change, tag, closeTagIdx, closeTagLength, plainTextEndIndex, startIndex, oldPlainTextEndIndex, mentionTagLength } = props;
763
- let processedChange = props.processedChange;
764
- let lastProcessedHTMLIndex = props.lastProcessedHTMLIndex;
765
- if (tag.tagType !== MSFT_MENTION_TAG || tag.plainTextBeginIndex === undefined) {
766
- // not a mention tag
767
- return {
768
- result: '',
769
- updatedChange: processedChange,
770
- htmlIndex: lastProcessedHTMLIndex,
771
- plainTextSelectionEndIndex: null
772
- };
773
- }
774
- let result = '';
775
- let plainTextSelectionEndIndex = null;
776
- let rangeStart;
777
- let rangeEnd;
778
- // check if space symbol is handled in case if string looks like '<1 2 3>'
779
- let isSpaceLengthHandled = false;
780
- rangeStart = oldPlainText.lastIndexOf(' ', startIndex);
781
- if (rangeStart !== -1 && rangeStart !== undefined && rangeStart > tag.plainTextBeginIndex) {
782
- isSpaceLengthHandled = true;
783
- }
784
- rangeEnd = oldPlainText.indexOf(' ', oldPlainTextEndIndex);
785
- if (rangeEnd === -1 || rangeEnd === undefined) {
786
- // check if space symbol is not found
787
- rangeEnd = plainTextEndIndex;
788
- }
789
- else if (!isSpaceLengthHandled) {
790
- // +1 to include the space symbol
791
- rangeEnd += 1;
792
- }
793
- isSpaceLengthHandled = true;
794
- if (rangeStart === -1 || rangeStart === undefined || rangeStart < tag.plainTextBeginIndex) {
795
- // rangeStart should be at least equal to tag.plainTextBeginIndex
796
- rangeStart = tag.plainTextBeginIndex;
797
- }
798
- if (rangeEnd > plainTextEndIndex) {
799
- // rangeEnd should be at most equal to plainTextEndIndex
800
- rangeEnd = plainTextEndIndex;
801
- }
802
- if (rangeStart === tag.plainTextBeginIndex && rangeEnd === plainTextEndIndex) {
803
- // the whole tag should be removed
804
- result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx) + processedChange;
805
- plainTextSelectionEndIndex = tag.plainTextBeginIndex + processedChange.length;
806
- processedChange = '';
807
- lastProcessedHTMLIndex = closeTagIdx + closeTagLength;
808
- }
809
- else {
810
- // only part of the tag should be removed
811
- let startChangeDiff = 0;
812
- let endChangeDiff = 0;
813
- // need to check only rangeStart > tag.plainTextBeginIndex as when rangeStart === tag.plainTextBeginIndex startChangeDiff = 0 and mentionTagLength shouldn't be subtracted
814
- if (rangeStart > tag.plainTextBeginIndex) {
815
- startChangeDiff = rangeStart - tag.plainTextBeginIndex - mentionTagLength;
816
- }
817
- endChangeDiff = rangeEnd - tag.plainTextBeginIndex - mentionTagLength;
818
- result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length + startChangeDiff);
819
- if (startIndex < tag.plainTextBeginIndex) {
820
- // if the change is before the tag, the selection should start from startIndex (rangeStart will be equal to tag.plainTextBeginIndex)
821
- plainTextSelectionEndIndex = startIndex + change.length;
822
- }
823
- else {
824
- // if the change is inside the tag, the selection should start with rangeStart
825
- plainTextSelectionEndIndex = rangeStart + processedChange.length;
826
- }
827
- lastProcessedHTMLIndex = tag.openTagIdx + tag.openTagBody.length + endChangeDiff;
828
- // processed change should not be changed as it should be added after the tag
829
- }
830
- return { result, updatedChange: processedChange, htmlIndex: lastProcessedHTMLIndex, plainTextSelectionEndIndex };
831
- };
832
- /* @conditional-compile-remove(mention) */
833
- /**
834
- * Get closing tag information if exists otherwise return information as for self closing tag
835
- *
836
- * @private
837
- * @param tag - Tag data.
838
- * @returns Closing tag information for the provided tag.
839
- */
840
- const getTagClosingTagInfo = (tag) => {
841
- let plainTextEndIndex = 0;
842
- let closeTagIdx = 0;
843
- let closeTagLength = 0;
844
- if (tag.plainTextEndIndex !== undefined && tag.closeTagIdx !== undefined) {
845
- // close tag exists
846
- plainTextEndIndex = tag.plainTextEndIndex;
847
- closeTagIdx = tag.closeTagIdx;
848
- // tag.tagType.length + </>
849
- closeTagLength = tag.tagType.length + 3;
850
- }
851
- else if (tag.plainTextBeginIndex !== undefined) {
852
- // no close tag
853
- plainTextEndIndex = tag.plainTextBeginIndex;
854
- closeTagIdx = tag.openTagIdx + tag.openTagBody.length;
855
- closeTagLength = 0;
856
- }
857
- return { plainTextEndIndex, closeTagIdx, closeTagLength };
858
- };
859
- /* @conditional-compile-remove(mention) */
860
- /**
861
- * Go through the text and update it with the changed text
862
- *
863
- * @private
864
- * @param props - Props for update HTML function.
865
- * @returns Updated HTML and selection index if the selection index should be set.
866
- */
867
- const updateHTML = (props) => {
868
- const { htmlText, oldPlainText, newPlainText, tags, startIndex, oldPlainTextEndIndex, change, mentionTrigger } = props;
869
- if (tags.length === 0 || (startIndex === 0 && oldPlainTextEndIndex === oldPlainText.length - 1)) {
870
- // no tags added yet or the whole text is changed
871
- return { updatedHTML: newPlainText, updatedSelectionIndex: null };
872
- }
873
- let result = '';
874
- let lastProcessedHTMLIndex = 0;
875
- // the value can be updated with empty string when the change covers more than 1 place (tag + before or after the tag)
876
- // in this case change won't be added as part of the tag
877
- // e.g.: change is before and partially in tag => change will be added before the tag and outdated text in the tag will be removed
878
- // e.g.: change is after and partially in tag => change will be added after the tag and outdated text in the tag will be removed
879
- // e.g.: change is on the beginning of the tag => change will be added before the tag
880
- // e.g.: change is on the end of the tag => change will be added to the tag if it's not mention and after the tag if it's mention
881
- let processedChange = change;
882
- // end tag plain text index of the last processed tag
883
- let lastProcessedPlainTextTagEndIndex = 0;
884
- // as some tags/text can be removed fully, selection should be updated correctly
885
- let changeNewEndIndex = null;
886
- for (let i = 0; i < tags.length; i++) {
887
- const tag = tags[i];
888
- if (tag.plainTextBeginIndex === undefined) {
889
- continue;
890
- }
891
- // all plain text indexes includes trigger length for the mention that shouldn't be included in
892
- // htmlText.substring because html strings don't include the trigger
893
- // mentionTagLength will be set only for mention tag, otherwise should be 0
894
- let mentionTagLength = 0;
895
- let isMentionTag = false;
896
- if (tag.tagType === MSFT_MENTION_TAG) {
897
- mentionTagLength = mentionTrigger.length;
898
- isMentionTag = true;
899
- }
900
- if (startIndex <= tag.plainTextBeginIndex) {
901
- // change start is before the open tag
902
- // Math.max(lastProcessedPlainTextTagEndIndex, startIndex) is used as startIndex may not be in [[previous tag].plainTextEndIndex - tag.plainTextBeginIndex] range
903
- const startChangeDiff = tag.plainTextBeginIndex - Math.max(lastProcessedPlainTextTagEndIndex, startIndex);
904
- result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx - startChangeDiff) + processedChange;
905
- processedChange = '';
906
- if (oldPlainTextEndIndex <= tag.plainTextBeginIndex) {
907
- // the whole change is before tag start
908
- // mentionTag length can be ignored here as the change is before the tag
909
- const endChangeDiff = tag.plainTextBeginIndex - oldPlainTextEndIndex;
910
- lastProcessedHTMLIndex = tag.openTagIdx - endChangeDiff;
911
- // the change is handled; exit
912
- break;
913
- }
914
- else {
915
- // change continues in the tag
916
- lastProcessedHTMLIndex = tag.openTagIdx;
917
- // proceed to the next check
918
- }
919
- }
920
- const closingTagInfo = getTagClosingTagInfo(tag);
921
- if (startIndex <= closingTagInfo.plainTextEndIndex) {
922
- // change started before the end tag
923
- if (startIndex <= tag.plainTextBeginIndex && oldPlainTextEndIndex === closingTagInfo.plainTextEndIndex) {
924
- // the change is a tag or starts before the tag
925
- // tag should be removed, no matter if there are subtags
926
- result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx) + processedChange;
927
- processedChange = '';
928
- lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
929
- // the change is handled; exit
930
- break;
931
- }
932
- else if (startIndex >= tag.plainTextBeginIndex && oldPlainTextEndIndex <= closingTagInfo.plainTextEndIndex) {
933
- // the change is between the tag
934
- if (isMentionTag) {
935
- if (change !== '') {
936
- if (startIndex !== tag.plainTextBeginIndex && startIndex !== closingTagInfo.plainTextEndIndex) {
937
- // mention tag should be deleted when user tries to edit it in the middle
938
- result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx) + processedChange;
939
- changeNewEndIndex = tag.plainTextBeginIndex + processedChange.length;
940
- lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
941
- }
942
- else if (startIndex === tag.plainTextBeginIndex) {
943
- // non empty change at the beginning of the mention tag to be added before the mention tag
944
- result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx) + processedChange;
945
- changeNewEndIndex = tag.plainTextBeginIndex + processedChange.length;
946
- lastProcessedHTMLIndex = tag.openTagIdx;
947
- }
948
- else if (startIndex === closingTagInfo.plainTextEndIndex) {
949
- // non empty change at the end of the mention tag to be added after the mention tag
950
- result +=
951
- htmlText.substring(lastProcessedHTMLIndex, closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength) +
952
- processedChange;
953
- changeNewEndIndex = closingTagInfo.plainTextEndIndex + processedChange.length;
954
- lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
955
- }
956
- processedChange = '';
957
- }
958
- else {
959
- const updateMentionTagResult = handleMentionTagUpdate({
960
- htmlText,
961
- oldPlainText,
962
- lastProcessedHTMLIndex,
963
- processedChange,
964
- change,
965
- tag,
966
- closeTagIdx: closingTagInfo.closeTagIdx,
967
- closeTagLength: closingTagInfo.closeTagLength,
968
- plainTextEndIndex: closingTagInfo.plainTextEndIndex,
969
- startIndex,
970
- oldPlainTextEndIndex,
971
- mentionTagLength
972
- });
973
- result += updateMentionTagResult.result;
974
- changeNewEndIndex = updateMentionTagResult.plainTextSelectionEndIndex;
975
- processedChange = updateMentionTagResult.updatedChange;
976
- lastProcessedHTMLIndex = updateMentionTagResult.htmlIndex;
977
- }
978
- }
979
- else if (tag.subTags !== undefined && tag.subTags.length !== 0 && tag.content !== undefined) {
980
- // with subtags
981
- const stringBefore = htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length);
982
- lastProcessedHTMLIndex = closingTagInfo.closeTagIdx;
983
- const updatedContent = updateHTML({
984
- htmlText: tag.content,
985
- oldPlainText,
986
- newPlainText,
987
- tags: tag.subTags,
988
- startIndex,
989
- oldPlainTextEndIndex,
990
- change: processedChange,
991
- mentionTrigger
992
- });
993
- result += stringBefore + updatedContent.updatedHTML;
994
- changeNewEndIndex = updatedContent.updatedSelectionIndex;
995
- }
996
- else {
997
- // no subtags
998
- const startChangeDiff = startIndex - tag.plainTextBeginIndex;
999
- result +=
1000
- htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length + startChangeDiff) +
1001
- processedChange;
1002
- processedChange = '';
1003
- if (oldPlainTextEndIndex < closingTagInfo.plainTextEndIndex) {
1004
- const endChangeDiff = oldPlainTextEndIndex - tag.plainTextBeginIndex;
1005
- lastProcessedHTMLIndex = tag.openTagIdx + tag.openTagBody.length + endChangeDiff;
1006
- }
1007
- else if (oldPlainTextEndIndex === closingTagInfo.plainTextEndIndex) {
1008
- lastProcessedHTMLIndex = closingTagInfo.closeTagIdx;
1009
- }
1010
- }
1011
- // the change is handled; exit
1012
- break;
1013
- }
1014
- else if (startIndex > tag.plainTextBeginIndex && oldPlainTextEndIndex > closingTagInfo.plainTextEndIndex) {
1015
- // the change started in the tag but finishes somewhere further
1016
- const startChangeDiff = startIndex - tag.plainTextBeginIndex - mentionTagLength;
1017
- if (isMentionTag) {
1018
- const updateMentionTagResult = handleMentionTagUpdate({
1019
- htmlText,
1020
- oldPlainText,
1021
- lastProcessedHTMLIndex,
1022
- processedChange: '',
1023
- change,
1024
- tag,
1025
- closeTagIdx: closingTagInfo.closeTagIdx,
1026
- closeTagLength: closingTagInfo.closeTagLength,
1027
- plainTextEndIndex: closingTagInfo.plainTextEndIndex,
1028
- startIndex,
1029
- oldPlainTextEndIndex,
1030
- mentionTagLength
1031
- });
1032
- result += updateMentionTagResult.result;
1033
- lastProcessedHTMLIndex = updateMentionTagResult.htmlIndex;
1034
- // no need to handle plainTextSelectionEndIndex as the change will be added later
1035
- }
1036
- else if (tag.subTags !== undefined && tag.subTags.length !== 0 && tag.content !== undefined) {
1037
- // with subtags
1038
- const stringBefore = htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length);
1039
- lastProcessedHTMLIndex = closingTagInfo.closeTagIdx;
1040
- const updatedContent = updateHTML({
1041
- htmlText: tag.content,
1042
- oldPlainText,
1043
- newPlainText,
1044
- tags: tag.subTags,
1045
- startIndex,
1046
- oldPlainTextEndIndex,
1047
- change: '',
1048
- mentionTrigger
1049
- });
1050
- result += stringBefore + updatedContent.updatedHTML;
1051
- }
1052
- else {
1053
- // no subtags
1054
- result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length + startChangeDiff);
1055
- lastProcessedHTMLIndex = closingTagInfo.closeTagIdx;
1056
- }
1057
- // proceed with the next calculations
1058
- }
1059
- else if (startIndex < tag.plainTextBeginIndex && oldPlainTextEndIndex > closingTagInfo.plainTextEndIndex) {
1060
- // the change starts before the tag and finishes after it
1061
- // tag should be removed, no matter if there are subtags
1062
- // no need to save anything between lastProcessedHTMLIndex and closeTagIdx + closeTagLength
1063
- lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
1064
- // proceed with the next calculations
1065
- }
1066
- else if (startIndex === tag.plainTextBeginIndex && oldPlainTextEndIndex > closingTagInfo.plainTextEndIndex) {
1067
- // the change starts in the tag and finishes after it
1068
- // tag should be removed, no matter if there are subtags
1069
- result += htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx);
1070
- // processedChange shouldn't be updated as it will be added after the tag
1071
- lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength;
1072
- // proceed with the next calculations
1073
- }
1074
- else if (startIndex < tag.plainTextBeginIndex && oldPlainTextEndIndex < closingTagInfo.plainTextEndIndex) {
1075
- // the change starts before the tag and ends in a tag
1076
- if (isMentionTag) {
1077
- // mention tag
1078
- const updateMentionTagResult = handleMentionTagUpdate({
1079
- htmlText,
1080
- oldPlainText,
1081
- lastProcessedHTMLIndex,
1082
- processedChange: '',
1083
- change,
1084
- tag,
1085
- closeTagIdx: closingTagInfo.closeTagIdx,
1086
- closeTagLength: closingTagInfo.closeTagLength,
1087
- plainTextEndIndex: closingTagInfo.plainTextEndIndex,
1088
- startIndex,
1089
- oldPlainTextEndIndex,
1090
- mentionTagLength
1091
- });
1092
- changeNewEndIndex = updateMentionTagResult.plainTextSelectionEndIndex;
1093
- result += updateMentionTagResult.result;
1094
- lastProcessedHTMLIndex = updateMentionTagResult.htmlIndex;
1095
- }
1096
- else if (tag.subTags !== undefined && tag.subTags.length !== 0 && tag.content !== undefined) {
1097
- // with subtags
1098
- const stringBefore = htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length);
1099
- lastProcessedHTMLIndex = closingTagInfo.closeTagIdx;
1100
- const updatedContent = updateHTML({
1101
- htmlText: tag.content,
1102
- oldPlainText,
1103
- newPlainText,
1104
- tags: tag.subTags,
1105
- startIndex,
1106
- oldPlainTextEndIndex,
1107
- change: processedChange,
1108
- mentionTrigger
1109
- });
1110
- processedChange = '';
1111
- result += stringBefore + updatedContent.updatedHTML;
1112
- }
1113
- else {
1114
- // no subtags
1115
- result +=
1116
- htmlText.substring(lastProcessedHTMLIndex, tag.openTagIdx + tag.openTagBody.length) + processedChange;
1117
- processedChange = '';
1118
- // oldPlainTextEndIndex already includes mentionTag length
1119
- const endChangeDiff = closingTagInfo.plainTextEndIndex - oldPlainTextEndIndex;
1120
- // as change may be before the end of the tag, we need to add the rest of the tag
1121
- lastProcessedHTMLIndex = closingTagInfo.closeTagIdx - endChangeDiff;
1122
- }
1123
- // the change is handled; exit
1124
- break;
1125
- }
1126
- lastProcessedPlainTextTagEndIndex = closingTagInfo.plainTextEndIndex;
1127
- }
1128
- if (i === tags.length - 1 && oldPlainTextEndIndex >= closingTagInfo.plainTextEndIndex) {
1129
- // the last tag should handle the end of the change if needed
1130
- // oldPlainTextEndIndex already includes mentionTag length
1131
- const endChangeDiff = oldPlainTextEndIndex - closingTagInfo.plainTextEndIndex;
1132
- if (startIndex >= closingTagInfo.plainTextEndIndex) {
1133
- const startChangeDiff = startIndex - closingTagInfo.plainTextEndIndex;
1134
- result +=
1135
- htmlText.substring(lastProcessedHTMLIndex, closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength + startChangeDiff) + processedChange;
1136
- }
1137
- else {
1138
- result +=
1139
- htmlText.substring(lastProcessedHTMLIndex, closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength) +
1140
- processedChange;
1141
- }
1142
- processedChange = '';
1143
- lastProcessedHTMLIndex = closingTagInfo.closeTagIdx + closingTagInfo.closeTagLength + endChangeDiff;
1144
- // the change is handled; exit
1145
- // break is not required here as this is the last element but added for consistency
1146
- break;
1147
- }
1148
- }
1149
- if (lastProcessedHTMLIndex < htmlText.length) {
1150
- // add the rest of the html string
1151
- result += htmlText.substring(lastProcessedHTMLIndex);
1152
- }
1153
- return { updatedHTML: result, updatedSelectionIndex: changeNewEndIndex };
1154
- };
1155
- /* @conditional-compile-remove(mention) */
1156
- /**
1157
- * Given the oldText and newText, find the start index, old end index and new end index for the changes
1158
- *
1159
- * @private
1160
- * @param props - Props for finding stings diff indexes function.
1161
- * @returns Indexes for change start and ends in new and old texts. The old and new end indexes are exclusive.
1162
- */
1163
- const findStringsDiffIndexes = (props) => {
1164
- const { oldText, newText, previousSelectionStart, previousSelectionEnd, currentSelectionStart, currentSelectionEnd } = props;
1165
- const newTextLength = newText.length;
1166
- const oldTextLength = oldText.length;
1167
- // let changeStart = 0;
1168
- let newChangeEnd = newTextLength;
1169
- let oldChangeEnd = oldTextLength;
1170
- const previousSelectionStartValue = previousSelectionStart > -1 ? previousSelectionStart : oldTextLength;
1171
- const previousSelectionEndValue = previousSelectionEnd > -1 ? previousSelectionEnd : oldTextLength;
1172
- const currentSelectionStartValue = currentSelectionStart > -1 ? currentSelectionStart : newTextLength;
1173
- const currentSelectionEndValue = currentSelectionEnd > -1 ? currentSelectionEnd : newTextLength;
1174
- const changeStart = Math.min(previousSelectionStartValue, previousSelectionEndValue, currentSelectionStartValue, currentSelectionEndValue, newTextLength, oldTextLength);
1175
- if (oldTextLength < newTextLength) {
1176
- //insert or replacement
1177
- if (oldTextLength === changeStart) {
1178
- // when change was at the end of string
1179
- // change is found
1180
- newChangeEnd = newTextLength;
1181
- oldChangeEnd = oldTextLength;
1182
- }
1183
- else {
1184
- for (let i = 1; i < newTextLength && oldTextLength - i >= changeStart; i++) {
1185
- newChangeEnd = newTextLength - i - 1;
1186
- oldChangeEnd = oldTextLength - i - 1;
1187
- if (newText[newChangeEnd] !== oldText[oldChangeEnd]) {
1188
- // change is found
1189
- break;
1190
- }
1191
- }
1192
- // make indexes exclusive
1193
- newChangeEnd += 1;
1194
- oldChangeEnd += 1;
1195
- }
1196
- }
1197
- else if (oldTextLength > newTextLength) {
1198
- //deletion or replacement
1199
- if (newTextLength === changeStart) {
1200
- // when change was at the end of string
1201
- // change is found
1202
- newChangeEnd = newTextLength;
1203
- oldChangeEnd = oldTextLength;
1204
- }
1205
- else {
1206
- for (let i = 1; i < oldTextLength && newTextLength - i >= changeStart; i++) {
1207
- newChangeEnd = newTextLength - i - 1;
1208
- oldChangeEnd = oldTextLength - i - 1;
1209
- if (newText[newChangeEnd] !== oldText[oldChangeEnd]) {
1210
- // change is found
1211
- break;
1212
- }
1213
- }
1214
- // make indexes exclusive
1215
- newChangeEnd += 1;
1216
- oldChangeEnd += 1;
1217
- }
1218
- }
1219
- else {
1220
- // replacement
1221
- for (let i = 1; i < oldTextLength && oldTextLength - i >= changeStart; i++) {
1222
- newChangeEnd = newTextLength - i - 1;
1223
- oldChangeEnd = oldTextLength - i - 1;
1224
- if (newText[newChangeEnd] !== oldText[oldChangeEnd]) {
1225
- // change is found
1226
- break;
1227
- }
1228
- }
1229
- // make indexes exclusive if they aren't equal to the length of the string
1230
- if (newChangeEnd !== newText.length) {
1231
- newChangeEnd += 1;
1232
- }
1233
- if (oldChangeEnd !== oldText.length) {
1234
- oldChangeEnd += 1;
1235
- }
1236
- }
1237
- return { changeStart, oldChangeEnd, newChangeEnd };
1238
- };
1239
- /* @conditional-compile-remove(mention) */
1240
- /**
1241
- * Get the html string for the mention suggestion.
1242
- *
1243
- * @private
1244
- * @param suggestion - The mention suggestion.
1245
- * @param localeStrings - The locale strings.
1246
- * @returns The html string for the mention suggestion.
1247
- */
1248
- const htmlStringForMentionSuggestion = (suggestion, localeStrings) => {
1249
- const idHTML = ' id ="' + suggestion.id + '"';
1250
- const displayTextHTML = ' displayText ="' + suggestion.displayText + '"';
1251
- const displayText = getDisplayNameForMentionSuggestion(suggestion, localeStrings);
1252
- return '<' + MSFT_MENTION_TAG + idHTML + displayTextHTML + '>' + displayText + '</' + MSFT_MENTION_TAG + '>';
1253
- };
1254
- /* @conditional-compile-remove(mention) */
1255
- /**
1256
- * Get display name for the mention suggestion.
1257
- *
1258
- * @private
1259
- *
1260
- * @param suggestion - The mention suggestion.
1261
- * @param localeStrings - The locale strings.
1262
- * @returns The display name for the mention suggestion or display name placeholder if display name is empty.
1263
- */
1264
- const getDisplayNameForMentionSuggestion = (suggestion, localeStrings) => {
1265
- const displayNamePlaceholder = localeStrings.participantItem.displayNamePlaceholder;
1266
- return suggestion.displayText !== '' ? suggestion.displayText : displayNamePlaceholder !== null && displayNamePlaceholder !== void 0 ? displayNamePlaceholder : '';
1267
- };
1268
- /* @conditional-compile-remove(mention) */
1269
- /**
1270
- * Parse the text and return the tags and the plain text in one go
1271
- * @private
1272
- * @param text - The text to parse for HTML tags
1273
- * @param trigger The trigger to show for the mention tag in plain text
1274
- *
1275
- * @returns An array of tags and the plain text representation
1276
- */
1277
- const textToTagParser = (text, trigger) => {
1278
- var _a, _b;
1279
- const tags = []; // Tags passed back to the caller
1280
- const tagParseStack = []; // Local stack to use while parsing
1281
- let plainTextRepresentation = '';
1282
- let parseIndex = 0;
1283
- while (parseIndex < text.length) {
1284
- const foundHtmlTag = findNextHtmlTag(text, parseIndex);
1285
- if (!foundHtmlTag) {
1286
- if (parseIndex !== 0) {
1287
- // Add the remaining text to the plain text representation
1288
- plainTextRepresentation += text.substring(parseIndex);
1289
- }
1290
- else {
1291
- plainTextRepresentation = text;
1292
- }
1293
- break;
1294
- }
1295
- if (foundHtmlTag.type === 'open' || foundHtmlTag.type === 'self-closing') {
1296
- const nextTag = parseOpenTag(foundHtmlTag.content, foundHtmlTag.startIdx);
1297
- // Add the plain text between the last tag and this one found
1298
- plainTextRepresentation += text.substring(parseIndex, foundHtmlTag.startIdx);
1299
- nextTag.plainTextBeginIndex = plainTextRepresentation.length;
1300
- if (foundHtmlTag.type === 'open') {
1301
- tagParseStack.push(nextTag);
1302
- }
1303
- else {
1304
- nextTag.content = '';
1305
- nextTag.plainTextBeginIndex = plainTextRepresentation.length;
1306
- nextTag.plainTextEndIndex = plainTextRepresentation.length;
1307
- addTag(nextTag, tagParseStack, tags);
1308
- }
1309
- }
1310
- if (foundHtmlTag.type === 'close') {
1311
- const currentOpenTag = tagParseStack.pop();
1312
- const closeTagType = foundHtmlTag.content.substring(2, foundHtmlTag.content.length - 1).toLowerCase();
1313
- if (currentOpenTag && currentOpenTag.tagType === closeTagType) {
1314
- // Tag startIdx is absolute to the text. This is updated later to be relative to the parent tag
1315
- currentOpenTag.content = text.substring(currentOpenTag.openTagIdx + currentOpenTag.openTagBody.length, foundHtmlTag.startIdx);
1316
- // Insert the plain text pieces for the sub tags
1317
- if (currentOpenTag.tagType === MSFT_MENTION_TAG) {
1318
- plainTextRepresentation =
1319
- plainTextRepresentation.slice(0, currentOpenTag.plainTextBeginIndex) +
1320
- trigger +
1321
- plainTextRepresentation.slice(currentOpenTag.plainTextBeginIndex);
1322
- }
1323
- if (!currentOpenTag.subTags) {
1324
- plainTextRepresentation += currentOpenTag.content;
1325
- }
1326
- else if (currentOpenTag.subTags.length > 0) {
1327
- // Add text after the last tag
1328
- const lastSubTag = currentOpenTag.subTags[currentOpenTag.subTags.length - 1];
1329
- const startOfRemainingText = ((_a = lastSubTag.closeTagIdx) !== null && _a !== void 0 ? _a : lastSubTag.openTagIdx) + lastSubTag.tagType.length + 3;
1330
- const trailingText = currentOpenTag.content.substring(startOfRemainingText);
1331
- plainTextRepresentation += trailingText;
1332
- }
1333
- currentOpenTag.plainTextEndIndex = plainTextRepresentation.length;
1334
- addTag(currentOpenTag, tagParseStack, tags);
1335
- }
1336
- else {
1337
- console.error('Unexpected close tag found. Got "' +
1338
- closeTagType +
1339
- '" but expected "' +
1340
- ((_b = tagParseStack[tagParseStack.length - 1]) === null || _b === void 0 ? void 0 : _b.tagType) +
1341
- '"');
1342
- }
1343
- }
1344
- // Update parsing index; move past the end of the close tag
1345
- parseIndex = foundHtmlTag.startIdx + foundHtmlTag.content.length;
1346
- } // While parseIndex < text.length loop
1347
- return { tags, plainText: plainTextRepresentation };
1348
- };
1349
- /* @conditional-compile-remove(mention) */
1350
- const parseOpenTag = (tag, startIdx) => {
1351
- const tagType = tag
1352
- .substring(1, tag.length - 1)
1353
- .split(' ')[0]
1354
- .toLowerCase()
1355
- .replace('/', '');
1356
- return {
1357
- tagType,
1358
- openTagIdx: startIdx,
1359
- openTagBody: tag
1360
- };
1361
- };
1362
- /* @conditional-compile-remove(mention) */
1363
- const findNextHtmlTag = (text, startIndex) => {
1364
- const tagStartIndex = text.indexOf('<', startIndex);
1365
- if (tagStartIndex === -1) {
1366
- // No more tags
1367
- return undefined;
1368
- }
1369
- const tagEndIndex = text.indexOf('>', tagStartIndex);
1370
- if (tagEndIndex === -1) {
1371
- // No close tag
1372
- return undefined;
1373
- }
1374
- const tag = text.substring(tagStartIndex, tagEndIndex + 1);
1375
- let type = 'open';
1376
- if (tag[1] === '/') {
1377
- type = 'close';
1378
- }
1379
- else if (tag[tag.length - 2] === '/') {
1380
- type = 'self-closing';
1381
- }
1382
- return {
1383
- content: tag,
1384
- startIdx: tagStartIndex,
1385
- type
1386
- };
1387
- };
1388
- /* @conditional-compile-remove(mention) */
1389
- const addTag = (tag, parseStack, tags) => {
1390
- var _a;
1391
- // Add as sub-tag to the parent stack tag, if there is one
1392
- const parentTag = parseStack[parseStack.length - 1];
1393
- if (parentTag) {
1394
- // Adjust the open tag index to be relative to the parent tag
1395
- const parentContentStartIdx = parentTag.openTagIdx + parentTag.openTagBody.length;
1396
- const relativeIdx = tag.openTagIdx - parentContentStartIdx;
1397
- tag.openTagIdx = relativeIdx;
1398
- }
1399
- if (!tag.closeTagIdx) {
1400
- // If the tag is self-closing, the close tag is the same as the open tag
1401
- if (tag.openTagBody[tag.openTagBody.length - 2] === '/') {
1402
- tag.closeTagIdx = tag.openTagIdx;
1403
- }
1404
- else {
1405
- // Otherwise, the close tag index is the open tag index + the open tag body + the content length
1406
- tag.closeTagIdx = tag.openTagIdx + tag.openTagBody.length + ((_a = tag.content) !== null && _a !== void 0 ? _a : []).length;
1407
- }
1408
- }
1409
- // Put the tag where it belongs
1410
- if (!parentTag) {
1411
- tags.push(tag);
1412
- }
1413
- else {
1414
- if (!parentTag.subTags) {
1415
- parentTag.subTags = [tag];
1416
- }
1417
- else {
1418
- parentTag.subTags.push(tag);
1419
- }
1420
- }
1421
- };
1422
105
  //# sourceMappingURL=InputBoxComponent.js.map